Coverage Report

Created: 2025-03-27 06:28

/src/znc/src/znc.cpp
Line
Count
Source (jump to first uncovered line)
1
/*
2
 * Copyright (C) 2004-2025 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
0
    : m_TimeStarted(time(nullptr)),
39
0
      m_eConfigState(ECONFIG_NOTHING),
40
0
      m_vpListeners(),
41
0
      m_msUsers(),
42
0
      m_msDelUsers(),
43
0
      m_Manager(),
44
0
      m_sCurPath(""),
45
0
      m_sZNCPath(""),
46
0
      m_sConfigFile(""),
47
0
      m_sSkinName(""),
48
0
      m_sStatusPrefix(""),
49
0
      m_sPidFile(""),
50
0
      m_sSSLCertFile(""),
51
0
      m_sSSLKeyFile(""),
52
0
      m_sSSLDHParamFile(""),
53
0
      m_sSSLCiphers(""),
54
0
      m_sSSLProtocols(""),
55
0
      m_vsBindHosts(),
56
0
      m_vsTrustedProxies(),
57
0
      m_vsMotd(),
58
0
      m_pLockFile(nullptr),
59
0
      m_uiConnectDelay(5),
60
0
      m_uiAnonIPLimit(10),
61
0
      m_uiMaxBufferSize(500),
62
0
      m_uDisabledSSLProtocols(Csock::EDP_SSL | Csock::EDP_TLSv1 | Csock::EDP_TLSv1_1),
63
0
      m_pModules(new CModules),
64
0
      m_uBytesRead(0),
65
0
      m_uBytesWritten(0),
66
0
      m_lpConnectQueue(),
67
0
      m_pConnectQueueTimer(nullptr),
68
0
      m_uiConnectPaused(0),
69
0
      m_uiForceEncoding(0),
70
0
      m_sConnectThrottle(),
71
0
      m_bProtectWebSessions(true),
72
0
      m_bHideVersion(false),
73
0
      m_bAuthOnlyViaModule(false),
74
0
      m_Translation("znc"),
75
0
      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
    sAnswer = CUtils::AskSaltedHashPassForConfig();
738
0
  vsLines.push_back(sAnswer);
739
740
0
    vsLines.push_back("\tAdmin      = true");
741
742
0
    CUtils::GetInput("Nick", sNick, CUser::MakeCleanUsername(sUser));
743
0
    vsLines.push_back("\tNick       = " + sNick);
744
0
    CUtils::GetInput("Alternate nick", sAnswer, sNick + "_");
745
0
    if (!sAnswer.empty()) {
746
0
        vsLines.push_back("\tAltNick    = " + sAnswer);
747
0
    }
748
0
    CUtils::GetInput("Ident", sAnswer, sUser);
749
0
    vsLines.push_back("\tIdent      = " + sAnswer);
750
0
    CUtils::GetInput("Real name", sAnswer, "", "optional");
751
0
    if (!sAnswer.empty()) {
752
0
        vsLines.push_back("\tRealName   = " + sAnswer);
753
0
    }
754
0
    CUtils::GetInput("Bind host", sAnswer, "", "optional");
755
0
    if (!sAnswer.empty()) {
756
0
        vsLines.push_back("\tBindHost   = " + sAnswer);
757
0
    }
758
759
0
    set<CModInfo> ssUserMods;
760
0
    GetModules().GetDefaultMods(ssUserMods, CModInfo::UserModule);
761
0
    vector<CString> vsUserModNames;
762
0
    for (const CModInfo& Info : ssUserMods) {
763
0
        vsUserModNames.push_back(Info.GetName());
764
0
        vsLines.push_back("\tLoadModule = " + Info.GetName());
765
0
    }
766
0
    CUtils::PrintMessage(
767
0
        "Enabled user modules [" +
768
0
        CString(", ").Join(vsUserModNames.begin(), vsUserModNames.end()) + "]");
769
770
0
    CUtils::PrintMessage("");
771
0
    if (CUtils::GetBoolInput("Set up a network?", true)) {
772
0
        vsLines.push_back("");
773
774
0
        CUtils::PrintMessage("");
775
0
        CUtils::PrintMessage("-- Network settings --");
776
0
        CUtils::PrintMessage("");
777
778
0
        do {
779
0
            CUtils::GetInput("Name", sNetwork, "libera");
780
0
        } while (!CIRCNetwork::IsValidNetwork(sNetwork));
781
782
0
        vsLines.push_back("\t<Network " + sNetwork + ">");
783
784
0
        set<CModInfo> ssNetworkMods;
785
0
        GetModules().GetDefaultMods(ssNetworkMods, CModInfo::NetworkModule);
786
0
        vector<CString> vsNetworkModNames;
787
0
        for (const CModInfo& Info : ssNetworkMods) {
788
0
            vsNetworkModNames.push_back(Info.GetName());
789
0
            vsLines.push_back("\t\tLoadModule = " + Info.GetName());
790
0
        }
791
792
0
        CString sHost, sPass, sHint;
793
0
        bool bSSL = false;
794
0
        unsigned int uServerPort = 0;
795
796
0
        if (sNetwork.Equals("libera")) {
797
0
            sHost = "irc.libera.chat";
798
#ifdef HAVE_LIBSSL
799
            bSSL = true;
800
#endif
801
0
        } else {
802
0
            sHint = "host only";
803
0
        }
804
805
0
        while (!CUtils::GetInput("Server host", sHost, sHost, sHint) ||
806
0
               !CServer::IsValidHostName(sHost))
807
0
            ;
808
#ifdef HAVE_LIBSSL
809
        bSSL = CUtils::GetBoolInput("Server uses SSL?", bSSL);
810
#endif
811
0
        while (!CUtils::GetNumInput("Server port", uServerPort, 1, 65535,
812
0
                                    bSSL ? 6697 : 6667))
813
0
            ;
814
0
        CUtils::GetInput("Server password (probably empty)", sPass);
815
816
0
        vsLines.push_back("\t\tServer     = " + sHost + ((bSSL) ? " +" : " ") +
817
0
                          CString(uServerPort) + " " + sPass);
818
819
0
        CString sChans;
820
0
        if (CUtils::GetInput("Initial channels", sChans)) {
821
0
            vsLines.push_back("");
822
0
            VCString vsChans;
823
0
            sChans.Replace(",", " ");
824
0
            sChans.Replace(";", " ");
825
0
            sChans.Split(" ", vsChans, false, "", "", true, true);
826
0
            for (const CString& sChan : vsChans) {
827
0
                vsLines.push_back("\t\t<Chan " + sChan + ">");
828
0
                vsLines.push_back("\t\t</Chan>");
829
0
            }
830
0
        }
831
832
0
        CUtils::PrintMessage("Enabled network modules [" +
833
0
                             CString(", ").Join(vsNetworkModNames.begin(),
834
0
                                                vsNetworkModNames.end()) +
835
0
                             "]");
836
837
0
        vsLines.push_back("\t</Network>");
838
0
    }
839
840
0
    vsLines.push_back("</User>");
841
842
0
    CUtils::PrintMessage("");
843
    // !User
844
845
0
    CFile File;
846
0
    bool bFileOK, bFileOpen = false;
847
0
    do {
848
0
        CUtils::PrintAction("Writing config [" + m_sConfigFile + "]");
849
850
0
        bFileOK = true;
851
0
        if (CFile::Exists(m_sConfigFile)) {
852
0
            if (!File.TryExLock(m_sConfigFile)) {
853
0
                CUtils::PrintStatus(false,
854
0
                                    "ZNC is currently running on this config.");
855
0
                bFileOK = false;
856
0
            } else {
857
0
                File.Close();
858
0
                CUtils::PrintStatus(false, "This config already exists.");
859
0
                if (CUtils::GetBoolInput(
860
0
                        "Are you sure you want to overwrite it?", false))
861
0
                    CUtils::PrintAction("Overwriting config [" + m_sConfigFile +
862
0
                                        "]");
863
0
                else
864
0
                    bFileOK = false;
865
0
            }
866
0
        }
867
868
0
        if (bFileOK) {
869
0
            File.SetFileName(m_sConfigFile);
870
0
            if (File.Open(O_WRONLY | O_CREAT | O_TRUNC, 0600)) {
871
0
                bFileOpen = true;
872
0
            } else {
873
0
                CUtils::PrintStatus(false, "Unable to open file");
874
0
                bFileOK = false;
875
0
            }
876
0
        }
877
0
        if (!bFileOK) {
878
0
            while (!CUtils::GetInput("Please specify an alternate location",
879
0
                                     m_sConfigFile, "",
880
0
                                     "or \"stdout\" for displaying the config"))
881
0
                ;
882
0
            if (m_sConfigFile.Equals("stdout"))
883
0
                bFileOK = true;
884
0
            else
885
0
                m_sConfigFile = ExpandConfigPath(m_sConfigFile);
886
0
        }
887
0
    } while (!bFileOK);
888
889
0
    if (!bFileOpen) {
890
0
        CUtils::PrintMessage("");
891
0
        CUtils::PrintMessage("Printing the new config to stdout:");
892
0
        CUtils::PrintMessage("");
893
0
        cout << endl << "------------------------------------------------------"
894
0
                        "----------------------" << endl << endl;
895
0
    }
896
897
0
    for (const CString& sLine : vsLines) {
898
0
        if (bFileOpen) {
899
0
            File.Write(sLine + "\n");
900
0
        } else {
901
0
            cout << sLine << endl;
902
0
        }
903
0
    }
904
905
0
    if (bFileOpen) {
906
0
        File.Close();
907
0
        if (File.HadError())
908
0
            CUtils::PrintStatus(false,
909
0
                                "There was an error while writing the config");
910
0
        else
911
0
            CUtils::PrintStatus(true);
912
0
    } else {
913
0
        cout << endl << "------------------------------------------------------"
914
0
                        "----------------------" << endl << endl;
915
0
    }
916
917
0
    if (File.HadError()) {
918
0
        bFileOpen = false;
919
0
        CUtils::PrintMessage("Printing the new config to stdout instead:");
920
0
        cout << endl << "------------------------------------------------------"
921
0
                        "----------------------" << endl << endl;
922
0
        for (const CString& sLine : vsLines) {
923
0
            cout << sLine << endl;
924
0
        }
925
0
        cout << endl << "------------------------------------------------------"
926
0
                        "----------------------" << endl << endl;
927
0
    }
928
929
0
    const CString sProtocol(bListenSSL ? "https" : "http");
930
0
    const CString sSSL(bListenSSL ? "+" : "");
931
0
    CUtils::PrintMessage("");
932
0
    CUtils::PrintMessage(
933
0
        "To connect to this ZNC you need to connect to it as your IRC server",
934
0
        true);
935
0
    CUtils::PrintMessage(
936
0
        "using the port that you supplied.  You have to supply your login info",
937
0
        true);
938
0
    CUtils::PrintMessage(
939
0
        "as the IRC server password like this: user/network:pass.", true);
940
0
    CUtils::PrintMessage("");
941
0
    CUtils::PrintMessage("Try something like this in your IRC client...", true);
942
0
    CUtils::PrintMessage("/server <znc_server_ip> " + sSSL +
943
0
                             CString(uListenPort) + " " + sUser + ":<pass>",
944
0
                         true);
945
0
    CUtils::PrintMessage("");
946
0
    CUtils::PrintMessage(
947
0
        "To manage settings, users and networks, point your web browser to",
948
0
        true);
949
0
    CUtils::PrintMessage(
950
0
        sProtocol + "://<znc_server_ip>:" + CString(uListenPort) + "/", true);
951
0
    CUtils::PrintMessage("");
952
953
0
    File.UnLock();
954
955
0
    bool bWantLaunch = bFileOpen;
956
0
    if (bWantLaunch) {
957
        // "export ZNC_NO_LAUNCH_AFTER_MAKECONF=1" would cause znc --makeconf to
958
        // not offer immediate launch.
959
        // Useful for distros which want to create config when znc package is
960
        // installed.
961
        // See https://github.com/znc/znc/pull/257
962
0
        char* szNoLaunch = getenv("ZNC_NO_LAUNCH_AFTER_MAKECONF");
963
0
        if (szNoLaunch && *szNoLaunch == '1') {
964
0
            bWantLaunch = false;
965
0
        }
966
0
    }
967
0
    if (bWantLaunch) {
968
0
        bWantLaunch = CUtils::GetBoolInput("Launch ZNC now?", true);
969
0
    }
970
0
    return bWantLaunch;
971
0
}
972
973
0
void CZNC::BackupConfigOnce(const CString& sSuffix) {
974
0
    static bool didBackup = false;
975
0
    if (didBackup) return;
976
0
    didBackup = true;
977
978
0
    CUtils::PrintAction("Creating a config backup");
979
980
0
    CString sBackup = CDir::ChangeDir(m_sConfigFile, "../znc.conf." + sSuffix);
981
0
    if (CFile::Copy(m_sConfigFile, sBackup))
982
0
        CUtils::PrintStatus(true, sBackup);
983
0
    else
984
0
        CUtils::PrintStatus(false, strerror(errno));
985
0
}
986
987
0
bool CZNC::ParseConfig(const CString& sConfig, CString& sError) {
988
0
    m_sConfigFile = ExpandConfigPath(sConfig, false);
989
990
0
    CConfig config;
991
0
    if (!ReadConfig(config, sError)) return false;
992
993
0
    if (!LoadGlobal(config, sError)) return false;
994
995
0
    if (!LoadUsers(config, sError)) return false;
996
997
0
    return true;
998
0
}
999
1000
0
bool CZNC::ReadConfig(CConfig& config, CString& sError) {
1001
0
    sError.clear();
1002
1003
0
    CUtils::PrintAction("Opening config [" + m_sConfigFile + "]");
1004
1005
0
    if (!CFile::Exists(m_sConfigFile)) {
1006
0
        sError = "No such file";
1007
0
        CUtils::PrintStatus(false, sError);
1008
0
        CUtils::PrintMessage(
1009
0
            "Restart ZNC with the --makeconf option if you wish to create this "
1010
0
            "config.");
1011
0
        return false;
1012
0
    }
1013
1014
0
    if (!CFile::IsReg(m_sConfigFile)) {
1015
0
        sError = "Not a file";
1016
0
        CUtils::PrintStatus(false, sError);
1017
0
        return false;
1018
0
    }
1019
1020
0
    CFile* pFile = new CFile(m_sConfigFile);
1021
1022
    // need to open the config file Read/Write for fcntl()
1023
    // exclusive locking to work properly!
1024
0
    if (!pFile->Open(m_sConfigFile, O_RDWR)) {
1025
0
        sError = "Can not open config file";
1026
0
        CUtils::PrintStatus(false, sError);
1027
0
        delete pFile;
1028
0
        return false;
1029
0
    }
1030
1031
0
    if (!pFile->TryExLock()) {
1032
0
        sError = "ZNC is already running on this config.";
1033
0
        CUtils::PrintStatus(false, sError);
1034
0
        delete pFile;
1035
0
        return false;
1036
0
    }
1037
1038
    // (re)open the config file
1039
0
    delete m_pLockFile;
1040
0
    m_pLockFile = pFile;
1041
0
    CFile& File = *pFile;
1042
1043
0
    if (!config.Parse(File, sError)) {
1044
0
        CUtils::PrintStatus(false, sError);
1045
0
        return false;
1046
0
    }
1047
0
    CUtils::PrintStatus(true);
1048
1049
    // check if config is from old ZNC version and
1050
    // create a backup file if necessary
1051
0
    CString sSavedVersion;
1052
0
    config.FindStringEntry("version", sSavedVersion);
1053
0
    config.AddKeyValuePair("version", sSavedVersion);
1054
0
    if (sSavedVersion.empty()) {
1055
0
        CUtils::PrintError(
1056
0
            "Config does not contain a version identifier. It may be be too "
1057
0
            "old or corrupt.");
1058
0
        return false;
1059
0
    }
1060
1061
0
    tuple<unsigned int, unsigned int> tSavedVersion =
1062
0
        make_tuple(sSavedVersion.Token(0, false, ".").ToUInt(),
1063
0
                   sSavedVersion.Token(1, false, ".").ToUInt());
1064
0
    tuple<unsigned int, unsigned int> tCurrentVersion =
1065
0
        make_tuple(VERSION_MAJOR, VERSION_MINOR);
1066
0
    if (tSavedVersion < tCurrentVersion) {
1067
0
        CUtils::PrintMessage("Found old config from ZNC " + sSavedVersion +
1068
0
                             ". Saving a backup of it.");
1069
0
        BackupConfigOnce("pre-" + CString(VERSION_STR));
1070
0
    } else if (tSavedVersion > tCurrentVersion) {
1071
0
        CUtils::PrintError("Config was saved from ZNC " + sSavedVersion +
1072
0
                           ". It may or may not work with current ZNC " +
1073
0
                           GetVersion());
1074
0
    }
1075
1076
0
    return true;
1077
0
}
1078
1079
0
bool CZNC::RehashConfig(CString& sError) {
1080
0
    ALLMODULECALL(OnPreRehash(), NOTHING);
1081
1082
0
    CConfig config;
1083
0
    if (!ReadConfig(config, sError)) return false;
1084
1085
0
    if (!LoadGlobal(config, sError)) return false;
1086
1087
    // do not reload users - it's dangerous!
1088
1089
0
    ALLMODULECALL(OnPostRehash(), NOTHING);
1090
0
    return true;
1091
0
}
1092
1093
0
bool CZNC::LoadGlobal(CConfig& config, CString& sError) {
1094
0
    sError.clear();
1095
1096
0
    CString sSavedVersion;
1097
0
    config.FindStringEntry("version", sSavedVersion);
1098
0
    tuple<unsigned int, unsigned int> tSavedVersion =
1099
0
        make_tuple(sSavedVersion.Token(0, false, ".").ToUInt(),
1100
0
                   sSavedVersion.Token(1, false, ".").ToUInt());
1101
1102
0
    MCString msModules;  // Modules are queued for later loading
1103
1104
0
    VCString vsList;
1105
0
    config.FindStringVector("loadmodule", vsList);
1106
1107
    // Automatically load corecaps if config was upgraded from old version, but
1108
    // don't force it if user explicitly unloaded it
1109
0
    if (tSavedVersion < make_tuple(1, 9)) {
1110
0
        vsList.push_back("corecaps");
1111
0
    }
1112
1113
0
    for (const CString& sModLine : vsList) {
1114
0
        CString sModName = sModLine.Token(0);
1115
0
        CString sArgs = sModLine.Token(1, true);
1116
1117
        // compatibility for pre-1.0 configs
1118
0
        if (sModName == "saslauth" && tSavedVersion < make_tuple(0, 207)) {
1119
0
            CUtils::PrintMessage(
1120
0
                "saslauth module was renamed to cyrusauth. Loading cyrusauth "
1121
0
                "instead.");
1122
0
            sModName = "cyrusauth";
1123
0
        }
1124
        // end-compatibility for pre-1.0 configs
1125
1126
0
        if (msModules.find(sModName) != msModules.end()) {
1127
0
            sError = "Module [" + sModName + "] already loaded";
1128
0
            CUtils::PrintError(sError);
1129
0
            return false;
1130
0
        }
1131
0
        CString sModRet;
1132
0
        CModule* pOldMod;
1133
1134
0
        pOldMod = GetModules().FindModule(sModName);
1135
0
        if (!pOldMod) {
1136
0
            CUtils::PrintAction("Loading global module [" + sModName + "]");
1137
1138
0
            bool bModRet =
1139
0
                GetModules().LoadModule(sModName, sArgs, CModInfo::GlobalModule,
1140
0
                                        nullptr, nullptr, sModRet);
1141
1142
0
            CUtils::PrintStatus(bModRet, bModRet ? "" : sModRet);
1143
0
            if (!bModRet) {
1144
0
                sError = sModRet;
1145
0
                return false;
1146
0
            }
1147
0
        } else if (pOldMod->GetArgs() != sArgs) {
1148
0
            CUtils::PrintAction("Reloading global module [" + sModName + "]");
1149
1150
0
            bool bModRet = GetModules().ReloadModule(sModName, sArgs, nullptr,
1151
0
                                                     nullptr, sModRet);
1152
1153
0
            CUtils::PrintStatus(bModRet, sModRet);
1154
0
            if (!bModRet) {
1155
0
                sError = sModRet;
1156
0
                return false;
1157
0
            }
1158
0
        } else
1159
0
            CUtils::PrintMessage("Module [" + sModName + "] already loaded.");
1160
1161
0
        msModules[sModName] = sArgs;
1162
0
    }
1163
1164
0
    m_vsMotd.clear();
1165
0
    config.FindStringVector("motd", vsList);
1166
0
    for (const CString& sMotd : vsList) {
1167
0
        AddMotd(sMotd);
1168
0
    }
1169
1170
0
    if (config.FindStringVector("bindhost", vsList)) {
1171
0
        CUtils::PrintStatus(false,
1172
0
                            "WARNING: the global BindHost list is deprecated. "
1173
0
                            "Ignoring the following lines:");
1174
0
        for (const CString& sHost : vsList) {
1175
0
            CUtils::PrintStatus(false, "BindHost = " + sHost);
1176
0
        }
1177
0
    }
1178
0
    if (config.FindStringVector("vhost", vsList)) {
1179
0
        CUtils::PrintStatus(false,
1180
0
                            "WARNING: the global vHost list is deprecated. "
1181
0
                            "Ignoring the following lines:");
1182
0
        for (const CString& sHost : vsList) {
1183
0
            CUtils::PrintStatus(false, "vHost = " + sHost);
1184
0
        }
1185
0
    }
1186
1187
0
    m_vsTrustedProxies.clear();
1188
0
    config.FindStringVector("trustedproxy", vsList);
1189
0
    for (const CString& sProxy : vsList) {
1190
0
        AddTrustedProxy(sProxy);
1191
0
    }
1192
1193
0
    CString sVal;
1194
0
    if (config.FindStringEntry("pidfile", sVal)) m_sPidFile = sVal;
1195
0
    if (config.FindStringEntry("statusprefix", sVal)) m_sStatusPrefix = sVal;
1196
0
    if (config.FindStringEntry("sslcertfile", sVal)) m_sSSLCertFile = sVal;
1197
0
    if (config.FindStringEntry("sslkeyfile", sVal)) m_sSSLKeyFile = sVal;
1198
0
    if (config.FindStringEntry("ssldhparamfile", sVal))
1199
0
        m_sSSLDHParamFile = sVal;
1200
0
    if (config.FindStringEntry("sslciphers", sVal)) m_sSSLCiphers = sVal;
1201
0
    if (config.FindStringEntry("skin", sVal)) SetSkinName(sVal);
1202
0
    if (config.FindStringEntry("connectdelay", sVal))
1203
0
        SetConnectDelay(sVal.ToUInt());
1204
0
    if (config.FindStringEntry("serverthrottle", sVal))
1205
0
        m_sConnectThrottle.SetTTL(sVal.ToUInt() * 1000);
1206
0
    if (config.FindStringEntry("anoniplimit", sVal))
1207
0
        m_uiAnonIPLimit = sVal.ToUInt();
1208
0
    if (config.FindStringEntry("maxbuffersize", sVal))
1209
0
        m_uiMaxBufferSize = sVal.ToUInt();
1210
0
    if (config.FindStringEntry("protectwebsessions", sVal))
1211
0
        m_bProtectWebSessions = sVal.ToBool();
1212
0
    if (config.FindStringEntry("hideversion", sVal))
1213
0
        m_bHideVersion = sVal.ToBool();
1214
0
    if (config.FindStringEntry("authonlyviamodule", sVal))
1215
0
        m_bAuthOnlyViaModule = sVal.ToBool();
1216
0
    if (config.FindStringEntry("sslprotocols", sVal)) {
1217
0
        if (!SetSSLProtocols(sVal)) {
1218
0
            VCString vsProtocols = GetAvailableSSLProtocols();
1219
0
            CUtils::PrintError("Invalid SSLProtocols value [" + sVal + "]");
1220
0
            CUtils::PrintError(
1221
0
                "The syntax is [SSLProtocols = [+|-]<protocol> ...]");
1222
0
            CUtils::PrintError(
1223
0
                "Available protocols are [" +
1224
0
                CString(", ").Join(vsProtocols.begin(), vsProtocols.end()) +
1225
0
                "]");
1226
0
            return false;
1227
0
        }
1228
0
    }
1229
0
    if (config.FindStringEntry("configwritedelay", sVal))
1230
0
        m_uiConfigWriteDelay = sVal.ToUInt();
1231
1232
0
    UnloadRemovedModules(msModules);
1233
1234
0
    if (!LoadListeners(config, sError)) return false;
1235
1236
0
    return true;
1237
0
}
1238
1239
0
bool CZNC::LoadUsers(CConfig& config, CString& sError) {
1240
0
    sError.clear();
1241
1242
0
    m_msUsers.clear();
1243
1244
0
    CConfig::SubConfig subConf;
1245
0
    config.FindSubConfig("user", subConf);
1246
1247
0
    for (const auto& subIt : subConf) {
1248
0
        const CString& sUsername = subIt.first;
1249
0
        CConfig* pSubConf = subIt.second.m_pSubConfig;
1250
1251
0
        CUtils::PrintMessage("Loading user [" + sUsername + "]");
1252
1253
0
        std::unique_ptr<CUser> pUser(new CUser(sUsername));
1254
1255
0
        if (!m_sStatusPrefix.empty()) {
1256
0
            if (!pUser->SetStatusPrefix(m_sStatusPrefix)) {
1257
0
                sError = "Invalid StatusPrefix [" + m_sStatusPrefix +
1258
0
                         "] Must be 1-5 chars, no spaces.";
1259
0
                CUtils::PrintError(sError);
1260
0
                return false;
1261
0
            }
1262
0
        }
1263
1264
0
        if (!pUser->ParseConfig(pSubConf, sError)) {
1265
0
            CUtils::PrintError(sError);
1266
0
            return false;
1267
0
        }
1268
1269
0
        if (!pSubConf->empty()) {
1270
0
            sError = "Unhandled lines in config for User [" + sUsername + "]!";
1271
0
            CUtils::PrintError(sError);
1272
0
            DumpConfig(pSubConf);
1273
0
            return false;
1274
0
        }
1275
1276
0
        CString sErr;
1277
0
        CUser* pRawUser = pUser.release();
1278
0
        if (!AddUser(pRawUser, sErr, true)) {
1279
0
            sError = "Invalid user [" + sUsername + "] " + sErr;
1280
0
            CUtils::PrintError(sError);
1281
0
            pRawUser->SetBeingDeleted(true);
1282
0
            delete pRawUser;
1283
0
            return false;
1284
0
        }
1285
0
    }
1286
1287
0
    if (m_msUsers.empty()) {
1288
0
        sError = "You must define at least one user in your config.";
1289
0
        CUtils::PrintError(sError);
1290
0
        return false;
1291
0
    }
1292
1293
0
    return true;
1294
0
}
1295
1296
0
bool CZNC::LoadListeners(CConfig& config, CString& sError) {
1297
0
    sError.clear();
1298
1299
    // Delete all listeners
1300
0
    while (!m_vpListeners.empty()) {
1301
0
        delete m_vpListeners[0];
1302
0
        m_vpListeners.erase(m_vpListeners.begin());
1303
0
    }
1304
1305
    // compatibility for pre-1.0 configs
1306
0
    const char* szListenerEntries[] = {"listen",   "listen6",   "listen4",
1307
0
                                       "listener", "listener6", "listener4"};
1308
1309
0
    VCString vsList;
1310
0
    config.FindStringVector("loadmodule", vsList);
1311
1312
    // This has to be after SSLCertFile is handled since it uses that value
1313
0
    for (const char* szEntry : szListenerEntries) {
1314
0
        config.FindStringVector(szEntry, vsList);
1315
0
        for (const CString& sListener : vsList) {
1316
0
            if (!AddListener(szEntry + CString(" ") + sListener, sError))
1317
0
                return false;
1318
0
        }
1319
0
    }
1320
    // end-compatibility for pre-1.0 configs
1321
1322
0
    CConfig::SubConfig subConf;
1323
0
    config.FindSubConfig("listener", subConf);
1324
1325
0
    for (const auto& subIt : subConf) {
1326
0
        CConfig* pSubConf = subIt.second.m_pSubConfig;
1327
0
        if (!AddListener(pSubConf, sError)) return false;
1328
0
        if (!pSubConf->empty()) {
1329
0
            sError = "Unhandled lines in Listener config!";
1330
0
            CUtils::PrintError(sError);
1331
1332
0
            CZNC::DumpConfig(pSubConf);
1333
0
            return false;
1334
0
        }
1335
0
    }
1336
1337
0
    if (m_vpListeners.empty()) {
1338
0
        sError = "You must supply at least one Listener in your config.";
1339
0
        CUtils::PrintError(sError);
1340
0
        return false;
1341
0
    }
1342
1343
0
    return true;
1344
0
}
1345
1346
0
void CZNC::UnloadRemovedModules(const MCString& msModules) {
1347
    // unload modules which are no longer in the config
1348
1349
0
    set<CString> ssUnload;
1350
0
    for (CModule* pCurMod : GetModules()) {
1351
0
        if (msModules.find(pCurMod->GetModName()) == msModules.end())
1352
0
            ssUnload.insert(pCurMod->GetModName());
1353
0
    }
1354
1355
0
    for (const CString& sMod : ssUnload) {
1356
0
        if (GetModules().UnloadModule(sMod))
1357
0
            CUtils::PrintMessage("Unloaded global module [" + sMod + "]");
1358
0
        else
1359
0
            CUtils::PrintMessage("Could not unload [" + sMod + "]");
1360
0
    }
1361
0
}
1362
1363
0
void CZNC::DumpConfig(const CConfig* pConfig) {
1364
0
    CConfig::EntryMapIterator eit = pConfig->BeginEntries();
1365
0
    for (; eit != pConfig->EndEntries(); ++eit) {
1366
0
        const CString& sKey = eit->first;
1367
0
        const VCString& vsList = eit->second;
1368
0
        VCString::const_iterator it = vsList.begin();
1369
0
        for (; it != vsList.end(); ++it) {
1370
0
            CUtils::PrintError(sKey + " = " + *it);
1371
0
        }
1372
0
    }
1373
1374
0
    CConfig::SubConfigMapIterator sit = pConfig->BeginSubConfigs();
1375
0
    for (; sit != pConfig->EndSubConfigs(); ++sit) {
1376
0
        const CString& sKey = sit->first;
1377
0
        const CConfig::SubConfig& sSub = sit->second;
1378
0
        CConfig::SubConfig::const_iterator it = sSub.begin();
1379
1380
0
        for (; it != sSub.end(); ++it) {
1381
0
            CUtils::PrintError("SubConfig [" + sKey + " " + it->first + "]:");
1382
0
            DumpConfig(it->second.m_pSubConfig);
1383
0
        }
1384
0
    }
1385
0
}
1386
1387
0
void CZNC::ClearTrustedProxies() { m_vsTrustedProxies.clear(); }
1388
1389
0
bool CZNC::AddTrustedProxy(const CString& sHost) {
1390
0
    if (sHost.empty()) {
1391
0
        return false;
1392
0
    }
1393
1394
0
    for (const CString& sTrustedProxy : m_vsTrustedProxies) {
1395
0
        if (sTrustedProxy.Equals(sHost)) {
1396
0
            return false;
1397
0
        }
1398
0
    }
1399
1400
0
    m_vsTrustedProxies.push_back(sHost);
1401
0
    return true;
1402
0
}
1403
1404
0
bool CZNC::RemTrustedProxy(const CString& sHost) {
1405
0
    VCString::iterator it;
1406
0
    for (it = m_vsTrustedProxies.begin(); it != m_vsTrustedProxies.end();
1407
0
         ++it) {
1408
0
        if (sHost.Equals(*it)) {
1409
0
            m_vsTrustedProxies.erase(it);
1410
0
            return true;
1411
0
        }
1412
0
    }
1413
1414
0
    return false;
1415
0
}
1416
1417
void CZNC::Broadcast(const CString& sMessage, bool bAdminOnly, CUser* pSkipUser,
1418
0
                     CClient* pSkipClient) {
1419
0
    for (const auto& it : m_msUsers) {
1420
0
        if (bAdminOnly && !it.second->IsAdmin()) continue;
1421
1422
0
        if (it.second != pSkipUser) {
1423
            // TODO: translate message to user's language
1424
0
            CString sMsg = sMessage;
1425
1426
0
            bool bContinue = false;
1427
0
            USERMODULECALL(OnBroadcast(sMsg), it.second, nullptr, &bContinue);
1428
0
            if (bContinue) continue;
1429
1430
0
            it.second->PutStatusNotice("*** " + sMsg, nullptr, pSkipClient);
1431
0
        }
1432
0
    }
1433
0
}
1434
1435
0
CModule* CZNC::FindModule(const CString& sModName, const CString& sUsername) {
1436
0
    if (sUsername.empty()) {
1437
0
        return CZNC::Get().GetModules().FindModule(sModName);
1438
0
    }
1439
1440
0
    CUser* pUser = FindUser(sUsername);
1441
1442
0
    return (!pUser) ? nullptr : pUser->GetModules().FindModule(sModName);
1443
0
}
1444
1445
0
CModule* CZNC::FindModule(const CString& sModName, CUser* pUser) {
1446
0
    if (pUser) {
1447
0
        return pUser->GetModules().FindModule(sModName);
1448
0
    }
1449
1450
0
    return CZNC::Get().GetModules().FindModule(sModName);
1451
0
}
1452
1453
0
bool CZNC::UpdateModule(const CString& sModule) {
1454
0
    CModule* pModule;
1455
1456
0
    map<CUser*, CString> musLoaded;
1457
0
    map<CIRCNetwork*, CString> mnsLoaded;
1458
1459
    // Unload the module for every user and network
1460
0
    for (const auto& it : m_msUsers) {
1461
0
        CUser* pUser = it.second;
1462
1463
0
        pModule = pUser->GetModules().FindModule(sModule);
1464
0
        if (pModule) {
1465
0
            musLoaded[pUser] = pModule->GetArgs();
1466
0
            pUser->GetModules().UnloadModule(sModule);
1467
0
        }
1468
1469
        // See if the user has this module loaded to a network
1470
0
        vector<CIRCNetwork*> vNetworks = pUser->GetNetworks();
1471
0
        for (CIRCNetwork* pNetwork : vNetworks) {
1472
0
            pModule = pNetwork->GetModules().FindModule(sModule);
1473
0
            if (pModule) {
1474
0
                mnsLoaded[pNetwork] = pModule->GetArgs();
1475
0
                pNetwork->GetModules().UnloadModule(sModule);
1476
0
            }
1477
0
        }
1478
0
    }
1479
1480
    // Unload the global module
1481
0
    bool bGlobal = false;
1482
0
    CString sGlobalArgs;
1483
1484
0
    pModule = GetModules().FindModule(sModule);
1485
0
    if (pModule) {
1486
0
        bGlobal = true;
1487
0
        sGlobalArgs = pModule->GetArgs();
1488
0
        GetModules().UnloadModule(sModule);
1489
0
    }
1490
1491
    // Lets reload everything
1492
0
    bool bError = false;
1493
0
    CString sErr;
1494
1495
    // Reload the global module
1496
0
    if (bGlobal) {
1497
0
        if (!GetModules().LoadModule(sModule, sGlobalArgs,
1498
0
                                     CModInfo::GlobalModule, nullptr, nullptr,
1499
0
                                     sErr)) {
1500
0
            DEBUG("Failed to reload [" << sModule << "] globally [" << sErr
1501
0
                                       << "]");
1502
0
            bError = true;
1503
0
        }
1504
0
    }
1505
1506
    // Reload the module for all users
1507
0
    for (const auto& it : musLoaded) {
1508
0
        CUser* pUser = it.first;
1509
0
        const CString& sArgs = it.second;
1510
1511
0
        if (!pUser->GetModules().LoadModule(
1512
0
                sModule, sArgs, CModInfo::UserModule, pUser, nullptr, sErr)) {
1513
0
            DEBUG("Failed to reload [" << sModule << "] for ["
1514
0
                                       << pUser->GetUsername() << "] [" << sErr
1515
0
                                       << "]");
1516
0
            bError = true;
1517
0
        }
1518
0
    }
1519
1520
    // Reload the module for all networks
1521
0
    for (const auto& it : mnsLoaded) {
1522
0
        CIRCNetwork* pNetwork = it.first;
1523
0
        const CString& sArgs = it.second;
1524
1525
0
        if (!pNetwork->GetModules().LoadModule(
1526
0
                sModule, sArgs, CModInfo::NetworkModule, pNetwork->GetUser(),
1527
0
                pNetwork, sErr)) {
1528
0
            DEBUG("Failed to reload ["
1529
0
                  << sModule << "] for [" << pNetwork->GetUser()->GetUsername()
1530
0
                  << "/" << pNetwork->GetName() << "] [" << sErr << "]");
1531
0
            bError = true;
1532
0
        }
1533
0
    }
1534
1535
0
    return !bError;
1536
0
}
1537
1538
0
CUser* CZNC::FindUser(const CString& sUsername) {
1539
0
    map<CString, CUser*>::iterator it = m_msUsers.find(sUsername);
1540
1541
0
    if (it != m_msUsers.end()) {
1542
0
        return it->second;
1543
0
    }
1544
1545
0
    return nullptr;
1546
0
}
1547
1548
0
bool CZNC::DeleteUser(const CString& sUsername) {
1549
0
    CUser* pUser = FindUser(sUsername);
1550
1551
0
    if (!pUser) {
1552
0
        return false;
1553
0
    }
1554
1555
0
    m_msDelUsers[pUser->GetUsername()] = pUser;
1556
0
    return true;
1557
0
}
1558
1559
0
bool CZNC::AddUser(CUser* pUser, CString& sErrorRet, bool bStartup) {
1560
0
    if (FindUser(pUser->GetUsername()) != nullptr) {
1561
0
        sErrorRet = t_s("User already exists");
1562
0
        DEBUG("User [" << pUser->GetUsername() << "] - already exists");
1563
0
        return false;
1564
0
    }
1565
0
    if (!pUser->IsValid(sErrorRet)) {
1566
0
        DEBUG("Invalid user [" << pUser->GetUsername() << "] - [" << sErrorRet
1567
0
                               << "]");
1568
0
        return false;
1569
0
    }
1570
0
    bool bFailed = false;
1571
1572
    // do not call OnAddUser hook during ZNC startup
1573
0
    if (!bStartup) {
1574
0
        GLOBALMODULECALL(OnAddUser(*pUser, sErrorRet), &bFailed);
1575
0
    }
1576
1577
0
    if (bFailed) {
1578
0
        DEBUG("AddUser [" << pUser->GetUsername() << "] aborted by a module ["
1579
0
                          << sErrorRet << "]");
1580
0
        return false;
1581
0
    }
1582
0
    m_msUsers[pUser->GetUsername()] = pUser;
1583
0
    return true;
1584
0
}
1585
1586
CListener* CZNC::FindListener(u_short uPort, const CString& sBindHost,
1587
0
                              EAddrType eAddr) {
1588
0
    for (CListener* pListener : m_vpListeners) {
1589
0
        if (pListener->GetPort() != uPort) continue;
1590
0
        if (pListener->GetBindHost() != sBindHost) continue;
1591
0
        if (pListener->GetAddrType() != eAddr) continue;
1592
0
        return pListener;
1593
0
    }
1594
0
    return nullptr;
1595
0
}
1596
1597
0
bool CZNC::AddListener(const CString& sLine, CString& sError) {
1598
0
    CString sName = sLine.Token(0);
1599
0
    CString sValue = sLine.Token(1, true);
1600
1601
0
    EAddrType eAddr = ADDR_ALL;
1602
0
    if (sName.Equals("Listen4") || sName.Equals("Listen") ||
1603
0
        sName.Equals("Listener4")) {
1604
0
        eAddr = ADDR_IPV4ONLY;
1605
0
    }
1606
0
    if (sName.Equals("Listener6")) {
1607
0
        eAddr = ADDR_IPV6ONLY;
1608
0
    }
1609
1610
0
    CListener::EAcceptType eAccept = CListener::ACCEPT_ALL;
1611
0
    if (sValue.TrimPrefix("irc_only "))
1612
0
        eAccept = CListener::ACCEPT_IRC;
1613
0
    else if (sValue.TrimPrefix("web_only "))
1614
0
        eAccept = CListener::ACCEPT_HTTP;
1615
1616
0
    bool bSSL = false;
1617
0
    CString sPort;
1618
0
    CString sBindHost;
1619
1620
0
    if (ADDR_IPV4ONLY == eAddr) {
1621
0
        sValue.Replace(":", " ");
1622
0
    }
1623
1624
0
    if (sValue.Contains(" ")) {
1625
0
        sBindHost = sValue.Token(0, false, " ");
1626
0
        sPort = sValue.Token(1, true, " ");
1627
0
    } else {
1628
0
        sPort = sValue;
1629
0
    }
1630
1631
0
    if (sPort.TrimPrefix("+")) {
1632
0
        bSSL = true;
1633
0
    }
1634
1635
    // No support for URIPrefix for old-style configs.
1636
0
    CString sURIPrefix;
1637
0
    unsigned short uPort = sPort.ToUShort();
1638
0
    return AddListener(uPort, sBindHost, sURIPrefix, bSSL, eAddr, eAccept,
1639
0
                       sError);
1640
0
}
1641
1642
bool CZNC::AddListener(unsigned short uPort, const CString& sBindHost,
1643
                       const CString& sURIPrefixRaw, bool bSSL, EAddrType eAddr,
1644
0
                       CListener::EAcceptType eAccept, CString& sError) {
1645
0
    CString sHostComment;
1646
1647
0
    if (!sBindHost.empty()) {
1648
0
        sHostComment = " on host [" + sBindHost + "]";
1649
0
    }
1650
1651
0
    CString sIPV6Comment;
1652
1653
0
    switch (eAddr) {
1654
0
        case ADDR_ALL:
1655
0
            sIPV6Comment = "";
1656
0
            break;
1657
0
        case ADDR_IPV4ONLY:
1658
0
            sIPV6Comment = " using ipv4";
1659
0
            break;
1660
0
        case ADDR_IPV6ONLY:
1661
0
            sIPV6Comment = " using ipv6";
1662
0
    }
1663
1664
0
    CUtils::PrintAction("Binding to port [" + CString((bSSL) ? "+" : "") +
1665
0
                        CString(uPort) + "]" + sHostComment + sIPV6Comment);
1666
1667
0
#ifndef HAVE_IPV6
1668
0
    if (ADDR_IPV6ONLY == eAddr) {
1669
0
        sError = t_s("IPv6 is not enabled");
1670
0
        CUtils::PrintStatus(false, sError);
1671
0
        return false;
1672
0
    }
1673
0
#endif
1674
1675
0
#ifndef HAVE_LIBSSL
1676
0
    if (bSSL) {
1677
0
        sError = t_s("SSL is not enabled");
1678
0
        CUtils::PrintStatus(false, sError);
1679
0
        return false;
1680
0
    }
1681
#else
1682
    CString sPemFile = GetPemLocation();
1683
1684
    if (bSSL && !CFile::Exists(sPemFile)) {
1685
        sError = t_f("Unable to locate pem file: {1}")(sPemFile);
1686
        CUtils::PrintStatus(false, sError);
1687
1688
        // If stdin is e.g. /dev/null and we call GetBoolInput(),
1689
        // we are stuck in an endless loop!
1690
        if (isatty(0) &&
1691
            CUtils::GetBoolInput("Would you like to create a new pem file?",
1692
                                 true)) {
1693
            sError.clear();
1694
            WritePemFile();
1695
        } else {
1696
            return false;
1697
        }
1698
1699
        CUtils::PrintAction("Binding to port [+" + CString(uPort) + "]" +
1700
                            sHostComment + sIPV6Comment);
1701
    }
1702
#endif
1703
0
    if (!uPort) {
1704
0
        sError = t_s("Invalid port");
1705
0
        CUtils::PrintStatus(false, sError);
1706
0
        return false;
1707
0
    }
1708
1709
    // URIPrefix must start with a slash and end without one.
1710
0
    CString sURIPrefix = CString(sURIPrefixRaw);
1711
0
    if (!sURIPrefix.empty()) {
1712
0
        if (!sURIPrefix.StartsWith("/")) {
1713
0
            sURIPrefix = "/" + sURIPrefix;
1714
0
        }
1715
0
        if (sURIPrefix.EndsWith("/")) {
1716
0
            sURIPrefix.TrimRight("/");
1717
0
        }
1718
0
    }
1719
1720
0
    CListener* pListener =
1721
0
        new CListener(uPort, sBindHost, sURIPrefix, bSSL, eAddr, eAccept);
1722
1723
0
    if (!pListener->Listen()) {
1724
0
        sError = FormatBindError();
1725
0
        CUtils::PrintStatus(false, sError);
1726
0
        delete pListener;
1727
0
        return false;
1728
0
    }
1729
1730
0
    m_vpListeners.push_back(pListener);
1731
0
    CUtils::PrintStatus(true);
1732
1733
0
    return true;
1734
0
}
1735
1736
0
bool CZNC::AddListener(CConfig* pConfig, CString& sError) {
1737
0
    CString sBindHost;
1738
0
    CString sURIPrefix;
1739
0
    bool bSSL;
1740
0
    bool b4;
1741
#ifdef HAVE_IPV6
1742
    bool b6 = true;
1743
#else
1744
0
    bool b6 = false;
1745
0
#endif
1746
0
    bool bIRC;
1747
0
    bool bWeb;
1748
0
    unsigned short uPort;
1749
0
    if (!pConfig->FindUShortEntry("port", uPort)) {
1750
0
        sError = "No port given";
1751
0
        CUtils::PrintError(sError);
1752
0
        return false;
1753
0
    }
1754
0
    pConfig->FindStringEntry("host", sBindHost);
1755
0
    pConfig->FindBoolEntry("ssl", bSSL, false);
1756
0
    pConfig->FindBoolEntry("ipv4", b4, true);
1757
0
    pConfig->FindBoolEntry("ipv6", b6, b6);
1758
0
    pConfig->FindBoolEntry("allowirc", bIRC, true);
1759
0
    pConfig->FindBoolEntry("allowweb", bWeb, true);
1760
0
    pConfig->FindStringEntry("uriprefix", sURIPrefix);
1761
1762
0
    EAddrType eAddr;
1763
0
    if (b4 && b6) {
1764
0
        eAddr = ADDR_ALL;
1765
0
    } else if (b4 && !b6) {
1766
0
        eAddr = ADDR_IPV4ONLY;
1767
0
    } else if (!b4 && b6) {
1768
0
        eAddr = ADDR_IPV6ONLY;
1769
0
    } else {
1770
0
        sError = "No address family given";
1771
0
        CUtils::PrintError(sError);
1772
0
        return false;
1773
0
    }
1774
1775
0
    CListener::EAcceptType eAccept;
1776
0
    if (bIRC && bWeb) {
1777
0
        eAccept = CListener::ACCEPT_ALL;
1778
0
    } else if (bIRC && !bWeb) {
1779
0
        eAccept = CListener::ACCEPT_IRC;
1780
0
    } else if (!bIRC && bWeb) {
1781
0
        eAccept = CListener::ACCEPT_HTTP;
1782
0
    } else {
1783
0
        sError = "Either Web or IRC or both should be selected";
1784
0
        CUtils::PrintError(sError);
1785
0
        return false;
1786
0
    }
1787
1788
0
    return AddListener(uPort, sBindHost, sURIPrefix, bSSL, eAddr, eAccept,
1789
0
                       sError);
1790
0
}
1791
1792
0
bool CZNC::AddListener(CListener* pListener) {
1793
0
    if (!pListener->GetRealListener()) {
1794
        // Listener doesn't actually listen
1795
0
        delete pListener;
1796
0
        return false;
1797
0
    }
1798
1799
    // We don't check if there is an identical listener already listening
1800
    // since one can't listen on e.g. the same port multiple times
1801
1802
0
    m_vpListeners.push_back(pListener);
1803
0
    return true;
1804
0
}
1805
1806
0
bool CZNC::DelListener(CListener* pListener) {
1807
0
    auto it = std::find(m_vpListeners.begin(), m_vpListeners.end(), pListener);
1808
0
    if (it != m_vpListeners.end()) {
1809
0
        m_vpListeners.erase(it);
1810
0
        delete pListener;
1811
0
        return true;
1812
0
    }
1813
1814
0
    return false;
1815
0
}
1816
1817
0
CString CZNC::FormatBindError() {
1818
0
    CString sError = (errno == 0 ? t_s(("unknown error, check the host name"))
1819
0
                                 : CString(strerror(errno)));
1820
0
    return t_f("Unable to bind: {1}")(sError);
1821
0
}
1822
1823
static CZNC* s_pZNC = nullptr;
1824
1825
0
void CZNC::CreateInstance() {
1826
0
    if (s_pZNC) abort();
1827
1828
0
    s_pZNC = new CZNC();
1829
0
}
1830
1831
0
CZNC& CZNC::Get() { return *s_pZNC; }
1832
1833
0
void CZNC::DestroyInstance() {
1834
0
    delete s_pZNC;
1835
0
    s_pZNC = nullptr;
1836
0
}
1837
1838
CZNC::TrafficStatsMap CZNC::GetTrafficStats(TrafficStatsPair& Users,
1839
                                            TrafficStatsPair& ZNC,
1840
0
                                            TrafficStatsPair& Total) {
1841
0
    TrafficStatsMap ret;
1842
0
    unsigned long long uiUsers_in, uiUsers_out, uiZNC_in, uiZNC_out;
1843
0
    const map<CString, CUser*>& msUsers = CZNC::Get().GetUserMap();
1844
1845
0
    uiUsers_in = uiUsers_out = 0;
1846
0
    uiZNC_in = BytesRead();
1847
0
    uiZNC_out = BytesWritten();
1848
1849
0
    for (const auto& it : msUsers) {
1850
0
        ret[it.first] =
1851
0
            TrafficStatsPair(it.second->BytesRead(), it.second->BytesWritten());
1852
0
        uiUsers_in += it.second->BytesRead();
1853
0
        uiUsers_out += it.second->BytesWritten();
1854
0
    }
1855
1856
0
    for (Csock* pSock : m_Manager) {
1857
0
        CUser* pUser = nullptr;
1858
0
        if (pSock->GetSockName().StartsWith("IRC::")) {
1859
0
            pUser = ((CIRCSock*)pSock)->GetNetwork()->GetUser();
1860
0
        } else if (pSock->GetSockName().StartsWith("USR::")) {
1861
0
            pUser = ((CClient*)pSock)->GetUser();
1862
0
        }
1863
1864
0
        if (pUser) {
1865
0
            ret[pUser->GetUsername()].first += pSock->GetBytesRead();
1866
0
            ret[pUser->GetUsername()].second += pSock->GetBytesWritten();
1867
0
            uiUsers_in += pSock->GetBytesRead();
1868
0
            uiUsers_out += pSock->GetBytesWritten();
1869
0
        } else {
1870
0
            uiZNC_in += pSock->GetBytesRead();
1871
0
            uiZNC_out += pSock->GetBytesWritten();
1872
0
        }
1873
0
    }
1874
1875
0
    Users = TrafficStatsPair(uiUsers_in, uiUsers_out);
1876
0
    ZNC = TrafficStatsPair(uiZNC_in, uiZNC_out);
1877
0
    Total = TrafficStatsPair(uiUsers_in + uiZNC_in, uiUsers_out + uiZNC_out);
1878
1879
0
    return ret;
1880
0
}
1881
1882
CZNC::TrafficStatsMap CZNC::GetNetworkTrafficStats(const CString& sUsername,
1883
0
                                                   TrafficStatsPair& Total) {
1884
0
    TrafficStatsMap Networks;
1885
1886
0
    CUser* pUser = FindUser(sUsername);
1887
0
    if (pUser) {
1888
0
        for (const CIRCNetwork* pNetwork : pUser->GetNetworks()) {
1889
0
            Networks[pNetwork->GetName()].first = pNetwork->BytesRead();
1890
0
            Networks[pNetwork->GetName()].second = pNetwork->BytesWritten();
1891
0
            Total.first += pNetwork->BytesRead();
1892
0
            Total.second += pNetwork->BytesWritten();
1893
0
        }
1894
1895
0
        for (Csock* pSock : m_Manager) {
1896
0
            CIRCNetwork* pNetwork = nullptr;
1897
0
            if (pSock->GetSockName().StartsWith("IRC::")) {
1898
0
                pNetwork = ((CIRCSock*)pSock)->GetNetwork();
1899
0
            } else if (pSock->GetSockName().StartsWith("USR::")) {
1900
0
                pNetwork = ((CClient*)pSock)->GetNetwork();
1901
0
            }
1902
1903
0
            if (pNetwork && pNetwork->GetUser() == pUser) {
1904
0
                Networks[pNetwork->GetName()].first = pSock->GetBytesRead();
1905
0
                Networks[pNetwork->GetName()].second = pSock->GetBytesWritten();
1906
0
                Total.first += pSock->GetBytesRead();
1907
0
                Total.second += pSock->GetBytesWritten();
1908
0
            }
1909
0
        }
1910
0
    }
1911
1912
0
    return Networks;
1913
0
}
1914
1915
0
void CZNC::AuthUser(std::shared_ptr<CAuthBase> AuthClass) {
1916
    // TODO unless the auth module calls it, CUser::IsHostAllowed() is not
1917
    // honoured
1918
0
    bool bReturn = false;
1919
0
    GLOBALMODULECALL(OnLoginAttempt(AuthClass), &bReturn);
1920
0
    if (bReturn) return;
1921
1922
0
    CUser* pUser = FindUser(AuthClass->GetUsername());
1923
1924
0
    if (!pUser || !pUser->CheckPass(AuthClass->GetPassword())) {
1925
0
        AuthClass->RefuseLogin("Invalid Password");
1926
0
        return;
1927
0
    }
1928
1929
0
    CString sHost = AuthClass->GetRemoteIP();
1930
1931
0
    if (!pUser->IsHostAllowed(sHost)) {
1932
0
        AuthClass->RefuseLogin("Your host [" + sHost + "] is not allowed");
1933
0
        return;
1934
0
    }
1935
1936
0
    AuthClass->AcceptLogin(*pUser);
1937
0
}
1938
1939
class CConnectQueueTimer : public CCron {
1940
  public:
1941
0
    CConnectQueueTimer(int iSecs) : CCron() {
1942
0
        SetName("Connect users");
1943
0
        Start(iSecs);
1944
        // Don't wait iSecs seconds for first timer run
1945
0
        m_bRunOnNextCall = true;
1946
0
    }
1947
0
    ~CConnectQueueTimer() override {
1948
        // This is only needed when ZNC shuts down:
1949
        // CZNC::~CZNC() sets its CConnectQueueTimer pointer to nullptr and
1950
        // calls the manager's Cleanup() which destroys all sockets and
1951
        // timers. If something calls CZNC::EnableConnectQueue() here
1952
        // (e.g. because a CIRCSock is destroyed), the socket manager
1953
        // deletes that timer almost immediately, but CZNC now got a
1954
        // dangling pointer to this timer which can crash later on.
1955
        //
1956
        // Unlikely but possible ;)
1957
0
        CZNC::Get().LeakConnectQueueTimer(this);
1958
0
    }
1959
1960
  protected:
1961
0
    void RunJob() override {
1962
0
        list<CIRCNetwork*> ConnectionQueue;
1963
0
        list<CIRCNetwork*>& RealConnectionQueue =
1964
0
            CZNC::Get().GetConnectionQueue();
1965
1966
        // Problem: If a network can't connect right now because e.g. it
1967
        // is throttled, it will re-insert itself into the connection
1968
        // queue. However, we must only give each network a single
1969
        // chance during this timer run.
1970
        //
1971
        // Solution: We move the connection queue to our local list at
1972
        // the beginning and work from that.
1973
0
        ConnectionQueue.swap(RealConnectionQueue);
1974
1975
0
        while (!ConnectionQueue.empty()) {
1976
0
            CIRCNetwork* pNetwork = ConnectionQueue.front();
1977
0
            ConnectionQueue.pop_front();
1978
1979
0
            if (pNetwork->Connect()) {
1980
0
                break;
1981
0
            }
1982
0
        }
1983
1984
        /* Now re-insert anything that is left in our local list into
1985
         * the real connection queue.
1986
         */
