]> jfr.im git - irc/rizon/znc.git/blame - znc.cpp
Write forceserver and webircpassword to conf
[irc/rizon/znc.git] / znc.cpp
CommitLineData
a09a7e79 1/*
b9b0fd4c 2 * Copyright (C) 2004-2011 See the AUTHORS file for details.
a09a7e79 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 */
6dcacaa7 8
538d3ece 9#include "znc.h"
e72c4456 10#include "Chan.h"
3f24f287 11#include "FileUtils.h"
538d3ece 12#include "IRCSock.h"
e72c4456 13#include "Server.h"
14#include "User.h"
eb449190 15#include "Listener.h"
70c77458 16#include "Config.h"
322483ab 17#include <list>
538d3ece 18
672df0a8
A
19#define CONFIG_WRITE_INTERVAL (60 * 5)
20
11ffe9db 21static inline CString FormatBindError() {
22 CString sError = (errno == 0 ? CString("unknown error, check the host name") : CString(strerror(errno)));
23 return "Unable to bind [" + sError + "]";
24}
25
538d3ece 26CZNC::CZNC() {
c03bd915 27 if (!InitCsocket()) {
4fd1b09f 28 CUtils::PrintError("Could not initialize Csocket!");
c03bd915 29 exit(-1);
30 }
31
89e5079c 32 m_pModules = new CGlobalModules();
7bff1c4d 33 m_uiConnectDelay = 5;
16507001 34 m_uiAnonIPLimit = 10;
b772e266 35 m_uBytesRead = 0;
36 m_uBytesWritten = 0;
e62ca411 37 m_uiMaxBufferSize = 500;
a213258c 38 m_pConnectUserTimer = NULL;
c1ee7248 39 m_eConfigState = ECONFIG_NOTHING;
78f8cce2 40 m_TimeStarted = time(NULL);
752f19c9 41 m_sConnectThrottle.SetTTL(30000);
a4d388b5 42 m_pLockFile = NULL;
a9ba4020 43 m_bProtectWebSessions = true;
672df0a8 44 m_pConfigTimer = NULL;
538d3ece 45}
46
47CZNC::~CZNC() {
0435ece3 48 m_pModules->UnloadAll();
3dde793e 49
115b1708 50 for (map<CString,CUser*>::iterator a = m_msUsers.begin(); a != m_msUsers.end(); ++a) {
2f0d4e67 51 a->second->GetModules().UnloadAll();
52 }
2f0d4e67 53
ca824705 54 for (size_t b = 0; b < m_vpListeners.size(); b++) {
55 delete m_vpListeners[b];
56 }
57
115b1708 58 for (map<CString,CUser*>::iterator a = m_msUsers.begin(); a != m_msUsers.end(); ++a) {
0dd41818 59 a->second->SetBeingDeleted(true);
60 }
61
c4891c5d 62 m_pConnectUserTimer = NULL;
a213258c 63 // This deletes m_pConnectUserTimer
538d3ece 64 m_Manager.Cleanup();
65 DeleteUsers();
f8bb245c 66
b8c4fc76 67 delete m_pModules;
ee9686e4 68 delete m_pLockFile;
9a1c3874 69
c03bd915 70 ShutdownCsocket();
9a1c3874 71 DeletePidFile();
538d3ece 72}
73
d397cb8a 74CString CZNC::GetVersion() {
75 char szBuf[128];
76
77 snprintf(szBuf, sizeof(szBuf), "%1.3f"VERSION_EXTRA, VERSION);
78 // If snprintf overflows (which I doubt), we want to be on the safe side
79 szBuf[sizeof(szBuf) - 1] = '\0';
80
81 return szBuf;
82}
83
beb5b49b 84CString CZNC::GetTag(bool bIncludeVersion) {
c2d92663 85 if (!bIncludeVersion) {
087f01e9 86 return "ZNC - http://znc.in";
c2d92663 87 }
88
44d38b77 89 char szBuf[128];
087f01e9 90 snprintf(szBuf, sizeof(szBuf), "ZNC %1.3f"VERSION_EXTRA" - http://znc.in", VERSION);
d397cb8a 91 // If snprintf overflows (which I doubt), we want to be on the safe side
92 szBuf[sizeof(szBuf) - 1] = '\0';
c2d92663 93
94 return szBuf;
95}
96
86924339 97CString CZNC::GetUptime() const {
bcf59c56 98 time_t now = time(NULL);
b2512e55 99 return CString::ToTimeStr(now - TimeStarted());
bcf59c56 100}
101
538d3ece 102bool CZNC::OnBoot() {
9ae959b8 103 ALLMODULECALL(OnBoot(), return false);
538d3ece 104
105 return true;
106}
107
a213258c 108bool CZNC::ConnectUser(CUser *pUser) {
109 CString sSockName = "IRC::" + pUser->GetUserName();
b359f968 110 CIRCSock* pIRCSock = pUser->GetIRCSock();
a213258c 111
5c7b586b 112 if (!pUser->GetIRCConnectEnabled())
113 return false;
a213258c 114
115 if (pIRCSock || !pUser->HasServers())
116 return false;
117
a213258c 118 CServer* pServer = pUser->GetNextServer();
119
120 if (!pServer)
121 return false;
122
752f19c9 123 if (m_sConnectThrottle.GetItem(pServer->GetName()))
124 return false;
125
752f19c9 126 m_sConnectThrottle.AddItem(pServer->GetName());
127
e0dda308 128 DEBUG("User [" << pUser->GetUserName() << "] is connecting to [" << pServer->GetString(false) << "] ...");
129 pUser->PutStatus("Attempting to connect to [" + pServer->GetString(false) + "] ...");
a213258c 130
131 pIRCSock = new CIRCSock(pUser);
132 pIRCSock->SetPass(pServer->GetPass());
133
134 bool bSSL = false;
135#ifdef HAVE_LIBSSL
136 if (pServer->IsSSL()) {
137 bSSL = true;
138 }
139#endif
140
d0a38e41 141 MODULECALL(OnIRCConnecting(pIRCSock), pUser, NULL,
142 DEBUG("Some module aborted the connection attempt");
3f493072 143 pUser->PutStatus("Some module aborted the connection attempt");
d0a38e41 144 delete pIRCSock;
145 return false;
146 );
147
341263f9 148 if (!m_Manager.Connect(pServer->GetName(), pServer->GetPort(), sSockName, 120, bSSL, pUser->GetBindHost(), pIRCSock)) {
a213258c 149 pUser->PutStatus("Unable to connect. (Bad host?)");
150 }
151
152 return true;
153}
154
45dae8a1 155bool CZNC::HandleUserDeletion()
156{
157 map<CString, CUser*>::iterator it;
158 map<CString, CUser*>::iterator end;
538d3ece 159
291ad479 160 if (m_msDelUsers.empty())
45dae8a1 161 return false;
b51302be 162
45dae8a1 163 end = m_msDelUsers.end();
115b1708 164 for (it = m_msDelUsers.begin(); it != end; ++it) {
45dae8a1 165 CUser* pUser = it->second;
166 pUser->SetBeingDeleted(true);
57fb9fc8 167
45dae8a1 168 if (GetModules().OnDeleteUser(*pUser)) {
169 pUser->SetBeingDeleted(false);
170 continue;
171 }
45dae8a1 172 m_msUsers.erase(pUser->GetUserName());
c315a07d 173
b359f968 174 CIRCSock* pIRCSock = pUser->GetIRCSock();
c315a07d 175
45dae8a1 176 if (pIRCSock) {
177 m_Manager.DelSockByAddr(pIRCSock);
178 }
c315a07d 179
45dae8a1 180 pUser->DelClients();
45dae8a1 181 pUser->DelModules();
c3728f4c 182 CWebSock::FinishUserSessions(*pUser);
45dae8a1 183 AddBytesRead(pUser->BytesRead());
184 AddBytesWritten(pUser->BytesWritten());
185 delete pUser;
186 }
187
188 m_msDelUsers.clear();
c315a07d 189
45dae8a1 190 return true;
191}
192
672df0a8
A
193class CConfigWriteTimer : public CCron {
194 public:
195 CConfigWriteTimer(int iSecs) : CCron() {
196 SetName("Config write timer");
197 Start(iSecs);
198 }
199
200 protected:
201 void RunJob() {
202 CZNC::Get().SetConfigState(CZNC::ECONFIG_NEED_WRITE);
203
204 CZNC::Get().DisableConfigTimer();
205 }
206};
207
e64b2549 208void CZNC::Loop() {
45dae8a1 209 while (true) {
e326f6eb 210 CString sError;
e326f6eb 211
672df0a8
A
212 ConfigState eState = GetConfigState();
213 switch (eState) {
c1ee7248 214 case ECONFIG_NEED_REHASH:
215 SetConfigState(ECONFIG_NOTHING);
e326f6eb 216
803b34c6 217 if (RehashConfig(sError)) {
218 Broadcast("Rehashing succeeded", true);
219 } else {
220 Broadcast("Rehashing failed: " + sError, true);
221 Broadcast("ZNC is in some possibly inconsistent state!", true);
e326f6eb 222 }
c1ee7248 223 break;
672df0a8
A
224 case ECONFIG_WANT_WRITE:
225 SetConfigState(ECONFIG_NOTHING);
226
227 if (m_pConfigTimer == NULL) {
228 m_pConfigTimer = new CConfigWriteTimer(CONFIG_WRITE_INTERVAL);
229 GetManager().AddCron(m_pConfigTimer);
230 }
231 break;
c1ee7248 232 case ECONFIG_NEED_WRITE:
672df0a8 233 case ECONFIG_NEED_VERBOSE_WRITE:
c1ee7248 234 SetConfigState(ECONFIG_NOTHING);
235
672df0a8 236 if (!WriteConfig()) {
c1ee7248 237 Broadcast("Writing the config file failed", true);
672df0a8
A
238 } else if (eState == ECONFIG_NEED_VERBOSE_WRITE) {
239 Broadcast("Writing the config succeeded", true);
c1ee7248 240 }
241 break;
242 case ECONFIG_NOTHING:
243 break;
e326f6eb 244 }
245
45dae8a1 246 // Check for users that need to be deleted
247 if (HandleUserDeletion()) {
248 // Also remove those user(s) from the config file
29e14b42 249 WriteConfig();
c315a07d 250 }
251
a213258c 252 // Csocket wants micro seconds
cf41b0ac 253 // 500 msec to 600 sec
254 m_Manager.DynamicSelectLoop(500 * 1000, 600 * 1000 * 1000);
538d3ece 255 }
538d3ece 256}
257
e2b91a32 258CFile* CZNC::InitPidFile() {
538d3ece 259 if (!m_sPidFile.empty()) {
8a9ddda2 260 CString sFile;
261
262 // absolute path or relative to the data dir?
263 if (m_sPidFile[0] != '/')
264 sFile = GetZNCPath() + "/" + m_sPidFile;
265 else
266 sFile = m_sPidFile;
267
e2b91a32 268 return new CFile(sFile);
269 }
538d3ece 270
e2b91a32 271 return NULL;
272}
e4aabe6c 273
e2b91a32 274bool CZNC::WritePidFile(int iPid) {
275 CFile* File = InitPidFile();
276 if (File == NULL)
277 return false;
278
279 CUtils::PrintAction("Writing pid file [" + File->GetLongName() + "]");
280
281 bool bRet = false;
282 if (File->Open(O_WRONLY | O_TRUNC | O_CREAT)) {
283 File->Write(CString(iPid) + "\n");
284 File->Close();
285 bRet = true;
538d3ece 286 }
287
e2b91a32 288 delete File;
289 CUtils::PrintStatus(bRet);
290 return bRet;
538d3ece 291}
292
9a1c3874 293bool CZNC::DeletePidFile() {
e2b91a32 294 CFile* File = InitPidFile();
295 if (File == NULL)
296 return false;
9a1c3874 297
e2b91a32 298 CUtils::PrintAction("Deleting pid file [" + File->GetLongName() + "]");
299
d735e9d8 300 bool bRet = File->Delete();
e2b91a32 301
302 delete File;
303 CUtils::PrintStatus(bRet);
304 return bRet;
9a1c3874 305}
306
b03f495b 307bool CZNC::WritePemFile() {
8f0fc2fa 308#ifndef HAVE_LIBSSL
309 CUtils::PrintError("ZNC was not compiled with ssl support.");
f74ab87e 310 return false;
8f0fc2fa 311#else
bf171597 312 CString sPemFile = GetPemLocation();
bf171597 313
314 CUtils::PrintAction("Writing Pem file [" + sPemFile + "]");
315 FILE *f = fopen(sPemFile.c_str(), "w");
316
317 if (!f) {
318 CUtils::PrintStatus(false, "Unable to open");
319 return false;
320 }
321
dcf357b1 322 CUtils::GenerateCert(f, "");
bf171597 323 fclose(f);
324
325 CUtils::PrintStatus(true);
326 return true;
8f0fc2fa 327#endif
bf171597 328}
329
538d3ece 330void CZNC::DeleteUsers() {
115b1708 331 for (map<CString,CUser*>::iterator a = m_msUsers.begin(); a != m_msUsers.end(); ++a) {
0dd41818 332 a->second->SetBeingDeleted(true);
538d3ece 333 delete a->second;
334 }
335
336 m_msUsers.clear();
c4891c5d 337 DisableConnectUser();
538d3ece 338}
339
86924339 340bool CZNC::IsHostAllowed(const CString& sHostMask) const {
115b1708 341 for (map<CString,CUser*>::const_iterator a = m_msUsers.begin(); a != m_msUsers.end(); ++a) {
538d3ece 342 if (a->second->IsHostAllowed(sHostMask)) {
343 return true;
344 }
345 }
346
347 return false;
348}
349
b25e65db 350bool CZNC::AllowConnectionFrom(const CString& sIP) const {
16507001 351 if (m_uiAnonIPLimit == 0)
352 return true;
36ffa163 353 return (GetManager().GetAnonConnectionCount(sIP) < m_uiAnonIPLimit);
b25e65db 354}
355
0a622749 356void CZNC::InitDirs(const CString& sArgvPath, const CString& sDataDir) {
538d3ece 357 // If the bin was not ran from the current directory, we need to add that dir onto our cwd
beb5b49b 358 CString::size_type uPos = sArgvPath.rfind('/');
8db106bf 359 if (uPos == CString::npos)
360 m_sCurPath = "./";
361 else
362 m_sCurPath = CDir::ChangeDir("./", sArgvPath.Left(uPos), "");
538d3ece 363
364 // Try to set the user's home dir, default to binpath on failure
f9ffe6f4 365 CFile::InitHomePath(m_sCurPath);
538d3ece 366
0a622749 367 if (sDataDir.empty()) {
f9ffe6f4 368 m_sZNCPath = CFile::GetHomePath() + "/.znc";
0a622749 369 } else {
3b8134c3 370 m_sZNCPath = sDataDir;
0a622749 371 }
7f53cc81 372
f82f3cf0 373 m_sSSLCertFile = m_sZNCPath + "/znc.pem";
4b1fbab6 374}
375
f5c2528c 376CString CZNC::GetConfPath(bool bAllowMkDir) const {
fd5dd4c3 377 CString sConfPath = m_sZNCPath + "/configs";
f5c2528c 378 if (bAllowMkDir && !CFile::Exists(sConfPath)) {
804b259f 379 CDir::MakeDir(sConfPath);
380 }
381
382 return sConfPath;
383}
384
4b1fbab6 385CString CZNC::GetUserPath() const {
fd5dd4c3 386 CString sUserPath = m_sZNCPath + "/users";
4b1fbab6 387 if (!CFile::Exists(sUserPath)) {
388 CDir::MakeDir(sUserPath);
389 }
390
391 return sUserPath;
392}
393
394CString CZNC::GetModPath() const {
fd5dd4c3 395 CString sModPath = m_sZNCPath + "/modules";
4b1fbab6 396
397 if (!CFile::Exists(sModPath)) {
398 CDir::MakeDir(sModPath);
399 }
933f1cc6 400
4b1fbab6 401 return sModPath;
538d3ece 402}
403
8e59f751
US
404const CString& CZNC::GetCurPath() const {
405 if (!CFile::Exists(m_sCurPath)) {
406 CDir::MakeDir(m_sCurPath);
407 }
408 return m_sCurPath;
409}
410
411const CString& CZNC::GetHomePath() const {
412 return CFile::GetHomePath();
413}
414
415const CString& CZNC::GetZNCPath() const {
416 if (!CFile::Exists(m_sZNCPath)) {
417 CDir::MakeDir(m_sZNCPath);
418 }
419 return m_sZNCPath;
420}
421
422CString CZNC::GetPemLocation() const {
423 return CDir::ChangeDir("", m_sSSLCertFile);
424}
0013ab2c 425
f5c2528c 426CString CZNC::ExpandConfigPath(const CString& sConfigFile, bool bAllowMkDir) {
beb5b49b 427 CString sRetPath;
538d3ece 428
6bd03a84 429 if (sConfigFile.empty()) {
f5c2528c 430 sRetPath = GetConfPath(bAllowMkDir) + "/znc.conf";
6bd03a84 431 } else {
078bbcf0 432 if (sConfigFile.Left(2) == "./" || sConfigFile.Left(3) == "../") {
0013ab2c 433 sRetPath = GetCurPath() + "/" + sConfigFile;
078bbcf0 434 } else if (sConfigFile.Left(1) != "/") {
f5c2528c 435 sRetPath = GetConfPath(bAllowMkDir) + "/" + sConfigFile;
44e1416b 436 } else {
437 sRetPath = sConfigFile;
0013ab2c 438 }
439 }
440
441 return sRetPath;
442}
443
1f090c4d 444bool CZNC::WriteConfig() {
eb0d9d43 445 if (GetConfigFile().empty()) {
446 return false;
447 }
448
e51189ea 449 // We first write to a temporary file and then move it to the right place
a4d388b5 450 CFile *pFile = new CFile(GetConfigFile() + "~");
1f090c4d 451
a4d388b5
US
452 if (!pFile->Open(O_WRONLY | O_CREAT | O_TRUNC, 0600)) {
453 delete pFile;
eb0d9d43 454 return false;
455 }
456
457 // We have to "transfer" our lock on the config to the new file.
458 // The old file (= inode) is going away and thus a lock on it would be
459 // useless. These lock should always succeed (races, anyone?).
a4d388b5
US
460 if (!pFile->TryExLock()) {
461 pFile->Delete();
462 delete pFile;
1f090c4d 463 return false;
464 }
465
a4d388b5 466 pFile->Write(MakeConfigHeader() + "\n");
2472ea7a 467
a4d388b5
US
468 pFile->Write("AnonIPLimit = " + CString(m_uiAnonIPLimit) + "\n");
469 pFile->Write("MaxBufferSize= " + CString(m_uiMaxBufferSize) + "\n");
470 pFile->Write("SSLCertFile = " + CString(m_sSSLCertFile) + "\n");
a9ba4020 471 pFile->Write("ProtectWebSessions = " + CString(m_bProtectWebSessions) + "\n");
16507001 472
ca824705 473 for (size_t l = 0; l < m_vpListeners.size(); l++) {
474 CListener* pListener = m_vpListeners[l];
475 CString sHostPortion = pListener->GetBindHost();
ef9d9b21 476
ca824705 477 if (!sHostPortion.empty()) {
3188856a 478 sHostPortion = sHostPortion.FirstLine() + " ";
ca824705 479 }
480
6bc45bf8 481 CString sAcceptProtocol;
482 if(pListener->GetAcceptType() == CListener::ACCEPT_IRC)
483 sAcceptProtocol = "irc_only ";
484 else if(pListener->GetAcceptType() == CListener::ACCEPT_HTTP)
485 sAcceptProtocol = "web_only ";
486
6acaebf7 487 CString s6;
488 switch (pListener->GetAddrType()) {
489 case ADDR_IPV4ONLY:
490 s6 = "4";
491 break;
492 case ADDR_IPV6ONLY:
493 s6 = "6";
494 break;
495 case ADDR_ALL:
496 s6 = " ";
497 break;
498 }
ca824705 499
a4d388b5 500 pFile->Write("Listener" + s6 + " = " + sAcceptProtocol + sHostPortion +
6bc45bf8 501 CString((pListener->IsSSL()) ? "+" : "") + CString(pListener->GetPort()) + "\n");
ca824705 502 }
ef9d9b21 503
a4d388b5
US
504 pFile->Write("ConnectDelay = " + CString(m_uiConnectDelay) + "\n");
505 pFile->Write("ServerThrottle = " + CString(m_sConnectThrottle.GetTTL()/1000) + "\n");
1fa9187b 506
3188856a 507 if (!m_sPidFile.empty()) {
a4d388b5 508 pFile->Write("PidFile = " + m_sPidFile.FirstLine() + "\n");
3188856a 509 }
ad92c58c 510
511 if (!m_sSkinName.empty()) {
a4d388b5 512 pFile->Write("Skin = " + m_sSkinName.FirstLine() + "\n");
ad92c58c 513 }
514
3188856a 515 if (!m_sStatusPrefix.empty()) {
a4d388b5 516 pFile->Write("StatusPrefix = " + m_sStatusPrefix.FirstLine() + "\n");
3188856a 517 }
1f090c4d 518
d2e72850 519 for (unsigned int m = 0; m < m_vsMotd.size(); m++) {
a4d388b5 520 pFile->Write("Motd = " + m_vsMotd[m].FirstLine() + "\n");
d2e72850 521 }
522
341263f9 523 for (unsigned int v = 0; v < m_vsBindHosts.size(); v++) {
a4d388b5 524 pFile->Write("BindHost = " + m_vsBindHosts[v].FirstLine() + "\n");
573c4be4 525 }
526
1f090c4d 527 CGlobalModules& Mods = GetModules();
528
529 for (unsigned int a = 0; a < Mods.size(); a++) {
3188856a 530 CString sName = Mods[a]->GetModName();
1f090c4d 531 CString sArgs = Mods[a]->GetArgs();
532
533 if (!sArgs.empty()) {
3188856a 534 sArgs = " " + sArgs.FirstLine();
1f090c4d 535 }
536
a4d388b5 537 pFile->Write("LoadModule = " + sName.FirstLine() + sArgs + "\n");
1f090c4d 538 }
1f090c4d 539
647045b7
A
540 pFile->Write("forceserver = " + m_forceServer + "\n");
541 pFile->Write("webircpassword = " + m_webircPassword + "\n");
542
115b1708 543 for (map<CString,CUser*>::iterator it = m_msUsers.begin(); it != m_msUsers.end(); ++it) {
1f090c4d 544 CString sErr;
545
546 if (!it->second->IsValid(sErr)) {
235b10c2 547 DEBUG("** Error writing config for user [" << it->first << "] [" << sErr << "]");
1f090c4d 548 continue;
549 }
550
a4d388b5 551 pFile->Write("\n");
29e14b42 552
a4d388b5 553 if (!it->second->WriteConfig(*pFile)) {
235b10c2 554 DEBUG("** Error writing config for user [" << it->first << "]");
1f090c4d 555 }
556 }
557
eb0d9d43 558 // If Sync() fails... well, let's hope nothing important breaks..
a4d388b5 559 pFile->Sync();
1f090c4d 560
a4d388b5 561 if (pFile->HadError()) {
7f3e55e7 562 DEBUG("Error while writing the config, errno says: " + CString(strerror(errno)));
a4d388b5
US
563 pFile->Delete();
564 delete pFile;
7f3e55e7
US
565 return false;
566 }
567
e51189ea 568 // We wrote to a temporary name, move it to the right place
a4d388b5
US
569 if (!pFile->Move(GetConfigFile(), true)) {
570 DEBUG("Error while replacing the config file with a new version");
571 pFile->Delete();
572 delete pFile;
eb0d9d43 573 return false;
a4d388b5 574 }
eb0d9d43 575
576 // Everything went fine, just need to update the saved path.
a4d388b5
US
577 pFile->SetFileName(GetConfigFile());
578
579 // Make sure the lock is kept alive as long as we need it.
580 delete m_pLockFile;
581 m_pLockFile = pFile;
e51189ea 582
1f090c4d 583 return true;
584}
585
2472ea7a 586CString CZNC::MakeConfigHeader() {
587 return
588 "// WARNING\n"
589 "//\n"
590 "// Do NOT edit this file while ZNC is running!\n"
8cadb672 591 "// Use webadmin or *admin instead.\n"
f5fe9868
AS
592 "//\n"
593 "// Buf if you feel risky, you might want to read help on /znc saveconfig and /znc rehash.\n"
594 "// Also check http://en.znc.in/wiki/Configuration\n";
2472ea7a 595}
596
de2f07ec 597bool CZNC::WriteNewConfig(const CString& sConfigFile) {
beb5b49b 598 CString sAnswer, sUser;
cc5c7716 599 VCString vsLines;
708366d9 600
2472ea7a 601 vsLines.push_back(MakeConfigHeader());
0013ab2c 602
ef27fee3 603 m_sConfigFile = ExpandConfigPath(sConfigFile);
cc5c7716 604 CUtils::PrintMessage("Building new config");
878c5d5a 605
0013ab2c 606 CUtils::PrintMessage("");
7bb4ed34 607 CUtils::PrintMessage("First let's start with some global settings...");
0013ab2c 608 CUtils::PrintMessage("");
609
ef9d9b21 610 // Listen
79512603
AS
611#ifdef HAVE_IPV6
612 bool b6 = true;
613#else
614 bool b6 = false;
615#endif
11ffe9db 616 CString sListenHost;
617 CString sSSL;
d0f33f8b 618 unsigned int uListenPort = 0;
11ffe9db 619 bool bSuccess;
620
621 do {
622 bSuccess = true;
63a5de13 623 while (!CUtils::GetNumInput("What port would you like ZNC to listen on?", uListenPort, 1025, 65535)) ;
ca824705 624
0013ab2c 625#ifdef HAVE_LIBSSL
11ffe9db 626 if (CUtils::GetBoolInput("Would you like ZNC to listen using SSL?", !sSSL.empty())) {
627 sSSL = "+";
628
629 CString sPemFile = GetPemLocation();
630 if (!CFile::Exists(sPemFile)) {
631 CUtils::PrintError("Unable to locate pem file: [" + sPemFile + "]");
632 if (CUtils::GetBoolInput("Would you like to create a new pem file now?",
633 true)) {
634 WritePemFile();
635 }
4bd90ab9 636 }
11ffe9db 637 } else
638 sSSL = "";
ca824705 639#endif
640
ca824705 641#ifdef HAVE_IPV6
79512603 642 b6 = CUtils::GetBoolInput("Would you like ZNC to listen using ipv6?", b6);
0013ab2c 643#endif
644
11ffe9db 645 CUtils::GetInput("Listen Host", sListenHost, sListenHost, "Blank for all ips");
646
647 CUtils::PrintAction("Verifying the listener");
648 CListener* pListener = new CListener(uListenPort, sListenHost, !sSSL.empty(),
79512603 649 b6 ? ADDR_ALL : ADDR_IPV4ONLY, CListener::ACCEPT_ALL);
11ffe9db 650 if (!pListener->Listen()) {
651 CUtils::PrintStatus(false, FormatBindError());
652 bSuccess = false;
653 } else
654 CUtils::PrintStatus(true);
655 delete pListener;
656 } while (!bSuccess);
ef9d9b21 657
658 if (!sListenHost.empty()) {
ca824705 659 sListenHost += " ";
ef9d9b21 660 }
661
79512603 662 vsLines.push_back("Listener" + CString(b6 ? " " : "4") + " = " + sListenHost + sSSL + CString(uListenPort));
ef9d9b21 663 // !Listen
0013ab2c 664
0bbab8f4 665 set<CModInfo> ssGlobalMods;
89e5079c 666 GetModules().GetAvailableMods(ssGlobalMods, true);
207478d3 667 size_t uNrOtherGlobalMods = FilterUncommonModules(ssGlobalMods);
0bbab8f4 668
291ad479 669 if (!ssGlobalMods.empty()) {
0bbab8f4 670 CUtils::PrintMessage("");
671 CUtils::PrintMessage("-- Global Modules --");
672 CUtils::PrintMessage("");
673
92304835
AS
674 CTable Table;
675 Table.AddColumn("Name");
676 Table.AddColumn("Description");
677 set<CModInfo>::iterator it;
678
679 for (it = ssGlobalMods.begin(); it != ssGlobalMods.end(); ++it) {
680 const CModInfo& Info = *it;
681 Table.AddRow();
682 Table.SetCell("Name", Info.GetName());
683 Table.SetCell("Description", Info.GetDescription().Ellipsize(128));
684 }
1c90fd9b 685
92304835
AS
686 unsigned int uTableIdx = 0; CString sLine;
687 while (Table.GetLine(uTableIdx++, sLine)) {
688 CUtils::PrintMessage(sLine);
689 }
1c90fd9b 690
92304835
AS
691 if (uNrOtherGlobalMods > 0) {
692 CUtils::PrintMessage("And " + CString(uNrOtherGlobalMods) + " other (uncommon) modules. You can enable those later.");
693 }
207478d3 694
92304835 695 CUtils::PrintMessage("");
1c90fd9b 696
92304835
AS
697 for (it = ssGlobalMods.begin(); it != ssGlobalMods.end(); ++it) {
698 const CModInfo& Info = *it;
699 CString sName = Info.GetName();
0bbab8f4 700
ac5c021c 701 if (CDebug::StdoutIsTTY()) {
92304835
AS
702 if (CUtils::GetBoolInput("Load global module <\033[1m" + sName + "\033[22m>?", false))
703 vsLines.push_back("LoadModule = " + sName);
704 } else {
705 if (CUtils::GetBoolInput("Load global module <" + sName + ">?", false))
706 vsLines.push_back("LoadModule = " + sName);
0bbab8f4 707 }
708 }
709 }
0bbab8f4 710
0013ab2c 711 // User
712 CUtils::PrintMessage("");
7bb4ed34 713 CUtils::PrintMessage("Now we need to set up a user...");
b3d6a86f 714 CUtils::PrintMessage("ZNC needs one user per IRC network.");
0013ab2c 715 CUtils::PrintMessage("");
716
ef1c8de5 717 bool bFirstUser = true;
718
0013ab2c 719 do {
720 vsLines.push_back("");
beb5b49b 721 CString sNick;
ecd587c5 722 do {
723 CUtils::GetInput("Username", sUser, "", "AlphaNumeric");
724 } while (!CUser::IsValidUserName(sUser));
725
726 vsLines.push_back("<User " + sUser + ">");
bf2bd397 727 CString sSalt;
728 sAnswer = CUtils::GetSaltedHashPass(sSalt);
cd63bae0 729 vsLines.push_back("\tPass = " + CUtils::sDefaultHash + "#" + sAnswer + "#" + sSalt + "#");
ef1c8de5 730
731 if (CUtils::GetBoolInput("Would you like this user to be an admin?", bFirstUser)) {
a3c71606 732 vsLines.push_back("\tAdmin = true");
ef1c8de5 733 } else {
a3c71606 734 vsLines.push_back("\tAdmin = false");
ef1c8de5 735 }
736
47c0cb9b 737 CUtils::GetInput("Nick", sNick, CUser::MakeCleanUserName(sUser));
738 vsLines.push_back("\tNick = " + sNick);
739 CUtils::GetInput("Alt Nick", sAnswer, sNick + "_");
740 if (!sAnswer.empty()) {
741 vsLines.push_back("\tAltNick = " + sAnswer);
742 }
743 CUtils::GetInput("Ident", sAnswer, sNick);
744 vsLines.push_back("\tIdent = " + sAnswer);
745 CUtils::GetInput("Real Name", sAnswer, "Got ZNC?");
746 vsLines.push_back("\tRealName = " + sAnswer);
341263f9 747 CUtils::GetInput("Bind Host", sAnswer, "", "optional");
47c0cb9b 748 if (!sAnswer.empty()) {
341263f9 749 vsLines.push_back("\tBindHost = " + sAnswer);
47c0cb9b 750 }
d2e72850 751 // todo: Possibly add motd
0013ab2c 752
04e1d6aa 753 unsigned int uBufferCount = 0;
754
47c0cb9b 755 CUtils::GetNumInput("Number of lines to buffer per channel", uBufferCount, 0, ~0, 50);
756 if (uBufferCount) {
757 vsLines.push_back("\tBuffer = " + CString(uBufferCount));
758 }
b475f5d6 759 if (CUtils::GetBoolInput("Would you like to keep buffers after replay?", false)) {
0013ab2c 760 vsLines.push_back("\tKeepBuffer = true");
761 } else {
762 vsLines.push_back("\tKeepBuffer = false");
763 }
764
765 CUtils::GetInput("Default channel modes", sAnswer, "+stn");
766 if (!sAnswer.empty()) {
767 vsLines.push_back("\tChanModes = " + sAnswer);
768 }
769
0bbab8f4 770 set<CModInfo> ssUserMods;
89e5079c 771 GetModules().GetAvailableMods(ssUserMods);
207478d3 772 size_t uNrOtherUserMods = FilterUncommonModules(ssUserMods);
e50580ab 773
0bbab8f4 774 if (ssUserMods.size()) {
e50580ab 775 vsLines.push_back("");
776 CUtils::PrintMessage("");
0bbab8f4 777 CUtils::PrintMessage("-- User Modules --");
e50580ab 778 CUtils::PrintMessage("");
779
92304835
AS
780 CTable Table;
781 Table.AddColumn("Name");
782 Table.AddColumn("Description");
783 set<CModInfo>::iterator it;
1c90fd9b 784
92304835
AS
785 for (it = ssUserMods.begin(); it != ssUserMods.end(); ++it) {
786 const CModInfo& Info = *it;
787 Table.AddRow();
788 Table.SetCell("Name", Info.GetName());
789 Table.SetCell("Description", Info.GetDescription().Ellipsize(128));
790 }
1c90fd9b 791
92304835
AS
792 unsigned int uTableIdx = 0; CString sLine;
793 while (Table.GetLine(uTableIdx++, sLine)) {
794 CUtils::PrintMessage(sLine);
795 }
796
797 if (uNrOtherUserMods > 0) {
798 CUtils::PrintMessage("And " + CString(uNrOtherUserMods) + " other (uncommon) modules. You can enable those later.");
799 }
207478d3 800
92304835 801 CUtils::PrintMessage("");
1c90fd9b 802
92304835
AS
803 for (it = ssUserMods.begin(); it != ssUserMods.end(); ++it) {
804 const CModInfo& Info = *it;
805 CString sName = Info.GetName();
279279fe 806
ac5c021c 807 if (CDebug::StdoutIsTTY()) {
92304835
AS
808 if (CUtils::GetBoolInput("Load module <\033[1m" + sName + "\033[22m>?", false))
809 vsLines.push_back("\tLoadModule = " + sName);
810 } else {
811 if (CUtils::GetBoolInput("Load module <" + sName + ">?", false))
812 vsLines.push_back("\tLoadModule = " + sName);
e50580ab 813 }
814 }
815 }
816
0013ab2c 817 vsLines.push_back("");
818 CUtils::PrintMessage("");
e50580ab 819 CUtils::PrintMessage("-- IRC Servers --");
35ae34fa 820 CUtils::PrintMessage("Only add servers from the same IRC network.");
7bb4ed34 821 CUtils::PrintMessage("If a server from the list can't be reached, another server will be used.");
0013ab2c 822 CUtils::PrintMessage("");
823
824 do {
beb5b49b 825 CString sHost, sPass;
0013ab2c 826 bool bSSL = false;
d0f33f8b 827 unsigned int uServerPort = 0;
04e1d6aa 828
c64d7bc1 829 while (!CUtils::GetInput("IRC server", sHost, "", "host only") || !CServer::IsValidHostName(sHost)) ;
d0f33f8b 830 while (!CUtils::GetNumInput("[" + sHost + "] Port", uServerPort, 1, 65535, 6667)) ;
0013ab2c 831 CUtils::GetInput("[" + sHost + "] Password (probably empty)", sPass);
832
833#ifdef HAVE_LIBSSL
b3d6a86f 834 bSSL = CUtils::GetBoolInput("Does this server use SSL?", false);
0013ab2c 835#endif
836
d0f33f8b 837 vsLines.push_back("\tServer = " + sHost + ((bSSL) ? " +" : " ") + CString(uServerPort) + " " + sPass);
cc5c7716 838
839 CUtils::PrintMessage("");
b3d6a86f 840 } while (CUtils::GetBoolInput("Would you like to add another server for this IRC network?", false));
0013ab2c 841
842 vsLines.push_back("");
843 CUtils::PrintMessage("");
e50580ab 844 CUtils::PrintMessage("-- Channels --");
0013ab2c 845 CUtils::PrintMessage("");
846
beb5b49b 847 CString sArg = "a";
848 CString sPost = " for ZNC to automatically join?";
ecd587c5 849 bool bDefault = true;
0013ab2c 850
ecd587c5 851 while (CUtils::GetBoolInput("Would you like to add " + sArg + " channel" + sPost, bDefault)) {
b490b120 852 while (!CUtils::GetInput("Channel name", sAnswer)) ;
0013ab2c 853 vsLines.push_back("\t<Chan " + sAnswer + ">");
854 vsLines.push_back("\t</Chan>");
855 sArg = "another";
04e1d6aa 856 sPost = "?";
ecd587c5 857 bDefault = false;
6cb31706 858 }
0013ab2c 859
860 vsLines.push_back("</User>");
861
862 CUtils::PrintMessage("");
ef1c8de5 863 bFirstUser = false;
7bb4ed34 864 } while (CUtils::GetBoolInput("Would you like to set up another user (e.g. for connecting to another network)?", false));
0013ab2c 865 // !User
866
cc5c7716 867 CFile File;
868 bool bFileOK, bFileOpen = false;
869 do {
de2f07ec 870 CUtils::PrintAction("Writing config [" + m_sConfigFile + "]");
878c5d5a 871
cc5c7716 872 bFileOK = true;
de2f07ec 873 if (CFile::Exists(m_sConfigFile)) {
a4d388b5 874 if (!File.TryExLock(m_sConfigFile)) {
cc5c7716 875 CUtils::PrintStatus(false, "ZNC is currently running on this config.");
876 bFileOK = false;
877 } else {
a4d388b5 878 File.Close();
cc5c7716 879 CUtils::PrintStatus(false, "This config already exists.");
880 if (CUtils::GetBoolInput("Would you like to overwrite it?", false))
de2f07ec 881 CUtils::PrintAction("Overwriting config [" + m_sConfigFile + "]");
cc5c7716 882 else
883 bFileOK = false;
884 }
885 }
878c5d5a 886
cc5c7716 887 if (bFileOK) {
de2f07ec 888 File.SetFileName(m_sConfigFile);
80a84ba4 889 if (File.Open(O_WRONLY | O_CREAT | O_TRUNC, 0600)) {
878c5d5a 890 bFileOpen = true;
891 } else {
cc5c7716 892 CUtils::PrintStatus(false, "Unable to open file");
893 bFileOK = false;
878c5d5a 894 }
895 }
cc5c7716 896 if (!bFileOK) {
de2f07ec 897 CUtils::GetInput("Please specify an alternate location (or \"stdout\" for displaying the config)", m_sConfigFile, m_sConfigFile);
898 if (m_sConfigFile.Equals("stdout"))
cc5c7716 899 bFileOK = true;
900 else
de2f07ec 901 m_sConfigFile = ExpandConfigPath(m_sConfigFile);
cc5c7716 902 }
903 } while (!bFileOK);
878c5d5a 904
905 if (!bFileOpen) {
906 CUtils::PrintMessage("");
cc5c7716 907 CUtils::PrintMessage("Printing the new config to stdout:");
878c5d5a 908 CUtils::PrintMessage("");
909 cout << endl << "----------------------------------------------------------------------------" << endl << endl;
0013ab2c 910 }
911
912 for (unsigned int a = 0; a < vsLines.size(); a++) {
878c5d5a 913 if (bFileOpen) {
914 File.Write(vsLines[a] + "\n");
915 } else {
916 cout << vsLines[a] << endl;
917 }
538d3ece 918 }
919
878c5d5a 920 if (bFileOpen) {
921 File.Close();
bff969f4
US
922 if (File.HadError())
923 CUtils::PrintStatus(false, "There was an error while writing the config");
924 else
925 CUtils::PrintStatus(true);
878c5d5a 926 } else {
927 cout << endl << "----------------------------------------------------------------------------" << endl << endl;
928 }
0d0c5462 929
bff969f4
US
930 if (File.HadError()) {
931 bFileOpen = false;
932 CUtils::PrintMessage("Printing the new config to stdout instead:");
933 cout << endl << "----------------------------------------------------------------------------" << endl << endl;
934 for (unsigned int a = 0; a < vsLines.size(); a++) {
935 cout << vsLines[a] << endl;
936 }
937 cout << endl << "----------------------------------------------------------------------------" << endl << endl;
938 }
939
16be1817 940 const CString sProtocol(sSSL.empty() ? "http" : "https");
0d0c5462 941 CUtils::PrintMessage("");
7bb4ed34 942 CUtils::PrintMessage("To connect to this ZNC you need to connect to it as your IRC server", true);
0d0c5462 943 CUtils::PrintMessage("using the port that you supplied. You have to supply your login info", true);
7bb4ed34 944 CUtils::PrintMessage("as the IRC server password like this: user:pass.", true);
0d0c5462 945 CUtils::PrintMessage("");
946 CUtils::PrintMessage("Try something like this in your IRC client...", true);
16be1817 947 CUtils::PrintMessage("/server <znc_server_ip> " + sSSL + CString(uListenPort) + " " + sUser + ":<pass>", true);
948 CUtils::PrintMessage("And this in your browser...", true);
949 CUtils::PrintMessage(sProtocol + "://<znc_server_ip>:" + CString(uListenPort) + "/", true);
0d0c5462 950 CUtils::PrintMessage("");
951
a4d388b5 952 File.UnLock();
7bb4ed34 953 return bFileOpen && CUtils::GetBoolInput("Launch ZNC now?", true);
0013ab2c 954}
955
207478d3 956size_t CZNC::FilterUncommonModules(set<CModInfo>& ssModules) {
957 const char* ns[] = { "webadmin", "admin",
958 "chansaver", "keepnick", "simple_away", "partyline",
959 "kickrejoin", "nickserv", "perform" };
960 const set<CString> ssNames(ns, ns + sizeof(ns) / sizeof(ns[0]));
961
962 size_t uNrRemoved = 0;
963 for(set<CModInfo>::iterator it = ssModules.begin(); it != ssModules.end(); ) {
964 if(ssNames.count(it->GetName()) > 0) {
965 it++;
966 } else {
967 set<CModInfo>::iterator it2 = it++;
968 ssModules.erase(it2);
969 uNrRemoved++;
970 }
971 }
972
973 return uNrRemoved;
974}
207478d3 975
8e33a737 976bool CZNC::ParseConfig(const CString& sConfig)
977{
978 CString s;
979
f5c2528c 980 m_sConfigFile = ExpandConfigPath(sConfig, false);
0013ab2c 981
8e33a737 982 return DoRehash(s);
983}
984
985bool CZNC::RehashConfig(CString& sError)
986{
1023d868 987 ALLMODULECALL(OnPreRehash(), NOTHING);
dde7921e 988
8e33a737 989 // This clears m_msDelUsers
990 HandleUserDeletion();
991
992 // Mark all users as going-to-be deleted
993 m_msDelUsers = m_msUsers;
994 m_msUsers.clear();
995
996 if (DoRehash(sError)) {
1023d868 997 ALLMODULECALL(OnPostRehash(), NOTHING);
cb066e24 998
8e33a737 999 return true;
1000 }
1001
1002 // Rehashing failed, try to recover
1003 CString s;
291ad479 1004 while (!m_msDelUsers.empty()) {
8e33a737 1005 AddUser(m_msDelUsers.begin()->second, s);
1006 m_msDelUsers.erase(m_msDelUsers.begin());
1007 }
1008
1009 return false;
1010}
1011
1012bool CZNC::DoRehash(CString& sError)
1013{
1014 sError.clear();
1015
1f090c4d 1016 CUtils::PrintAction("Opening Config [" + m_sConfigFile + "]");
a9d08422 1017
1f090c4d 1018 if (!CFile::Exists(m_sConfigFile)) {
8e33a737 1019 sError = "No such file";
1020 CUtils::PrintStatus(false, sError);
7bb4ed34 1021 CUtils::PrintMessage("Restart ZNC with the --makeconf option if you wish to create this config.");
878c5d5a 1022 return false;
7e7bbfad 1023 }
1024
1f090c4d 1025 if (!CFile::IsReg(m_sConfigFile)) {
8e33a737 1026 sError = "Not a file";
1027 CUtils::PrintStatus(false, sError);
7e7bbfad 1028 return false;
1029 }
1030
a4d388b5 1031 CFile *pFile = new CFile(m_sConfigFile);
12734782 1032
33ce80f4 1033 // need to open the config file Read/Write for fcntl()
1034 // exclusive locking to work properly!
a4d388b5 1035 if (!pFile->Open(m_sConfigFile, O_RDWR)) {
fe361f7a 1036 sError = "Can not open config file";
1037 CUtils::PrintStatus(false, sError);
a4d388b5 1038 delete pFile;
fe361f7a 1039 return false;
1040 }
1041
a4d388b5 1042 if (!pFile->TryExLock()) {
8e33a737 1043 sError = "ZNC is already running on this config.";
1044 CUtils::PrintStatus(false, sError);
a4d388b5 1045 delete pFile;
f32fb1b3 1046 return false;
1047 }
1048
a4d388b5
US
1049 // (re)open the config file
1050 delete m_pLockFile;
1051 m_pLockFile = pFile;
1052 CFile &File = *pFile;
538d3ece 1053
70c77458
US
1054 CConfig config;
1055 if (!config.Parse(File, sError)) {
52f755f0 1056 CUtils::PrintStatus(false, sError);
538d3ece 1057 return false;
1058 }
a9d08422 1059 CUtils::PrintStatus(true);
1060
341263f9 1061 m_vsBindHosts.clear();
8e33a737 1062 m_vsMotd.clear();
1063
1064 // Delete all listeners
291ad479 1065 while (!m_vpListeners.empty()) {
8e33a737 1066 delete m_vpListeners[0];
1067 m_vpListeners.erase(m_vpListeners.begin());
1068 }
1069
99f1efc8 1070 MCString msModules; // Modules are queued for later loading
538d3ece 1071
70c77458 1072 VCString vsList;
70c77458
US
1073 VCString::const_iterator vit;
1074 config.FindStringVector("loadmodule", vsList);
1075 for (vit = vsList.begin(); vit != vsList.end(); ++vit) {
1076 CString sModName = vit->Token(0);
1077 CString sArgs = vit->Token(1, true);
538d3ece 1078
70c77458
US
1079 if (msModules.find(sModName) != msModules.end()) {
1080 sError = "Module [" + sModName +
1081 "] already loaded";
1082 CUtils::PrintError(sError);
1083 return false;
538d3ece 1084 }
70c77458
US
1085 CString sModRet;
1086 CModule *pOldMod;
538d3ece 1087
70c77458
US
1088 pOldMod = GetModules().FindModule(sModName);
1089 if (!pOldMod) {
1090 CUtils::PrintAction("Loading Global Module [" + sModName + "]");
538d3ece 1091
70c77458 1092 bool bModRet = GetModules().LoadModule(sModName, sArgs, NULL, sModRet);
67f4a73f 1093
70c77458
US
1094 CUtils::PrintStatus(bModRet, sModRet);
1095 if (!bModRet) {
1096 sError = sModRet;
1097 return false;
1098 }
1099 } else if (pOldMod->GetArgs() != sArgs) {
1100 CUtils::PrintAction("Reloading Global Module [" + sModName + "]");
8e33a737 1101
70c77458 1102 bool bModRet = GetModules().ReloadModule(sModName, sArgs, NULL, sModRet);
8e33a737 1103
70c77458
US
1104 CUtils::PrintStatus(bModRet, sModRet);
1105 if (!bModRet) {
1106 sError = sModRet;
1107 return false;
538d3ece 1108 }
70c77458
US
1109 } else
1110 CUtils::PrintMessage("Module [" + sModName + "] already loaded.");
1111
1112 msModules[sModName] = sArgs;
1113 }
1114
90fb9e8c
US
1115 CString sISpoofFormat, sISpoofFile;
1116 config.FindStringEntry("ispoofformat", sISpoofFormat);
1117 config.FindStringEntry("ispooffile", sISpoofFile);
1118 if (!sISpoofFormat.empty() || !sISpoofFile.empty()) {
1119 CModule *pIdentFileMod = GetModules().FindModule("identfile");
1120 if (!pIdentFileMod) {
1121 CUtils::PrintAction("Loading Global Module [identfile]");
1122
1123 CString sModRet;
1124 bool bModRet = GetModules().LoadModule("identfile", "", NULL, sModRet);
1125
90fb9e8c
US
1126 CUtils::PrintStatus(bModRet, sModRet);
1127 if (!bModRet) {
1128 sError = sModRet;
1129 return false;
1130 }
1131
1132 pIdentFileMod = GetModules().FindModule("identfile");
1133 msModules["identfile"] = "";
1134 }
1135
1136 pIdentFileMod->SetNV("File", sISpoofFile);
1137 pIdentFileMod->SetNV("Format", sISpoofFormat);
1138 }
1139
70c77458
US
1140 config.FindStringVector("motd", vsList);
1141 for (vit = vsList.begin(); vit != vsList.end(); ++vit) {
1142 AddMotd(*vit);
1143 }
1144
1145 config.FindStringVector("bindhost", vsList);
1146 for (vit = vsList.begin(); vit != vsList.end(); ++vit) {
1147 AddBindHost(*vit);
1148 }
1149 config.FindStringVector("vhost", vsList);
1150 for (vit = vsList.begin(); vit != vsList.end(); ++vit) {
1151 AddBindHost(*vit);
1152 }
1153
1154 CString sVal;
1155 if (config.FindStringEntry("pidfile", sVal))
1156 m_sPidFile = sVal;
1157 if (config.FindStringEntry("statusprefix", sVal))
1158 m_sStatusPrefix = sVal;
1159 if (config.FindStringEntry("sslcertfile", sVal))
1160 m_sSSLCertFile = sVal;
1161 if (config.FindStringEntry("skin", sVal))
1162 SetSkinName(sVal);
1163 if (config.FindStringEntry("connectdelay", sVal))
1164 m_uiConnectDelay = sVal.ToUInt();
1165 if (config.FindStringEntry("serverthrottle", sVal))
1166 m_sConnectThrottle.SetTTL(sVal.ToUInt() * 1000);
1167 if (config.FindStringEntry("anoniplimit", sVal))
1168 m_uiAnonIPLimit = sVal.ToUInt();
1169 if (config.FindStringEntry("maxbuffersize", sVal))
1170 m_uiMaxBufferSize = sVal.ToUInt();
a9ba4020
ME
1171 if (config.FindStringEntry("protectwebsessions", sVal))
1172 m_bProtectWebSessions = sVal.ToBool();
a0cc3de4
A
1173 if (config.FindStringEntry("webircpassword", sVal))
1174 m_webircPassword = sVal;
6cabf380
A
1175 if (config.FindStringEntry("forceserver", sVal))
1176 m_forceServer = sVal;
70c77458 1177
3fa04f8c
US
1178 // This has to be after SSLCertFile is handled since it uses that value
1179 const char *szListenerEntries[] = {
1180 "listen", "listen6", "listen4",
1181 "listener", "listener6", "listener4"
1182 };
1183 const size_t numListenerEntries = sizeof(szListenerEntries) / sizeof(szListenerEntries[0]);
1184
1185 for (size_t i = 0; i < numListenerEntries; i++) {
1186 config.FindStringVector(szListenerEntries[i], vsList);
1187 vit = vsList.begin();
1188
1189 for (; vit != vsList.end(); ++vit) {
1190 if (!AddListener(szListenerEntries[i] + CString(" ") + *vit, sError))
1191 return false;
1192 }
1193 }
1194
70c77458
US
1195 CConfig::SubConfig subConf;
1196 CConfig::SubConfig::const_iterator subIt;
1197 config.FindSubConfig("user", subConf);
1198 for (subIt = subConf.begin(); subIt != subConf.end(); ++subIt) {
1199 const CString& sUserName = subIt->first;
1200 CConfig* pSubConf = subIt->second.m_pSubConfig;
1201 CUser* pRealUser = NULL;
1202
1203 CUtils::PrintMessage("Loading user [" + sUserName + "]");
1204
1205 // Either create a CUser* or use an existing one
1206 map<CString, CUser*>::iterator it = m_msDelUsers.find(sUserName);
1207
1208 if (it != m_msDelUsers.end()) {
1209 pRealUser = it->second;
1210 m_msDelUsers.erase(it);
538d3ece 1211 }
1212
70c77458 1213 CUser* pUser = new CUser(sUserName);
a3ef9b2d 1214
70c77458
US
1215 if (!m_sStatusPrefix.empty()) {
1216 if (!pUser->SetStatusPrefix(m_sStatusPrefix)) {
1217 sError = "Invalid StatusPrefix [" + m_sStatusPrefix + "] Must be 1-5 chars, no spaces.";
1218 CUtils::PrintError(sError);
1219 return false;
1220 }
1221 }
a3ef9b2d 1222
70c77458
US
1223 if (!pUser->ParseConfig(pSubConf, sError)) {
1224 CUtils::PrintError(sError);
1225 delete pUser;
1226 pUser = NULL;
1227 return false;
1228 }
538d3ece 1229
70c77458
US
1230 if (!pSubConf->empty()) {
1231 sError = "Unhandled lines in config for User [" + sUserName + "]!";
1232 CUtils::PrintError(sError);
ca824705 1233
70c77458
US
1234 DumpConfig(pSubConf);
1235 return false;
1236 }
538d3ece 1237
70c77458
US
1238 CString sErr;
1239 if (pRealUser) {
1240 if (!pRealUser->Clone(*pUser, sErr)
1241 || !AddUser(pRealUser, sErr)) {
1242 sError = "Invalid user [" + pUser->GetUserName() + "] " + sErr;
1243 DEBUG("CUser::Clone() failed in rehash");
538d3ece 1244 }
70c77458
US
1245 pUser->SetBeingDeleted(true);
1246 delete pUser;
1247 pUser = NULL;
1248 } else if (!AddUser(pUser, sErr)) {
1249 sError = "Invalid user [" + pUser->GetUserName() + "] " + sErr;
538d3ece 1250 }
1251
70c77458 1252 if (!sError.empty()) {
322483ab 1253 CUtils::PrintError(sError);
70c77458
US
1254 if (pUser) {
1255 pUser->SetBeingDeleted(true);
1256 delete pUser;
1257 pUser = NULL;
1258 }
322483ab 1259 return false;
1260 }
70c77458
US
1261
1262 pUser = NULL;
1263 pRealUser = NULL;
538d3ece 1264 }
1265
70c77458
US
1266 if (!config.empty()) {
1267 sError = "Unhandled lines in config!";
1268 CUtils::PrintError(sError);
1269
1270 DumpConfig(&config);
1271 return false;
1272 }
1273
1274
9109df92 1275 // Unload modules which are no longer in the config
8e33a737 1276 set<CString> ssUnload;
1277 for (size_t i = 0; i < GetModules().size(); i++) {
1278 CModule *pCurMod = GetModules()[i];
1279
1280 if (msModules.find(pCurMod->GetModName()) == msModules.end())
1281 ssUnload.insert(pCurMod->GetModName());
1282 }
1283
115b1708 1284 for (set<CString>::iterator it = ssUnload.begin(); it != ssUnload.end(); ++it) {
8e33a737 1285 if (GetModules().UnloadModule(*it))
1286 CUtils::PrintMessage("Unloaded Global Module [" + *it + "]");
1287 else
1288 CUtils::PrintMessage("Could not unload [" + *it + "]");
1289 }
df1c36ca 1290
291ad479 1291 if (m_msUsers.empty()) {
8e33a737 1292 sError = "You must define at least one user in your config.";
1293 CUtils::PrintError(sError);
538d3ece 1294 return false;
1295 }
1296
291ad479 1297 if (m_vpListeners.empty()) {
8e33a737 1298 sError = "You must supply at least one Listen port in your config.";
1299 CUtils::PrintError(sError);
077b25e7 1300 return false;
1301 }
1302
d08829ee 1303 // Make sure that users that want to connect do so and also make sure a
1304 // new ConnectDelay setting is applied.
1305 DisableConnectUser();
abeba8ae 1306 EnableConnectUser();
c20f8427 1307
538d3ece 1308 return true;
1309}
3dde793e 1310
70c77458
US
1311void CZNC::DumpConfig(const CConfig* pConfig) {
1312 CConfig::EntryMapIterator eit = pConfig->BeginEntries();
1313 for (; eit != pConfig->EndEntries(); ++eit) {
1314 const CString& sKey = eit->first;
1315 const VCString& vsList = eit->second;
1316 VCString::const_iterator it = vsList.begin();
1317 for (; it != vsList.end(); ++it) {
1318 CUtils::PrintError(sKey + " = " + *it);
1319 }
1320 }
1321
1322 CConfig::SubConfigMapIterator sit = pConfig->BeginSubConfigs();
1323 for (; sit != pConfig->EndSubConfigs(); ++sit) {
1324 const CString& sKey = sit->first;
1325 const CConfig::SubConfig& sSub = sit->second;
1326 CConfig::SubConfig::const_iterator it = sSub.begin();
1327
1328 for (; it != sSub.end(); ++it) {
1329 CUtils::PrintError("SubConfig [" + sKey + " " + it->first + "]:");
1330 DumpConfig(it->second.m_pSubConfig);
1331 }
1332 }
1333}
1334
341263f9 1335void CZNC::ClearBindHosts() {
1336 m_vsBindHosts.clear();
7cb1d2f9 1337}
1338
341263f9 1339bool CZNC::AddBindHost(const CString& sHost) {
7cb1d2f9 1340 if (sHost.empty()) {
1341 return false;
1342 }
1343
341263f9 1344 for (unsigned int a = 0; a < m_vsBindHosts.size(); a++) {
1345 if (m_vsBindHosts[a].Equals(sHost)) {
573c4be4 1346 return false;
1347 }
1348 }
1349
341263f9 1350 m_vsBindHosts.push_back(sHost);
573c4be4 1351 return true;
1352}
1353
341263f9 1354bool CZNC::RemBindHost(const CString& sHost) {
ce31b29b 1355 VCString::iterator it;
341263f9 1356 for (it = m_vsBindHosts.begin(); it != m_vsBindHosts.end(); ++it) {
ce31b29b 1357 if (sHost.Equals(*it)) {
341263f9 1358 m_vsBindHosts.erase(it);
ce31b29b 1359 return true;
1360 }
1361 }
1362
1363 return false;
573c4be4 1364}
1365
a773c13f 1366void CZNC::Broadcast(const CString& sMessage, bool bAdminOnly,
1367 CUser* pSkipUser, CClient *pSkipClient) {
115b1708 1368 for (map<CString,CUser*>::iterator a = m_msUsers.begin(); a != m_msUsers.end(); ++a) {
a773c13f 1369 if (bAdminOnly && !a->second->IsAdmin())
1370 continue;
1371
1372 if (a->second != pSkipUser) {
ef1c8de5 1373 CString sMsg = sMessage;
a773c13f 1374
b740d7cc 1375 MODULECALL(OnBroadcast(sMsg), a->second, NULL, continue);
a773c13f 1376 a->second->PutStatusNotice("*** " + sMsg, NULL, pSkipClient);
ef1c8de5 1377 }
1378 }
1379}
1380
ad92c58c 1381CModule* CZNC::FindModule(const CString& sModName, const CString& sUsername) {
1382 if (sUsername.empty()) {
1383 return CZNC::Get().GetModules().FindModule(sModName);
1384 }
1385
1386 CUser* pUser = FindUser(sUsername);
1387
1388 return (!pUser) ? NULL : pUser->GetModules().FindModule(sModName);
1389}
1390
1391CModule* CZNC::FindModule(const CString& sModName, CUser* pUser) {
1392 if (pUser) {
1393 return pUser->GetModules().FindModule(sModName);
1394 }
1395
1396 return CZNC::Get().GetModules().FindModule(sModName);
1397}
1398
c315a07d 1399CUser* CZNC::FindUser(const CString& sUsername) {
1400 map<CString,CUser*>::iterator it = m_msUsers.find(sUsername);
1401
1402 if (it != m_msUsers.end()) {
1403 return it->second;
1404 }
1405
1406 return NULL;
1407}
1408
1409bool CZNC::DeleteUser(const CString& sUsername) {
1410 CUser* pUser = FindUser(sUsername);
1411
1412 if (!pUser) {
1413 return false;
1414 }
1415
b51302be 1416 m_msDelUsers[pUser->GetUserName()] = pUser;
c315a07d 1417 return true;
1418}
1419
e8d176ea 1420bool CZNC::AddUser(CUser* pUser, CString& sErrorRet) {
48ee5c71 1421 if (FindUser(pUser->GetUserName()) != NULL) {
ea569a2b 1422 sErrorRet = "User already exists";
235b10c2 1423 DEBUG("User [" << pUser->GetUserName() << "] - already exists");
48ee5c71 1424 return false;
1425 }
1426 if (!pUser->IsValid(sErrorRet)) {
235b10c2 1427 DEBUG("Invalid user [" << pUser->GetUserName() << "] - ["
1428 << sErrorRet << "]");
48ee5c71 1429 return false;
c315a07d 1430 }
9ae959b8 1431 GLOBALMODULECALL(OnAddUser(*pUser, sErrorRet), pUser, NULL,
28022ca7 1432 DEBUG("AddUser [" << pUser->GetUserName() << "] aborted by a module ["
1433 << sErrorRet << "]");
1434 return false;
9ae959b8 1435 );
48ee5c71 1436 m_msUsers[pUser->GetUserName()] = pUser;
1437 return true;
c315a07d 1438}
2a32927d 1439
cac29752 1440CListener* CZNC::FindListener(u_short uPort, const CString& sBindHost, EAddrType eAddr) {
1441 vector<CListener*>::iterator it;
1442
1443 for (it = m_vpListeners.begin(); it < m_vpListeners.end(); ++it) {
1444 if ((*it)->GetPort() != uPort)
1445 continue;
1446 if ((*it)->GetBindHost() != sBindHost)
1447 continue;
1448 if ((*it)->GetAddrType() != eAddr)
1449 continue;
1450 return *it;
1451 }
1452 return NULL;
1453}
1454
70c77458
US
1455bool CZNC::AddListener(const CString& sLine, CString& sError) {
1456 CString sName = sLine.Token(0);
1457 CString sValue = sLine.Token(1, true);
1458
1459 EAddrType eAddr = ADDR_ALL;
1460 if (sName.Equals("Listen4") || sName.Equals("Listen") || sName.Equals("Listener4")) {
1461 eAddr = ADDR_IPV4ONLY;
1462 }
1463 if (sName.Equals("Listener6")) {
1464 eAddr = ADDR_IPV6ONLY;
1465 }
1466
1467 CListener::EAcceptType eAccept = CListener::ACCEPT_ALL;
1468 if (sValue.TrimPrefix("irc_only "))
1469 eAccept = CListener::ACCEPT_IRC;
1470 else if (sValue.TrimPrefix("web_only "))
1471 eAccept = CListener::ACCEPT_HTTP;
1472
1473 bool bSSL = false;
1474 CString sPort;
1475 CString sBindHost;
1476
1477 if (ADDR_IPV4ONLY == eAddr) {
1478 sValue.Replace(":", " ");
1479 }
1480
1481 if (sValue.find(" ") != CString::npos) {
1482 sBindHost = sValue.Token(0, false, " ");
1483 sPort = sValue.Token(1, true, " ");
1484 } else {
1485 sPort = sValue;
1486 }
1487
1488 if (sPort.Left(1) == "+") {
1489 sPort.LeftChomp();
1490 bSSL = true;
1491 }
1492
1493 CString sHostComment;
1494
1495 if (!sBindHost.empty()) {
1496 sHostComment = " on host [" + sBindHost + "]";
1497 }
1498
1499 CString sIPV6Comment;
1500
1501 switch (eAddr) {
1502 case ADDR_ALL:
1503 sIPV6Comment = "";
1504 break;
1505 case ADDR_IPV4ONLY:
1506 sIPV6Comment = " using ipv4";
1507 break;
1508 case ADDR_IPV6ONLY:
1509 sIPV6Comment = " using ipv6";
1510 }
1511
1512 unsigned short uPort = sPort.ToUShort();
1513 CUtils::PrintAction("Binding to port [" + CString((bSSL) ? "+" : "") + CString(uPort) + "]" + sHostComment + sIPV6Comment);
1514
1515#ifndef HAVE_IPV6
1516 if (ADDR_IPV6ONLY == eAddr) {
1517 sError = "IPV6 is not enabled";
1518 CUtils::PrintStatus(false, sError);
1519 return false;
1520 }
1521#endif
1522
1523#ifndef HAVE_LIBSSL
1524 if (bSSL) {
1525 sError = "SSL is not enabled";
1526 CUtils::PrintStatus(false, sError);
1527 return false;
1528 }
1529#else
1530 CString sPemFile = GetPemLocation();
1531
1532 if (bSSL && !CFile::Exists(sPemFile)) {
1533 sError = "Unable to locate pem file: [" + sPemFile + "]";
1534 CUtils::PrintStatus(false, sError);
1535
1536 // If stdin is e.g. /dev/null and we call GetBoolInput(),
1537 // we are stuck in an endless loop!
1538 if (isatty(0) && CUtils::GetBoolInput("Would you like to create a new pem file?", true)) {
1539 sError.clear();
1540 WritePemFile();
1541 } else {
1542 return false;
1543 }
1544
1545 CUtils::PrintAction("Binding to port [+" + CString(uPort) + "]" + sHostComment + sIPV6Comment);
1546 }
1547#endif
1548 if (!uPort) {
1549 sError = "Invalid port";
1550 CUtils::PrintStatus(false, sError);
1551 return false;
1552 }
1553
1554 CListener* pListener = new CListener(uPort, sBindHost, bSSL, eAddr, eAccept);
1555
1556 if (!pListener->Listen()) {
1557 sError = FormatBindError();
1558 CUtils::PrintStatus(false, sError);
1559 delete pListener;
1560 return false;
1561 }
1562
1563 m_vpListeners.push_back(pListener);
1564 CUtils::PrintStatus(true);
1565
1566 return true;
1567}
1568
cac29752 1569bool CZNC::AddListener(CListener* pListener) {
1570 if (!pListener->GetRealListener()) {
1571 // Listener doesnt actually listen
1572 delete pListener;
1573 return false;
1574 }
1575
1576 // We don't check if there is an identical listener already listening
1577 // since one can't listen on e.g. the same port multiple times
1578
1579 m_vpListeners.push_back(pListener);
1580 return true;
1581}
1582
1583bool CZNC::DelListener(CListener* pListener) {
1584 vector<CListener*>::iterator it;
1585
1586 for (it = m_vpListeners.begin(); it < m_vpListeners.end(); ++it) {
1587 if (*it == pListener) {
1588 m_vpListeners.erase(it);
505bdd1e 1589 delete pListener;
cac29752 1590 return true;
1591 }
1592 }
1593
1594 return false;
1595}
1596
2a32927d 1597CZNC& CZNC::Get() {
1598 static CZNC* pZNC = new CZNC;
1599 return *pZNC;
1600}
1601
85dce87d 1602CZNC::TrafficStatsMap CZNC::GetTrafficStats(TrafficStatsPair &Users,
1603 TrafficStatsPair &ZNC, TrafficStatsPair &Total) {
1604 TrafficStatsMap ret;
f36ad9e6 1605 unsigned long long uiUsers_in, uiUsers_out, uiZNC_in, uiZNC_out;
1606 const map<CString, CUser*>& msUsers = CZNC::Get().GetUserMap();
1607
1608 uiUsers_in = uiUsers_out = 0;
1609 uiZNC_in = BytesRead();
1610 uiZNC_out = BytesWritten();
1611
115b1708 1612 for (map<CString, CUser*>::const_iterator it = msUsers.begin(); it != msUsers.end(); ++it) {
85dce87d 1613 ret[it->first] = TrafficStatsPair(it->second->BytesRead(), it->second->BytesWritten());
f36ad9e6 1614 uiUsers_in += it->second->BytesRead();
1615 uiUsers_out += it->second->BytesWritten();
1616 }
1617
115b1708 1618 for (CSockManager::const_iterator it = m_Manager.begin(); it != m_Manager.end(); ++it) {
1fcb7b2b 1619 CUser *pUser = NULL;
f36ad9e6 1620 if ((*it)->GetSockName().Left(5) == "IRC::") {
1fcb7b2b 1621 pUser = ((CIRCSock *) *it)->GetUser();
f36ad9e6 1622 } else if ((*it)->GetSockName().Left(5) == "USR::") {
1fcb7b2b 1623 pUser = ((CClient*) *it)->GetUser();
1624 }
1625
1626 if (pUser) {
1627 ret[pUser->GetUserName()].first += (*it)->GetBytesRead();
1628 ret[pUser->GetUserName()].second += (*it)->GetBytesWritten();
1629 uiUsers_in += (*it)->GetBytesRead();
1630 uiUsers_out += (*it)->GetBytesWritten();
f36ad9e6 1631 } else {
1632 uiZNC_in += (*it)->GetBytesRead();
1633 uiZNC_out += (*it)->GetBytesWritten();
0a0c8260 1634 }
b772e266 1635 }
f36ad9e6 1636
85dce87d 1637 Users = TrafficStatsPair(uiUsers_in, uiUsers_out);
1638 ZNC = TrafficStatsPair(uiZNC_in, uiZNC_out);
1639 Total = TrafficStatsPair(uiUsers_in + uiZNC_in, uiUsers_out + uiZNC_out);
f36ad9e6 1640
1641 return ret;
b772e266 1642}
1643
0bd4927d 1644void CZNC::AuthUser(CSmartPtr<CAuthBase> AuthClass) {
0bd4927d 1645 // TODO unless the auth module calls it, CUser::IsHostAllowed() is not honoured
9ae959b8 1646 GLOBALMODULECALL(OnLoginAttempt(AuthClass), NULL, NULL, return);
0bd4927d 1647
9c92d93a 1648 CUser* pUser = FindUser(AuthClass->GetUsername());
0bd4927d 1649
1650 if (!pUser || !pUser->CheckPass(AuthClass->GetPassword())) {
0bd4927d 1651 AuthClass->RefuseLogin("Invalid Password");
1652 return;
1653 }
1654
1655 CString sHost = AuthClass->GetRemoteIP();
1656
1657 if (!pUser->IsHostAllowed(sHost)) {
1658 AuthClass->RefuseLogin("Your host [" + sHost + "] is not allowed");
1659 return;
1660 }
1661
1662 AuthClass->AcceptLogin(*pUser);
1663}
1664
a213258c 1665class CConnectUserTimer : public CCron {
1666public:
1667 CConnectUserTimer(int iSecs) : CCron() {
1668 SetName("Connect users");
1669 Start(iSecs);
abeba8ae 1670 m_uiPosNextUser = 0;
ef31d6e2 1671 // Don't wait iSecs seconds for first timer run
1672 m_bRunOnNextCall = true;
a213258c 1673 }
79212882 1674 virtual ~CConnectUserTimer() {
1675 // This is only needed when ZNC shuts down:
1676 // CZNC::~CZNC() sets its CConnectUserTimer pointer to NULL and
1677 // calls the manager's Cleanup() which destroys all sockets and
1678 // timers. If something calls CZNC::EnableConnectUser() here
1679 // (e.g. because a CIRCSock is destroyed), the socket manager
1680 // deletes that timer almost immediately, but CZNC now got a
1681 // dangling pointer to this timer which can crash later on.
1682 //
1683 // Unlikely but possible ;)
1684 CZNC::Get().LeakConnectUser(this);
1685 }
a213258c 1686
1687protected:
1688 virtual void RunJob() {
d78369fe 1689 unsigned int uiUserCount;
d78369fe 1690 bool bUsersLeft = false;
abeba8ae 1691 const map<CString,CUser*>& mUsers = CZNC::Get().GetUserMap();
1692 map<CString,CUser*>::const_iterator it = mUsers.begin();
a213258c 1693
d78369fe 1694 uiUserCount = CZNC::Get().GetUserMap().size();
abeba8ae 1695
1696 if (m_uiPosNextUser >= uiUserCount) {
1697 m_uiPosNextUser = 0;
1698 }
1699
1700 for (unsigned int i = 0; i < m_uiPosNextUser; i++) {
1701 it++;
1702 }
a213258c 1703
1704 // Try to connect each user, if this doesnt work, abort
d78369fe 1705 for (unsigned int i = 0; i < uiUserCount; i++) {
abeba8ae 1706 if (it == mUsers.end())
1707 it = mUsers.begin();
a213258c 1708
abeba8ae 1709 CUser* pUser = it->second;
1710 it++;
1711 m_uiPosNextUser = (m_uiPosNextUser + 1) % uiUserCount;
a213258c 1712
87a368cb 1713 // Is this user disconnected?
1714 if (pUser->GetIRCSock() != NULL)
1715 continue;
a213258c 1716
87a368cb 1717 // Does this user want to connect?
1718 if (!pUser->GetIRCConnectEnabled())
1719 continue;
abeba8ae 1720
87a368cb 1721 // Does this user have any servers?
1722 if (!pUser->HasServers())
1723 continue;
1724
1725 // The timer runs until it once didn't find any users to connect
1726 bUsersLeft = true;
1727
235b10c2 1728 DEBUG("Connecting user [" << pUser->GetUserName() << "]");
87a368cb 1729
1730 if (CZNC::Get().ConnectUser(pUser))
1731 // User connecting, wait until next time timer fires
1732 return;
d78369fe 1733 }
a213258c 1734
abeba8ae 1735 if (bUsersLeft == false) {
235b10c2 1736 DEBUG("ConnectUserTimer done");
a213258c 1737 CZNC::Get().DisableConnectUser();
abeba8ae 1738 }
a213258c 1739 }
1740
1741private:
f2d7ae1a 1742 size_t m_uiPosNextUser;
a213258c 1743};
1744
0456e3f0 1745void CZNC::SetConnectDelay(unsigned int i) {
1746 if (m_uiConnectDelay != i && m_pConnectUserTimer != NULL) {
1747 m_pConnectUserTimer->Start(i);
1748 }
1749 m_uiConnectDelay = i;
1750}
1751
a213258c 1752void CZNC::EnableConnectUser() {
1753 if (m_pConnectUserTimer != NULL)
1754 return;
1755
6b07726e 1756 m_pConnectUserTimer = new CConnectUserTimer(m_uiConnectDelay);
a213258c 1757 GetManager().AddCron(m_pConnectUserTimer);
1758}
1759
1760void CZNC::DisableConnectUser() {
1761 if (m_pConnectUserTimer == NULL)
1762 return;
1763
1764 // This will kill the cron
1765 m_pConnectUserTimer->Stop();
1766 m_pConnectUserTimer = NULL;
1767}
79212882 1768
1769void CZNC::LeakConnectUser(CConnectUserTimer *pTimer) {
1770 if (m_pConnectUserTimer == pTimer)
1771 m_pConnectUserTimer = NULL;
1772}
33ce80f4 1773
1774bool CZNC::WaitForChildLock() {
a4d388b5 1775 return m_pLockFile && m_pLockFile->ExLock();
33ce80f4 1776}
672df0a8
A
1777
1778void CZNC::DisableConfigTimer() {
1779 if (m_pConfigTimer) {
1780 m_pConfigTimer->Stop();
1781 m_pConfigTimer = NULL;
1782 }
1783}