~ubuntu-branches/ubuntu/trusty/znc/trusty

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