1 /*
2 * Copyright (C) 2004-2020 ZNC, see the NOTICE file for details.
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17 #include <znc/znc.h>
18 #include <znc/FileUtils.h>
19 #include <znc/IRCSock.h>
20 #include <znc/Server.h>
21 #include <znc/User.h>
22 #include <znc/IRCNetwork.h>
23 #include <znc/Config.h>
24 #include <time.h>
25 #include <tuple>
26 #include <algorithm>
27
28 using std::endl;
29 using std::cout;
30 using std::map;
31 using std::set;
32 using std::vector;
33 using std::list;
34 using std::tuple;
35 using std::make_tuple;
36
CZNC()37 CZNC::CZNC()
38 : m_TimeStarted(time(nullptr)),
39 m_eConfigState(ECONFIG_NOTHING),
40 m_vpListeners(),
41 m_msUsers(),
42 m_msDelUsers(),
43 m_Manager(),
44 m_sCurPath(""),
45 m_sZNCPath(""),
46 m_sConfigFile(""),
47 m_sSkinName(""),
48 m_sStatusPrefix(""),
49 m_sPidFile(""),
50 m_sSSLCertFile(""),
51 m_sSSLKeyFile(""),
52 m_sSSLDHParamFile(""),
53 m_sSSLCiphers(""),
54 m_sSSLProtocols(""),
55 m_vsBindHosts(),
56 m_vsTrustedProxies(),
57 m_vsMotd(),
58 m_pLockFile(nullptr),
59 m_uiConnectDelay(5),
60 m_uiAnonIPLimit(10),
61 m_uiMaxBufferSize(500),
62 m_uDisabledSSLProtocols(Csock::EDP_SSL),
63 m_pModules(new CModules),
64 m_uBytesRead(0),
65 m_uBytesWritten(0),
66 m_lpConnectQueue(),
67 m_pConnectQueueTimer(nullptr),
68 m_uiConnectPaused(0),
69 m_uiForceEncoding(0),
70 m_sConnectThrottle(),
71 m_bProtectWebSessions(true),
72 m_bHideVersion(false),
73 m_bAuthOnlyViaModule(false),
74 m_Translation("znc"),
75 m_uiConfigWriteDelay(0),
76 m_pConfigTimer(nullptr) {
77 if (!InitCsocket()) {
78 CUtils::PrintError("Could not initialize Csocket!");
79 exit(-1);
80 }
81 m_sConnectThrottle.SetTTL(30000);
82 }
83
~CZNC()84 CZNC::~CZNC() {
85 m_pModules->UnloadAll();
86
87 for (const auto& it : m_msUsers) {
88 it.second->GetModules().UnloadAll();
89
90 const vector<CIRCNetwork*>& networks = it.second->GetNetworks();
91 for (CIRCNetwork* pNetwork : networks) {
92 pNetwork->GetModules().UnloadAll();
93 }
94 }
95
96 for (CListener* pListener : m_vpListeners) {
97 delete pListener;
98 }
99
100 for (const auto& it : m_msUsers) {
101 it.second->SetBeingDeleted(true);
102 }
103
104 m_pConnectQueueTimer = nullptr;
105 // This deletes m_pConnectQueueTimer
106 m_Manager.Cleanup();
107 DeleteUsers();
108
109 delete m_pModules;
110 delete m_pLockFile;
111
112 ShutdownCsocket();
113 DeletePidFile();
114 }
115
GetVersion()116 CString CZNC::GetVersion() {
117 return CString(VERSION_STR) + CString(ZNC_VERSION_EXTRA);
118 }
119
GetTag(bool bIncludeVersion,bool bHTML)120 CString CZNC::GetTag(bool bIncludeVersion, bool bHTML) {
121 if (!Get().m_bHideVersion) {
122 bIncludeVersion = true;
123 }
124 CString sAddress = bHTML ? "<a href=\"https://znc.in\">https://znc.in</a>"
125 : "https://znc.in";
126
127 if (!bIncludeVersion) {
128 return "ZNC - " + sAddress;
129 }
130
131 CString sVersion = GetVersion();
132
133 return "ZNC " + sVersion + " - " + sAddress;
134 }
135
GetCompileOptionsString()136 CString CZNC::GetCompileOptionsString() {
137 // Build system doesn't affect ABI
138 return ZNC_COMPILE_OPTIONS_STRING + CString(
139 ", build: "
140 #ifdef BUILD_WITH_CMAKE
141 "cmake"
142 #else
143 "autoconf"
144 #endif
145 );
146 }
147
GetUptime() const148 CString CZNC::GetUptime() const {
149 time_t now = time(nullptr);
150 return CString::ToTimeStr(now - TimeStarted());
151 }
152
OnBoot()153 bool CZNC::OnBoot() {
154 bool bFail = false;
155 ALLMODULECALL(OnBoot(), &bFail);
156 if (bFail) return false;
157
158 return true;
159 }
160
HandleUserDeletion()161 bool CZNC::HandleUserDeletion() {
162 if (m_msDelUsers.empty()) return false;
163
164 for (const auto& it : m_msDelUsers) {
165 CUser* pUser = it.second;
166 pUser->SetBeingDeleted(true);
167
168 if (GetModules().OnDeleteUser(*pUser)) {
169 pUser->SetBeingDeleted(false);
170 continue;
171 }
172 m_msUsers.erase(pUser->GetUsername());
173 CWebSock::FinishUserSessions(*pUser);
174 delete pUser;
175 }
176
177 m_msDelUsers.clear();
178
179 return true;
180 }
181
182 class CConfigWriteTimer : public CCron {
183 public:
CConfigWriteTimer(int iSecs)184 CConfigWriteTimer(int iSecs) : CCron() {
185 SetName("Config write timer");
186 Start(iSecs);
187 }
188
189 protected:
RunJob()190 void RunJob() override {
191 CZNC::Get().SetConfigState(CZNC::ECONFIG_NEED_WRITE);
192
193 CZNC::Get().DisableConfigTimer();
194 }
195 };
196
Loop()197 void CZNC::Loop() {
198 while (true) {
199 CString sError;
200
201 ConfigState eState = GetConfigState();
202 switch (eState) {
203 case ECONFIG_NEED_REHASH:
204 SetConfigState(ECONFIG_NOTHING);
205
206 if (RehashConfig(sError)) {
207 Broadcast("Rehashing succeeded", true);
208 } else {
209 Broadcast("Rehashing failed: " + sError, true);
210 Broadcast("ZNC is in some possibly inconsistent state!",
211 true);
212 }
213 break;
214 case ECONFIG_DELAYED_WRITE:
215 SetConfigState(ECONFIG_NOTHING);
216
217 if (GetConfigWriteDelay() > 0) {
218 if (m_pConfigTimer == nullptr) {
219 m_pConfigTimer = new CConfigWriteTimer(GetConfigWriteDelay());
220 GetManager().AddCron(m_pConfigTimer);
221 }
222 break;
223 }
224 /* Fall through */
225 case ECONFIG_NEED_WRITE:
226 case ECONFIG_NEED_VERBOSE_WRITE:
227 SetConfigState(ECONFIG_NOTHING);
228
229 // stop pending configuration timer
230 DisableConfigTimer();
231
232 if (!WriteConfig()) {
233 Broadcast("Writing the config file failed", true);
234 } else if (eState == ECONFIG_NEED_VERBOSE_WRITE) {
235 Broadcast("Writing the config succeeded", true);
236 }
237 break;
238 case ECONFIG_NOTHING:
239 break;
240 case ECONFIG_NEED_QUIT:
241 return;
242 }
243
244 // Check for users that need to be deleted
245 if (HandleUserDeletion()) {
246 // Also remove those user(s) from the config file
247 WriteConfig();
248 }
249
250 // Csocket wants micro seconds
251 // 100 msec to 5 min
252 m_Manager.DynamicSelectLoop(100 * 1000, 5 * 60 * 1000 * 1000);
253 }
254 }
255
InitPidFile()256 CFile* CZNC::InitPidFile() {
257 if (!m_sPidFile.empty()) {
258 CString sFile;
259
260 // absolute path or relative to the data dir?
261 if (m_sPidFile[0] != '/')
262 sFile = GetZNCPath() + "/" + m_sPidFile;
263 else
264 sFile = m_sPidFile;
265
266 return new CFile(sFile);
267 }
268
269 return nullptr;
270 }
271
WritePidFile(int iPid)272 bool CZNC::WritePidFile(int iPid) {
273 CFile* File = InitPidFile();
274 if (File == nullptr) return false;
275
276 CUtils::PrintAction("Writing pid file [" + File->GetLongName() + "]");
277
278 bool bRet = false;
279 if (File->Open(O_WRONLY | O_TRUNC | O_CREAT)) {
280 File->Write(CString(iPid) + "\n");
281 File->Close();
282 bRet = true;
283 }
284
285 delete File;
286 CUtils::PrintStatus(bRet);
287 return bRet;
288 }
289
DeletePidFile()290 bool CZNC::DeletePidFile() {
291 CFile* File = InitPidFile();
292 if (File == nullptr) return false;
293
294 CUtils::PrintAction("Deleting pid file [" + File->GetLongName() + "]");
295
296 bool bRet = File->Delete();
297
298 delete File;
299 CUtils::PrintStatus(bRet);
300 return bRet;
301 }
302
WritePemFile()303 bool CZNC::WritePemFile() {
304 #ifndef HAVE_LIBSSL
305 CUtils::PrintError("ZNC was not compiled with ssl support.");
306 return false;
307 #else
308 CString sPemFile = GetPemLocation();
309
310 CUtils::PrintAction("Writing Pem file [" + sPemFile + "]");
311 #ifndef _WIN32
312 int fd = creat(sPemFile.c_str(), 0600);
313 if (fd == -1) {
314 CUtils::PrintStatus(false, "Unable to open");
315 return false;
316 }
317 FILE* f = fdopen(fd, "w");
318 #else
319 FILE* f = fopen(sPemFile.c_str(), "w");
320 #endif
321
322 if (!f) {
323 CUtils::PrintStatus(false, "Unable to open");
324 return false;
325 }
326
327 CUtils::GenerateCert(f, "");
328 fclose(f);
329
330 CUtils::PrintStatus(true);
331 return true;
332 #endif
333 }
334
DeleteUsers()335 void CZNC::DeleteUsers() {
336 for (const auto& it : m_msUsers) {
337 it.second->SetBeingDeleted(true);
338 delete it.second;
339 }
340
341 m_msUsers.clear();
342 DisableConnectQueue();
343 }
344
IsHostAllowed(const CString & sHostMask) const345 bool CZNC::IsHostAllowed(const CString& sHostMask) const {
346 for (const auto& it : m_msUsers) {
347 if (it.second->IsHostAllowed(sHostMask)) {
348 return true;
349 }
350 }
351
352 return false;
353 }
354
AllowConnectionFrom(const CString & sIP) const355 bool CZNC::AllowConnectionFrom(const CString& sIP) const {
356 if (m_uiAnonIPLimit == 0) return true;
357 return (GetManager().GetAnonConnectionCount(sIP) < m_uiAnonIPLimit);
358 }
359
InitDirs(const CString & sArgvPath,const CString & sDataDir)360 void CZNC::InitDirs(const CString& sArgvPath, const CString& sDataDir) {
361 // If the bin was not ran from the current directory, we need to add that
362 // dir onto our cwd
363 CString::size_type uPos = sArgvPath.rfind('/');
364 if (uPos == CString::npos)
365 m_sCurPath = "./";
366 else
367 m_sCurPath = CDir::ChangeDir("./", sArgvPath.Left(uPos), "");
368
369 // Try to set the user's home dir, default to binpath on failure
370 CFile::InitHomePath(m_sCurPath);
371
372 if (sDataDir.empty()) {
373 m_sZNCPath = CFile::GetHomePath() + "/.znc";
374 } else {
375 m_sZNCPath = sDataDir;
376 }
377
378 m_sSSLCertFile = m_sZNCPath + "/znc.pem";
379 }
380
GetConfPath(bool bAllowMkDir) const381 CString CZNC::GetConfPath(bool bAllowMkDir) const {
382 CString sConfPath = m_sZNCPath + "/configs";
383 if (bAllowMkDir && !CFile::Exists(sConfPath)) {
384 CDir::MakeDir(sConfPath);
385 }
386
387 return sConfPath;
388 }
389
GetUserPath() const390 CString CZNC::GetUserPath() const {
391 CString sUserPath = m_sZNCPath + "/users";
392 if (!CFile::Exists(sUserPath)) {
393 CDir::MakeDir(sUserPath);
394 }
395
396 return sUserPath;
397 }
398
GetModPath() const399 CString CZNC::GetModPath() const {
400 CString sModPath = m_sZNCPath + "/modules";
401
402 return sModPath;
403 }
404
GetCurPath() const405 const CString& CZNC::GetCurPath() const {
406 if (!CFile::Exists(m_sCurPath)) {
407 CDir::MakeDir(m_sCurPath);
408 }
409 return m_sCurPath;
410 }
411
GetHomePath() const412 const CString& CZNC::GetHomePath() const { return CFile::GetHomePath(); }
413
GetZNCPath() const414 const CString& CZNC::GetZNCPath() const {
415 if (!CFile::Exists(m_sZNCPath)) {
416 CDir::MakeDir(m_sZNCPath);
417 }
418 return m_sZNCPath;
419 }
420
GetPemLocation() const421 CString CZNC::GetPemLocation() const {
422 return CDir::ChangeDir("", m_sSSLCertFile);
423 }
424
GetKeyLocation() const425 CString CZNC::GetKeyLocation() const {
426 return CDir::ChangeDir(
427 "", m_sSSLKeyFile.empty() ? m_sSSLCertFile : m_sSSLKeyFile);
428 }
429
GetDHParamLocation() const430 CString CZNC::GetDHParamLocation() const {
431 return CDir::ChangeDir(
432 "", m_sSSLDHParamFile.empty() ? m_sSSLCertFile : m_sSSLDHParamFile);
433 }
434
ExpandConfigPath(const CString & sConfigFile,bool bAllowMkDir)435 CString CZNC::ExpandConfigPath(const CString& sConfigFile, bool bAllowMkDir) {
436 CString sRetPath;
437
438 if (sConfigFile.empty()) {
439 sRetPath = GetConfPath(bAllowMkDir) + "/znc.conf";
440 } else {
441 if (sConfigFile.StartsWith("./") || sConfigFile.StartsWith("../")) {
442 sRetPath = GetCurPath() + "/" + sConfigFile;
443 } else if (!sConfigFile.StartsWith("/")) {
444 sRetPath = GetConfPath(bAllowMkDir) + "/" + sConfigFile;
445 } else {
446 sRetPath = sConfigFile;
447 }
448 }
449
450 return sRetPath;
451 }
452
WriteConfig()453 bool CZNC::WriteConfig() {
454 if (GetConfigFile().empty()) {
455 DEBUG("Config file name is empty?!");
456 return false;
457 }
458
459 // We first write to a temporary file and then move it to the right place
460 CFile* pFile = new CFile(GetConfigFile() + "~");
461
462 if (!pFile->Open(O_WRONLY | O_CREAT | O_TRUNC, 0600)) {
463 DEBUG("Could not write config to " + GetConfigFile() + "~: " +
464 CString(strerror(errno)));
465 delete pFile;
466 return false;
467 }
468
469 // We have to "transfer" our lock on the config to the new file.
470 // The old file (= inode) is going away and thus a lock on it would be
471 // useless. These lock should always succeed (races, anyone?).
472 if (!pFile->TryExLock()) {
473 DEBUG("Error while locking the new config file, errno says: " +
474 CString(strerror(errno)));
475 pFile->Delete();
476 delete pFile;
477 return false;
478 }
479
480 pFile->Write(MakeConfigHeader() + "\n");
481
482 CConfig config;
483 config.AddKeyValuePair("AnonIPLimit", CString(m_uiAnonIPLimit));
484 config.AddKeyValuePair("MaxBufferSize", CString(m_uiMaxBufferSize));
485 config.AddKeyValuePair("SSLCertFile", CString(GetPemLocation()));
486 config.AddKeyValuePair("SSLKeyFile", CString(GetKeyLocation()));
487 config.AddKeyValuePair("SSLDHParamFile", CString(GetDHParamLocation()));
488 config.AddKeyValuePair("ProtectWebSessions",
489 CString(m_bProtectWebSessions));
490 config.AddKeyValuePair("HideVersion", CString(m_bHideVersion));
491 config.AddKeyValuePair("AuthOnlyViaModule", CString(m_bAuthOnlyViaModule));
492 config.AddKeyValuePair("Version", CString(VERSION_STR));
493 config.AddKeyValuePair("ConfigWriteDelay", CString(m_uiConfigWriteDelay));
494
495 unsigned int l = 0;
496 for (CListener* pListener : m_vpListeners) {
497 CConfig listenerConfig;
498
499 listenerConfig.AddKeyValuePair("Host", pListener->GetBindHost());
500 listenerConfig.AddKeyValuePair("URIPrefix",
501 pListener->GetURIPrefix() + "/");
502 listenerConfig.AddKeyValuePair("Port", CString(pListener->GetPort()));
503
504 listenerConfig.AddKeyValuePair(
505 "IPv4", CString(pListener->GetAddrType() != ADDR_IPV6ONLY));
506 listenerConfig.AddKeyValuePair(
507 "IPv6", CString(pListener->GetAddrType() != ADDR_IPV4ONLY));
508
509 listenerConfig.AddKeyValuePair("SSL", CString(pListener->IsSSL()));
510
511 listenerConfig.AddKeyValuePair(
512 "AllowIRC",
513 CString(pListener->GetAcceptType() != CListener::ACCEPT_HTTP));
514 listenerConfig.AddKeyValuePair(
515 "AllowWeb",
516 CString(pListener->GetAcceptType() != CListener::ACCEPT_IRC));
517
518 config.AddSubConfig("Listener", "listener" + CString(l++),
519 listenerConfig);
520 }
521
522 config.AddKeyValuePair("ConnectDelay", CString(m_uiConnectDelay));
523 config.AddKeyValuePair("ServerThrottle",
524 CString(m_sConnectThrottle.GetTTL() / 1000));
525
526 if (!m_sPidFile.empty()) {
527 config.AddKeyValuePair("PidFile", m_sPidFile.FirstLine());
528 }
529
530 if (!m_sSkinName.empty()) {
531 config.AddKeyValuePair("Skin", m_sSkinName.FirstLine());
532 }
533
534 if (!m_sStatusPrefix.empty()) {
535 config.AddKeyValuePair("StatusPrefix", m_sStatusPrefix.FirstLine());
536 }
537
538 if (!m_sSSLCiphers.empty()) {
539 config.AddKeyValuePair("SSLCiphers", CString(m_sSSLCiphers));
540 }
541
542 if (!m_sSSLProtocols.empty()) {
543 config.AddKeyValuePair("SSLProtocols", m_sSSLProtocols);
544 }
545
546 for (const CString& sLine : m_vsMotd) {
547 config.AddKeyValuePair("Motd", sLine.FirstLine());
548 }
549
550 for (const CString& sProxy : m_vsTrustedProxies) {
551 config.AddKeyValuePair("TrustedProxy", sProxy.FirstLine());
552 }
553
554 CModules& Mods = GetModules();
555
556 for (const CModule* pMod : Mods) {
557 CString sName = pMod->GetModName();
558 CString sArgs = pMod->GetArgs();
559
560 if (!sArgs.empty()) {
561 sArgs = " " + sArgs.FirstLine();
562 }
563
564 config.AddKeyValuePair("LoadModule", sName.FirstLine() + sArgs);
565 }
566
567 for (const auto& it : m_msUsers) {
568 CString sErr;
569
570 if (!it.second->IsValid(sErr)) {
571 DEBUG("** Error writing config for user [" << it.first << "] ["
572 << sErr << "]");
573 continue;
574 }
575
576 config.AddSubConfig("User", it.second->GetUsername(),
577 it.second->ToConfig());
578 }
579
580 config.Write(*pFile);
581
582 // If Sync() fails... well, let's hope nothing important breaks..
583 pFile->Sync();
584
585 if (pFile->HadError()) {
586 DEBUG("Error while writing the config, errno says: " +
587 CString(strerror(errno)));
588 pFile->Delete();
589 delete pFile;
590 return false;
591 }
592
593 // We wrote to a temporary name, move it to the right place
594 if (!pFile->Move(GetConfigFile(), true)) {
595 DEBUG(
596 "Error while replacing the config file with a new version, errno "
597 "says "
598 << strerror(errno));
599 pFile->Delete();
600 delete pFile;
601 return false;
602 }
603
604 // Everything went fine, just need to update the saved path.
605 pFile->SetFileName(GetConfigFile());
606
607 // Make sure the lock is kept alive as long as we need it.
608 delete m_pLockFile;
609 m_pLockFile = pFile;
610
611 return true;
612 }
613
MakeConfigHeader()614 CString CZNC::MakeConfigHeader() {
615 return "// WARNING\n"
616 "//\n"
617 "// Do NOT edit this file while ZNC is running!\n"
618 "// Use webadmin or *controlpanel instead.\n"
619 "//\n"
620 "// Altering this file by hand will forfeit all support.\n"
621 "//\n"
622 "// But if you feel risky, you might want to read help on /znc "
623 "saveconfig and /znc rehash.\n"
624 "// Also check https://wiki.znc.in/Configuration\n";
625 }
626
WriteNewConfig(const CString & sConfigFile)627 bool CZNC::WriteNewConfig(const CString& sConfigFile) {
628 CString sAnswer, sUser, sNetwork;
629 VCString vsLines;
630
631 vsLines.push_back(MakeConfigHeader());
632 vsLines.push_back("Version = " + CString(VERSION_STR));
633
634 m_sConfigFile = ExpandConfigPath(sConfigFile);
635
636 if (CFile::Exists(m_sConfigFile)) {
637 CUtils::PrintStatus(
638 false, "WARNING: config [" + m_sConfigFile + "] already exists.");
639 }
640
641 CUtils::PrintMessage("");
642 CUtils::PrintMessage("-- Global settings --");
643 CUtils::PrintMessage("");
644
645 // Listen
646 #ifdef HAVE_IPV6
647 bool b6 = true;
648 #else
649 bool b6 = false;
650 #endif
651 CString sListenHost;
652 CString sURIPrefix;
653 bool bListenSSL = false;
654 unsigned int uListenPort = 0;
655 bool bSuccess;
656
657 do {
658 bSuccess = true;
659 while (true) {
660 if (!CUtils::GetNumInput("Listen on port", uListenPort, 1025,
661 65534)) {
662 continue;
663 }
664 if (uListenPort == 6667 || uListenPort == 6697) {
665 CUtils::PrintStatus(false,
666 "WARNING: Some web browsers reject ports "
667 "6667 and 6697. If you intend to");
668 CUtils::PrintStatus(false,
669 "use ZNC's web interface, you might want "
670 "to use another port.");
671 if (!CUtils::GetBoolInput("Proceed anyway?",
672 true)) {
673 continue;
674 }
675 }
676 break;
677 }
678
679 #ifdef HAVE_LIBSSL
680 bListenSSL = CUtils::GetBoolInput("Listen using SSL", bListenSSL);
681 #endif
682
683 #ifdef HAVE_IPV6
684 b6 = CUtils::GetBoolInput("Listen using both IPv4 and IPv6", b6);
685 #endif
686
687 // Don't ask for listen host, it may be configured later if needed.
688
689 CUtils::PrintAction("Verifying the listener");
690 CListener* pListener = new CListener(
691 (unsigned short int)uListenPort, sListenHost, sURIPrefix,
692 bListenSSL, b6 ? ADDR_ALL : ADDR_IPV4ONLY, CListener::ACCEPT_ALL);
693 if (!pListener->Listen()) {
694 CUtils::PrintStatus(false, FormatBindError());
695 bSuccess = false;
696 } else
697 CUtils::PrintStatus(true);
698 delete pListener;
699 } while (!bSuccess);
700
701 #ifdef HAVE_LIBSSL
702 CString sPemFile = GetPemLocation();
703 if (!CFile::Exists(sPemFile)) {
704 CUtils::PrintMessage("Unable to locate pem file: [" + sPemFile +
705 "], creating it");
706 WritePemFile();
707 }
708 #endif
709
710 vsLines.push_back("<Listener l>");
711 vsLines.push_back("\tPort = " + CString(uListenPort));
712 vsLines.push_back("\tIPv4 = true");
713 vsLines.push_back("\tIPv6 = " + CString(b6));
714 vsLines.push_back("\tSSL = " + CString(bListenSSL));
715 if (!sListenHost.empty()) {
716 vsLines.push_back("\tHost = " + sListenHost);
717 }
718 vsLines.push_back("</Listener>");
719 // !Listen
720
721 set<CModInfo> ssGlobalMods;
722 GetModules().GetDefaultMods(ssGlobalMods, CModInfo::GlobalModule);
723 vector<CString> vsGlobalModNames;
724 for (const CModInfo& Info : ssGlobalMods) {
725 vsGlobalModNames.push_back(Info.GetName());
726 vsLines.push_back("LoadModule = " + Info.GetName());
727 }
728 CUtils::PrintMessage(
729 "Enabled global modules [" +
730 CString(", ").Join(vsGlobalModNames.begin(), vsGlobalModNames.end()) +
731 "]");
732
733 // User
734 CUtils::PrintMessage("");
735 CUtils::PrintMessage("-- Admin user settings --");
736 CUtils::PrintMessage("");
737
738 vsLines.push_back("");
739 CString sNick;
740 do {
741 CUtils::GetInput("Username", sUser, "", "alphanumeric");
742 } while (!CUser::IsValidUsername(sUser));
743
744 vsLines.push_back("<User " + sUser + ">");
745 CString sSalt;
746 sAnswer = CUtils::GetSaltedHashPass(sSalt);
747 vsLines.push_back("\tPass = " + CUtils::sDefaultHash + "#" + sAnswer +
748 "#" + sSalt + "#");
749
750 vsLines.push_back("\tAdmin = true");
751
752 CUtils::GetInput("Nick", sNick, CUser::MakeCleanUsername(sUser));
753 vsLines.push_back("\tNick = " + sNick);
754 CUtils::GetInput("Alternate nick", sAnswer, sNick + "_");
755 if (!sAnswer.empty()) {
756 vsLines.push_back("\tAltNick = " + sAnswer);
757 }
758 CUtils::GetInput("Ident", sAnswer, sUser);
759 vsLines.push_back("\tIdent = " + sAnswer);
760 CUtils::GetInput("Real name", sAnswer, "", "optional");
761 if (!sAnswer.empty()) {
762 vsLines.push_back("\tRealName = " + sAnswer);
763 }
764 CUtils::GetInput("Bind host", sAnswer, "", "optional");
765 if (!sAnswer.empty()) {
766 vsLines.push_back("\tBindHost = " + sAnswer);
767 }
768
769 set<CModInfo> ssUserMods;
770 GetModules().GetDefaultMods(ssUserMods, CModInfo::UserModule);
771 vector<CString> vsUserModNames;
772 for (const CModInfo& Info : ssUserMods) {
773 vsUserModNames.push_back(Info.GetName());
774 vsLines.push_back("\tLoadModule = " + Info.GetName());
775 }
776 CUtils::PrintMessage(
777 "Enabled user modules [" +
778 CString(", ").Join(vsUserModNames.begin(), vsUserModNames.end()) + "]");
779
780 CUtils::PrintMessage("");
781 if (CUtils::GetBoolInput("Set up a network?", true)) {
782 vsLines.push_back("");
783
784 CUtils::PrintMessage("");
785 CUtils::PrintMessage("-- Network settings --");
786 CUtils::PrintMessage("");
787
788 do {
789 CUtils::GetInput("Name", sNetwork, "freenode");
790 } while (!CIRCNetwork::IsValidNetwork(sNetwork));
791
792 vsLines.push_back("\t<Network " + sNetwork + ">");
793
794 set<CModInfo> ssNetworkMods;
795 GetModules().GetDefaultMods(ssNetworkMods, CModInfo::NetworkModule);
796 vector<CString> vsNetworkModNames;
797 for (const CModInfo& Info : ssNetworkMods) {
798 vsNetworkModNames.push_back(Info.GetName());
799 vsLines.push_back("\t\tLoadModule = " + Info.GetName());
800 }
801
802 CString sHost, sPass, sHint;
803 bool bSSL = false;
804 unsigned int uServerPort = 0;
805
806 if (sNetwork.Equals("freenode")) {
807 sHost = "chat.freenode.net";
808 #ifdef HAVE_LIBSSL
809 bSSL = true;
810 #endif
811 } else {
812 sHint = "host only";
813 }
814
815 while (!CUtils::GetInput("Server host", sHost, sHost, sHint) ||
816 !CServer::IsValidHostName(sHost))
817 ;
818 #ifdef HAVE_LIBSSL
819 bSSL = CUtils::GetBoolInput("Server uses SSL?", bSSL);
820 #endif
821 while (!CUtils::GetNumInput("Server port", uServerPort, 1, 65535,
822 bSSL ? 6697 : 6667))
823 ;
824 CUtils::GetInput("Server password (probably empty)", sPass);
825
826 vsLines.push_back("\t\tServer = " + sHost + ((bSSL) ? " +" : " ") +
827 CString(uServerPort) + " " + sPass);
828
829 CString sChans;
830 if (CUtils::GetInput("Initial channels", sChans)) {
831 vsLines.push_back("");
832 VCString vsChans;
833 sChans.Replace(",", " ");
834 sChans.Replace(";", " ");
835 sChans.Split(" ", vsChans, false, "", "", true, true);
836 for (const CString& sChan : vsChans) {
837 vsLines.push_back("\t\t<Chan " + sChan + ">");
838 vsLines.push_back("\t\t</Chan>");
839 }
840 }
841
842 CUtils::PrintMessage("Enabled network modules [" +
843 CString(", ").Join(vsNetworkModNames.begin(),
844 vsNetworkModNames.end()) +
845 "]");
846
847 vsLines.push_back("\t</Network>");
848 }
849
850 vsLines.push_back("</User>");
851
852 CUtils::PrintMessage("");
853 // !User
854
855 CFile File;
856 bool bFileOK, bFileOpen = false;
857 do {
858 CUtils::PrintAction("Writing config [" + m_sConfigFile + "]");
859
860 bFileOK = true;
861 if (CFile::Exists(m_sConfigFile)) {
862 if (!File.TryExLock(m_sConfigFile)) {
863 CUtils::PrintStatus(false,
864 "ZNC is currently running on this config.");
865 bFileOK = false;
866 } else {
867 File.Close();
868 CUtils::PrintStatus(false, "This config already exists.");
869 if (CUtils::GetBoolInput(
870 "Are you sure you want to overwrite it?", false))
871 CUtils::PrintAction("Overwriting config [" + m_sConfigFile +
872 "]");
873 else
874 bFileOK = false;
875 }
876 }
877
878 if (bFileOK) {
879 File.SetFileName(m_sConfigFile);
880 if (File.Open(O_WRONLY | O_CREAT | O_TRUNC, 0600)) {
881 bFileOpen = true;
882 } else {
883 CUtils::PrintStatus(false, "Unable to open file");
884 bFileOK = false;
885 }
886 }
887 if (!bFileOK) {
888 while (!CUtils::GetInput("Please specify an alternate location",
889 m_sConfigFile, "",
890 "or \"stdout\" for displaying the config"))
891 ;
892 if (m_sConfigFile.Equals("stdout"))
893 bFileOK = true;
894 else
895 m_sConfigFile = ExpandConfigPath(m_sConfigFile);
896 }
897 } while (!bFileOK);
898
899 if (!bFileOpen) {
900 CUtils::PrintMessage("");
901 CUtils::PrintMessage("Printing the new config to stdout:");
902 CUtils::PrintMessage("");
903 cout << endl << "------------------------------------------------------"
904 "----------------------" << endl << endl;
905 }
906
907 for (const CString& sLine : vsLines) {
908 if (bFileOpen) {
909 File.Write(sLine + "\n");
910 } else {
911 cout << sLine << endl;
912 }
913 }
914
915 if (bFileOpen) {
916 File.Close();
917 if (File.HadError())
918 CUtils::PrintStatus(false,
919 "There was an error while writing the config");
920 else
921 CUtils::PrintStatus(true);
922 } else {
923 cout << endl << "------------------------------------------------------"
924 "----------------------" << endl << endl;
925 }
926
927 if (File.HadError()) {
928 bFileOpen = false;
929 CUtils::PrintMessage("Printing the new config to stdout instead:");
930 cout << endl << "------------------------------------------------------"
931 "----------------------" << endl << endl;
932 for (const CString& sLine : vsLines) {
933 cout << sLine << endl;
934 }
935 cout << endl << "------------------------------------------------------"
936 "----------------------" << endl << endl;
937 }
938
939 const CString sProtocol(bListenSSL ? "https" : "http");
940 const CString sSSL(bListenSSL ? "+" : "");
941 CUtils::PrintMessage("");
942 CUtils::PrintMessage(
943 "To connect to this ZNC you need to connect to it as your IRC server",
944 true);
945 CUtils::PrintMessage(
946 "using the port that you supplied. You have to supply your login info",
947 true);
948 CUtils::PrintMessage(
949 "as the IRC server password like this: user/network:pass.", true);
950 CUtils::PrintMessage("");
951 CUtils::PrintMessage("Try something like this in your IRC client...", true);
952 CUtils::PrintMessage("/server <znc_server_ip> " + sSSL +
953 CString(uListenPort) + " " + sUser + ":<pass>",
954 true);
955 CUtils::PrintMessage("");
956 CUtils::PrintMessage(
957 "To manage settings, users and networks, point your web browser to",
958 true);
959 CUtils::PrintMessage(
960 sProtocol + "://<znc_server_ip>:" + CString(uListenPort) + "/", true);
961 CUtils::PrintMessage("");
962
963 File.UnLock();
964
965 bool bWantLaunch = bFileOpen;
966 if (bWantLaunch) {
967 // "export ZNC_NO_LAUNCH_AFTER_MAKECONF=1" would cause znc --makeconf to
968 // not offer immediate launch.
969 // Useful for distros which want to create config when znc package is
970 // installed.
971 // See https://github.com/znc/znc/pull/257
972 char* szNoLaunch = getenv("ZNC_NO_LAUNCH_AFTER_MAKECONF");
973 if (szNoLaunch && *szNoLaunch == '1') {
974 bWantLaunch = false;
975 }
976 }
977 if (bWantLaunch) {
978 bWantLaunch = CUtils::GetBoolInput("Launch ZNC now?", true);
979 }
980 return bWantLaunch;
981 }
982
BackupConfigOnce(const CString & sSuffix)983 void CZNC::BackupConfigOnce(const CString& sSuffix) {
984 static bool didBackup = false;
985 if (didBackup) return;
986 didBackup = true;
987
988 CUtils::PrintAction("Creating a config backup");
989
990 CString sBackup = CDir::ChangeDir(m_sConfigFile, "../znc.conf." + sSuffix);
991 if (CFile::Copy(m_sConfigFile, sBackup))
992 CUtils::PrintStatus(true, sBackup);
993 else
994 CUtils::PrintStatus(false, strerror(errno));
995 }
996
ParseConfig(const CString & sConfig,CString & sError)997 bool CZNC::ParseConfig(const CString& sConfig, CString& sError) {
998 m_sConfigFile = ExpandConfigPath(sConfig, false);
999
1000 CConfig config;
1001 if (!ReadConfig(config, sError)) return false;
1002
1003 if (!LoadGlobal(config, sError)) return false;
1004
1005 if (!LoadUsers(config, sError)) return false;
1006
1007 return true;
1008 }
1009
ReadConfig(CConfig & config,CString & sError)1010 bool CZNC::ReadConfig(CConfig& config, CString& sError) {
1011 sError.clear();
1012
1013 CUtils::PrintAction("Opening config [" + m_sConfigFile + "]");
1014
1015 if (!CFile::Exists(m_sConfigFile)) {
1016 sError = "No such file";
1017 CUtils::PrintStatus(false, sError);
1018 CUtils::PrintMessage(
1019 "Restart ZNC with the --makeconf option if you wish to create this "
1020 "config.");
1021 return false;
1022 }
1023
1024 if (!CFile::IsReg(m_sConfigFile)) {
1025 sError = "Not a file";
1026 CUtils::PrintStatus(false, sError);
1027 return false;
1028 }
1029
1030 CFile* pFile = new CFile(m_sConfigFile);
1031
1032 // need to open the config file Read/Write for fcntl()
1033 // exclusive locking to work properly!
1034 if (!pFile->Open(m_sConfigFile, O_RDWR)) {
1035 sError = "Can not open config file";
1036 CUtils::PrintStatus(false, sError);
1037 delete pFile;
1038 return false;
1039 }
1040
1041 if (!pFile->TryExLock()) {
1042 sError = "ZNC is already running on this config.";
1043 CUtils::PrintStatus(false, sError);
1044 delete pFile;
1045 return false;
1046 }
1047
1048 // (re)open the config file
1049 delete m_pLockFile;
1050 m_pLockFile = pFile;
1051 CFile& File = *pFile;
1052
1053 if (!config.Parse(File, sError)) {
1054 CUtils::PrintStatus(false, sError);
1055 return false;
1056 }
1057 CUtils::PrintStatus(true);
1058
1059 // check if config is from old ZNC version and
1060 // create a backup file if necessary
1061 CString sSavedVersion;
1062 config.FindStringEntry("version", sSavedVersion);
1063 if (sSavedVersion.empty()) {
1064 CUtils::PrintError(
1065 "Config does not contain a version identifier. It may be be too "
1066 "old or corrupt.");
1067 return false;
1068 }
1069
1070 tuple<unsigned int, unsigned int> tSavedVersion =
1071 make_tuple(sSavedVersion.Token(0, false, ".").ToUInt(),
1072 sSavedVersion.Token(1, false, ".").ToUInt());
1073 tuple<unsigned int, unsigned int> tCurrentVersion =
1074 make_tuple(VERSION_MAJOR, VERSION_MINOR);
1075 if (tSavedVersion < tCurrentVersion) {
1076 CUtils::PrintMessage("Found old config from ZNC " + sSavedVersion +
1077 ". Saving a backup of it.");
1078 BackupConfigOnce("pre-" + CString(VERSION_STR));
1079 } else if (tSavedVersion > tCurrentVersion) {
1080 CUtils::PrintError("Config was saved from ZNC " + sSavedVersion +
1081 ". It may or may not work with current ZNC " +
1082 GetVersion());
1083 }
1084
1085 return true;
1086 }
1087
RehashConfig(CString & sError)1088 bool CZNC::RehashConfig(CString& sError) {
1089 ALLMODULECALL(OnPreRehash(), NOTHING);
1090
1091 CConfig config;
1092 if (!ReadConfig(config, sError)) return false;
1093
1094 if (!LoadGlobal(config, sError)) return false;
1095
1096 // do not reload users - it's dangerous!
1097
1098 ALLMODULECALL(OnPostRehash(), NOTHING);
1099 return true;
1100 }
1101
LoadGlobal(CConfig & config,CString & sError)1102 bool CZNC::LoadGlobal(CConfig& config, CString& sError) {
1103 sError.clear();
1104
1105 MCString msModules; // Modules are queued for later loading
1106
1107 VCString vsList;
1108 config.FindStringVector("loadmodule", vsList);
1109 for (const CString& sModLine : vsList) {
1110 CString sModName = sModLine.Token(0);
1111 CString sArgs = sModLine.Token(1, true);
1112
1113 // compatibility for pre-1.0 configs
1114 CString sSavedVersion;
1115 config.FindStringEntry("version", sSavedVersion);
1116 tuple<unsigned int, unsigned int> tSavedVersion =
1117 make_tuple(sSavedVersion.Token(0, false, ".").ToUInt(),
1118 sSavedVersion.Token(1, false, ".").ToUInt());
1119 if (sModName == "saslauth" && tSavedVersion < make_tuple(0, 207)) {
1120 CUtils::PrintMessage(
1121 "saslauth module was renamed to cyrusauth. Loading cyrusauth "
1122 "instead.");
1123 sModName = "cyrusauth";
1124 }
1125 // end-compatibility for pre-1.0 configs
1126
1127 if (msModules.find(sModName) != msModules.end()) {
1128 sError = "Module [" + sModName + "] already loaded";
1129 CUtils::PrintError(sError);
1130 return false;
1131 }
1132 CString sModRet;
1133 CModule* pOldMod;
1134
1135 pOldMod = GetModules().FindModule(sModName);
1136 if (!pOldMod) {
1137 CUtils::PrintAction("Loading global module [" + sModName + "]");
1138
1139 bool bModRet =
1140 GetModules().LoadModule(sModName, sArgs, CModInfo::GlobalModule,
1141 nullptr, nullptr, sModRet);
1142
1143 CUtils::PrintStatus(bModRet, bModRet ? "" : sModRet);
1144 if (!bModRet) {
1145 sError = sModRet;
1146 return false;
1147 }
1148 } else if (pOldMod->GetArgs() != sArgs) {
1149 CUtils::PrintAction("Reloading global module [" + sModName + "]");
1150
1151 bool bModRet = GetModules().ReloadModule(sModName, sArgs, nullptr,
1152 nullptr, sModRet);
1153
1154 CUtils::PrintStatus(bModRet, sModRet);
1155 if (!bModRet) {
1156 sError = sModRet;
1157 return false;
1158 }
1159 } else
1160 CUtils::PrintMessage("Module [" + sModName + "] already loaded.");
1161
1162 msModules[sModName] = sArgs;
1163 }
1164
1165 m_vsMotd.clear();
1166 config.FindStringVector("motd", vsList);
1167 for (const CString& sMotd : vsList) {
1168 AddMotd(sMotd);
1169 }
1170
1171 if (config.FindStringVector("bindhost", vsList)) {
1172 CUtils::PrintStatus(false,
1173 "WARNING: the global BindHost list is deprecated. "
1174 "Ignoring the following lines:");
1175 for (const CString& sHost : vsList) {
1176 CUtils::PrintStatus(false, "BindHost = " + sHost);
1177 }
1178 }
1179 if (config.FindStringVector("vhost", vsList)) {
1180 CUtils::PrintStatus(false,
1181 "WARNING: the global vHost list is deprecated. "
1182 "Ignoring the following lines:");
1183 for (const CString& sHost : vsList) {
1184 CUtils::PrintStatus(false, "vHost = " + sHost);
1185 }
1186 }
1187
1188 m_vsTrustedProxies.clear();
1189 config.FindStringVector("trustedproxy", vsList);
1190 for (const CString& sProxy : vsList) {
1191 AddTrustedProxy(sProxy);
1192 }
1193
1194 CString sVal;
1195 if (config.FindStringEntry("pidfile", sVal)) m_sPidFile = sVal;
1196 if (config.FindStringEntry("statusprefix", sVal)) m_sStatusPrefix = sVal;
1197 if (config.FindStringEntry("sslcertfile", sVal)) m_sSSLCertFile = sVal;
1198 if (config.FindStringEntry("sslkeyfile", sVal)) m_sSSLKeyFile = sVal;
1199 if (config.FindStringEntry("ssldhparamfile", sVal))
1200 m_sSSLDHParamFile = sVal;
1201 if (config.FindStringEntry("sslciphers", sVal)) m_sSSLCiphers = sVal;
1202 if (config.FindStringEntry("skin", sVal)) SetSkinName(sVal);
1203 if (config.FindStringEntry("connectdelay", sVal))
1204 SetConnectDelay(sVal.ToUInt());
1205 if (config.FindStringEntry("serverthrottle", sVal))
1206 m_sConnectThrottle.SetTTL(sVal.ToUInt() * 1000);
1207 if (config.FindStringEntry("anoniplimit", sVal))
1208 m_uiAnonIPLimit = sVal.ToUInt();
1209 if (config.FindStringEntry("maxbuffersize", sVal))
1210 m_uiMaxBufferSize = sVal.ToUInt();
1211 if (config.FindStringEntry("protectwebsessions", sVal))
1212 m_bProtectWebSessions = sVal.ToBool();
1213 if (config.FindStringEntry("hideversion", sVal))
1214 m_bHideVersion = sVal.ToBool();
1215 if (config.FindStringEntry("authonlyviamodule", sVal))
1216 m_bAuthOnlyViaModule = sVal.ToBool();
1217 if (config.FindStringEntry("sslprotocols", sVal)) {
1218 if (!SetSSLProtocols(sVal)) {
1219 VCString vsProtocols = GetAvailableSSLProtocols();
1220 CUtils::PrintError("Invalid SSLProtocols value [" + sVal + "]");
1221 CUtils::PrintError(
1222 "The syntax is [SSLProtocols = [+|-]<protocol> ...]");
1223 CUtils::PrintError(
1224 "Available protocols are [" +
1225 CString(", ").Join(vsProtocols.begin(), vsProtocols.end()) +
1226 "]");
1227 return false;
1228 }
1229 }
1230 if (config.FindStringEntry("configwritedelay", sVal))
1231 m_uiConfigWriteDelay = sVal.ToUInt();
1232
1233 UnloadRemovedModules(msModules);
1234
1235 if (!LoadListeners(config, sError)) return false;
1236
1237 return true;
1238 }
1239
LoadUsers(CConfig & config,CString & sError)1240 bool CZNC::LoadUsers(CConfig& config, CString& sError) {
1241 sError.clear();
1242
1243 m_msUsers.clear();
1244
1245 CConfig::SubConfig subConf;
1246 config.FindSubConfig("user", subConf);
1247
1248 for (const auto& subIt : subConf) {
1249 const CString& sUsername = subIt.first;
1250 CConfig* pSubConf = subIt.second.m_pSubConfig;
1251
1252 CUtils::PrintMessage("Loading user [" + sUsername + "]");
1253
1254 std::unique_ptr<CUser> pUser(new CUser(sUsername));
1255
1256 if (!m_sStatusPrefix.empty()) {
1257 if (!pUser->SetStatusPrefix(m_sStatusPrefix)) {
1258 sError = "Invalid StatusPrefix [" + m_sStatusPrefix +
1259 "] Must be 1-5 chars, no spaces.";
1260 CUtils::PrintError(sError);
1261 return false;
1262 }
1263 }
1264
1265 if (!pUser->ParseConfig(pSubConf, sError)) {
1266 CUtils::PrintError(sError);
1267 return false;
1268 }
1269
1270 if (!pSubConf->empty()) {
1271 sError = "Unhandled lines in config for User [" + sUsername + "]!";
1272 CUtils::PrintError(sError);
1273 DumpConfig(pSubConf);
1274 return false;
1275 }
1276
1277 CString sErr;
1278 CUser* pRawUser = pUser.release();
1279 if (!AddUser(pRawUser, sErr, true)) {
1280 sError = "Invalid user [" + sUsername + "] " + sErr;
1281 CUtils::PrintError(sError);
1282 pRawUser->SetBeingDeleted(true);
1283 delete pRawUser;
1284 return false;
1285 }
1286 }
1287
1288 if (m_msUsers.empty()) {
1289 sError = "You must define at least one user in your config.";
1290 CUtils::PrintError(sError);
1291 return false;
1292 }
1293
1294 return true;
1295 }
1296
LoadListeners(CConfig & config,CString & sError)1297 bool CZNC::LoadListeners(CConfig& config, CString& sError) {
1298 sError.clear();
1299
1300 // Delete all listeners
1301 while (!m_vpListeners.empty()) {
1302 delete m_vpListeners[0];
1303 m_vpListeners.erase(m_vpListeners.begin());
1304 }
1305
1306 // compatibility for pre-1.0 configs
1307 const char* szListenerEntries[] = {"listen", "listen6", "listen4",
1308 "listener", "listener6", "listener4"};
1309
1310 VCString vsList;
1311 config.FindStringVector("loadmodule", vsList);
1312
1313 // This has to be after SSLCertFile is handled since it uses that value
1314 for (const char* szEntry : szListenerEntries) {
1315 config.FindStringVector(szEntry, vsList);
1316 for (const CString& sListener : vsList) {
1317 if (!AddListener(szEntry + CString(" ") + sListener, sError))
1318 return false;
1319 }
1320 }
1321 // end-compatibility for pre-1.0 configs
1322
1323 CConfig::SubConfig subConf;
1324 config.FindSubConfig("listener", subConf);
1325
1326 for (const auto& subIt : subConf) {
1327 CConfig* pSubConf = subIt.second.m_pSubConfig;
1328 if (!AddListener(pSubConf, sError)) return false;
1329 if (!pSubConf->empty()) {
1330 sError = "Unhandled lines in Listener config!";
1331 CUtils::PrintError(sError);
1332
1333 CZNC::DumpConfig(pSubConf);
1334 return false;
1335 }
1336 }
1337
1338 if (m_vpListeners.empty()) {
1339 sError = "You must supply at least one Listener in your config.";
1340 CUtils::PrintError(sError);
1341 return false;
1342 }
1343
1344 return true;
1345 }
1346
UnloadRemovedModules(const MCString & msModules)1347 void CZNC::UnloadRemovedModules(const MCString& msModules) {
1348 // unload modules which are no longer in the config
1349
1350 set<CString> ssUnload;
1351 for (CModule* pCurMod : GetModules()) {
1352 if (msModules.find(pCurMod->GetModName()) == msModules.end())
1353 ssUnload.insert(pCurMod->GetModName());
1354 }
1355
1356 for (const CString& sMod : ssUnload) {
1357 if (GetModules().UnloadModule(sMod))
1358 CUtils::PrintMessage("Unloaded global module [" + sMod + "]");
1359 else
1360 CUtils::PrintMessage("Could not unload [" + sMod + "]");
1361 }
1362 }
1363
DumpConfig(const CConfig * pConfig)1364 void CZNC::DumpConfig(const CConfig* pConfig) {
1365 CConfig::EntryMapIterator eit = pConfig->BeginEntries();
1366 for (; eit != pConfig->EndEntries(); ++eit) {
1367 const CString& sKey = eit->first;
1368 const VCString& vsList = eit->second;
1369 VCString::const_iterator it = vsList.begin();
1370 for (; it != vsList.end(); ++it) {
1371 CUtils::PrintError(sKey + " = " + *it);
1372 }
1373 }
1374
1375 CConfig::SubConfigMapIterator sit = pConfig->BeginSubConfigs();
1376 for (; sit != pConfig->EndSubConfigs(); ++sit) {
1377 const CString& sKey = sit->first;
1378 const CConfig::SubConfig& sSub = sit->second;
1379 CConfig::SubConfig::const_iterator it = sSub.begin();
1380
1381 for (; it != sSub.end(); ++it) {
1382 CUtils::PrintError("SubConfig [" + sKey + " " + it->first + "]:");
1383 DumpConfig(it->second.m_pSubConfig);
1384 }
1385 }
1386 }
1387
ClearTrustedProxies()1388 void CZNC::ClearTrustedProxies() { m_vsTrustedProxies.clear(); }
1389
AddTrustedProxy(const CString & sHost)1390 bool CZNC::AddTrustedProxy(const CString& sHost) {
1391 if (sHost.empty()) {
1392 return false;
1393 }
1394
1395 for (const CString& sTrustedProxy : m_vsTrustedProxies) {
1396 if (sTrustedProxy.Equals(sHost)) {
1397 return false;
1398 }
1399 }
1400
1401 m_vsTrustedProxies.push_back(sHost);
1402 return true;
1403 }
1404
RemTrustedProxy(const CString & sHost)1405 bool CZNC::RemTrustedProxy(const CString& sHost) {
1406 VCString::iterator it;
1407 for (it = m_vsTrustedProxies.begin(); it != m_vsTrustedProxies.end();
1408 ++it) {
1409 if (sHost.Equals(*it)) {
1410 m_vsTrustedProxies.erase(it);
1411 return true;
1412 }
1413 }
1414
1415 return false;
1416 }
1417
Broadcast(const CString & sMessage,bool bAdminOnly,CUser * pSkipUser,CClient * pSkipClient)1418 void CZNC::Broadcast(const CString& sMessage, bool bAdminOnly, CUser* pSkipUser,
1419 CClient* pSkipClient) {
1420 for (const auto& it : m_msUsers) {
1421 if (bAdminOnly && !it.second->IsAdmin()) continue;
1422
1423 if (it.second != pSkipUser) {
1424 // TODO: translate message to user's language
1425 CString sMsg = sMessage;
1426
1427 bool bContinue = false;
1428 USERMODULECALL(OnBroadcast(sMsg), it.second, nullptr, &bContinue);
1429 if (bContinue) continue;
1430
1431 it.second->PutStatusNotice("*** " + sMsg, nullptr, pSkipClient);
1432 }
1433 }
1434 }
1435
FindModule(const CString & sModName,const CString & sUsername)1436 CModule* CZNC::FindModule(const CString& sModName, const CString& sUsername) {
1437 if (sUsername.empty()) {
1438 return CZNC::Get().GetModules().FindModule(sModName);
1439 }
1440
1441 CUser* pUser = FindUser(sUsername);
1442
1443 return (!pUser) ? nullptr : pUser->GetModules().FindModule(sModName);
1444 }
1445
FindModule(const CString & sModName,CUser * pUser)1446 CModule* CZNC::FindModule(const CString& sModName, CUser* pUser) {
1447 if (pUser) {
1448 return pUser->GetModules().FindModule(sModName);
1449 }
1450
1451 return CZNC::Get().GetModules().FindModule(sModName);
1452 }
1453
UpdateModule(const CString & sModule)1454 bool CZNC::UpdateModule(const CString& sModule) {
1455 CModule* pModule;
1456
1457 map<CUser*, CString> musLoaded;
1458 map<CIRCNetwork*, CString> mnsLoaded;
1459
1460 // Unload the module for every user and network
1461 for (const auto& it : m_msUsers) {
1462 CUser* pUser = it.second;
1463
1464 pModule = pUser->GetModules().FindModule(sModule);
1465 if (pModule) {
1466 musLoaded[pUser] = pModule->GetArgs();
1467 pUser->GetModules().UnloadModule(sModule);
1468 }
1469
1470 // See if the user has this module loaded to a network
1471 vector<CIRCNetwork*> vNetworks = pUser->GetNetworks();
1472 for (CIRCNetwork* pNetwork : vNetworks) {
1473 pModule = pNetwork->GetModules().FindModule(sModule);
1474 if (pModule) {
1475 mnsLoaded[pNetwork] = pModule->GetArgs();
1476 pNetwork->GetModules().UnloadModule(sModule);
1477 }
1478 }
1479 }
1480
1481 // Unload the global module
1482 bool bGlobal = false;
1483 CString sGlobalArgs;
1484
1485 pModule = GetModules().FindModule(sModule);
1486 if (pModule) {
1487 bGlobal = true;
1488 sGlobalArgs = pModule->GetArgs();
1489 GetModules().UnloadModule(sModule);
1490 }
1491
1492 // Lets reload everything
1493 bool bError = false;
1494 CString sErr;
1495
1496 // Reload the global module
1497 if (bGlobal) {
1498 if (!GetModules().LoadModule(sModule, sGlobalArgs,
1499 CModInfo::GlobalModule, nullptr, nullptr,
1500 sErr)) {
1501 DEBUG("Failed to reload [" << sModule << "] globally [" << sErr
1502 << "]");
1503 bError = true;
1504 }
1505 }
1506
1507 // Reload the module for all users
1508 for (const auto& it : musLoaded) {
1509 CUser* pUser = it.first;
1510 const CString& sArgs = it.second;
1511
1512 if (!pUser->GetModules().LoadModule(
1513 sModule, sArgs, CModInfo::UserModule, pUser, nullptr, sErr)) {
1514 DEBUG("Failed to reload [" << sModule << "] for ["
1515 << pUser->GetUsername() << "] [" << sErr
1516 << "]");
1517 bError = true;
1518 }
1519 }
1520
1521 // Reload the module for all networks
1522 for (const auto& it : mnsLoaded) {
1523 CIRCNetwork* pNetwork = it.first;
1524 const CString& sArgs = it.second;
1525
1526 if (!pNetwork->GetModules().LoadModule(
1527 sModule, sArgs, CModInfo::NetworkModule, pNetwork->GetUser(),
1528 pNetwork, sErr)) {
1529 DEBUG("Failed to reload ["
1530 << sModule << "] for [" << pNetwork->GetUser()->GetUsername()
1531 << "/" << pNetwork->GetName() << "] [" << sErr << "]");
1532 bError = true;
1533 }
1534 }
1535
1536 return !bError;
1537 }
1538
FindUser(const CString & sUsername)1539 CUser* CZNC::FindUser(const CString& sUsername) {
1540 map<CString, CUser*>::iterator it = m_msUsers.find(sUsername);
1541
1542 if (it != m_msUsers.end()) {
1543 return it->second;
1544 }
1545
1546 return nullptr;
1547 }
1548
DeleteUser(const CString & sUsername)1549 bool CZNC::DeleteUser(const CString& sUsername) {
1550 CUser* pUser = FindUser(sUsername);
1551
1552 if (!pUser) {
1553 return false;
1554 }
1555
1556 m_msDelUsers[pUser->GetUsername()] = pUser;
1557 return true;
1558 }
1559
AddUser(CUser * pUser,CString & sErrorRet,bool bStartup)1560 bool CZNC::AddUser(CUser* pUser, CString& sErrorRet, bool bStartup) {
1561 if (FindUser(pUser->GetUsername()) != nullptr) {
1562 sErrorRet = t_s("User already exists");
1563 DEBUG("User [" << pUser->GetUsername() << "] - already exists");
1564 return false;
1565 }
1566 if (!pUser->IsValid(sErrorRet)) {
1567 DEBUG("Invalid user [" << pUser->GetUsername() << "] - [" << sErrorRet
1568 << "]");
1569 return false;
1570 }
1571 bool bFailed = false;
1572
1573 // do not call OnAddUser hook during ZNC startup
1574 if (!bStartup) {
1575 GLOBALMODULECALL(OnAddUser(*pUser, sErrorRet), &bFailed);
1576 }
1577
1578 if (bFailed) {
1579 DEBUG("AddUser [" << pUser->GetUsername() << "] aborted by a module ["
1580 << sErrorRet << "]");
1581 return false;
1582 }
1583 m_msUsers[pUser->GetUsername()] = pUser;
1584 return true;
1585 }
1586
FindListener(u_short uPort,const CString & sBindHost,EAddrType eAddr)1587 CListener* CZNC::FindListener(u_short uPort, const CString& sBindHost,
1588 EAddrType eAddr) {
1589 for (CListener* pListener : m_vpListeners) {
1590 if (pListener->GetPort() != uPort) continue;
1591 if (pListener->GetBindHost() != sBindHost) continue;
1592 if (pListener->GetAddrType() != eAddr) continue;
1593 return pListener;
1594 }
1595 return nullptr;
1596 }
1597
AddListener(const CString & sLine,CString & sError)1598 bool CZNC::AddListener(const CString& sLine, CString& sError) {
1599 CString sName = sLine.Token(0);
1600 CString sValue = sLine.Token(1, true);
1601
1602 EAddrType eAddr = ADDR_ALL;
1603 if (sName.Equals("Listen4") || sName.Equals("Listen") ||
1604 sName.Equals("Listener4")) {
1605 eAddr = ADDR_IPV4ONLY;
1606 }
1607 if (sName.Equals("Listener6")) {
1608 eAddr = ADDR_IPV6ONLY;
1609 }
1610
1611 CListener::EAcceptType eAccept = CListener::ACCEPT_ALL;
1612 if (sValue.TrimPrefix("irc_only "))
1613 eAccept = CListener::ACCEPT_IRC;
1614 else if (sValue.TrimPrefix("web_only "))
1615 eAccept = CListener::ACCEPT_HTTP;
1616
1617 bool bSSL = false;
1618 CString sPort;
1619 CString sBindHost;
1620
1621 if (ADDR_IPV4ONLY == eAddr) {
1622 sValue.Replace(":", " ");
1623 }
1624
1625 if (sValue.Contains(" ")) {
1626 sBindHost = sValue.Token(0, false, " ");
1627 sPort = sValue.Token(1, true, " ");
1628 } else {
1629 sPort = sValue;
1630 }
1631
1632 if (sPort.TrimPrefix("+")) {
1633 bSSL = true;
1634 }
1635
1636 // No support for URIPrefix for old-style configs.
1637 CString sURIPrefix;
1638 unsigned short uPort = sPort.ToUShort();
1639 return AddListener(uPort, sBindHost, sURIPrefix, bSSL, eAddr, eAccept,
1640 sError);
1641 }
1642
AddListener(unsigned short uPort,const CString & sBindHost,const CString & sURIPrefixRaw,bool bSSL,EAddrType eAddr,CListener::EAcceptType eAccept,CString & sError)1643 bool CZNC::AddListener(unsigned short uPort, const CString& sBindHost,
1644 const CString& sURIPrefixRaw, bool bSSL, EAddrType eAddr,
1645 CListener::EAcceptType eAccept, CString& sError) {
1646 CString sHostComment;
1647
1648 if (!sBindHost.empty()) {
1649 sHostComment = " on host [" + sBindHost + "]";
1650 }
1651
1652 CString sIPV6Comment;
1653
1654 switch (eAddr) {
1655 case ADDR_ALL:
1656 sIPV6Comment = "";
1657 break;
1658 case ADDR_IPV4ONLY:
1659 sIPV6Comment = " using ipv4";
1660 break;
1661 case ADDR_IPV6ONLY:
1662 sIPV6Comment = " using ipv6";
1663 }
1664
1665 CUtils::PrintAction("Binding to port [" + CString((bSSL) ? "+" : "") +
1666 CString(uPort) + "]" + sHostComment + sIPV6Comment);
1667
1668 #ifndef HAVE_IPV6
1669 if (ADDR_IPV6ONLY == eAddr) {
1670 sError = t_s("IPv6 is not enabled");
1671 CUtils::PrintStatus(false, sError);
1672 return false;
1673 }
1674 #endif
1675
1676 #ifndef HAVE_LIBSSL
1677 if (bSSL) {
1678 sError = t_s("SSL is not enabled");
1679 CUtils::PrintStatus(false, sError);
1680 return false;
1681 }
1682 #else
1683 CString sPemFile = GetPemLocation();
1684
1685 if (bSSL && !CFile::Exists(sPemFile)) {
1686 sError = t_f("Unable to locate pem file: {1}")(sPemFile);
1687 CUtils::PrintStatus(false, sError);
1688
1689 // If stdin is e.g. /dev/null and we call GetBoolInput(),
1690 // we are stuck in an endless loop!
1691 if (isatty(0) &&
1692 CUtils::GetBoolInput("Would you like to create a new pem file?",
1693 true)) {
1694 sError.clear();
1695 WritePemFile();
1696 } else {
1697 return false;
1698 }
1699
1700 CUtils::PrintAction("Binding to port [+" + CString(uPort) + "]" +
1701 sHostComment + sIPV6Comment);
1702 }
1703 #endif
1704 if (!uPort) {
1705 sError = t_s("Invalid port");
1706 CUtils::PrintStatus(false, sError);
1707 return false;
1708 }
1709
1710 // URIPrefix must start with a slash and end without one.
1711 CString sURIPrefix = CString(sURIPrefixRaw);
1712 if (!sURIPrefix.empty()) {
1713 if (!sURIPrefix.StartsWith("/")) {
1714 sURIPrefix = "/" + sURIPrefix;
1715 }
1716 if (sURIPrefix.EndsWith("/")) {
1717 sURIPrefix.TrimRight("/");
1718 }
1719 }
1720
1721 CListener* pListener =
1722 new CListener(uPort, sBindHost, sURIPrefix, bSSL, eAddr, eAccept);
1723
1724 if (!pListener->Listen()) {
1725 sError = FormatBindError();
1726 CUtils::PrintStatus(false, sError);
1727 delete pListener;
1728 return false;
1729 }
1730
1731 m_vpListeners.push_back(pListener);
1732 CUtils::PrintStatus(true);
1733
1734 return true;
1735 }
1736
AddListener(CConfig * pConfig,CString & sError)1737 bool CZNC::AddListener(CConfig* pConfig, CString& sError) {
1738 CString sBindHost;
1739 CString sURIPrefix;
1740 bool bSSL;
1741 bool b4;
1742 #ifdef HAVE_IPV6
1743 bool b6 = true;
1744 #else
1745 bool b6 = false;
1746 #endif
1747 bool bIRC;
1748 bool bWeb;
1749 unsigned short uPort;
1750 if (!pConfig->FindUShortEntry("port", uPort)) {
1751 sError = "No port given";
1752 CUtils::PrintError(sError);
1753 return false;
1754 }
1755 pConfig->FindStringEntry("host", sBindHost);
1756 pConfig->FindBoolEntry("ssl", bSSL, false);
1757 pConfig->FindBoolEntry("ipv4", b4, true);
1758 pConfig->FindBoolEntry("ipv6", b6, b6);
1759 pConfig->FindBoolEntry("allowirc", bIRC, true);
1760 pConfig->FindBoolEntry("allowweb", bWeb, true);
1761 pConfig->FindStringEntry("uriprefix", sURIPrefix);
1762
1763 EAddrType eAddr;
1764 if (b4 && b6) {
1765 eAddr = ADDR_ALL;
1766 } else if (b4 && !b6) {
1767 eAddr = ADDR_IPV4ONLY;
1768 } else if (!b4 && b6) {
1769 eAddr = ADDR_IPV6ONLY;
1770 } else {
1771 sError = "No address family given";
1772 CUtils::PrintError(sError);
1773 return false;
1774 }
1775
1776 CListener::EAcceptType eAccept;
1777 if (bIRC && bWeb) {
1778 eAccept = CListener::ACCEPT_ALL;
1779 } else if (bIRC && !bWeb) {
1780 eAccept = CListener::ACCEPT_IRC;
1781 } else if (!bIRC && bWeb) {
1782 eAccept = CListener::ACCEPT_HTTP;
1783 } else {
1784 sError = "Either Web or IRC or both should be selected";
1785 CUtils::PrintError(sError);
1786 return false;
1787 }
1788
1789 return AddListener(uPort, sBindHost, sURIPrefix, bSSL, eAddr, eAccept,
1790 sError);
1791 }
1792
AddListener(CListener * pListener)1793 bool CZNC::AddListener(CListener* pListener) {
1794 if (!pListener->GetRealListener()) {
1795 // Listener doesn't actually listen
1796 delete pListener;
1797 return false;
1798 }
1799
1800 // We don't check if there is an identical listener already listening
1801 // since one can't listen on e.g. the same port multiple times
1802
1803 m_vpListeners.push_back(pListener);
1804 return true;
1805 }
1806
DelListener(CListener * pListener)1807 bool CZNC::DelListener(CListener* pListener) {
1808 auto it = std::find(m_vpListeners.begin(), m_vpListeners.end(), pListener);
1809 if (it != m_vpListeners.end()) {
1810 m_vpListeners.erase(it);
1811 delete pListener;
1812 return true;
1813 }
1814
1815 return false;
1816 }
1817
FormatBindError()1818 CString CZNC::FormatBindError() {
1819 CString sError = (errno == 0 ? t_s(("unknown error, check the host name"))
1820 : CString(strerror(errno)));
1821 return t_f("Unable to bind: {1}")(sError);
1822 }
1823
1824 static CZNC* s_pZNC = nullptr;
1825
CreateInstance()1826 void CZNC::CreateInstance() {
1827 if (s_pZNC) abort();
1828
1829 s_pZNC = new CZNC();
1830 }
1831
Get()1832 CZNC& CZNC::Get() { return *s_pZNC; }
1833
DestroyInstance()1834 void CZNC::DestroyInstance() {
1835 delete s_pZNC;
1836 s_pZNC = nullptr;
1837 }
1838
GetTrafficStats(TrafficStatsPair & Users,TrafficStatsPair & ZNC,TrafficStatsPair & Total)1839 CZNC::TrafficStatsMap CZNC::GetTrafficStats(TrafficStatsPair& Users,
1840 TrafficStatsPair& ZNC,
1841 TrafficStatsPair& Total) {
1842 TrafficStatsMap ret;
1843 unsigned long long uiUsers_in, uiUsers_out, uiZNC_in, uiZNC_out;
1844 const map<CString, CUser*>& msUsers = CZNC::Get().GetUserMap();
1845
1846 uiUsers_in = uiUsers_out = 0;
1847 uiZNC_in = BytesRead();
1848 uiZNC_out = BytesWritten();
1849
1850 for (const auto& it : msUsers) {
1851 ret[it.first] =
1852 TrafficStatsPair(it.second->BytesRead(), it.second->BytesWritten());
1853 uiUsers_in += it.second->BytesRead();
1854 uiUsers_out += it.second->BytesWritten();
1855 }
1856
1857 for (Csock* pSock : m_Manager) {
1858 CUser* pUser = nullptr;
1859 if (pSock->GetSockName().StartsWith("IRC::")) {
1860 pUser = ((CIRCSock*)pSock)->GetNetwork()->GetUser();
1861 } else if (pSock->GetSockName().StartsWith("USR::")) {
1862 pUser = ((CClient*)pSock)->GetUser();
1863 }
1864
1865 if (pUser) {
1866 ret[pUser->GetUsername()].first += pSock->GetBytesRead();
1867 ret[pUser->GetUsername()].second += pSock->GetBytesWritten();
1868 uiUsers_in += pSock->GetBytesRead();
1869 uiUsers_out += pSock->GetBytesWritten();
1870 } else {
1871 uiZNC_in += pSock->GetBytesRead();
1872 uiZNC_out += pSock->GetBytesWritten();
1873 }
1874 }
1875
1876 Users = TrafficStatsPair(uiUsers_in, uiUsers_out);
1877 ZNC = TrafficStatsPair(uiZNC_in, uiZNC_out);
1878 Total = TrafficStatsPair(uiUsers_in + uiZNC_in, uiUsers_out + uiZNC_out);
1879
1880 return ret;
1881 }
1882
GetNetworkTrafficStats(const CString & sUsername,TrafficStatsPair & Total)1883 CZNC::TrafficStatsMap CZNC::GetNetworkTrafficStats(const CString& sUsername,
1884 TrafficStatsPair& Total) {
1885 TrafficStatsMap Networks;
1886
1887 CUser* pUser = FindUser(sUsername);
1888 if (pUser) {
1889 for (const CIRCNetwork* pNetwork : pUser->GetNetworks()) {
1890 Networks[pNetwork->GetName()].first = pNetwork->BytesRead();
1891 Networks[pNetwork->GetName()].second = pNetwork->BytesWritten();
1892 Total.first += pNetwork->BytesRead();
1893 Total.second += pNetwork->BytesWritten();
1894 }
1895
1896 for (Csock* pSock : m_Manager) {
1897 CIRCNetwork* pNetwork = nullptr;
1898 if (pSock->GetSockName().StartsWith("IRC::")) {
1899 pNetwork = ((CIRCSock*)pSock)->GetNetwork();
1900 } else if (pSock->GetSockName().StartsWith("USR::")) {
1901 pNetwork = ((CClient*)pSock)->GetNetwork();
1902 }
1903
1904 if (pNetwork && pNetwork->GetUser() == pUser) {
1905 Networks[pNetwork->GetName()].first = pSock->GetBytesRead();
1906 Networks[pNetwork->GetName()].second = pSock->GetBytesWritten();
1907 Total.first += pSock->GetBytesRead();
1908 Total.second += pSock->GetBytesWritten();
1909 }
1910 }
1911 }
1912
1913 return Networks;
1914 }
1915
AuthUser(std::shared_ptr<CAuthBase> AuthClass)1916 void CZNC::AuthUser(std::shared_ptr<CAuthBase> AuthClass) {
1917 // TODO unless the auth module calls it, CUser::IsHostAllowed() is not
1918 // honoured
1919 bool bReturn = false;
1920 GLOBALMODULECALL(OnLoginAttempt(AuthClass), &bReturn);
1921 if (bReturn) return;
1922
1923 CUser* pUser = FindUser(AuthClass->GetUsername());
1924
1925 if (!pUser || !pUser->CheckPass(AuthClass->GetPassword())) {
1926 AuthClass->RefuseLogin("Invalid Password");
1927 return;
1928 }
1929
1930 CString sHost = AuthClass->GetRemoteIP();
1931
1932 if (!pUser->IsHostAllowed(sHost)) {
1933 AuthClass->RefuseLogin("Your host [" + sHost + "] is not allowed");
1934 return;
1935 }
1936
1937 AuthClass->AcceptLogin(*pUser);
1938 }
1939
1940 class CConnectQueueTimer : public CCron {
1941 public:
CConnectQueueTimer(int iSecs)1942 CConnectQueueTimer(int iSecs) : CCron() {
1943 SetName("Connect users");
1944 Start(iSecs);
1945 // Don't wait iSecs seconds for first timer run
1946 m_bRunOnNextCall = true;
1947 }
~CConnectQueueTimer()1948 ~CConnectQueueTimer() override {
1949 // This is only needed when ZNC shuts down:
1950 // CZNC::~CZNC() sets its CConnectQueueTimer pointer to nullptr and
1951 // calls the manager's Cleanup() which destroys all sockets and
1952 // timers. If something calls CZNC::EnableConnectQueue() here
1953 // (e.g. because a CIRCSock is destroyed), the socket manager
1954 // deletes that timer almost immediately, but CZNC now got a
1955 // dangling pointer to this timer which can crash later on.
1956 //
1957 // Unlikely but possible ;)
1958 CZNC::Get().LeakConnectQueueTimer(this);
1959 }
1960
1961 protected:
RunJob()1962 void RunJob() override {
1963 list<CIRCNetwork*> ConnectionQueue;
1964 list<CIRCNetwork*>& RealConnectionQueue =
1965 CZNC::Get().GetConnectionQueue();
1966
1967 // Problem: If a network can't connect right now because e.g. it
1968 // is throttled, it will re-insert itself into the connection
1969 // queue. However, we must only give each network a single
1970 // chance during this timer run.
1971 //
1972 // Solution: We move the connection queue to our local list at
1973 // the beginning and work from that.
1974 ConnectionQueue.swap(RealConnectionQueue);
1975
1976 while (!ConnectionQueue.empty()) {
1977 CIRCNetwork* pNetwork = ConnectionQueue.front();
1978 ConnectionQueue.pop_front();
1979
1980 if (pNetwork->Connect()) {
1981 break;
1982 }
1983 }
1984
1985 /* Now re-insert anything that is left in our local list into
1986 * the real connection queue.
1987 */
1988 RealConnectionQueue.splice(RealConnectionQueue.begin(),
1989 ConnectionQueue);
1990
1991 if (RealConnectionQueue.empty()) {
1992 DEBUG("ConnectQueueTimer done");
1993 CZNC::Get().DisableConnectQueue();
1994 }
1995 }
1996 };
1997
SetConnectDelay(unsigned int i)1998 void CZNC::SetConnectDelay(unsigned int i) {
1999 if (i < 1) {
2000 // Don't hammer server with our failed connects
2001 i = 1;
2002 }
2003 if (m_uiConnectDelay != i && m_pConnectQueueTimer != nullptr) {
2004 m_pConnectQueueTimer->Start(i);
2005 }
2006 m_uiConnectDelay = i;
2007 }
2008
GetAvailableSSLProtocols()2009 VCString CZNC::GetAvailableSSLProtocols() {
2010 // NOTE: keep in sync with SetSSLProtocols()
2011 return {"SSLv2", "SSLv3", "TLSv1", "TLSV1.1", "TLSv1.2"};
2012 }
2013
SetSSLProtocols(const CString & sProtocols)2014 bool CZNC::SetSSLProtocols(const CString& sProtocols) {
2015 VCString vsProtocols;
2016 sProtocols.Split(" ", vsProtocols, false, "", "", true, true);
2017
2018 unsigned int uDisabledProtocols = Csock::EDP_SSL;
2019 for (CString& sProtocol : vsProtocols) {
2020 unsigned int uFlag = 0;
2021 bool bEnable = sProtocol.TrimPrefix("+");
2022 bool bDisable = sProtocol.TrimPrefix("-");
2023
2024 // NOTE: keep in sync with GetAvailableSSLProtocols()
2025 if (sProtocol.Equals("All")) {
2026 uFlag = ~0;
2027 } else if (sProtocol.Equals("SSLv2")) {
2028 uFlag = Csock::EDP_SSLv2;
2029 } else if (sProtocol.Equals("SSLv3")) {
2030 uFlag = Csock::EDP_SSLv3;
2031 } else if (sProtocol.Equals("TLSv1")) {
2032 uFlag = Csock::EDP_TLSv1;
2033 } else if (sProtocol.Equals("TLSv1.1")) {
2034 uFlag = Csock::EDP_TLSv1_1;
2035 } else if (sProtocol.Equals("TLSv1.2")) {
2036 uFlag = Csock::EDP_TLSv1_2;
2037 } else {
2038 return false;
2039 }
2040
2041 if (bEnable) {
2042 uDisabledProtocols &= ~uFlag;
2043 } else if (bDisable) {
2044 uDisabledProtocols |= uFlag;
2045 } else {
2046 uDisabledProtocols = ~uFlag;
2047 }
2048 }
2049
2050 m_sSSLProtocols = sProtocols;
2051 m_uDisabledSSLProtocols = uDisabledProtocols;
2052 return true;
2053 }
2054
EnableConnectQueue()2055 void CZNC::EnableConnectQueue() {
2056 if (!m_pConnectQueueTimer && !m_uiConnectPaused &&
2057 !m_lpConnectQueue.empty()) {
2058 m_pConnectQueueTimer = new CConnectQueueTimer(m_uiConnectDelay);
2059 GetManager().AddCron(m_pConnectQueueTimer);
2060 }
2061 }
2062
DisableConnectQueue()2063 void CZNC::DisableConnectQueue() {
2064 if (m_pConnectQueueTimer) {
2065 // This will kill the cron
2066 m_pConnectQueueTimer->Stop();
2067 m_pConnectQueueTimer = nullptr;
2068 }
2069 }
2070
PauseConnectQueue()2071 void CZNC::PauseConnectQueue() {
2072 DEBUG("Connection queue paused");
2073 m_uiConnectPaused++;
2074
2075 if (m_pConnectQueueTimer) {
2076 m_pConnectQueueTimer->Pause();
2077 }
2078 }
2079
ResumeConnectQueue()2080 void CZNC::ResumeConnectQueue() {
2081 DEBUG("Connection queue resumed");
2082 m_uiConnectPaused--;
2083
2084 EnableConnectQueue();
2085 if (m_pConnectQueueTimer) {
2086 m_pConnectQueueTimer->UnPause();
2087 }
2088 }
2089
ForceEncoding()2090 void CZNC::ForceEncoding() {
2091 m_uiForceEncoding++;
2092 #ifdef HAVE_ICU
2093 for (Csock* pSock : GetManager()) {
2094 pSock->SetEncoding(FixupEncoding(pSock->GetEncoding()));
2095 }
2096 #endif
2097 }
UnforceEncoding()2098 void CZNC::UnforceEncoding() { m_uiForceEncoding--; }
IsForcingEncoding() const2099 bool CZNC::IsForcingEncoding() const { return m_uiForceEncoding; }
FixupEncoding(const CString & sEncoding) const2100 CString CZNC::FixupEncoding(const CString& sEncoding) const {
2101 if (!m_uiForceEncoding) {
2102 return sEncoding;
2103 }
2104 if (sEncoding.empty()) {
2105 return "UTF-8";
2106 }
2107 const char* sRealEncoding = sEncoding.c_str();
2108 if (sEncoding[0] == '*' || sEncoding[0] == '^') {
2109 sRealEncoding++;
2110 }
2111 if (!*sRealEncoding) {
2112 return "UTF-8";
2113 }
2114 #ifdef HAVE_ICU
2115 UErrorCode e = U_ZERO_ERROR;
2116 UConverter* cnv = ucnv_open(sRealEncoding, &e);
2117 if (cnv) {
2118 ucnv_close(cnv);
2119 }
2120 if (U_FAILURE(e)) {
2121 return "UTF-8";
2122 }
2123 #endif
2124 return sEncoding;
2125 }
2126
AddNetworkToQueue(CIRCNetwork * pNetwork)2127 void CZNC::AddNetworkToQueue(CIRCNetwork* pNetwork) {
2128 // Make sure we are not already in the queue
2129 if (std::find(m_lpConnectQueue.begin(), m_lpConnectQueue.end(), pNetwork) !=
2130 m_lpConnectQueue.end()) {
2131 return;
2132 }
2133
2134 m_lpConnectQueue.push_back(pNetwork);
2135 EnableConnectQueue();
2136 }
2137
LeakConnectQueueTimer(CConnectQueueTimer * pTimer)2138 void CZNC::LeakConnectQueueTimer(CConnectQueueTimer* pTimer) {
2139 if (m_pConnectQueueTimer == pTimer) m_pConnectQueueTimer = nullptr;
2140 }
2141
WaitForChildLock()2142 bool CZNC::WaitForChildLock() { return m_pLockFile && m_pLockFile->ExLock(); }
2143
DisableConfigTimer()2144 void CZNC::DisableConfigTimer() {
2145 if (m_pConfigTimer) {
2146 m_pConfigTimer->Stop();
2147 m_pConfigTimer = nullptr;
2148 }
2149 }
2150