]> jfr.im git - irc/rizon/znc.git/blob - Modules.cpp
Write forceserver and webircpassword to conf
[irc/rizon/znc.git] / Modules.cpp
1 /*
2 * Copyright (C) 2004-2011 See the AUTHORS file for details.
3 *
4 * This program is free software; you can redistribute it and/or modify it
5 * under the terms of the GNU General Public License version 2 as published
6 * by the Free Software Foundation.
7 */
8
9 #include "Modules.h"
10 #include "FileUtils.h"
11 #include "Template.h"
12 #include "User.h"
13 #include "WebModules.h"
14 #include "znc.h"
15 #include <dlfcn.h>
16
17 #ifndef RTLD_LOCAL
18 # define RTLD_LOCAL 0
19 # warning "your crap box doesnt define RTLD_LOCAL !?"
20 #endif
21
22 #define _MODUNLOADCHK(func, type) \
23 for (unsigned int a = 0; a < size(); a++) { \
24 try { \
25 type* pMod = (type *) (*this)[a]; \
26 CClient* pOldClient = pMod->GetClient(); \
27 pMod->SetClient(m_pClient); \
28 if (m_pUser) { \
29 CUser* pOldUser = pMod->GetUser(); \
30 pMod->SetUser(m_pUser); \
31 pMod->func; \
32 pMod->SetUser(pOldUser); \
33 } else { \
34 pMod->func; \
35 } \
36 pMod->SetClient(pOldClient); \
37 } catch (CModule::EModException e) { \
38 if (e == CModule::UNLOAD) { \
39 UnloadModule((*this)[a]->GetModName()); \
40 } \
41 } \
42 }
43
44 #define MODUNLOADCHK(func) _MODUNLOADCHK(func, CModule)
45 #define GLOBALMODCALL(func) _MODUNLOADCHK(func, CGlobalModule)
46
47 #define _MODHALTCHK(func, type) \
48 bool bHaltCore = false; \
49 for (unsigned int a = 0; a < size(); a++) { \
50 try { \
51 type* pMod = (type*) (*this)[a]; \
52 CModule::EModRet e = CModule::CONTINUE; \
53 CClient* pOldClient = pMod->GetClient(); \
54 pMod->SetClient(m_pClient); \
55 if (m_pUser) { \
56 CUser* pOldUser = pMod->GetUser(); \
57 pMod->SetUser(m_pUser); \
58 e = pMod->func; \
59 pMod->SetUser(pOldUser); \
60 } else { \
61 e = pMod->func; \
62 } \
63 pMod->SetClient(pOldClient); \
64 if (e == CModule::HALTMODS) { \
65 break; \
66 } else if (e == CModule::HALTCORE) { \
67 bHaltCore = true; \
68 } else if (e == CModule::HALT) { \
69 bHaltCore = true; \
70 break; \
71 } \
72 } catch (CModule::EModException e) { \
73 if (e == CModule::UNLOAD) { \
74 UnloadModule((*this)[a]->GetModName()); \
75 } \
76 } \
77 } \
78 return bHaltCore;
79
80 #define MODHALTCHK(func) _MODHALTCHK(func, CModule)
81 #define GLOBALMODHALTCHK(func) _MODHALTCHK(func, CGlobalModule)
82
83 /////////////////// Timer ///////////////////
84 CTimer::CTimer(CModule* pModule, unsigned int uInterval, unsigned int uCycles, const CString& sLabel, const CString& sDescription) : CCron() {
85 SetName(sLabel);
86 m_sDescription = sDescription;
87 m_pModule = pModule;
88
89 if (uCycles) {
90 StartMaxCycles(uInterval, uCycles);
91 } else {
92 Start(uInterval);
93 }
94 }
95
96 CTimer::~CTimer() {
97 m_pModule->UnlinkTimer(this);
98 }
99
100 void CTimer::SetModule(CModule* p) { m_pModule = p; }
101 void CTimer::SetDescription(const CString& s) { m_sDescription = s; }
102 CModule* CTimer::GetModule() const { return m_pModule; }
103 const CString& CTimer::GetDescription() const { return m_sDescription; }
104 /////////////////// !Timer ///////////////////
105
106
107 CModule::CModule(ModHandle pDLL, CUser* pUser, const CString& sModName, const CString& sDataDir) {
108 m_bGlobal = false;
109 m_pDLL = pDLL;
110 m_pManager = &(CZNC::Get().GetManager());;
111 m_pUser = pUser;
112 m_pClient = NULL;
113 m_sModName = sModName;
114 m_sDataDir = sDataDir;
115
116 if (m_pUser) {
117 m_sSavePath = m_pUser->GetUserPath() + "/moddata/" + m_sModName;
118 LoadRegistry();
119 }
120 }
121
122 CModule::CModule(ModHandle pDLL, const CString& sModName, const CString& sDataDir) {
123 m_pDLL = pDLL;
124 m_pManager = &(CZNC::Get().GetManager());
125 m_pUser = NULL;
126 m_pClient = NULL;
127 m_sModName = sModName;
128 m_sDataDir = sDataDir;
129
130 m_sSavePath = CZNC::Get().GetZNCPath() + "/moddata/" + m_sModName;
131 LoadRegistry();
132 }
133
134 CModule::~CModule() {
135 while (!m_sTimers.empty()) {
136 RemTimer(*m_sTimers.begin());
137 }
138
139 while (!m_sSockets.empty()) {
140 RemSocket(*m_sSockets.begin());
141 }
142
143 SaveRegistry();
144 }
145
146 void CModule::SetUser(CUser* pUser) { m_pUser = pUser; }
147 void CModule::SetClient(CClient* pClient) { m_pClient = pClient; }
148
149 const CString& CModule::GetSavePath() const {
150 if (!CFile::Exists(m_sSavePath)) {
151 CDir::MakeDir(m_sSavePath);
152 }
153 return m_sSavePath;
154 }
155
156 bool CModule::LoadRegistry() {
157 //CString sPrefix = (m_pUser) ? m_pUser->GetUserName() : ".global";
158 return (m_mssRegistry.ReadFromDisk(GetSavePath() + "/.registry") == MCString::MCS_SUCCESS);
159 }
160
161 bool CModule::SaveRegistry() const {
162 //CString sPrefix = (m_pUser) ? m_pUser->GetUserName() : ".global";
163 return (m_mssRegistry.WriteToDisk(GetSavePath() + "/.registry", 0600) == MCString::MCS_SUCCESS);
164 }
165
166 bool CModule::SetNV(const CString & sName, const CString & sValue, bool bWriteToDisk) {
167 m_mssRegistry[sName] = sValue;
168 if (bWriteToDisk) {
169 return SaveRegistry();
170 }
171
172 return true;
173 }
174
175 CString CModule::GetNV(const CString & sName) const {
176 MCString::const_iterator it = m_mssRegistry.find(sName);
177
178 if (it != m_mssRegistry.end()) {
179 return it->second;
180 }
181
182 return "";
183 }
184
185 bool CModule::DelNV(const CString & sName, bool bWriteToDisk) {
186 MCString::iterator it = m_mssRegistry.find(sName);
187
188 if (it != m_mssRegistry.end()) {
189 m_mssRegistry.erase(it);
190 } else {
191 return false;
192 }
193
194 if (bWriteToDisk) {
195 return SaveRegistry();
196 }
197
198 return true;
199 }
200
201 bool CModule::ClearNV(bool bWriteToDisk) {
202 m_mssRegistry.clear();
203
204 if (bWriteToDisk) {
205 return SaveRegistry();
206 }
207 return true;
208 }
209
210 bool CModule::AddTimer(CTimer* pTimer) {
211 if ((!pTimer) || (FindTimer(pTimer->GetName()))) {
212 delete pTimer;
213 return false;
214 }
215
216 if (!m_sTimers.insert(pTimer).second)
217 // Was already added
218 return true;
219
220 m_pManager->AddCron(pTimer);
221 return true;
222 }
223
224 bool CModule::AddTimer(FPTimer_t pFBCallback, const CString& sLabel, u_int uInterval, u_int uCycles, const CString& sDescription) {
225 CFPTimer *pTimer = new CFPTimer(this, uInterval, uCycles, sLabel, sDescription);
226 pTimer->SetFPCallback(pFBCallback);
227
228 return AddTimer(pTimer);
229 }
230
231 bool CModule::RemTimer(CTimer* pTimer) {
232 if (m_sTimers.erase(pTimer) == 0)
233 return false;
234 m_pManager->DelCronByAddr(pTimer);
235 return true;
236 }
237
238 bool CModule::RemTimer(const CString& sLabel) {
239 CTimer *pTimer = FindTimer(sLabel);
240 if (!pTimer)
241 return false;
242 return RemTimer(pTimer);
243 }
244
245 bool CModule::UnlinkTimer(CTimer* pTimer) {
246 set<CTimer*>::iterator it;
247 for (it = m_sTimers.begin(); it != m_sTimers.end(); ++it) {
248 if (pTimer == *it) {
249 m_sTimers.erase(it);
250 return true;
251 }
252 }
253
254 return false;
255 }
256
257 CTimer* CModule::FindTimer(const CString& sLabel) {
258 set<CTimer*>::iterator it;
259 for (it = m_sTimers.begin(); it != m_sTimers.end(); ++it) {
260 CTimer* pTimer = *it;
261 if (pTimer->GetName().Equals(sLabel)) {
262 return pTimer;
263 }
264 }
265
266 return NULL;
267 }
268
269 void CModule::ListTimers() {
270 if (m_sTimers.empty()) {
271 PutModule("You have no timers running.");
272 return;
273 }
274
275 CTable Table;
276 Table.AddColumn("Name");
277 Table.AddColumn("Secs");
278 Table.AddColumn("Cycles");
279 Table.AddColumn("Description");
280
281 set<CTimer*>::iterator it;
282 for (it = m_sTimers.begin(); it != m_sTimers.end(); ++it) {
283 CTimer* pTimer = *it;
284 unsigned int uCycles = pTimer->GetCyclesLeft();
285
286 Table.AddRow();
287 Table.SetCell("Name", pTimer->GetName());
288 Table.SetCell("Secs", CString(pTimer->GetInterval()));
289 Table.SetCell("Cycles", ((uCycles) ? CString(uCycles) : "INF"));
290 Table.SetCell("Description", pTimer->GetDescription());
291 }
292
293 PutModule(Table);
294 }
295
296 bool CModule::AddSocket(CSocket* pSocket) {
297 if (!pSocket) {
298 return false;
299 }
300
301 m_sSockets.insert(pSocket);
302 return true;
303 }
304
305 bool CModule::RemSocket(CSocket* pSocket) {
306 set<CSocket*>::iterator it;
307 for (it = m_sSockets.begin(); it != m_sSockets.end(); ++it) {
308 if (*it == pSocket) {
309 m_sSockets.erase(it);
310 m_pManager->DelSockByAddr(pSocket);
311 return true;
312 }
313 }
314
315 return false;
316 }
317
318 bool CModule::RemSocket(const CString& sSockName) {
319 set<CSocket*>::iterator it;
320 for (it = m_sSockets.begin(); it != m_sSockets.end(); ++it) {
321 CSocket* pSocket = *it;
322
323 if (pSocket->GetSockName().Equals(sSockName)) {
324 m_sSockets.erase(it);
325 m_pManager->DelSockByAddr(pSocket);
326 return true;
327 }
328 }
329
330 return false;
331 }
332
333 bool CModule::UnlinkSocket(CSocket* pSocket) {
334 set<CSocket*>::iterator it;
335 for (it = m_sSockets.begin(); it != m_sSockets.end(); ++it) {
336 if (pSocket == *it) {
337 m_sSockets.erase(it);
338 return true;
339 }
340 }
341
342 return false;
343 }
344
345 CSocket* CModule::FindSocket(const CString& sSockName) {
346 set<CSocket*>::iterator it;
347 for (it = m_sSockets.begin(); it != m_sSockets.end(); ++it) {
348 CSocket* pSocket = *it;
349 if (pSocket->GetSockName().Equals(sSockName)) {
350 return pSocket;
351 }
352 }
353
354 return NULL;
355 }
356
357 void CModule::ListSockets() {
358 if (m_sSockets.empty()) {
359 PutModule("You have no open sockets.");
360 return;
361 }
362
363 CTable Table;
364 Table.AddColumn("Name");
365 Table.AddColumn("State");
366 Table.AddColumn("LocalPort");
367 Table.AddColumn("SSL");
368 Table.AddColumn("RemoteIP");
369 Table.AddColumn("RemotePort");
370
371 set<CSocket*>::iterator it;
372 for (it = m_sSockets.begin(); it != m_sSockets.end(); ++it) {
373 CSocket* pSocket = *it;
374
375 Table.AddRow();
376 Table.SetCell("Name", pSocket->GetSockName());
377
378 if (pSocket->GetType() == CSocket::LISTENER) {
379 Table.SetCell("State", "Listening");
380 } else {
381 Table.SetCell("State", (pSocket->IsConnected() ? "Connected" : ""));
382 }
383
384 Table.SetCell("LocalPort", CString(pSocket->GetLocalPort()));
385 Table.SetCell("SSL", (pSocket->GetSSL() ? "yes" : "no"));
386 Table.SetCell("RemoteIP", pSocket->GetRemoteIP());
387 Table.SetCell("RemotePort", (pSocket->GetRemotePort()) ? CString(pSocket->GetRemotePort()) : CString(""));
388 }
389
390 PutModule(Table);
391 }
392
393 bool CModule::AddCommand(const CModCommand& Command)
394 {
395 if (Command.GetFunction() == NULL)
396 return false;
397 if (Command.GetCommand().find(' ') != CString::npos)
398 return false;
399 if (FindCommand(Command.GetCommand()) != NULL)
400 return false;
401
402 m_mCommands[Command.GetCommand()] = Command;
403 return true;
404 }
405
406 bool CModule::AddCommand(const CString& sCmd, CModCommand::ModCmdFunc func, const CString& sArgs, const CString& sDesc)
407 {
408 CModCommand cmd(sCmd, func, sArgs, sDesc);
409 return AddCommand(cmd);
410 }
411
412 void CModule::AddHelpCommand()
413 {
414 AddCommand("Help", &CModule::HandleHelpCommand, "search", "Generate this output");
415 }
416
417 bool CModule::RemCommand(const CString& sCmd)
418 {
419 return m_mCommands.erase(sCmd) > 0;
420 }
421
422 const CModCommand* CModule::FindCommand(const CString& sCmd) const
423 {
424 map<CString, CModCommand>::const_iterator it;
425 for (it = m_mCommands.begin(); it != m_mCommands.end(); ++it) {
426 if (!it->first.Equals(sCmd))
427 continue;
428 return &it->second;
429 }
430 return NULL;
431 }
432
433 bool CModule::HandleCommand(const CString& sLine) {
434 const CString& sCmd = sLine.Token(0);
435 const CModCommand* pCmd = FindCommand(sCmd);
436
437 if (pCmd) {
438 pCmd->Call(this, sLine);
439 return true;
440 }
441
442 OnUnknownModCommand(sLine);
443
444 return false;
445 }
446
447 void CModule::HandleHelpCommand(const CString& sLine) {
448 CString sFilter = sLine.Token(1, true);
449 unsigned int iFilterLength = sFilter.size();
450 CTable Table;
451 map<CString, CModCommand>::const_iterator it;
452
453 CModCommand::InitHelp(Table);
454 for (it = m_mCommands.begin(); it != m_mCommands.end(); ++it) {
455 if (sFilter.empty() || (it->second.GetCommand().Equals(sFilter, false, iFilterLength))) {
456 it->second.AddHelp(Table);
457 }
458 }
459 PutModule(Table);
460 }
461
462 CString CModule::GetModNick() const { return ((m_pUser) ? m_pUser->GetStatusPrefix() : "*") + m_sModName; }
463
464 // Webmods
465 bool CModule::OnWebPreRequest(CWebSock& WebSock, const CString& sPageName) { return false; }
466 bool CModule::OnWebRequest(CWebSock& WebSock, const CString& sPageName, CTemplate& Tmpl) { return false; }
467 bool CModule::OnEmbeddedWebRequest(CWebSock& WebSock, const CString& sPageName, CTemplate& Tmpl) { return false; }
468 // !Webmods
469
470 bool CModule::OnLoad(const CString& sArgs, CString& sMessage) { sMessage = ""; return true; }
471 bool CModule::OnBoot() { return true; }
472 void CModule::OnPreRehash() {}
473 void CModule::OnPostRehash() {}
474 void CModule::OnIRCDisconnected() {}
475 void CModule::OnIRCConnected() {}
476 CModule::EModRet CModule::OnIRCConnecting(CIRCSock *IRCSock) { return CONTINUE; }
477 void CModule::OnIRCConnectionError(CIRCSock *IRCSock) {}
478 CModule::EModRet CModule::OnIRCRegistration(CString& sPass, CString& sNick, CString& sIdent, CString& sRealName) { return CONTINUE; }
479 CModule::EModRet CModule::OnBroadcast(CString& sMessage) { return CONTINUE; }
480
481 void CModule::OnChanPermission(const CNick& OpNick, const CNick& Nick, CChan& Channel, unsigned char uMode, bool bAdded, bool bNoChange) {}
482 void CModule::OnOp(const CNick& OpNick, const CNick& Nick, CChan& Channel, bool bNoChange) {}
483 void CModule::OnDeop(const CNick& OpNick, const CNick& Nick, CChan& Channel, bool bNoChange) {}
484 void CModule::OnVoice(const CNick& OpNick, const CNick& Nick, CChan& Channel, bool bNoChange) {}
485 void CModule::OnDevoice(const CNick& OpNick, const CNick& Nick, CChan& Channel, bool bNoChange) {}
486 void CModule::OnRawMode(const CNick& OpNick, CChan& Channel, const CString& sModes, const CString& sArgs) {}
487 void CModule::OnMode(const CNick& OpNick, CChan& Channel, char uMode, const CString& sArg, bool bAdded, bool bNoChange) {}
488
489 CModule::EModRet CModule::OnRaw(CString& sLine) { return CONTINUE; }
490
491 CModule::EModRet CModule::OnStatusCommand(CString& sCommand) { return CONTINUE; }
492 void CModule::OnModNotice(const CString& sMessage) {}
493 void CModule::OnModCTCP(const CString& sMessage) {}
494
495 void CModule::OnModCommand(const CString& sCommand) {
496 HandleCommand(sCommand);
497 }
498 void CModule::OnUnknownModCommand(const CString& sLine) {
499 if (m_mCommands.empty())
500 // This function is only called if OnModCommand wasn't
501 // overriden, so no false warnings for modules which don't use
502 // CModCommand for command handling.
503 PutModule("This module doesn't implement any commands.");
504 else
505 PutModule("Unknown command!");
506 }
507
508 void CModule::OnQuit(const CNick& Nick, const CString& sMessage, const vector<CChan*>& vChans) {}
509 void CModule::OnNick(const CNick& Nick, const CString& sNewNick, const vector<CChan*>& vChans) {}
510 void CModule::OnKick(const CNick& Nick, const CString& sKickedNick, CChan& Channel, const CString& sMessage) {}
511 void CModule::OnJoin(const CNick& Nick, CChan& Channel) {}
512 void CModule::OnPart(const CNick& Nick, CChan& Channel, const CString& sMessage) {}
513
514 CModule::EModRet CModule::OnChanBufferStarting(CChan& Chan, CClient& Client) { return CONTINUE; }
515 CModule::EModRet CModule::OnChanBufferEnding(CChan& Chan, CClient& Client) { return CONTINUE; }
516 CModule::EModRet CModule::OnChanBufferPlayLine(CChan& Chan, CClient& Client, CString& sLine) { return CONTINUE; }
517 CModule::EModRet CModule::OnPrivBufferPlayLine(CClient& Client, CString& sLine) { return CONTINUE; }
518
519 void CModule::OnClientLogin() {}
520 void CModule::OnClientDisconnect() {}
521 CModule::EModRet CModule::OnUserRaw(CString& sLine) { return CONTINUE; }
522 CModule::EModRet CModule::OnUserCTCPReply(CString& sTarget, CString& sMessage) { return CONTINUE; }
523 CModule::EModRet CModule::OnUserCTCP(CString& sTarget, CString& sMessage) { return CONTINUE; }
524 CModule::EModRet CModule::OnUserAction(CString& sTarget, CString& sMessage) { return CONTINUE; }
525 CModule::EModRet CModule::OnUserMsg(CString& sTarget, CString& sMessage) { return CONTINUE; }
526 CModule::EModRet CModule::OnUserNotice(CString& sTarget, CString& sMessage) { return CONTINUE; }
527 CModule::EModRet CModule::OnUserJoin(CString& sChannel, CString& sKey) { return CONTINUE; }
528 CModule::EModRet CModule::OnUserPart(CString& sChannel, CString& sMessage) { return CONTINUE; }
529 CModule::EModRet CModule::OnUserTopic(CString& sChannel, CString& sTopic) { return CONTINUE; }
530 CModule::EModRet CModule::OnUserTopicRequest(CString& sChannel) { return CONTINUE; }
531
532 CModule::EModRet CModule::OnCTCPReply(CNick& Nick, CString& sMessage) { return CONTINUE; }
533 CModule::EModRet CModule::OnPrivCTCP(CNick& Nick, CString& sMessage) { return CONTINUE; }
534 CModule::EModRet CModule::OnChanCTCP(CNick& Nick, CChan& Channel, CString& sMessage) { return CONTINUE; }
535 CModule::EModRet CModule::OnPrivAction(CNick& Nick, CString& sMessage) { return CONTINUE; }
536 CModule::EModRet CModule::OnChanAction(CNick& Nick, CChan& Channel, CString& sMessage) { return CONTINUE; }
537 CModule::EModRet CModule::OnPrivMsg(CNick& Nick, CString& sMessage) { return CONTINUE; }
538 CModule::EModRet CModule::OnChanMsg(CNick& Nick, CChan& Channel, CString& sMessage) { return CONTINUE; }
539 CModule::EModRet CModule::OnPrivNotice(CNick& Nick, CString& sMessage) { return CONTINUE; }
540 CModule::EModRet CModule::OnChanNotice(CNick& Nick, CChan& Channel, CString& sMessage) { return CONTINUE; }
541 CModule::EModRet CModule::OnTopic(CNick& Nick, CChan& Channel, CString& sTopic) { return CONTINUE; }
542 CModule::EModRet CModule::OnTimerAutoJoin(CChan& Channel) { return CONTINUE; }
543
544 bool CModule::OnServerCapAvailable(const CString& sCap) { return false; }
545 void CModule::OnServerCapResult(const CString& sCap, bool bSuccess) {}
546
547 bool CModule::PutIRC(const CString& sLine) {
548 return (m_pUser) ? m_pUser->PutIRC(sLine) : false;
549 }
550 bool CModule::PutUser(const CString& sLine) {
551 return (m_pUser) ? m_pUser->PutUser(sLine, m_pClient) : false;
552 }
553 bool CModule::PutStatus(const CString& sLine) {
554 return (m_pUser) ? m_pUser->PutStatus(sLine, m_pClient) : false;
555 }
556 unsigned int CModule::PutModule(const CTable& table) {
557 if (!m_pUser)
558 return 0;
559
560 unsigned int idx = 0;
561 CString sLine;
562 while (table.GetLine(idx++, sLine))
563 PutModule(sLine);
564 return idx - 1;
565 }
566 bool CModule::PutModule(const CString& sLine) {
567 if (!m_pUser)
568 return false;
569 return m_pUser->PutModule(GetModName(), sLine, m_pClient);
570 }
571 bool CModule::PutModNotice(const CString& sLine) {
572 if (!m_pUser)
573 return false;
574 return m_pUser->PutModNotice(GetModName(), sLine, m_pClient);
575 }
576
577 ///////////////////
578 // CGlobalModule //
579 ///////////////////
580 CModule::EModRet CGlobalModule::OnAddUser(CUser& User, CString& sErrorRet) { return CONTINUE; }
581 CModule::EModRet CGlobalModule::OnDeleteUser(CUser& User) { return CONTINUE; }
582 void CGlobalModule::OnClientConnect(CZNCSock* pClient, const CString& sHost, unsigned short uPort) {}
583 CModule::EModRet CGlobalModule::OnLoginAttempt(CSmartPtr<CAuthBase> Auth) { return CONTINUE; }
584 void CGlobalModule::OnFailedLogin(const CString& sUsername, const CString& sRemoteIP) {}
585 CModule::EModRet CGlobalModule::OnUnknownUserRaw(CString& sLine) { return CONTINUE; }
586 void CGlobalModule::OnClientCapLs(SCString& ssCaps) {}
587 bool CGlobalModule::IsClientCapSupported(const CString& sCap, bool bState) { return false; }
588 void CGlobalModule::OnClientCapRequest(const CString& sCap, bool bState) {}
589 CModule::EModRet CGlobalModule::OnModuleLoading(const CString& sModName, const CString& sArgs,
590 bool& bSuccess, CString& sRetMsg) { return CONTINUE; }
591 CModule::EModRet CGlobalModule::OnModuleUnloading(CModule* pModule, bool& bSuccess, CString& sRetMsg) {
592 return CONTINUE;
593 }
594 CModule::EModRet CGlobalModule::OnGetModInfo(CModInfo& ModInfo, const CString& sModule,
595 bool& bSuccess, CString& sRetMsg) { return CONTINUE; }
596 void CGlobalModule::OnGetAvailableMods(set<CModInfo>& ssMods, bool bGlobal) {}
597
598
599 CModules::CModules() {
600 m_pUser = NULL;
601 m_pClient = NULL;
602 }
603
604 CModules::~CModules() {
605 UnloadAll();
606 }
607
608 void CModules::UnloadAll() {
609 while (size()) {
610 CString sRetMsg;
611 CString sModName = back()->GetModName();
612 UnloadModule(sModName, sRetMsg);
613 }
614 }
615
616 bool CModules::OnBoot() {
617 for (unsigned int a = 0; a < size(); a++) {
618 try {
619 if (!(*this)[a]->OnBoot()) {
620 return true;
621 }
622 } catch (CModule::EModException e) {
623 if (e == CModule::UNLOAD) {
624 UnloadModule((*this)[a]->GetModName());
625 }
626 }
627 }
628
629 return false;
630 }
631
632 bool CModules::OnPreRehash() { MODUNLOADCHK(OnPreRehash()); return false; }
633 bool CModules::OnPostRehash() { MODUNLOADCHK(OnPostRehash()); return false; }
634 bool CModules::OnIRCConnected() { MODUNLOADCHK(OnIRCConnected()); return false; }
635 bool CModules::OnIRCConnecting(CIRCSock *pIRCSock) { MODHALTCHK(OnIRCConnecting(pIRCSock)); }
636 bool CModules::OnIRCConnectionError(CIRCSock *pIRCSock) { MODUNLOADCHK(OnIRCConnectionError(pIRCSock)); return false; }
637 bool CModules::OnIRCRegistration(CString& sPass, CString& sNick, CString& sIdent, CString& sRealName) { MODHALTCHK(OnIRCRegistration(sPass, sNick, sIdent, sRealName)); }
638 bool CModules::OnBroadcast(CString& sMessage) { MODHALTCHK(OnBroadcast(sMessage)); }
639 bool CModules::OnIRCDisconnected() { MODUNLOADCHK(OnIRCDisconnected()); return false; }
640 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; }
641 bool CModules::OnOp(const CNick& OpNick, const CNick& Nick, CChan& Channel, bool bNoChange) { MODUNLOADCHK(OnOp(OpNick, Nick, Channel, bNoChange)); return false; }
642 bool CModules::OnDeop(const CNick& OpNick, const CNick& Nick, CChan& Channel, bool bNoChange) { MODUNLOADCHK(OnDeop(OpNick, Nick, Channel, bNoChange)); return false; }
643 bool CModules::OnVoice(const CNick& OpNick, const CNick& Nick, CChan& Channel, bool bNoChange) { MODUNLOADCHK(OnVoice(OpNick, Nick, Channel, bNoChange)); return false; }
644 bool CModules::OnDevoice(const CNick& OpNick, const CNick& Nick, CChan& Channel, bool bNoChange) { MODUNLOADCHK(OnDevoice(OpNick, Nick, Channel, bNoChange)); return false; }
645 bool CModules::OnRawMode(const CNick& OpNick, CChan& Channel, const CString& sModes, const CString& sArgs) { MODUNLOADCHK(OnRawMode(OpNick, Channel, sModes, sArgs)); return false; }
646 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; }
647 bool CModules::OnRaw(CString& sLine) { MODHALTCHK(OnRaw(sLine)); }
648
649 bool CModules::OnClientLogin() { MODUNLOADCHK(OnClientLogin()); return false; }
650 bool CModules::OnClientDisconnect() { MODUNLOADCHK(OnClientDisconnect()); return false; }
651 bool CModules::OnUserRaw(CString& sLine) { MODHALTCHK(OnUserRaw(sLine)); }
652 bool CModules::OnUserCTCPReply(CString& sTarget, CString& sMessage) { MODHALTCHK(OnUserCTCPReply(sTarget, sMessage)); }
653 bool CModules::OnUserCTCP(CString& sTarget, CString& sMessage) { MODHALTCHK(OnUserCTCP(sTarget, sMessage)); }
654 bool CModules::OnUserAction(CString& sTarget, CString& sMessage) { MODHALTCHK(OnUserAction(sTarget, sMessage)); }
655 bool CModules::OnUserMsg(CString& sTarget, CString& sMessage) { MODHALTCHK(OnUserMsg(sTarget, sMessage)); }
656 bool CModules::OnUserNotice(CString& sTarget, CString& sMessage) { MODHALTCHK(OnUserNotice(sTarget, sMessage)); }
657 bool CModules::OnUserJoin(CString& sChannel, CString& sKey) { MODHALTCHK(OnUserJoin(sChannel, sKey)); }
658 bool CModules::OnUserPart(CString& sChannel, CString& sMessage) { MODHALTCHK(OnUserPart(sChannel, sMessage)); }
659 bool CModules::OnUserTopic(CString& sChannel, CString& sTopic) { MODHALTCHK(OnUserTopic(sChannel, sTopic)); }
660 bool CModules::OnUserTopicRequest(CString& sChannel) { MODHALTCHK(OnUserTopicRequest(sChannel)); }
661
662 bool CModules::OnQuit(const CNick& Nick, const CString& sMessage, const vector<CChan*>& vChans) { MODUNLOADCHK(OnQuit(Nick, sMessage, vChans)); return false; }
663 bool CModules::OnNick(const CNick& Nick, const CString& sNewNick, const vector<CChan*>& vChans) { MODUNLOADCHK(OnNick(Nick, sNewNick, vChans)); return false; }
664 bool CModules::OnKick(const CNick& Nick, const CString& sKickedNick, CChan& Channel, const CString& sMessage) { MODUNLOADCHK(OnKick(Nick, sKickedNick, Channel, sMessage)); return false; }
665 bool CModules::OnJoin(const CNick& Nick, CChan& Channel) { MODUNLOADCHK(OnJoin(Nick, Channel)); return false; }
666 bool CModules::OnPart(const CNick& Nick, CChan& Channel, const CString& sMessage) { MODUNLOADCHK(OnPart(Nick, Channel, sMessage)); return false; }
667 bool CModules::OnChanBufferStarting(CChan& Chan, CClient& Client) { MODHALTCHK(OnChanBufferStarting(Chan, Client)); }
668 bool CModules::OnChanBufferEnding(CChan& Chan, CClient& Client) { MODHALTCHK(OnChanBufferEnding(Chan, Client)); }
669 bool CModules::OnChanBufferPlayLine(CChan& Chan, CClient& Client, CString& sLine) { MODHALTCHK(OnChanBufferPlayLine(Chan, Client, sLine)); }
670 bool CModules::OnPrivBufferPlayLine(CClient& Client, CString& sLine) { MODHALTCHK(OnPrivBufferPlayLine(Client, sLine)); }
671 bool CModules::OnCTCPReply(CNick& Nick, CString& sMessage) { MODHALTCHK(OnCTCPReply(Nick, sMessage)); }
672 bool CModules::OnPrivCTCP(CNick& Nick, CString& sMessage) { MODHALTCHK(OnPrivCTCP(Nick, sMessage)); }
673 bool CModules::OnChanCTCP(CNick& Nick, CChan& Channel, CString& sMessage) { MODHALTCHK(OnChanCTCP(Nick, Channel, sMessage)); }
674 bool CModules::OnPrivAction(CNick& Nick, CString& sMessage) { MODHALTCHK(OnPrivAction(Nick, sMessage)); }
675 bool CModules::OnChanAction(CNick& Nick, CChan& Channel, CString& sMessage) { MODHALTCHK(OnChanAction(Nick, Channel, sMessage)); }
676 bool CModules::OnPrivMsg(CNick& Nick, CString& sMessage) { MODHALTCHK(OnPrivMsg(Nick, sMessage)); }
677 bool CModules::OnChanMsg(CNick& Nick, CChan& Channel, CString& sMessage) { MODHALTCHK(OnChanMsg(Nick, Channel, sMessage)); }
678 bool CModules::OnPrivNotice(CNick& Nick, CString& sMessage) { MODHALTCHK(OnPrivNotice(Nick, sMessage)); }
679 bool CModules::OnChanNotice(CNick& Nick, CChan& Channel, CString& sMessage) { MODHALTCHK(OnChanNotice(Nick, Channel, sMessage)); }
680 bool CModules::OnTopic(CNick& Nick, CChan& Channel, CString& sTopic) { MODHALTCHK(OnTopic(Nick, Channel, sTopic)); }
681 bool CModules::OnTimerAutoJoin(CChan& Channel) { MODHALTCHK(OnTimerAutoJoin(Channel)); }
682 bool CModules::OnStatusCommand(CString& sCommand) { MODHALTCHK(OnStatusCommand(sCommand)); }
683 bool CModules::OnModCommand(const CString& sCommand) { MODUNLOADCHK(OnModCommand(sCommand)); return false; }
684 bool CModules::OnModNotice(const CString& sMessage) { MODUNLOADCHK(OnModNotice(sMessage)); return false; }
685 bool CModules::OnModCTCP(const CString& sMessage) { MODUNLOADCHK(OnModCTCP(sMessage)); return false; }
686
687 // Why MODHALTCHK works only with functions returning EModRet ? :(
688 bool CModules::OnServerCapAvailable(const CString& sCap) {
689 bool bResult = false;
690 for (unsigned int a = 0; a < size(); ++a) {
691 try {
692 CModule* pMod = (*this)[a];
693 CClient* pOldClient = pMod->GetClient();
694 pMod->SetClient(m_pClient);
695 if (m_pUser) {
696 CUser* pOldUser = pMod->GetUser();
697 pMod->SetUser(m_pUser);
698 bResult |= pMod->OnServerCapAvailable(sCap);
699 pMod->SetUser(pOldUser);
700 } else {
701 // WTF? Is that possible?
702 bResult |= pMod->OnServerCapAvailable(sCap);
703 }
704 pMod->SetClient(pOldClient);
705 } catch (CModule::EModException e) {
706 if (CModule::UNLOAD == e) {
707 UnloadModule((*this)[a]->GetModName());
708 }
709 }
710 }
711 return bResult;
712 }
713
714 bool CModules::OnServerCapResult(const CString& sCap, bool bSuccess) { MODUNLOADCHK(OnServerCapResult(sCap, bSuccess)); return false; }
715
716 ////////////////////
717 // CGlobalModules //
718 ////////////////////
719 bool CGlobalModules::OnAddUser(CUser& User, CString& sErrorRet) {
720 GLOBALMODHALTCHK(OnAddUser(User, sErrorRet));
721 }
722
723 bool CGlobalModules::OnDeleteUser(CUser& User) {
724 GLOBALMODHALTCHK(OnDeleteUser(User));
725 }
726
727 bool CGlobalModules::OnClientConnect(CZNCSock* pClient, const CString& sHost, unsigned short uPort) {
728 GLOBALMODCALL(OnClientConnect(pClient, sHost, uPort));
729 return false;
730 }
731
732 bool CGlobalModules::OnLoginAttempt(CSmartPtr<CAuthBase> Auth) {
733 GLOBALMODHALTCHK(OnLoginAttempt(Auth));
734 }
735
736 bool CGlobalModules::OnFailedLogin(const CString& sUsername, const CString& sRemoteIP) {
737 GLOBALMODCALL(OnFailedLogin(sUsername, sRemoteIP));
738 return false;
739 }
740
741 bool CGlobalModules::OnUnknownUserRaw(CString& sLine) {
742 GLOBALMODHALTCHK(OnUnknownUserRaw(sLine));
743 }
744
745 bool CGlobalModules::OnClientCapLs(SCString& ssCaps) {
746 GLOBALMODCALL(OnClientCapLs(ssCaps));
747 return false;
748 }
749
750 // Maybe create new macro for this?
751 bool CGlobalModules::IsClientCapSupported(const CString& sCap, bool bState) {
752 bool bResult = false;
753 for (unsigned int a = 0; a < size(); ++a) {
754 try {
755 CGlobalModule* pMod = (CGlobalModule*) (*this)[a];
756 CClient* pOldClient = pMod->GetClient();
757 pMod->SetClient(m_pClient);
758 if (m_pUser) {
759 CUser* pOldUser = pMod->GetUser();
760 pMod->SetUser(m_pUser);
761 bResult |= pMod->IsClientCapSupported(sCap, bState);
762 pMod->SetUser(pOldUser);
763 } else {
764 // WTF? Is that possible?
765 bResult |= pMod->IsClientCapSupported(sCap, bState);
766 }
767 pMod->SetClient(pOldClient);
768 } catch (CModule::EModException e) {
769 if (CModule::UNLOAD == e) {
770 UnloadModule((*this)[a]->GetModName());
771 }
772 }
773 }
774 return bResult;
775 }
776
777 bool CGlobalModules::OnClientCapRequest(const CString& sCap, bool bState) {
778 GLOBALMODCALL(OnClientCapRequest(sCap, bState));
779 return false;
780 }
781
782 bool CGlobalModules::OnModuleLoading(const CString& sModName, const CString& sArgs,
783 bool& bSuccess, CString& sRetMsg) {
784 GLOBALMODHALTCHK(OnModuleLoading(sModName, sArgs, bSuccess, sRetMsg));
785 }
786
787 bool CGlobalModules::OnModuleUnloading(CModule* pModule, bool& bSuccess, CString& sRetMsg) {
788 GLOBALMODHALTCHK(OnModuleUnloading(pModule, bSuccess, sRetMsg));
789 }
790
791 bool CGlobalModules::OnGetModInfo(CModInfo& ModInfo, const CString& sModule,
792 bool& bSuccess, CString& sRetMsg) {
793 GLOBALMODHALTCHK(OnGetModInfo(ModInfo, sModule, bSuccess, sRetMsg));
794 }
795
796 bool CGlobalModules::OnGetAvailableMods(set<CModInfo>& ssMods, bool bGlobal) {
797 GLOBALMODCALL(OnGetAvailableMods(ssMods, bGlobal));
798 return false;
799 }
800
801
802 CModule* CModules::FindModule(const CString& sModule) const {
803 for (unsigned int a = 0; a < size(); a++) {
804 if (sModule.Equals((*this)[a]->GetModName())) {
805 return (*this)[a];
806 }
807 }
808
809 return NULL;
810 }
811
812 bool CModules::LoadModule(const CString& sModule, const CString& sArgs, CUser* pUser, CString& sRetMsg) {
813 sRetMsg = "";
814
815 if (FindModule(sModule) != NULL) {
816 sRetMsg = "Module [" + sModule + "] already loaded.";
817 return false;
818 }
819
820 bool bSuccess;
821 GLOBALMODULECALL(OnModuleLoading(sModule, sArgs, bSuccess, sRetMsg), pUser, NULL, return bSuccess);
822
823 CString sModPath, sDataPath;
824 bool bVersionMismatch;
825 CModInfo Info;
826
827 if (!FindModPath(sModule, sModPath, sDataPath)) {
828 sRetMsg = "Unable to find module [" + sModule + "]";
829 return false;
830 }
831
832 ModHandle p = OpenModule(sModule, sModPath, bVersionMismatch, Info, sRetMsg);
833
834 if (!p)
835 return false;
836
837 if (bVersionMismatch) {
838 dlclose(p);
839 sRetMsg = "Version mismatch, recompile this module.";
840 return false;
841 }
842
843 if ((pUser == NULL) != Info.IsGlobal()) {
844 dlclose(p);
845 sRetMsg = "Module [" + sModule + "] is ";
846 sRetMsg += Info.IsGlobal() ? "" : "not ";
847 sRetMsg += "a global module.";
848 return false;
849 }
850
851 CModule* pModule = NULL;
852
853 if (pUser) {
854 pModule = Info.GetLoader()(p, pUser, sModule, sDataPath);
855 } else {
856 pModule = Info.GetGlobalLoader()(p, sModule, sDataPath);
857 }
858
859 pModule->SetDescription(Info.GetDescription());
860 pModule->SetGlobal(Info.IsGlobal());
861 pModule->SetArgs(sArgs);
862 pModule->SetModPath(CDir::ChangeDir(CZNC::Get().GetCurPath(), sModPath));
863 push_back(pModule);
864
865 bool bLoaded;
866 try {
867 bLoaded = pModule->OnLoad(sArgs, sRetMsg);
868 } catch (CModule::EModException) {
869 bLoaded = false;
870 sRetMsg = "Caught an exception";
871 }
872
873 if (!bLoaded) {
874 UnloadModule(sModule, sModPath);
875 if (!sRetMsg.empty())
876 sRetMsg = "Module [" + sModule + "] aborted: " + sRetMsg;
877 else
878 sRetMsg = "Module [" + sModule + "] aborted.";
879 return false;
880 }
881
882 if (!sRetMsg.empty()) {
883 sRetMsg += "[" + sRetMsg + "] ";
884 }
885 sRetMsg += "[" + sModPath + "]";
886 return true;
887 }
888
889 bool CModules::UnloadModule(const CString& sModule) {
890 CString s;
891 return UnloadModule(sModule, s);
892 }
893
894 bool CModules::UnloadModule(const CString& sModule, CString& sRetMsg) {
895 CString sMod = sModule; // Make a copy incase the reference passed in is from CModule::GetModName()
896 CModule* pModule = FindModule(sMod);
897 sRetMsg = "";
898
899 if (!pModule) {
900 sRetMsg = "Module [" + sMod + "] not loaded.";
901 return false;
902 }
903
904 bool bSuccess;
905 GLOBALMODULECALL(OnModuleUnloading(pModule, bSuccess, sRetMsg), pModule->GetUser(), NULL, return bSuccess);
906
907 ModHandle p = pModule->GetDLL();
908
909 if (p) {
910 delete pModule;
911
912 for (iterator it = begin(); it != end(); ++it) {
913 if (*it == pModule) {
914 erase(it);
915 break;
916 }
917 }
918
919 dlclose(p);
920 sRetMsg = "Module [" + sMod + "] unloaded";
921
922 return true;
923 }
924
925 sRetMsg = "Unable to unload module [" + sMod + "]";
926 return false;
927 }
928
929 bool CModules::ReloadModule(const CString& sModule, const CString& sArgs, CUser* pUser, CString& sRetMsg) {
930 CString sMod = sModule; // Make a copy incase the reference passed in is from CModule::GetModName()
931 sRetMsg = "";
932 if (!UnloadModule(sMod, sRetMsg)) {
933 return false;
934 }
935
936 if (!LoadModule(sMod, sArgs, pUser, sRetMsg)) {
937 return false;
938 }
939
940 sRetMsg = "Reloaded module [" + sMod + "]";
941 return true;
942 }
943
944 bool CModules::GetModInfo(CModInfo& ModInfo, const CString& sModule, CString& sRetMsg) {
945 CString sModPath, sTmp;
946
947 bool bSuccess;
948 GLOBALMODULECALL(OnGetModInfo(ModInfo, sModule, bSuccess, sRetMsg), NULL, NULL, return bSuccess);
949
950 if (!FindModPath(sModule, sModPath, sTmp)) {
951 sRetMsg = "Unable to find module [" + sModule + "]";
952 return false;
953 }
954
955 return GetModPathInfo(ModInfo, sModule, sModPath, sRetMsg);
956 }
957
958 bool CModules::GetModPathInfo(CModInfo& ModInfo, const CString& sModule, const CString& sModPath, CString& sRetMsg) {
959 bool bVersionMismatch;
960
961 ModHandle p = OpenModule(sModule, sModPath, bVersionMismatch, ModInfo, sRetMsg);
962
963 if (!p)
964 return false;
965
966 ModInfo.SetName(sModule);
967 ModInfo.SetPath(sModPath);
968
969 if (bVersionMismatch) {
970 ModInfo.SetDescription("--- Version mismatch, recompile this module. ---");
971 }
972
973 dlclose(p);
974
975 return true;
976 }
977
978 void CModules::GetAvailableMods(set<CModInfo>& ssMods, bool bGlobal) {
979 ssMods.clear();
980
981 unsigned int a = 0;
982 CDir Dir;
983
984 ModDirList dirs = GetModDirs();
985
986 while (!dirs.empty()) {
987 Dir.FillByWildcard(dirs.front().first, "*.so");
988 dirs.pop();
989
990 for (a = 0; a < Dir.size(); a++) {
991 CFile& File = *Dir[a];
992 CString sName = File.GetShortName();
993 CString sPath = File.GetLongName();
994 CModInfo ModInfo;
995 sName.RightChomp(3);
996
997 CString sIgnoreRetMsg;
998 if (GetModPathInfo(ModInfo, sName, sPath, sIgnoreRetMsg)) {
999 if (ModInfo.IsGlobal() == bGlobal) {
1000 ssMods.insert(ModInfo);
1001 }
1002 }
1003 }
1004 }
1005
1006 GLOBALMODULECALL(OnGetAvailableMods(ssMods, bGlobal), NULL, NULL, NOTHING);
1007 }
1008
1009 bool CModules::FindModPath(const CString& sModule, CString& sModPath,
1010 CString& sDataPath) {
1011 CString sMod = sModule;
1012 CString sDir = sMod;
1013 if (sModule.find(".") == CString::npos)
1014 sMod += ".so";
1015
1016 ModDirList dirs = GetModDirs();
1017
1018 while (!dirs.empty()) {
1019 sModPath = dirs.front().first + sMod;
1020 sDataPath = dirs.front().second;
1021 dirs.pop();
1022
1023 if (CFile::Exists(sModPath)) {
1024 sDataPath += sDir;
1025 return true;
1026 }
1027 }
1028
1029 return false;
1030 }
1031
1032 CModules::ModDirList CModules::GetModDirs() {
1033 ModDirList ret;
1034 CString sDir;
1035
1036 #ifdef RUN_FROM_SOURCE
1037 // ./modules
1038 sDir = CZNC::Get().GetCurPath() + "/modules/";
1039 ret.push(std::make_pair(sDir, sDir + "data/"));
1040
1041 // ./modules/extra
1042 sDir = CZNC::Get().GetCurPath() + "/modules/extra/";
1043 ret.push(std::make_pair(sDir, sDir + "data/"));
1044 #endif
1045
1046 // ~/.znc/modules
1047 sDir = CZNC::Get().GetModPath() + "/";
1048 ret.push(std::make_pair(sDir, sDir));
1049
1050 // <moduledir> and <datadir> (<prefix>/lib/znc)
1051 ret.push(std::make_pair(_MODDIR_ + CString("/"), _DATADIR_ + CString("/modules/")));
1052
1053 return ret;
1054 }
1055
1056 ModHandle CModules::OpenModule(const CString& sModule, const CString& sModPath, bool &bVersionMismatch,
1057 CModInfo& Info, CString& sRetMsg) {
1058 // Some sane defaults in case anything errors out below
1059 bVersionMismatch = false;
1060 sRetMsg.clear();
1061
1062 for (unsigned int a = 0; a < sModule.length(); a++) {
1063 if (((sModule[a] < '0') || (sModule[a] > '9')) && ((sModule[a] < 'a') || (sModule[a] > 'z')) && ((sModule[a] < 'A') || (sModule[a] > 'Z')) && (sModule[a] != '_')) {
1064 sRetMsg = "Module names can only contain letters, numbers and underscores, [" + sModule + "] is invalid.";
1065 return NULL;
1066 }
1067 }
1068
1069 // The second argument to dlopen() has a long history. It seems clear
1070 // that (despite what the man page says) we must include either of
1071 // RTLD_NOW and RTLD_LAZY and either of RTLD_GLOBAL and RTLD_LOCAL.
1072 //
1073 // RTLD_NOW vs. RTLD_LAZY: We use RTLD_NOW to avoid znc dying due to
1074 // failed symbol lookups later on. Doesn't really seem to have much of a
1075 // performance impact.
1076 //
1077 // RTLD_GLOBAL vs. RTLD_LOCAL: If perl is loaded with RTLD_LOCAL and later on
1078 // loads own modules (which it apparently does with RTLD_LAZY), we will die in a
1079 // name lookup since one of perl's symbols isn't found. That's worse
1080 // than any theoretical issue with RTLD_GLOBAL.
1081 ModHandle p = dlopen((sModPath).c_str(), RTLD_NOW | RTLD_GLOBAL);
1082
1083 if (!p) {
1084 sRetMsg = "Unable to open module [" + sModule + "] [" + dlerror() + "]";
1085 return NULL;
1086 }
1087
1088 typedef bool (*InfoFP)(double, CModInfo&);
1089 InfoFP ZNCModInfo = (InfoFP) dlsym(p, "ZNCModInfo");
1090
1091 if (!ZNCModInfo) {
1092 dlclose(p);
1093 sRetMsg = "Could not find ZNCModInfo() in module [" + sModule + "]";
1094 return NULL;
1095 }
1096
1097 if (ZNCModInfo(CModule::GetCoreVersion(), Info)) {
1098 sRetMsg = "";
1099 bVersionMismatch = false;
1100 } else {
1101 bVersionMismatch = true;
1102 sRetMsg = "Version mismatch, recompile this module.";
1103 }
1104
1105 return p;
1106 }
1107
1108 CModCommand::CModCommand()
1109 : m_sCmd(), m_pFunc(NULL), m_sArgs(), m_sDesc()
1110 {
1111 }
1112
1113 CModCommand::CModCommand(const CString& sCmd, ModCmdFunc func, const CString& sArgs, const CString& sDesc)
1114 : m_sCmd(sCmd), m_pFunc(func), m_sArgs(sArgs), m_sDesc(sDesc)
1115 {
1116 }
1117
1118 CModCommand::CModCommand(const CModCommand& other)
1119 : m_sCmd(other.m_sCmd), m_pFunc(other.m_pFunc), m_sArgs(other.m_sArgs), m_sDesc(other.m_sDesc)
1120 {
1121 }
1122
1123 CModCommand& CModCommand::operator=(const CModCommand& other)
1124 {
1125 m_sCmd = other.m_sCmd;
1126 m_pFunc = other.m_pFunc;
1127 m_sArgs = other.m_sArgs;
1128 m_sDesc = other.m_sDesc;
1129 return *this;
1130 }
1131
1132 void CModCommand::InitHelp(CTable& Table) {
1133 Table.AddColumn("Command");
1134 Table.AddColumn("Arguments");
1135 Table.AddColumn("Description");
1136 }
1137
1138 void CModCommand::AddHelp(CTable& Table) const {
1139 Table.AddRow();
1140 Table.SetCell("Command", GetCommand());
1141 Table.SetCell("Arguments", GetArgs());
1142 Table.SetCell("Description", GetDescription());
1143 }