1987
0
        RealConnectionQueue.splice(RealConnectionQueue.begin(),
1988
0
                                   ConnectionQueue);
1989
1990
0
        if (RealConnectionQueue.empty()) {
1991
0
            DEBUG("ConnectQueueTimer done");
1992
0
            CZNC::Get().DisableConnectQueue();
1993
0
        }
1994
0
    }
1995
};
1996
1997
0
void CZNC::SetConnectDelay(unsigned int i) {
1998
0
    if (i < 1) {
1999
        // Don't hammer server with our failed connects
2000
0
        i = 1;
2001
0
    }
2002
0
    if (m_uiConnectDelay != i && m_pConnectQueueTimer != nullptr) {
2003
0
        m_pConnectQueueTimer->Start(i);
2004
0
    }
2005
0
    m_uiConnectDelay = i;
2006
0
}
2007
2008
0
VCString CZNC::GetAvailableSSLProtocols() {
2009
    // NOTE: keep in sync with SetSSLProtocols()
2010
0
    return {"SSLv2", "SSLv3", "TLSv1", "TLSV1.1", "TLSv1.2"};
2011
0
}
2012
2013
0
bool CZNC::SetSSLProtocols(const CString& sProtocols) {
2014
0
    VCString vsProtocols;
2015
0
    sProtocols.Split(" ", vsProtocols, false, "", "", true, true);
2016
2017
0
    unsigned int uDisabledProtocols = Csock::EDP_SSL;
2018
0
    for (CString& sProtocol : vsProtocols) {
2019
0
        unsigned int uFlag = 0;
2020
0
        bool bEnable = sProtocol.TrimPrefix("+");
2021
0
        bool bDisable = sProtocol.TrimPrefix("-");
2022
2023
        // NOTE: keep in sync with GetAvailableSSLProtocols()
2024
0
        if (sProtocol.Equals("All")) {
2025
0
            uFlag = ~0;
2026
0
        } else if (sProtocol.Equals("SSLv2")) {
2027
0
            uFlag = Csock::EDP_SSLv2;
2028
0
        } else if (sProtocol.Equals("SSLv3")) {
2029
0
            uFlag = Csock::EDP_SSLv3;
2030
0
        } else if (sProtocol.Equals("TLSv1")) {
2031
0
            uFlag = Csock::EDP_TLSv1;
2032
0
        } else if (sProtocol.Equals("TLSv1.1")) {
2033
0
            uFlag = Csock::EDP_TLSv1_1;
2034
0
        } else if (sProtocol.Equals("TLSv1.2")) {
2035
0
            uFlag = Csock::EDP_TLSv1_2;
2036
0
        } else {
2037
0
            return false;
2038
0
        }
2039
2040
0
        if (bEnable) {
2041
0
            uDisabledProtocols &= ~uFlag;
2042
0
        } else if (bDisable) {
2043
0
            uDisabledProtocols |= uFlag;
2044
0
        } else {
2045
0
            uDisabledProtocols = ~uFlag;
2046
0
        }
2047
0
    }
2048
2049
0
    m_sSSLProtocols = sProtocols;
2050
0
    m_uDisabledSSLProtocols = uDisabledProtocols;
2051
0
    return true;
2052
0
}
2053
2054
0
void CZNC::EnableConnectQueue() {
2055
0
    if (!m_pConnectQueueTimer && !m_uiConnectPaused &&
2056
0
        !m_lpConnectQueue.empty()) {
2057
0
        m_pConnectQueueTimer = new CConnectQueueTimer(m_uiConnectDelay);
2058
0
        GetManager().AddCron(m_pConnectQueueTimer);
2059
0
    }
2060
0
}
2061
2062
0
void CZNC::DisableConnectQueue() {
2063
0
    if (m_pConnectQueueTimer) {
2064
        // This will kill the cron
2065
0
        m_pConnectQueueTimer->Stop();
2066
0
        m_pConnectQueueTimer = nullptr;
2067
0
    }
2068
0
}
2069
2070
0
void CZNC::PauseConnectQueue() {
2071
0
    DEBUG("Connection queue paused");
2072
0
    m_uiConnectPaused++;
2073
2074
0
    if (m_pConnectQueueTimer) {
2075
0
        m_pConnectQueueTimer->Pause();
2076
0
    }
2077
0
}
2078
2079
0
void CZNC::ResumeConnectQueue() {
2080
0
    DEBUG("Connection queue resumed");
2081
0
    m_uiConnectPaused--;
2082
2083
0
    EnableConnectQueue();
2084
0
    if (m_pConnectQueueTimer) {
2085
0
        m_pConnectQueueTimer->UnPause();
2086
0
    }
2087
0
}
2088
2089
0
void CZNC::ForceEncoding() {
2090
0
    m_uiForceEncoding++;
2091
#ifdef HAVE_ICU
2092
    for (Csock* pSock : GetManager()) {
2093
        pSock->SetEncoding(FixupEncoding(pSock->GetEncoding()));
2094
    }
2095
#endif
2096
0
}
2097
0
void CZNC::UnforceEncoding() { m_uiForceEncoding--; }
2098
0
bool CZNC::IsForcingEncoding() const { return m_uiForceEncoding; }
2099
0
CString CZNC::FixupEncoding(const CString& sEncoding) const {
2100
0
    if (!m_uiForceEncoding) {
2101
0
        return sEncoding;
2102
0
    }
2103
0
    if (sEncoding.empty()) {
2104
0
        return "UTF-8";
2105
0
    }
2106
0
    const char* sRealEncoding = sEncoding.c_str();
2107
0
    if (sEncoding[0] == '*' || sEncoding[0] == '^') {
2108
0
        sRealEncoding++;
2109
0
    }
2110
0
    if (!*sRealEncoding) {
2111
0
        return "UTF-8";
2112
0
    }
2113
#ifdef HAVE_ICU
2114
    UErrorCode e = U_ZERO_ERROR;
2115
    UConverter* cnv = ucnv_open(sRealEncoding, &e);
2116
    if (cnv) {
2117
        ucnv_close(cnv);
2118
    }
2119
    if (U_FAILURE(e)) {
2120
        return "UTF-8";
2121
    }
2122
#endif
2123
0
    return sEncoding;
2124
0
}
2125
2126
0
void CZNC::AddNetworkToQueue(CIRCNetwork* pNetwork) {
2127
    // Make sure we are not already in the queue
2128
0
    if (std::find(m_lpConnectQueue.begin(), m_lpConnectQueue.end(), pNetwork) !=
2129
0
        m_lpConnectQueue.end()) {
2130
0
        return;
2131
0
    }
2132
2133
0
    m_lpConnectQueue.push_back(pNetwork);
2134
0
    EnableConnectQueue();
2135
0
}
2136
2137
0
void CZNC::LeakConnectQueueTimer(CConnectQueueTimer* pTimer) {
2138
0
    if (m_pConnectQueueTimer == pTimer) m_pConnectQueueTimer = nullptr;
2139
0
}
2140
2141
0
bool CZNC::WaitForChildLock() { return m_pLockFile && m_pLockFile->ExLock(); }
2142
2143
0
void CZNC::DisableConfigTimer() {
2144
0
    if (m_pConfigTimer) {
2145
0
        m_pConfigTimer->Stop();
2146
0
        m_pConfigTimer = nullptr;
2147
0
    }
2148
0
}