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