Files
znc/src/Modules.cpp
Alexey Sokolov 8de9e376ce Fix remote code execution and privilege escalation vulnerability.
To trigger this, need to have a user already.

Thanks for Jeriko One <jeriko.one@gmx.us> for finding and reporting this.

CVE-2019-12816
2019-06-15 02:00:42 +01:00

2057 lines
68 KiB
C++

/*
* Copyright (C) 2004-2018 ZNC, see the NOTICE file for details.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <znc/Modules.h>
#include <znc/FileUtils.h>
#include <znc/Template.h>
#include <znc/User.h>
#include <znc/Query.h>
#include <znc/IRCNetwork.h>
#include <znc/WebModules.h>
#include <znc/znc.h>
#include <dlfcn.h>
using std::map;
using std::set;
using std::vector;
bool ZNC_NO_NEED_TO_DO_ANYTHING_ON_MODULE_CALL_EXITER;
#ifndef RTLD_LOCAL
#define RTLD_LOCAL 0
#warning "your crap box doesn't define RTLD_LOCAL !?"
#endif
#define MODUNLOADCHK(func) \
for (CModule * pMod : *this) { \
try { \
CClient* pOldClient = pMod->GetClient(); \
pMod->SetClient(m_pClient); \
CUser* pOldUser = nullptr; \
if (m_pUser) { \
pOldUser = pMod->GetUser(); \
pMod->SetUser(m_pUser); \
} \
CIRCNetwork* pNetwork = nullptr; \
if (m_pNetwork) { \
pNetwork = pMod->GetNetwork(); \
pMod->SetNetwork(m_pNetwork); \
} \
pMod->func; \
if (m_pUser) pMod->SetUser(pOldUser); \
if (m_pNetwork) pMod->SetNetwork(pNetwork); \
pMod->SetClient(pOldClient); \
} catch (const CModule::EModException& e) { \
if (e == CModule::UNLOAD) { \
UnloadModule(pMod->GetModName()); \
} \
} \
}
#define MODHALTCHK(func) \
bool bHaltCore = false; \
for (CModule * pMod : *this) { \
try { \
CModule::EModRet e = CModule::CONTINUE; \
CClient* pOldClient = pMod->GetClient(); \
pMod->SetClient(m_pClient); \
CUser* pOldUser = nullptr; \
if (m_pUser) { \
pOldUser = pMod->GetUser(); \
pMod->SetUser(m_pUser); \
} \
CIRCNetwork* pNetwork = nullptr; \
if (m_pNetwork) { \
pNetwork = pMod->GetNetwork(); \
pMod->SetNetwork(m_pNetwork); \
} \
e = pMod->func; \
if (m_pUser) pMod->SetUser(pOldUser); \
if (m_pNetwork) pMod->SetNetwork(pNetwork); \
pMod->SetClient(pOldClient); \
if (e == CModule::HALTMODS) { \
break; \
} else if (e == CModule::HALTCORE) { \
bHaltCore = true; \
} else if (e == CModule::HALT) { \
bHaltCore = true; \
break; \
} \
} catch (const CModule::EModException& e) { \
if (e == CModule::UNLOAD) { \
UnloadModule(pMod->GetModName()); \
} \
} \
} \
return bHaltCore;
/////////////////// Timer ///////////////////
CTimer::CTimer(CModule* pModule, unsigned int uInterval, unsigned int uCycles,
const CString& sLabel, const CString& sDescription)
: CCron(), m_pModule(pModule), m_sDescription(sDescription) {
SetName(sLabel);
// Make integration test faster
char* szDebugTimer = getenv("ZNC_DEBUG_TIMER");
if (szDebugTimer && *szDebugTimer == '1') {
uInterval = std::max(1u, uInterval / 4u);
}
if (uCycles) {
StartMaxCycles(uInterval, uCycles);
} else {
Start(uInterval);
}
}
CTimer::~CTimer() { m_pModule->UnlinkTimer(this); }
void CTimer::SetModule(CModule* p) { m_pModule = p; }
void CTimer::SetDescription(const CString& s) { m_sDescription = s; }
CModule* CTimer::GetModule() const { return m_pModule; }
const CString& CTimer::GetDescription() const { return m_sDescription; }
/////////////////// !Timer ///////////////////
CModule::CModule(ModHandle pDLL, CUser* pUser, CIRCNetwork* pNetwork,
const CString& sModName, const CString& sDataDir,
CModInfo::EModuleType eType)
: m_eType(eType),
m_sDescription(""),
m_sTimers(),
m_sSockets(),
#ifdef HAVE_PTHREAD
m_sJobs(),
#endif
m_pDLL(pDLL),
m_pManager(&(CZNC::Get().GetManager())),
m_pUser(pUser),
m_pNetwork(pNetwork),
m_pClient(nullptr),
m_sModName(sModName),
m_sDataDir(sDataDir),
m_sSavePath(""),
m_sArgs(""),
m_sModPath(""),
m_Translation("znc-" + sModName),
m_mssRegistry(),
m_vSubPages(),
m_mCommands() {
if (m_pNetwork) {
m_sSavePath = m_pNetwork->GetNetworkPath() + "/moddata/" + m_sModName;
} else if (m_pUser) {
m_sSavePath = m_pUser->GetUserPath() + "/moddata/" + m_sModName;
} else {
m_sSavePath = CZNC::Get().GetZNCPath() + "/moddata/" + m_sModName;
}
LoadRegistry();
}
CModule::~CModule() {
while (!m_sTimers.empty()) {
RemTimer(*m_sTimers.begin());
}
while (!m_sSockets.empty()) {
RemSocket(*m_sSockets.begin());
}
SaveRegistry();
#ifdef HAVE_PTHREAD
CancelJobs(m_sJobs);
#endif
}
void CModule::SetUser(CUser* pUser) { m_pUser = pUser; }
void CModule::SetNetwork(CIRCNetwork* pNetwork) { m_pNetwork = pNetwork; }
void CModule::SetClient(CClient* pClient) { m_pClient = pClient; }
CString CModule::ExpandString(const CString& sStr) const {
CString sRet;
return ExpandString(sStr, sRet);
}
CString& CModule::ExpandString(const CString& sStr, CString& sRet) const {
sRet = sStr;
if (m_pNetwork) {
return m_pNetwork->ExpandString(sRet, sRet);
}
if (m_pUser) {
return m_pUser->ExpandString(sRet, sRet);
}
return sRet;
}
const CString& CModule::GetSavePath() const {
if (!CFile::Exists(m_sSavePath)) {
CDir::MakeDir(m_sSavePath);
}
return m_sSavePath;
}
CString CModule::GetWebPath() {
switch (m_eType) {
case CModInfo::GlobalModule:
return "/mods/global/" + GetModName() + "/";
case CModInfo::UserModule:
return "/mods/user/" + GetModName() + "/";
case CModInfo::NetworkModule:
return "/mods/network/" + m_pNetwork->GetName() + "/" +
GetModName() + "/";
default:
return "/";
}
}
CString CModule::GetWebFilesPath() {
switch (m_eType) {
case CModInfo::GlobalModule:
return "/modfiles/global/" + GetModName() + "/";
case CModInfo::UserModule:
return "/modfiles/user/" + GetModName() + "/";
case CModInfo::NetworkModule:
return "/modfiles/network/" + m_pNetwork->GetName() + "/" +
GetModName() + "/";
default:
return "/";
}
}
bool CModule::LoadRegistry() {
// CString sPrefix = (m_pUser) ? m_pUser->GetUserName() : ".global";
return (m_mssRegistry.ReadFromDisk(GetSavePath() + "/.registry") ==
MCString::MCS_SUCCESS);
}
bool CModule::SaveRegistry() const {
// CString sPrefix = (m_pUser) ? m_pUser->GetUserName() : ".global";
return (m_mssRegistry.WriteToDisk(GetSavePath() + "/.registry", 0600) ==
MCString::MCS_SUCCESS);
}
bool CModule::MoveRegistry(const CString& sPath) {
if (m_sSavePath != sPath) {
CFile fOldNVFile = CFile(m_sSavePath + "/.registry");
if (!fOldNVFile.Exists()) {
return false;
}
if (!CFile::Exists(sPath) && !CDir::MakeDir(sPath)) {
return false;
}
fOldNVFile.Copy(sPath + "/.registry");
m_sSavePath = sPath;
return true;
}
return false;
}
bool CModule::SetNV(const CString& sName, const CString& sValue,
bool bWriteToDisk) {
m_mssRegistry[sName] = sValue;
if (bWriteToDisk) {
return SaveRegistry();
}
return true;
}
CString CModule::GetNV(const CString& sName) const {
MCString::const_iterator it = m_mssRegistry.find(sName);
if (it != m_mssRegistry.end()) {
return it->second;
}
return "";
}
bool CModule::DelNV(const CString& sName, bool bWriteToDisk) {
MCString::iterator it = m_mssRegistry.find(sName);
if (it != m_mssRegistry.end()) {
m_mssRegistry.erase(it);
} else {
return false;
}
if (bWriteToDisk) {
return SaveRegistry();
}
return true;
}
bool CModule::ClearNV(bool bWriteToDisk) {
m_mssRegistry.clear();
if (bWriteToDisk) {
return SaveRegistry();
}
return true;
}
bool CModule::AddTimer(CTimer* pTimer) {
if ((!pTimer) ||
(!pTimer->GetName().empty() && FindTimer(pTimer->GetName()))) {
delete pTimer;
return false;
}
if (!m_sTimers.insert(pTimer).second)
// Was already added
return true;
m_pManager->AddCron(pTimer);
return true;
}
bool CModule::AddTimer(FPTimer_t pFBCallback, const CString& sLabel,
u_int uInterval, u_int uCycles,
const CString& sDescription) {
CFPTimer* pTimer =
new CFPTimer(this, uInterval, uCycles, sLabel, sDescription);
pTimer->SetFPCallback(pFBCallback);
return AddTimer(pTimer);
}
bool CModule::RemTimer(CTimer* pTimer) {
if (m_sTimers.erase(pTimer) == 0) return false;
m_pManager->DelCronByAddr(pTimer);
return true;
}
bool CModule::RemTimer(const CString& sLabel) {
CTimer* pTimer = FindTimer(sLabel);
if (!pTimer) return false;
return RemTimer(pTimer);
}
bool CModule::UnlinkTimer(CTimer* pTimer) { return m_sTimers.erase(pTimer); }
CTimer* CModule::FindTimer(const CString& sLabel) {
if (sLabel.empty()) {
return nullptr;
}
for (CTimer* pTimer : m_sTimers) {
if (pTimer->GetName().Equals(sLabel)) {
return pTimer;
}
}
return nullptr;
}
void CModule::ListTimers() {
if (m_sTimers.empty()) {
PutModule("You have no timers running.");
return;
}
CTable Table;
Table.AddColumn("Name");
Table.AddColumn("Secs");
Table.AddColumn("Cycles");
Table.AddColumn("Description");
for (const CTimer* pTimer : m_sTimers) {
unsigned int uCycles = pTimer->GetCyclesLeft();
timeval Interval = pTimer->GetInterval();
Table.AddRow();
Table.SetCell("Name", pTimer->GetName());
Table.SetCell(
"Secs", CString(Interval.tv_sec) + "seconds" +
(Interval.tv_usec
? " " + CString(Interval.tv_usec) + " microseconds"
: ""));
Table.SetCell("Cycles", ((uCycles) ? CString(uCycles) : "INF"));
Table.SetCell("Description", pTimer->GetDescription());
}
PutModule(Table);
}
bool CModule::AddSocket(CSocket* pSocket) {
if (!pSocket) {
return false;
}
m_sSockets.insert(pSocket);
return true;
}
bool CModule::RemSocket(CSocket* pSocket) {
if (m_sSockets.erase(pSocket)) {
m_pManager->DelSockByAddr(pSocket);
return true;
}
return false;
}
bool CModule::RemSocket(const CString& sSockName) {
for (CSocket* pSocket : m_sSockets) {
if (pSocket->GetSockName().Equals(sSockName)) {
m_sSockets.erase(pSocket);
m_pManager->DelSockByAddr(pSocket);
return true;
}
}
return false;
}
bool CModule::UnlinkSocket(CSocket* pSocket) {
return m_sSockets.erase(pSocket);
}
CSocket* CModule::FindSocket(const CString& sSockName) {
for (CSocket* pSocket : m_sSockets) {
if (pSocket->GetSockName().Equals(sSockName)) {
return pSocket;
}
}
return nullptr;
}
void CModule::ListSockets() {
if (m_sSockets.empty()) {
PutModule("You have no open sockets.");
return;
}
CTable Table;
Table.AddColumn("Name");
Table.AddColumn("State");
Table.AddColumn("LocalPort");
Table.AddColumn("SSL");
Table.AddColumn("RemoteIP");
Table.AddColumn("RemotePort");
for (const CSocket* pSocket : m_sSockets) {
Table.AddRow();
Table.SetCell("Name", pSocket->GetSockName());
if (pSocket->GetType() == CSocket::LISTENER) {
Table.SetCell("State", "Listening");
} else {
Table.SetCell("State", (pSocket->IsConnected() ? "Connected" : ""));
}
Table.SetCell("LocalPort", CString(pSocket->GetLocalPort()));
Table.SetCell("SSL", (pSocket->GetSSL() ? "yes" : "no"));
Table.SetCell("RemoteIP", pSocket->GetRemoteIP());
Table.SetCell("RemotePort", (pSocket->GetRemotePort())
? CString(pSocket->GetRemotePort())
: CString(""));
}
PutModule(Table);
}
#ifdef HAVE_PTHREAD
CModuleJob::~CModuleJob() { m_pModule->UnlinkJob(this); }
void CModule::AddJob(CModuleJob* pJob) {
CThreadPool::Get().addJob(pJob);
m_sJobs.insert(pJob);
}
void CModule::CancelJob(CModuleJob* pJob) {
if (pJob == nullptr) return;
// Destructor calls UnlinkJob and removes the job from m_sJobs
CThreadPool::Get().cancelJob(pJob);
}
bool CModule::CancelJob(const CString& sJobName) {
for (CModuleJob* pJob : m_sJobs) {
if (pJob->GetName().Equals(sJobName)) {
CancelJob(pJob);
return true;
}
}
return false;
}
void CModule::CancelJobs(const std::set<CModuleJob*>& sJobs) {
set<CJob*> sPlainJobs(sJobs.begin(), sJobs.end());
// Destructor calls UnlinkJob and removes the jobs from m_sJobs
CThreadPool::Get().cancelJobs(sPlainJobs);
}
bool CModule::UnlinkJob(CModuleJob* pJob) { return 0 != m_sJobs.erase(pJob); }
#endif
bool CModule::AddCommand(const CModCommand& Command) {
if (Command.GetFunction() == nullptr) return false;
if (Command.GetCommand().Contains(" ")) return false;
if (FindCommand(Command.GetCommand()) != nullptr) return false;
m_mCommands[Command.GetCommand()] = Command;
return true;
}
bool CModule::AddCommand(const CString& sCmd, CModCommand::ModCmdFunc func,
const CString& sArgs, const CString& sDesc) {
CModCommand cmd(sCmd, this, func, sArgs, sDesc);
return AddCommand(cmd);
}
bool CModule::AddCommand(const CString& sCmd, const COptionalTranslation& Args,
const COptionalTranslation& Desc,
std::function<void(const CString& sLine)> func) {
CModCommand cmd(sCmd, std::move(func), Args, Desc);
return AddCommand(std::move(cmd));
}
void CModule::AddHelpCommand() {
AddCommand("Help", t_d("<search>", "modhelpcmd"),
t_d("Generate this output", "modhelpcmd"),
[=](const CString& sLine) { HandleHelpCommand(sLine); });
}
bool CModule::RemCommand(const CString& sCmd) {
return m_mCommands.erase(sCmd) > 0;
}
const CModCommand* CModule::FindCommand(const CString& sCmd) const {
for (const auto& it : m_mCommands) {
if (!it.first.Equals(sCmd)) continue;
return &it.second;
}
return nullptr;
}
bool CModule::HandleCommand(const CString& sLine) {
const CString& sCmd = sLine.Token(0);
const CModCommand* pCmd = FindCommand(sCmd);
if (pCmd) {
pCmd->Call(sLine);
return true;
}
OnUnknownModCommand(sLine);
return false;
}
void CModule::HandleHelpCommand(const CString& sLine) {
CString sFilter = sLine.Token(1).AsLower();
CTable Table;
CModCommand::InitHelp(Table);
for (const auto& it : m_mCommands) {
CString sCmd = it.second.GetCommand().AsLower();
if (sFilter.empty() ||
(sCmd.StartsWith(sFilter, CString::CaseSensitive)) ||
sCmd.WildCmp(sFilter)) {
it.second.AddHelp(Table);
}
}
if (Table.empty()) {
PutModule(t_f("No matches for '{1}'")(sFilter));
} else {
PutModule(Table);
}
}
CString CModule::GetModNick() const {
return ((m_pUser) ? m_pUser->GetStatusPrefix() : "*") + m_sModName;
}
// Webmods
bool CModule::OnWebPreRequest(CWebSock& WebSock, const CString& sPageName) {
return false;
}
bool CModule::OnWebRequest(CWebSock& WebSock, const CString& sPageName,
CTemplate& Tmpl) {
return false;
}
bool CModule::ValidateWebRequestCSRFCheck(CWebSock& WebSock,
const CString& sPageName) {
return WebSock.ValidateCSRFCheck(WebSock.GetURI());
}
bool CModule::OnEmbeddedWebRequest(CWebSock& WebSock, const CString& sPageName,
CTemplate& Tmpl) {
return false;
}
// !Webmods
bool CModule::OnLoad(const CString& sArgs, CString& sMessage) {
sMessage = "";
return true;
}
bool CModule::OnBoot() { return true; }
void CModule::OnPreRehash() {}
void CModule::OnPostRehash() {}
void CModule::OnIRCDisconnected() {}
void CModule::OnIRCConnected() {}
CModule::EModRet CModule::OnIRCConnecting(CIRCSock* IRCSock) {
return CONTINUE;
}
void CModule::OnIRCConnectionError(CIRCSock* IRCSock) {}
CModule::EModRet CModule::OnIRCRegistration(CString& sPass, CString& sNick,
CString& sIdent,
CString& sRealName) {
return CONTINUE;
}
CModule::EModRet CModule::OnBroadcast(CString& sMessage) { return CONTINUE; }
void CModule::OnChanPermission3(const CNick* pOpNick, const CNick& Nick,
CChan& Channel, char cMode,
bool bAdded, bool bNoChange) {
OnChanPermission2(pOpNick, Nick, Channel, cMode, bAdded, bNoChange);
}
void CModule::OnChanPermission2(const CNick* pOpNick, const CNick& Nick,
CChan& Channel, unsigned char uMode,
bool bAdded, bool bNoChange) {
if (pOpNick)
OnChanPermission(*pOpNick, Nick, Channel, uMode, bAdded, bNoChange);
}
void CModule::OnOp2(const CNick* pOpNick, const CNick& Nick, CChan& Channel,
bool bNoChange) {
if (pOpNick) OnOp(*pOpNick, Nick, Channel, bNoChange);
}
void CModule::OnDeop2(const CNick* pOpNick, const CNick& Nick, CChan& Channel,
bool bNoChange) {
if (pOpNick) OnDeop(*pOpNick, Nick, Channel, bNoChange);
}
void CModule::OnVoice2(const CNick* pOpNick, const CNick& Nick, CChan& Channel,
bool bNoChange) {
if (pOpNick) OnVoice(*pOpNick, Nick, Channel, bNoChange);
}
void CModule::OnDevoice2(const CNick* pOpNick, const CNick& Nick,
CChan& Channel, bool bNoChange) {
if (pOpNick) OnDevoice(*pOpNick, Nick, Channel, bNoChange);
}
void CModule::OnRawMode2(const CNick* pOpNick, CChan& Channel,
const CString& sModes, const CString& sArgs) {
if (pOpNick) OnRawMode(*pOpNick, Channel, sModes, sArgs);
}
void CModule::OnMode2(const CNick* pOpNick, CChan& Channel, char uMode,
const CString& sArg, bool bAdded, bool bNoChange) {
if (pOpNick) OnMode(*pOpNick, Channel, uMode, sArg, bAdded, bNoChange);
}
void CModule::OnChanPermission(const CNick& pOpNick, const CNick& Nick,
CChan& Channel, unsigned char uMode, bool bAdded,
bool bNoChange) {}
void CModule::OnOp(const CNick& pOpNick, const CNick& Nick, CChan& Channel,
bool bNoChange) {}
void CModule::OnDeop(const CNick& pOpNick, const CNick& Nick, CChan& Channel,
bool bNoChange) {}
void CModule::OnVoice(const CNick& pOpNick, const CNick& Nick, CChan& Channel,
bool bNoChange) {}
void CModule::OnDevoice(const CNick& pOpNick, const CNick& Nick, CChan& Channel,
bool bNoChange) {}
void CModule::OnRawMode(const CNick& pOpNick, CChan& Channel,
const CString& sModes, const CString& sArgs) {}
void CModule::OnMode(const CNick& pOpNick, CChan& Channel, char uMode,
const CString& sArg, bool bAdded, bool bNoChange) {}
CModule::EModRet CModule::OnRaw(CString& sLine) { return CONTINUE; }
CModule::EModRet CModule::OnRawMessage(CMessage& Message) { return CONTINUE; }
CModule::EModRet CModule::OnNumericMessage(CNumericMessage& Message) {
return CONTINUE;
}
CModule::EModRet CModule::OnStatusCommand(CString& sCommand) {
return CONTINUE;
}
void CModule::OnModNotice(const CString& sMessage) {}
void CModule::OnModCTCP(const CString& sMessage) {}
void CModule::OnModCommand(const CString& sCommand) { HandleCommand(sCommand); }
void CModule::OnUnknownModCommand(const CString& sLine) {
if (m_mCommands.empty())
// This function is only called if OnModCommand wasn't
// overriden, so no false warnings for modules which don't use
// CModCommand for command handling.
PutModule(t_s("This module doesn't implement any commands."));
else
PutModule(t_s("Unknown command!"));
}
void CModule::OnQuit(const CNick& Nick, const CString& sMessage,
const vector<CChan*>& vChans) {}
void CModule::OnQuitMessage(CQuitMessage& Message,
const vector<CChan*>& vChans) {
OnQuit(Message.GetNick(), Message.GetReason(), vChans);
}
void CModule::OnNick(const CNick& Nick, const CString& sNewNick,
const vector<CChan*>& vChans) {}
void CModule::OnNickMessage(CNickMessage& Message,
const vector<CChan*>& vChans) {
OnNick(Message.GetNick(), Message.GetNewNick(), vChans);
}
void CModule::OnKick(const CNick& Nick, const CString& sKickedNick,
CChan& Channel, const CString& sMessage) {}
void CModule::OnKickMessage(CKickMessage& Message) {
OnKick(Message.GetNick(), Message.GetKickedNick(), *Message.GetChan(),
Message.GetReason());
}
CModule::EModRet CModule::OnJoining(CChan& Channel) { return CONTINUE; }
void CModule::OnJoin(const CNick& Nick, CChan& Channel) {}
void CModule::OnJoinMessage(CJoinMessage& Message) {
OnJoin(Message.GetNick(), *Message.GetChan());
}
void CModule::OnPart(const CNick& Nick, CChan& Channel,
const CString& sMessage) {}
void CModule::OnPartMessage(CPartMessage& Message) {
OnPart(Message.GetNick(), *Message.GetChan(), Message.GetReason());
}
CModule::EModRet CModule::OnInvite(const CNick& Nick, const CString& sChan) {
return CONTINUE;
}
CModule::EModRet CModule::OnChanBufferStarting(CChan& Chan, CClient& Client) {
return CONTINUE;
}
CModule::EModRet CModule::OnChanBufferEnding(CChan& Chan, CClient& Client) {
return CONTINUE;
}
CModule::EModRet CModule::OnChanBufferPlayLine(CChan& Chan, CClient& Client,
CString& sLine) {
return CONTINUE;
}
CModule::EModRet CModule::OnPrivBufferStarting(CQuery& Query, CClient& Client) {
return CONTINUE;
}
CModule::EModRet CModule::OnPrivBufferEnding(CQuery& Query, CClient& Client) {
return CONTINUE;
}
CModule::EModRet CModule::OnPrivBufferPlayLine(CClient& Client,
CString& sLine) {
return CONTINUE;
}
CModule::EModRet CModule::OnChanBufferPlayLine2(CChan& Chan, CClient& Client,
CString& sLine,
const timeval& tv) {
return OnChanBufferPlayLine(Chan, Client, sLine);
}
CModule::EModRet CModule::OnPrivBufferPlayLine2(CClient& Client, CString& sLine,
const timeval& tv) {
return OnPrivBufferPlayLine(Client, sLine);
}
CModule::EModRet CModule::OnChanBufferPlayMessage(CMessage& Message) {
CString sOriginal, sModified;
sOriginal = sModified = Message.ToString(CMessage::ExcludeTags);
EModRet ret = OnChanBufferPlayLine2(
*Message.GetChan(), *Message.GetClient(), sModified, Message.GetTime());
if (sOriginal != sModified) {
Message.Parse(sModified);
}
return ret;
}
CModule::EModRet CModule::OnPrivBufferPlayMessage(CMessage& Message) {
CString sOriginal, sModified;
sOriginal = sModified = Message.ToString(CMessage::ExcludeTags);
EModRet ret = OnPrivBufferPlayLine2(*Message.GetClient(), sModified,
Message.GetTime());
if (sOriginal != sModified) {
Message.Parse(sModified);
}
return ret;
}
void CModule::OnClientLogin() {}
void CModule::OnClientDisconnect() {}
CModule::EModRet CModule::OnUserRaw(CString& sLine) { return CONTINUE; }
CModule::EModRet CModule::OnUserRawMessage(CMessage& Message) {
return CONTINUE;
}
CModule::EModRet CModule::OnUserCTCPReply(CString& sTarget, CString& sMessage) {
return CONTINUE;
}
CModule::EModRet CModule::OnUserCTCPReplyMessage(CCTCPMessage& Message) {
CString sTarget = Message.GetTarget();
CString sText = Message.GetText();
EModRet ret = OnUserCTCPReply(sTarget, sText);
Message.SetTarget(sTarget);
Message.SetText(sText);
return ret;
}
CModule::EModRet CModule::OnUserCTCP(CString& sTarget, CString& sMessage) {
return CONTINUE;
}
CModule::EModRet CModule::OnUserCTCPMessage(CCTCPMessage& Message) {
CString sTarget = Message.GetTarget();
CString sText = Message.GetText();
EModRet ret = OnUserCTCP(sTarget, sText);
Message.SetTarget(sTarget);
Message.SetText(sText);
return ret;
}
CModule::EModRet CModule::OnUserAction(CString& sTarget, CString& sMessage) {
return CONTINUE;
}
CModule::EModRet CModule::OnUserActionMessage(CActionMessage& Message) {
CString sTarget = Message.GetTarget();
CString sText = Message.GetText();
EModRet ret = OnUserAction(sTarget, sText);
Message.SetTarget(sTarget);
Message.SetText(sText);
return ret;
}
CModule::EModRet CModule::OnUserMsg(CString& sTarget, CString& sMessage) {
return CONTINUE;
}
CModule::EModRet CModule::OnUserTextMessage(CTextMessage& Message) {
CString sTarget = Message.GetTarget();
CString sText = Message.GetText();
EModRet ret = OnUserMsg(sTarget, sText);
Message.SetTarget(sTarget);
Message.SetText(sText);
return ret;
}
CModule::EModRet CModule::OnUserNotice(CString& sTarget, CString& sMessage) {
return CONTINUE;
}
CModule::EModRet CModule::OnUserNoticeMessage(CNoticeMessage& Message) {
CString sTarget = Message.GetTarget();
CString sText = Message.GetText();
EModRet ret = OnUserNotice(sTarget, sText);
Message.SetTarget(sTarget);
Message.SetText(sText);
return ret;
}
CModule::EModRet CModule::OnUserJoin(CString& sChannel, CString& sKey) {
return CONTINUE;
}
CModule::EModRet CModule::OnUserJoinMessage(CJoinMessage& Message) {
CString sChan = Message.GetTarget();
CString sKey = Message.GetKey();
EModRet ret = OnUserJoin(sChan, sKey);
Message.SetTarget(sChan);
Message.SetKey(sKey);
return ret;
}
CModule::EModRet CModule::OnUserPart(CString& sChannel, CString& sMessage) {
return CONTINUE;
}
CModule::EModRet CModule::OnUserPartMessage(CPartMessage& Message) {
CString sChan = Message.GetTarget();
CString sReason = Message.GetReason();
EModRet ret = OnUserPart(sChan, sReason);
Message.SetTarget(sChan);
Message.SetReason(sReason);
return ret;
}
CModule::EModRet CModule::OnUserTopic(CString& sChannel, CString& sTopic) {
return CONTINUE;
}
CModule::EModRet CModule::OnUserTopicMessage(CTopicMessage& Message) {
CString sChan = Message.GetTarget();
CString sTopic = Message.GetTopic();
EModRet ret = OnUserTopic(sChan, sTopic);
Message.SetTarget(sChan);
Message.SetTopic(sTopic);
return ret;
}
CModule::EModRet CModule::OnUserTopicRequest(CString& sChannel) {
return CONTINUE;
}
CModule::EModRet CModule::OnUserQuit(CString& sMessage) { return CONTINUE; }
CModule::EModRet CModule::OnUserQuitMessage(CQuitMessage& Message) {
CString sReason = Message.GetReason();
EModRet ret = OnUserQuit(sReason);
Message.SetReason(sReason);
return ret;
}
CModule::EModRet CModule::OnCTCPReply(CNick& Nick, CString& sMessage) {
return CONTINUE;
}
CModule::EModRet CModule::OnCTCPReplyMessage(CCTCPMessage& Message) {
CString sText = Message.GetText();
EModRet ret = OnCTCPReply(Message.GetNick(), sText);
Message.SetText(sText);
return ret;
}
CModule::EModRet CModule::OnPrivCTCP(CNick& Nick, CString& sMessage) {
return CONTINUE;
}
CModule::EModRet CModule::OnPrivCTCPMessage(CCTCPMessage& Message) {
CString sText = Message.GetText();
EModRet ret = OnPrivCTCP(Message.GetNick(), sText);
Message.SetText(sText);
return ret;
}
CModule::EModRet CModule::OnChanCTCP(CNick& Nick, CChan& Channel,
CString& sMessage) {
return CONTINUE;
}
CModule::EModRet CModule::OnChanCTCPMessage(CCTCPMessage& Message) {
CString sText = Message.GetText();
EModRet ret = OnChanCTCP(Message.GetNick(), *Message.GetChan(), sText);
Message.SetText(sText);
return ret;
}
CModule::EModRet CModule::OnPrivAction(CNick& Nick, CString& sMessage) {
return CONTINUE;
}
CModule::EModRet CModule::OnPrivActionMessage(CActionMessage& Message) {
CString sText = Message.GetText();
EModRet ret = OnPrivAction(Message.GetNick(), sText);
Message.SetText(sText);
return ret;
}
CModule::EModRet CModule::OnChanAction(CNick& Nick, CChan& Channel,
CString& sMessage) {
return CONTINUE;
}
CModule::EModRet CModule::OnChanActionMessage(CActionMessage& Message) {
CString sText = Message.GetText();
EModRet ret = OnChanAction(Message.GetNick(), *Message.GetChan(), sText);
Message.SetText(sText);
return ret;
}
CModule::EModRet CModule::OnPrivMsg(CNick& Nick, CString& sMessage) {
return CONTINUE;
}
CModule::EModRet CModule::OnPrivTextMessage(CTextMessage& Message) {
CString sText = Message.GetText();
EModRet ret = OnPrivMsg(Message.GetNick(), sText);
Message.SetText(sText);
return ret;
}
CModule::EModRet CModule::OnChanMsg(CNick& Nick, CChan& Channel,
CString& sMessage) {
return CONTINUE;
}
CModule::EModRet CModule::OnChanTextMessage(CTextMessage& Message) {
CString sText = Message.GetText();
EModRet ret = OnChanMsg(Message.GetNick(), *Message.GetChan(), sText);
Message.SetText(sText);
return ret;
}
CModule::EModRet CModule::OnPrivNotice(CNick& Nick, CString& sMessage) {
return CONTINUE;
}
CModule::EModRet CModule::OnPrivNoticeMessage(CNoticeMessage& Message) {
CString sText = Message.GetText();
EModRet ret = OnPrivNotice(Message.GetNick(), sText);
Message.SetText(sText);
return ret;
}
CModule::EModRet CModule::OnChanNotice(CNick& Nick, CChan& Channel,
CString& sMessage) {
return CONTINUE;
}
CModule::EModRet CModule::OnChanNoticeMessage(CNoticeMessage& Message) {
CString sText = Message.GetText();
EModRet ret = OnChanNotice(Message.GetNick(), *Message.GetChan(), sText);
Message.SetText(sText);
return ret;
}
CModule::EModRet CModule::OnTopic(CNick& Nick, CChan& Channel,
CString& sTopic) {
return CONTINUE;
}
CModule::EModRet CModule::OnTopicMessage(CTopicMessage& Message) {
CString sTopic = Message.GetTopic();
EModRet ret = OnTopic(Message.GetNick(), *Message.GetChan(), sTopic);
Message.SetTopic(sTopic);
return ret;
}
CModule::EModRet CModule::OnTimerAutoJoin(CChan& Channel) { return CONTINUE; }
CModule::EModRet CModule::OnAddNetwork(CIRCNetwork& Network,
CString& sErrorRet) {
return CONTINUE;
}
CModule::EModRet CModule::OnDeleteNetwork(CIRCNetwork& Network) {
return CONTINUE;
}
CModule::EModRet CModule::OnSendToClient(CString& sLine, CClient& Client) {
return CONTINUE;
}
CModule::EModRet CModule::OnSendToClientMessage(CMessage& Message) {
return CONTINUE;
}
CModule::EModRet CModule::OnSendToIRC(CString& sLine) { return CONTINUE; }
CModule::EModRet CModule::OnSendToIRCMessage(CMessage& Message) {
return CONTINUE;
}
bool CModule::OnServerCapAvailable(const CString& sCap) { return false; }
void CModule::OnServerCapResult(const CString& sCap, bool bSuccess) {}
bool CModule::PutIRC(const CString& sLine) {
return m_pNetwork ? m_pNetwork->PutIRC(sLine) : false;
}
bool CModule::PutIRC(const CMessage& Message) {
return m_pNetwork ? m_pNetwork->PutIRC(Message) : false;
}
bool CModule::PutUser(const CString& sLine) {
return m_pNetwork ? m_pNetwork->PutUser(sLine, m_pClient) : false;
}
bool CModule::PutStatus(const CString& sLine) {
return m_pNetwork ? m_pNetwork->PutStatus(sLine, m_pClient) : false;
}
unsigned int CModule::PutModule(const CTable& table) {
if (!m_pUser) return 0;
unsigned int idx = 0;
CString sLine;
while (table.GetLine(idx++, sLine)) PutModule(sLine);
return idx - 1;
}
bool CModule::PutModule(const CString& sLine) {
if (m_pClient) {
m_pClient->PutModule(GetModName(), sLine);
return true;
}
if (m_pNetwork) {
return m_pNetwork->PutModule(GetModName(), sLine);
}
if (m_pUser) {
return m_pUser->PutModule(GetModName(), sLine);
}
return false;
}
bool CModule::PutModNotice(const CString& sLine) {
if (!m_pUser) return false;
if (m_pClient) {
m_pClient->PutModNotice(GetModName(), sLine);
return true;
}
return m_pUser->PutModNotice(GetModName(), sLine);
}
///////////////////
// Global Module //
///////////////////
CModule::EModRet CModule::OnAddUser(CUser& User, CString& sErrorRet) {
return CONTINUE;
}
CModule::EModRet CModule::OnDeleteUser(CUser& User) { return CONTINUE; }
void CModule::OnClientConnect(CZNCSock* pClient, const CString& sHost,
unsigned short uPort) {}
CModule::EModRet CModule::OnLoginAttempt(std::shared_ptr<CAuthBase> Auth) {
return CONTINUE;
}
void CModule::OnFailedLogin(const CString& sUsername,
const CString& sRemoteIP) {}
CModule::EModRet CModule::OnUnknownUserRaw(CClient* pClient, CString& sLine) {
return CONTINUE;
}
CModule::EModRet CModule::OnUnknownUserRawMessage(CMessage& Message) {
return CONTINUE;
}
void CModule::OnClientCapLs(CClient* pClient, SCString& ssCaps) {}
bool CModule::IsClientCapSupported(CClient* pClient, const CString& sCap,
bool bState) {
return false;
}
void CModule::OnClientCapRequest(CClient* pClient, const CString& sCap,
bool bState) {}
CModule::EModRet CModule::OnModuleLoading(const CString& sModName,
const CString& sArgs,
CModInfo::EModuleType eType,
bool& bSuccess, CString& sRetMsg) {
return CONTINUE;
}
CModule::EModRet CModule::OnModuleUnloading(CModule* pModule, bool& bSuccess,
CString& sRetMsg) {
return CONTINUE;
}
CModule::EModRet CModule::OnGetModInfo(CModInfo& ModInfo,
const CString& sModule, bool& bSuccess,
CString& sRetMsg) {
return CONTINUE;
}
void CModule::OnGetAvailableMods(set<CModInfo>& ssMods,
CModInfo::EModuleType eType) {}
CModules::CModules()
: m_pUser(nullptr), m_pNetwork(nullptr), m_pClient(nullptr) {}
CModules::~CModules() { UnloadAll(); }
void CModules::UnloadAll() {
while (size()) {
CString sRetMsg;
CString sModName = back()->GetModName();
UnloadModule(sModName, sRetMsg);
}
}
bool CModules::OnBoot() {
for (CModule* pMod : *this) {
try {
if (!pMod->OnBoot()) {
return true;
}
} catch (const CModule::EModException& e) {
if (e == CModule::UNLOAD) {
UnloadModule(pMod->GetModName());
}
}
}
return false;
}
bool CModules::OnPreRehash() {
MODUNLOADCHK(OnPreRehash());
return false;
}
bool CModules::OnPostRehash() {
MODUNLOADCHK(OnPostRehash());
return false;
}
bool CModules::OnIRCConnected() {
MODUNLOADCHK(OnIRCConnected());
return false;
}
bool CModules::OnIRCConnecting(CIRCSock* pIRCSock) {
MODHALTCHK(OnIRCConnecting(pIRCSock));
}
bool CModules::OnIRCConnectionError(CIRCSock* pIRCSock) {
MODUNLOADCHK(OnIRCConnectionError(pIRCSock));
return false;
}
bool CModules::OnIRCRegistration(CString& sPass, CString& sNick,
CString& sIdent, CString& sRealName) {
MODHALTCHK(OnIRCRegistration(sPass, sNick, sIdent, sRealName));
}
bool CModules::OnBroadcast(CString& sMessage) {
MODHALTCHK(OnBroadcast(sMessage));
}
bool CModules::OnIRCDisconnected() {
MODUNLOADCHK(OnIRCDisconnected());
return false;
}
bool CModules::OnChanPermission3(const CNick* pOpNick, const CNick& Nick,
CChan& Channel, char cMode,
bool bAdded, bool bNoChange) {
MODUNLOADCHK(
OnChanPermission3(pOpNick, Nick, Channel, cMode, bAdded, bNoChange));
return false;
}
bool CModules::OnChanPermission2(const CNick* pOpNick, const CNick& Nick,
CChan& Channel, unsigned char uMode,
bool bAdded, bool bNoChange) {
MODUNLOADCHK(
OnChanPermission2(pOpNick, Nick, Channel, uMode, bAdded, bNoChange));
return false;
}
bool CModules::OnChanPermission(const CNick& OpNick, const CNick& Nick,
CChan& Channel, unsigned char uMode,
bool bAdded, bool bNoChange) {
MODUNLOADCHK(
OnChanPermission(OpNick, Nick, Channel, uMode, bAdded, bNoChange));
return false;
}
bool CModules::OnOp2(const CNick* pOpNick, const CNick& Nick, CChan& Channel,
bool bNoChange) {
MODUNLOADCHK(OnOp2(pOpNick, Nick, Channel, bNoChange));
return false;
}
bool CModules::OnOp(const CNick& OpNick, const CNick& Nick, CChan& Channel,
bool bNoChange) {
MODUNLOADCHK(OnOp(OpNick, Nick, Channel, bNoChange));
return false;
}
bool CModules::OnDeop2(const CNick* pOpNick, const CNick& Nick, CChan& Channel,
bool bNoChange) {
MODUNLOADCHK(OnDeop2(pOpNick, Nick, Channel, bNoChange));
return false;
}
bool CModules::OnDeop(const CNick& OpNick, const CNick& Nick, CChan& Channel,
bool bNoChange) {
MODUNLOADCHK(OnDeop(OpNick, Nick, Channel, bNoChange));
return false;
}
bool CModules::OnVoice2(const CNick* pOpNick, const CNick& Nick, CChan& Channel,
bool bNoChange) {
MODUNLOADCHK(OnVoice2(pOpNick, Nick, Channel, bNoChange));
return false;
}
bool CModules::OnVoice(const CNick& OpNick, const CNick& Nick, CChan& Channel,
bool bNoChange) {
MODUNLOADCHK(OnVoice(OpNick, Nick, Channel, bNoChange));
return false;
}
bool CModules::OnDevoice2(const CNick* pOpNick, const CNick& Nick,
CChan& Channel, bool bNoChange) {
MODUNLOADCHK(OnDevoice2(pOpNick, Nick, Channel, bNoChange));
return false;
}
bool CModules::OnDevoice(const CNick& OpNick, const CNick& Nick, CChan& Channel,
bool bNoChange) {
MODUNLOADCHK(OnDevoice(OpNick, Nick, Channel, bNoChange));
return false;
}
bool CModules::OnRawMode2(const CNick* pOpNick, CChan& Channel,
const CString& sModes, const CString& sArgs) {
MODUNLOADCHK(OnRawMode2(pOpNick, Channel, sModes, sArgs));
return false;
}
bool CModules::OnRawMode(const CNick& OpNick, CChan& Channel,
const CString& sModes, const CString& sArgs) {
MODUNLOADCHK(OnRawMode(OpNick, Channel, sModes, sArgs));
return false;
}
bool CModules::OnMode2(const CNick* pOpNick, CChan& Channel, char uMode,
const CString& sArg, bool bAdded, bool bNoChange) {
MODUNLOADCHK(OnMode2(pOpNick, Channel, uMode, sArg, bAdded, bNoChange));
return false;
}
bool CModules::OnMode(const CNick& OpNick, CChan& Channel, char uMode,
const CString& sArg, bool bAdded, bool bNoChange) {
MODUNLOADCHK(OnMode(OpNick, Channel, uMode, sArg, bAdded, bNoChange));
return false;
}
bool CModules::OnRaw(CString& sLine) { MODHALTCHK(OnRaw(sLine)); }
bool CModules::OnRawMessage(CMessage& Message) {
MODHALTCHK(OnRawMessage(Message));
}
bool CModules::OnNumericMessage(CNumericMessage& Message) {
MODHALTCHK(OnNumericMessage(Message));
}
bool CModules::OnClientLogin() {
MODUNLOADCHK(OnClientLogin());
return false;
}
bool CModules::OnClientDisconnect() {
MODUNLOADCHK(OnClientDisconnect());
return false;
}
bool CModules::OnUserRaw(CString& sLine) { MODHALTCHK(OnUserRaw(sLine)); }
bool CModules::OnUserRawMessage(CMessage& Message) {
MODHALTCHK(OnUserRawMessage(Message));
}
bool CModules::OnUserCTCPReply(CString& sTarget, CString& sMessage) {
MODHALTCHK(OnUserCTCPReply(sTarget, sMessage));
}
bool CModules::OnUserCTCPReplyMessage(CCTCPMessage& Message) {
MODHALTCHK(OnUserCTCPReplyMessage(Message));
}
bool CModules::OnUserCTCP(CString& sTarget, CString& sMessage) {
MODHALTCHK(OnUserCTCP(sTarget, sMessage));
}
bool CModules::OnUserCTCPMessage(CCTCPMessage& Message) {
MODHALTCHK(OnUserCTCPMessage(Message));
}
bool CModules::OnUserAction(CString& sTarget, CString& sMessage) {
MODHALTCHK(OnUserAction(sTarget, sMessage));
}
bool CModules::OnUserActionMessage(CActionMessage& Message) {
MODHALTCHK(OnUserActionMessage(Message));
}
bool CModules::OnUserMsg(CString& sTarget, CString& sMessage) {
MODHALTCHK(OnUserMsg(sTarget, sMessage));
}
bool CModules::OnUserTextMessage(CTextMessage& Message) {
MODHALTCHK(OnUserTextMessage(Message));
}
bool CModules::OnUserNotice(CString& sTarget, CString& sMessage) {
MODHALTCHK(OnUserNotice(sTarget, sMessage));
}
bool CModules::OnUserNoticeMessage(CNoticeMessage& Message) {
MODHALTCHK(OnUserNoticeMessage(Message));
}
bool CModules::OnUserJoin(CString& sChannel, CString& sKey) {
MODHALTCHK(OnUserJoin(sChannel, sKey));
}
bool CModules::OnUserJoinMessage(CJoinMessage& Message) {
MODHALTCHK(OnUserJoinMessage(Message));
}
bool CModules::OnUserPart(CString& sChannel, CString& sMessage) {
MODHALTCHK(OnUserPart(sChannel, sMessage));
}
bool CModules::OnUserPartMessage(CPartMessage& Message) {
MODHALTCHK(OnUserPartMessage(Message));
}
bool CModules::OnUserTopic(CString& sChannel, CString& sTopic) {
MODHALTCHK(OnUserTopic(sChannel, sTopic));
}
bool CModules::OnUserTopicMessage(CTopicMessage& Message) {
MODHALTCHK(OnUserTopicMessage(Message));
}
bool CModules::OnUserTopicRequest(CString& sChannel) {
MODHALTCHK(OnUserTopicRequest(sChannel));
}
bool CModules::OnUserQuit(CString& sMessage) {
MODHALTCHK(OnUserQuit(sMessage));
}
bool CModules::OnUserQuitMessage(CQuitMessage& Message) {
MODHALTCHK(OnUserQuitMessage(Message));
}
bool CModules::OnQuit(const CNick& Nick, const CString& sMessage,
const vector<CChan*>& vChans) {
MODUNLOADCHK(OnQuit(Nick, sMessage, vChans));
return false;
}
bool CModules::OnQuitMessage(CQuitMessage& Message,
const vector<CChan*>& vChans) {
MODUNLOADCHK(OnQuitMessage(Message, vChans));
return false;
}
bool CModules::OnNick(const CNick& Nick, const CString& sNewNick,
const vector<CChan*>& vChans) {
MODUNLOADCHK(OnNick(Nick, sNewNick, vChans));
return false;
}
bool CModules::OnNickMessage(CNickMessage& Message,
const vector<CChan*>& vChans) {
MODUNLOADCHK(OnNickMessage(Message, vChans));
return false;
}
bool CModules::OnKick(const CNick& Nick, const CString& sKickedNick,
CChan& Channel, const CString& sMessage) {
MODUNLOADCHK(OnKick(Nick, sKickedNick, Channel, sMessage));
return false;
}
bool CModules::OnKickMessage(CKickMessage& Message) {
MODUNLOADCHK(OnKickMessage(Message));
return false;
}
bool CModules::OnJoining(CChan& Channel) { MODHALTCHK(OnJoining(Channel)); }
bool CModules::OnJoin(const CNick& Nick, CChan& Channel) {
MODUNLOADCHK(OnJoin(Nick, Channel));
return false;
}
bool CModules::OnJoinMessage(CJoinMessage& Message) {
MODUNLOADCHK(OnJoinMessage(Message));
return false;
}
bool CModules::OnPart(const CNick& Nick, CChan& Channel,
const CString& sMessage) {
MODUNLOADCHK(OnPart(Nick, Channel, sMessage));
return false;
}
bool CModules::OnPartMessage(CPartMessage& Message) {
MODUNLOADCHK(OnPartMessage(Message));
return false;
}
bool CModules::OnInvite(const CNick& Nick, const CString& sChan) {
MODHALTCHK(OnInvite(Nick, sChan));
}
bool CModules::OnChanBufferStarting(CChan& Chan, CClient& Client) {
MODHALTCHK(OnChanBufferStarting(Chan, Client));
}
bool CModules::OnChanBufferEnding(CChan& Chan, CClient& Client) {
MODHALTCHK(OnChanBufferEnding(Chan, Client));
}
bool CModules::OnChanBufferPlayLine2(CChan& Chan, CClient& Client,
CString& sLine, const timeval& tv) {
MODHALTCHK(OnChanBufferPlayLine2(Chan, Client, sLine, tv));
}
bool CModules::OnChanBufferPlayLine(CChan& Chan, CClient& Client,
CString& sLine) {
MODHALTCHK(OnChanBufferPlayLine(Chan, Client, sLine));
}
bool CModules::OnPrivBufferStarting(CQuery& Query, CClient& Client) {
MODHALTCHK(OnPrivBufferStarting(Query, Client));
}
bool CModules::OnPrivBufferEnding(CQuery& Query, CClient& Client) {
MODHALTCHK(OnPrivBufferEnding(Query, Client));
}
bool CModules::OnPrivBufferPlayLine2(CClient& Client, CString& sLine,
const timeval& tv) {
MODHALTCHK(OnPrivBufferPlayLine2(Client, sLine, tv));
}
bool CModules::OnPrivBufferPlayLine(CClient& Client, CString& sLine) {
MODHALTCHK(OnPrivBufferPlayLine(Client, sLine));
}
bool CModules::OnChanBufferPlayMessage(CMessage& Message) {
MODHALTCHK(OnChanBufferPlayMessage(Message));
}
bool CModules::OnPrivBufferPlayMessage(CMessage& Message) {
MODHALTCHK(OnPrivBufferPlayMessage(Message));
}
bool CModules::OnCTCPReply(CNick& Nick, CString& sMessage) {
MODHALTCHK(OnCTCPReply(Nick, sMessage));
}
bool CModules::OnCTCPReplyMessage(CCTCPMessage& Message) {
MODHALTCHK(OnCTCPReplyMessage(Message));
}
bool CModules::OnPrivCTCP(CNick& Nick, CString& sMessage) {
MODHALTCHK(OnPrivCTCP(Nick, sMessage));
}
bool CModules::OnPrivCTCPMessage(CCTCPMessage& Message) {
MODHALTCHK(OnPrivCTCPMessage(Message));
}
bool CModules::OnChanCTCP(CNick& Nick, CChan& Channel, CString& sMessage) {
MODHALTCHK(OnChanCTCP(Nick, Channel, sMessage));
}
bool CModules::OnChanCTCPMessage(CCTCPMessage& Message) {
MODHALTCHK(OnChanCTCPMessage(Message));
}
bool CModules::OnPrivAction(CNick& Nick, CString& sMessage) {
MODHALTCHK(OnPrivAction(Nick, sMessage));
}
bool CModules::OnPrivActionMessage(CActionMessage& Message) {
MODHALTCHK(OnPrivActionMessage(Message));
}
bool CModules::OnChanAction(CNick& Nick, CChan& Channel, CString& sMessage) {
MODHALTCHK(OnChanAction(Nick, Channel, sMessage));
}
bool CModules::OnChanActionMessage(CActionMessage& Message) {
MODHALTCHK(OnChanActionMessage(Message));
}
bool CModules::OnPrivMsg(CNick& Nick, CString& sMessage) {
MODHALTCHK(OnPrivMsg(Nick, sMessage));
}
bool CModules::OnPrivTextMessage(CTextMessage& Message) {
MODHALTCHK(OnPrivTextMessage(Message));
}
bool CModules::OnChanMsg(CNick& Nick, CChan& Channel, CString& sMessage) {
MODHALTCHK(OnChanMsg(Nick, Channel, sMessage));
}
bool CModules::OnChanTextMessage(CTextMessage& Message) {
MODHALTCHK(OnChanTextMessage(Message));
}
bool CModules::OnPrivNotice(CNick& Nick, CString& sMessage) {
MODHALTCHK(OnPrivNotice(Nick, sMessage));
}
bool CModules::OnPrivNoticeMessage(CNoticeMessage& Message) {
MODHALTCHK(OnPrivNoticeMessage(Message));
}
bool CModules::OnChanNotice(CNick& Nick, CChan& Channel, CString& sMessage) {
MODHALTCHK(OnChanNotice(Nick, Channel, sMessage));
}
bool CModules::OnChanNoticeMessage(CNoticeMessage& Message) {
MODHALTCHK(OnChanNoticeMessage(Message));
}
bool CModules::OnTopic(CNick& Nick, CChan& Channel, CString& sTopic) {
MODHALTCHK(OnTopic(Nick, Channel, sTopic));
}
bool CModules::OnTopicMessage(CTopicMessage& Message) {
MODHALTCHK(OnTopicMessage(Message));
}
bool CModules::OnTimerAutoJoin(CChan& Channel) {
MODHALTCHK(OnTimerAutoJoin(Channel));
}
bool CModules::OnAddNetwork(CIRCNetwork& Network, CString& sErrorRet) {
MODHALTCHK(OnAddNetwork(Network, sErrorRet));
}
bool CModules::OnDeleteNetwork(CIRCNetwork& Network) {
MODHALTCHK(OnDeleteNetwork(Network));
}
bool CModules::OnSendToClient(CString& sLine, CClient& Client) {
MODHALTCHK(OnSendToClient(sLine, Client));
}
bool CModules::OnSendToClientMessage(CMessage& Message) {
MODHALTCHK(OnSendToClientMessage(Message));
}
bool CModules::OnSendToIRC(CString& sLine) { MODHALTCHK(OnSendToIRC(sLine)); }
bool CModules::OnSendToIRCMessage(CMessage& Message) {
MODHALTCHK(OnSendToIRCMessage(Message));
}
bool CModules::OnStatusCommand(CString& sCommand) {
MODHALTCHK(OnStatusCommand(sCommand));
}
bool CModules::OnModCommand(const CString& sCommand) {
MODUNLOADCHK(OnModCommand(sCommand));
return false;
}
bool CModules::OnModNotice(const CString& sMessage) {
MODUNLOADCHK(OnModNotice(sMessage));
return false;
}
bool CModules::OnModCTCP(const CString& sMessage) {
MODUNLOADCHK(OnModCTCP(sMessage));
return false;
}
// Why MODHALTCHK works only with functions returning EModRet ? :(
bool CModules::OnServerCapAvailable(const CString& sCap) {
bool bResult = false;
for (CModule* pMod : *this) {
try {
CClient* pOldClient = pMod->GetClient();
pMod->SetClient(m_pClient);
if (m_pUser) {
CUser* pOldUser = pMod->GetUser();
pMod->SetUser(m_pUser);
bResult |= pMod->OnServerCapAvailable(sCap);
pMod->SetUser(pOldUser);
} else {
// WTF? Is that possible?
bResult |= pMod->OnServerCapAvailable(sCap);
}
pMod->SetClient(pOldClient);
} catch (const CModule::EModException& e) {
if (CModule::UNLOAD == e) {
UnloadModule(pMod->GetModName());
}
}
}
return bResult;
}
bool CModules::OnServerCapResult(const CString& sCap, bool bSuccess) {
MODUNLOADCHK(OnServerCapResult(sCap, bSuccess));
return false;
}
////////////////////
// Global Modules //
////////////////////
bool CModules::OnAddUser(CUser& User, CString& sErrorRet) {
MODHALTCHK(OnAddUser(User, sErrorRet));
}
bool CModules::OnDeleteUser(CUser& User) { MODHALTCHK(OnDeleteUser(User)); }
bool CModules::OnClientConnect(CZNCSock* pClient, const CString& sHost,
unsigned short uPort) {
MODUNLOADCHK(OnClientConnect(pClient, sHost, uPort));
return false;
}
bool CModules::OnLoginAttempt(std::shared_ptr<CAuthBase> Auth) {
MODHALTCHK(OnLoginAttempt(Auth));
}
bool CModules::OnFailedLogin(const CString& sUsername,
const CString& sRemoteIP) {
MODUNLOADCHK(OnFailedLogin(sUsername, sRemoteIP));
return false;
}
bool CModules::OnUnknownUserRaw(CClient* pClient, CString& sLine) {
MODHALTCHK(OnUnknownUserRaw(pClient, sLine));
}
bool CModules::OnUnknownUserRawMessage(CMessage& Message) {
MODHALTCHK(OnUnknownUserRawMessage(Message));
}
bool CModules::OnClientCapLs(CClient* pClient, SCString& ssCaps) {
MODUNLOADCHK(OnClientCapLs(pClient, ssCaps));
return false;
}
// Maybe create new macro for this?
bool CModules::IsClientCapSupported(CClient* pClient, const CString& sCap,
bool bState) {
bool bResult = false;
for (CModule* pMod : *this) {
try {
CClient* pOldClient = pMod->GetClient();
pMod->SetClient(m_pClient);
if (m_pUser) {
CUser* pOldUser = pMod->GetUser();
pMod->SetUser(m_pUser);
bResult |= pMod->IsClientCapSupported(pClient, sCap, bState);
pMod->SetUser(pOldUser);
} else {
// WTF? Is that possible?
bResult |= pMod->IsClientCapSupported(pClient, sCap, bState);
}
pMod->SetClient(pOldClient);
} catch (const CModule::EModException& e) {
if (CModule::UNLOAD == e) {
UnloadModule(pMod->GetModName());
}
}
}
return bResult;
}
bool CModules::OnClientCapRequest(CClient* pClient, const CString& sCap,
bool bState) {
MODUNLOADCHK(OnClientCapRequest(pClient, sCap, bState));
return false;
}
bool CModules::OnModuleLoading(const CString& sModName, const CString& sArgs,
CModInfo::EModuleType eType, bool& bSuccess,
CString& sRetMsg) {
MODHALTCHK(OnModuleLoading(sModName, sArgs, eType, bSuccess, sRetMsg));
}
bool CModules::OnModuleUnloading(CModule* pModule, bool& bSuccess,
CString& sRetMsg) {
MODHALTCHK(OnModuleUnloading(pModule, bSuccess, sRetMsg));
}
bool CModules::OnGetModInfo(CModInfo& ModInfo, const CString& sModule,
bool& bSuccess, CString& sRetMsg) {
MODHALTCHK(OnGetModInfo(ModInfo, sModule, bSuccess, sRetMsg));
}
bool CModules::OnGetAvailableMods(set<CModInfo>& ssMods,
CModInfo::EModuleType eType) {
MODUNLOADCHK(OnGetAvailableMods(ssMods, eType));
return false;
}
CModule* CModules::FindModule(const CString& sModule) const {
for (CModule* pMod : *this) {
if (sModule.Equals(pMod->GetModName())) {
return pMod;
}
}
return nullptr;
}
bool CModules::ValidateModuleName(const CString& sModule, CString& sRetMsg) {
for (unsigned int a = 0; a < sModule.length(); a++) {
if (((sModule[a] < '0') || (sModule[a] > '9')) &&
((sModule[a] < 'a') || (sModule[a] > 'z')) &&
((sModule[a] < 'A') || (sModule[a] > 'Z')) && (sModule[a] != '_')) {
sRetMsg =
t_f("Module names can only contain letters, numbers and "
"underscores, [{1}] is invalid")(sModule);
return false;
}
}
return true;
}
bool CModules::LoadModule(const CString& sModule, const CString& sArgs,
CModInfo::EModuleType eType, CUser* pUser,
CIRCNetwork* pNetwork, CString& sRetMsg) {
sRetMsg = "";
if (!ValidateModuleName(sModule, sRetMsg)) {
return false;
}
if (FindModule(sModule) != nullptr) {
sRetMsg = t_f("Module {1} already loaded.")(sModule);
return false;
}
bool bSuccess;
bool bHandled = false;
_GLOBALMODULECALL(OnModuleLoading(sModule, sArgs, eType, bSuccess, sRetMsg),
pUser, pNetwork, nullptr, &bHandled);
if (bHandled) return bSuccess;
CString sModPath, sDataPath;
CModInfo Info;
if (!FindModPath(sModule, sModPath, sDataPath)) {
sRetMsg = t_f("Unable to find module {1}")(sModule);
return false;
}
Info.SetName(sModule);
Info.SetPath(sModPath);
ModHandle p = OpenModule(sModule, sModPath, Info, sRetMsg);
if (!p) return false;
if (!Info.SupportsType(eType)) {
dlclose(p);
sRetMsg = t_f("Module {1} does not support module type {2}.")(
sModule, CModInfo::ModuleTypeToString(eType));
return false;
}
if (!pUser && eType == CModInfo::UserModule) {
dlclose(p);
sRetMsg = t_f("Module {1} requires a user.")(sModule);
return false;
}
if (!pNetwork && eType == CModInfo::NetworkModule) {
dlclose(p);
sRetMsg = t_f("Module {1} requires a network.")(sModule);
return false;
}
CModule* pModule =
Info.GetLoader()(p, pUser, pNetwork, sModule, sDataPath, eType);
pModule->SetDescription(Info.GetDescription());
pModule->SetArgs(sArgs);
pModule->SetModPath(CDir::ChangeDir(CZNC::Get().GetCurPath(), sModPath));
push_back(pModule);
bool bLoaded;
try {
bLoaded = pModule->OnLoad(sArgs, sRetMsg);
} catch (const CModule::EModException&) {
bLoaded = false;
sRetMsg = t_s("Caught an exception");
}
if (!bLoaded) {
UnloadModule(sModule, sModPath);
if (!sRetMsg.empty())
sRetMsg = t_f("Module {1} aborted: {2}")(sModule, sRetMsg);
else
sRetMsg = t_f("Module {1} aborted.")(sModule);
return false;
}
if (!sRetMsg.empty()) {
sRetMsg += " ";
}
sRetMsg += "[" + sModPath + "]";
return true;
}
bool CModules::UnloadModule(const CString& sModule) {
CString s;
return UnloadModule(sModule, s);
}
bool CModules::UnloadModule(const CString& sModule, CString& sRetMsg) {
// Make a copy incase the reference passed in is from CModule::GetModName()
CString sMod = sModule;
CModule* pModule = FindModule(sMod);
sRetMsg = "";
if (!pModule) {
sRetMsg = t_f("Module [{1}] not loaded.")(sMod);
return false;
}
bool bSuccess;
bool bHandled = false;
_GLOBALMODULECALL(OnModuleUnloading(pModule, bSuccess, sRetMsg),
pModule->GetUser(), pModule->GetNetwork(), nullptr,
&bHandled);
if (bHandled) return bSuccess;
ModHandle p = pModule->GetDLL();
if (p) {
delete pModule;
for (iterator it = begin(); it != end(); ++it) {
if (*it == pModule) {
erase(it);
break;
}
}
dlclose(p);
sRetMsg = t_f("Module {1} unloaded.")(sMod);
return true;
}
sRetMsg = t_f("Unable to unload module {1}.")(sMod);
return false;
}
bool CModules::ReloadModule(const CString& sModule, const CString& sArgs,
CUser* pUser, CIRCNetwork* pNetwork,
CString& sRetMsg) {
// Make a copy incase the reference passed in is from CModule::GetModName()
CString sMod = sModule;
CModule* pModule = FindModule(sMod);
if (!pModule) {
sRetMsg = t_f("Module [{1}] not loaded.")(sMod);
return false;
}
CModInfo::EModuleType eType = pModule->GetType();
pModule = nullptr;
sRetMsg = "";
if (!UnloadModule(sMod, sRetMsg)) {
return false;
}
if (!LoadModule(sMod, sArgs, eType, pUser, pNetwork, sRetMsg)) {
return false;
}
sRetMsg = t_f("Reloaded module {1}.")(sMod);
return true;
}
bool CModules::GetModInfo(CModInfo& ModInfo, const CString& sModule,
CString& sRetMsg) {
if (!ValidateModuleName(sModule, sRetMsg)) {
return false;
}
CString sModPath, sTmp;
bool bSuccess;
bool bHandled = false;
GLOBALMODULECALL(OnGetModInfo(ModInfo, sModule, bSuccess, sRetMsg),
&bHandled);
if (bHandled) return bSuccess;
if (!FindModPath(sModule, sModPath, sTmp)) {
sRetMsg = t_f("Unable to find module {1}.")(sModule);
return false;
}
return GetModPathInfo(ModInfo, sModule, sModPath, sRetMsg);
}
bool CModules::GetModPathInfo(CModInfo& ModInfo, const CString& sModule,
const CString& sModPath, CString& sRetMsg) {
if (!ValidateModuleName(sModule, sRetMsg)) {
return false;
}
ModInfo.SetName(sModule);
ModInfo.SetPath(sModPath);
ModHandle p = OpenModule(sModule, sModPath, ModInfo, sRetMsg);
if (!p) return false;
dlclose(p);
return true;
}
void CModules::GetAvailableMods(set<CModInfo>& ssMods,
CModInfo::EModuleType eType) {
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.SupportsType(eType)) {
ssMods.insert(ModInfo);
}
}
}
}
GLOBALMODULECALL(OnGetAvailableMods(ssMods, eType), NOTHING);
}
void CModules::GetDefaultMods(set<CModInfo>& ssMods,
CModInfo::EModuleType eType) {
GetAvailableMods(ssMods, eType);
const map<CString, CModInfo::EModuleType> ns = {
{"chansaver", CModInfo::UserModule},
{"controlpanel", CModInfo::UserModule},
{"simple_away", CModInfo::NetworkModule},
{"webadmin", CModInfo::GlobalModule}};
auto it = ssMods.begin();
while (it != ssMods.end()) {
auto it2 = ns.find(it->GetName());
if (it2 != ns.end() && it2->second == eType) {
++it;
} else {
it = ssMods.erase(it);
}
}
}
bool CModules::FindModPath(const CString& sModule, CString& sModPath,
CString& sDataPath) {
CString sMod = sModule;
CString sDir = sMod;
if (!sModule.Contains(".")) sMod += ".so";
ModDirList dirs = GetModDirs();
while (!dirs.empty()) {
sModPath = dirs.front().first + sMod;
sDataPath = dirs.front().second;
dirs.pop();
if (CFile::Exists(sModPath)) {
sDataPath += sDir;
return true;
}
}
return false;
}
CModules::ModDirList CModules::GetModDirs() {
ModDirList ret;
CString sDir;
#ifdef RUN_FROM_SOURCE
// ./modules
sDir = CZNC::Get().GetCurPath() + "/modules/";
ret.push(std::make_pair(sDir, sDir + "data/"));
#endif
// ~/.znc/modules
sDir = CZNC::Get().GetModPath() + "/";
ret.push(std::make_pair(sDir, sDir));
// <moduledir> and <datadir> (<prefix>/lib/znc)
ret.push(std::make_pair(_MODDIR_ + CString("/"),
_DATADIR_ + CString("/modules/")));
return ret;
}
ModHandle CModules::OpenModule(const CString& sModule, const CString& sModPath,
CModInfo& Info, CString& sRetMsg) {
// Some sane defaults in case anything errors out below
sRetMsg.clear();
if (!ValidateModuleName(sModule, sRetMsg)) {
return nullptr;
}
// The second argument to dlopen() has a long history. It seems clear
// that (despite what the man page says) we must include either of
// RTLD_NOW and RTLD_LAZY and either of RTLD_GLOBAL and RTLD_LOCAL.
//
// RTLD_NOW vs. RTLD_LAZY: We use RTLD_NOW to avoid ZNC dying due to
// failed symbol lookups later on. Doesn't really seem to have much of a
// performance impact.
//
// RTLD_GLOBAL vs. RTLD_LOCAL: If perl is loaded with RTLD_LOCAL and later
// on loads own modules (which it apparently does with RTLD_LAZY), we will
// die in a name lookup since one of perl's symbols isn't found. That's
// worse than any theoretical issue with RTLD_GLOBAL.
ModHandle p = dlopen((sModPath).c_str(), RTLD_NOW | RTLD_GLOBAL);
if (!p) {
// dlerror() returns pointer to static buffer, which may be overwritten
// very soon with another dl call also it may just return null.
const char* cDlError = dlerror();
CString sDlError = cDlError ? cDlError : t_s("Unknown error");
sRetMsg = t_f("Unable to open module {1}: {2}")(sModule, sDlError);
return nullptr;
}
const CModuleEntry* (*fpZNCModuleEntry)() = nullptr;
// man dlsym(3) explains this
*reinterpret_cast<void**>(&fpZNCModuleEntry) = dlsym(p, "ZNCModuleEntry");
if (!fpZNCModuleEntry) {
dlclose(p);
sRetMsg = t_f("Could not find ZNCModuleEntry in module {1}")(sModule);
return nullptr;
}
const CModuleEntry* pModuleEntry = fpZNCModuleEntry();
if (std::strcmp(pModuleEntry->pcVersion, VERSION_STR) ||
std::strcmp(pModuleEntry->pcVersionExtra, VERSION_EXTRA)) {
sRetMsg = t_f(
"Version mismatch for module {1}: core is {2}, module is built for "
"{3}. Recompile this module.")(
sModule, VERSION_STR VERSION_EXTRA,
CString(pModuleEntry->pcVersion) + pModuleEntry->pcVersionExtra);
dlclose(p);
return nullptr;
}
if (std::strcmp(pModuleEntry->pcCompileOptions,
ZNC_COMPILE_OPTIONS_STRING)) {
sRetMsg = t_f(
"Module {1} is built incompatibly: core is '{2}', module is '{3}'. "
"Recompile this module.")(sModule, ZNC_COMPILE_OPTIONS_STRING,
pModuleEntry->pcCompileOptions);
dlclose(p);
return nullptr;
}
CTranslationDomainRefHolder translation("znc-" + sModule);
pModuleEntry->fpFillModInfo(Info);
sRetMsg = "";
return p;
}
CModCommand::CModCommand()
: m_sCmd(), m_pFunc(nullptr), m_Args(""), m_Desc("") {}
CModCommand::CModCommand(const CString& sCmd, CModule* pMod, ModCmdFunc func,
const CString& sArgs, const CString& sDesc)
: m_sCmd(sCmd),
m_pFunc([pMod, func](const CString& sLine) { (pMod->*func)(sLine); }),
m_Args(sArgs),
m_Desc(sDesc) {}
CModCommand::CModCommand(const CString& sCmd, CmdFunc func,
const COptionalTranslation& Args,
const COptionalTranslation& Desc)
: m_sCmd(sCmd), m_pFunc(std::move(func)), m_Args(Args), m_Desc(Desc) {}
void CModCommand::InitHelp(CTable& Table) {
Table.AddColumn(t_s("Command", "modhelpcmd"));
Table.AddColumn(t_s("Description", "modhelpcmd"));
}
void CModCommand::AddHelp(CTable& Table) const {
Table.AddRow();
Table.SetCell(t_s("Command", "modhelpcmd"), GetCommand() + " " + GetArgs());
Table.SetCell(t_s("Description", "modhelpcmd"), GetDescription());
}
CString CModule::t_s(const CString& sEnglish, const CString& sContext) const {
return CTranslation::Get().Singular("znc-" + GetModName(), sContext,
sEnglish);
}
CInlineFormatMessage CModule::t_f(const CString& sEnglish,
const CString& sContext) const {
return CInlineFormatMessage(t_s(sEnglish, sContext));
}
CInlineFormatMessage CModule::t_p(const CString& sEnglish,
const CString& sEnglishes, int iNum,
const CString& sContext) const {
return CInlineFormatMessage(CTranslation::Get().Plural(
"znc-" + GetModName(), sContext, sEnglish, sEnglishes, iNum));
}
CDelayedTranslation CModule::t_d(const CString& sEnglish,
const CString& sContext) const {
return CDelayedTranslation("znc-" + GetModName(), sContext, sEnglish);
}
CString CModInfo::t_s(const CString& sEnglish, const CString& sContext) const {
return CTranslation::Get().Singular("znc-" + GetName(), sContext, sEnglish);
}