Coverage Report

Created: 2023-09-25 06:17

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