Coverage Report

Created: 2025-08-26 06:32

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