Coverage Report

Created: 2026-01-16 06:39

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/znc/src/IRCNetwork.cpp
Line
Count
Source
1
/*
2
 * Copyright (C) 2004-2026 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/IRCNetwork.h>
18
#include <znc/User.h>
19
#include <znc/FileUtils.h>
20
#include <znc/Config.h>
21
#include <znc/IRCSock.h>
22
#include <znc/Server.h>
23
#include <znc/Chan.h>
24
#include <znc/Query.h>
25
#include <znc/Message.h>
26
#include <algorithm>
27
#include <memory>
28
29
using std::vector;
30
using std::set;
31
32
class CIRCNetworkPingTimer : public CCron {
33
  public:
34
    CIRCNetworkPingTimer(CIRCNetwork* pNetwork)
35
0
        : CCron(), m_pNetwork(pNetwork) {
36
0
        SetName("CIRCNetworkPingTimer::" +
37
0
                m_pNetwork->GetUser()->GetUsername() + "::" +
38
0
                m_pNetwork->GetName());
39
0
        Start(m_pNetwork->GetUser()->GetPingSlack());
40
0
    }
41
42
0
    ~CIRCNetworkPingTimer() override {}
43
44
    CIRCNetworkPingTimer(const CIRCNetworkPingTimer&) = delete;
45
    CIRCNetworkPingTimer& operator=(const CIRCNetworkPingTimer&) = delete;
46
47
  protected:
48
0
    void RunJob() override {
49
0
        CIRCSock* pIRCSock = m_pNetwork->GetIRCSock();
50
0
        auto uFrequency = m_pNetwork->GetUser()->GetPingFrequency();
51
52
0
        if (pIRCSock &&
53
0
            pIRCSock->GetTimeSinceLastDataTransaction() >= uFrequency) {
54
0
            pIRCSock->PutIRCQuick("PING :ZNC");
55
0
        }
56
57
0
        const vector<CClient*>& vClients = m_pNetwork->GetClients();
58
0
        for (CClient* pClient : vClients) {
59
0
            if (pClient->GetTimeSinceLastDataTransaction() >= uFrequency) {
60
0
                pClient->PutClient("PING :ZNC");
61
0
            }
62
0
        }
63
64
        // Restart timer for the case if the period had changed. Usually this is
65
        // noop
66
0
        Start(m_pNetwork->GetUser()->GetPingSlack());
67
0
    }
68
69
  private:
70
    CIRCNetwork* m_pNetwork;
71
};
72
73
class CIRCNetworkJoinTimer : public CCron {
74
    constexpr static int JOIN_FREQUENCY = 30 /* seconds */;
75
76
  public:
77
    CIRCNetworkJoinTimer(CIRCNetwork* pNetwork)
78
0
        : CCron(), m_bDelayed(false), m_pNetwork(pNetwork) {
79
0
        SetName("CIRCNetworkJoinTimer::" +
80
0
                m_pNetwork->GetUser()->GetUsername() + "::" +
81
0
                m_pNetwork->GetName());
82
0
        Start(JOIN_FREQUENCY);
83
0
    }
84
85
0
    ~CIRCNetworkJoinTimer() override {}
86
87
    CIRCNetworkJoinTimer(const CIRCNetworkJoinTimer&) = delete;
88
    CIRCNetworkJoinTimer& operator=(const CIRCNetworkJoinTimer&) = delete;
89
90
0
    void Delay(unsigned short int uDelay) {
91
0
        m_bDelayed = true;
92
0
        Start(uDelay);
93
0
    }
94
95
  protected:
96
0
    void RunJob() override {
97
0
        if (m_bDelayed) {
98
0
            m_bDelayed = false;
99
0
            Start(JOIN_FREQUENCY);
100
0
        }
101
0
        if (m_pNetwork->IsIRCConnected()) {
102
0
            m_pNetwork->JoinChans();
103
0
        }
104
0
    }
105
106
  private:
107
    bool m_bDelayed;
108
    CIRCNetwork* m_pNetwork;
109
};
110
111
0
bool CIRCNetwork::IsValidNetwork(const CString& sNetwork) {
112
    // ^[-\w]+$
113
114
0
    if (sNetwork.empty()) {
115
0
        return false;
116
0
    }
117
118
0
    const char* p = sNetwork.c_str();
119
0
    while (*p) {
120
0
        if (*p != '_' && *p != '-' && !isalnum(*p)) {
121
0
            return false;
122
0
        }
123
124
0
        p++;
125
0
    }
126
127
0
    return true;
128
0
}
129
130
CIRCNetwork::CIRCNetwork(CUser* pUser, const CString& sName)
131
0
    : m_sName(sName),
132
0
      m_pUser(nullptr),
133
0
      m_sNick(""),
134
0
      m_sAltNick(""),
135
0
      m_sIdent(""),
136
0
      m_sRealName(""),
137
0
      m_sBindHost(""),
138
0
      m_sEncoding(""),
139
0
      m_sQuitMsg(""),
140
0
      m_ssTrustedFingerprints(),
141
0
      m_pModules(new CModules),
142
0
      m_vClients(),
143
0
      m_pIRCSock(nullptr),
144
0
      m_vChans(),
145
0
      m_vQueries(),
146
0
      m_sChanPrefixes(""),
147
0
      m_bIRCConnectEnabled(true),
148
0
      m_bTrustAllCerts(false),
149
0
      m_bTrustPKI(true),
150
0
      m_sIRCServer(""),
151
0
      m_vServers(),
152
0
      m_uServerIdx(0),
153
0
      m_IRCNick(),
154
0
      m_bIRCAway(false),
155
0
      m_fFloodRate(2),
156
0
      m_uFloodBurst(9),
157
0
      m_RawBuffer(),
158
0
      m_MotdBuffer(),
159
0
      m_NoticeBuffer(),
160
0
      m_pPingTimer(nullptr),
161
0
      m_pJoinTimer(nullptr),
162
0
      m_uJoinDelay(0),
163
0
      m_uBytesRead(0),
164
0
      m_uBytesWritten(0) {
165
0
    SetUser(pUser);
166
167
    // This should be more than enough raws, especially since we are buffering
168
    // the MOTD separately
169
0
    m_RawBuffer.SetLineCount(100, true);
170
    // This should be more than enough motd lines
171
0
    m_MotdBuffer.SetLineCount(200, true);
172
0
    m_NoticeBuffer.SetLineCount(250, true);
173
174
0
    m_pPingTimer = new CIRCNetworkPingTimer(this);
175
0
    CZNC::Get().GetManager().AddCron(m_pPingTimer);
176
177
0
    m_pJoinTimer = new CIRCNetworkJoinTimer(this);
178
0
    CZNC::Get().GetManager().AddCron(m_pJoinTimer);
179
180
0
    SetIRCConnectEnabled(true);
181
0
}
182
183
CIRCNetwork::CIRCNetwork(CUser* pUser, const CIRCNetwork& Network)
184
0
    : CIRCNetwork(pUser, "") {
185
0
    Clone(Network);
186
0
}
187
188
0
void CIRCNetwork::Clone(const CIRCNetwork& Network, bool bCloneName) {
189
0
    if (bCloneName) {
190
0
        m_sName = Network.GetName();
191
0
    }
192
193
0
    m_fFloodRate = Network.GetFloodRate();
194
0
    m_uFloodBurst = Network.GetFloodBurst();
195
0
    m_uJoinDelay = Network.GetJoinDelay();
196
197
0
    SetNick(Network.GetNick());
198
0
    SetAltNick(Network.GetAltNick());
199
0
    SetIdent(Network.GetIdent());
200
0
    SetRealName(Network.GetRealName());
201
0
    SetBindHost(Network.GetBindHost());
202
0
    SetEncoding(Network.GetEncoding());
203
0
    SetQuitMsg(Network.GetQuitMsg());
204
0
    m_ssTrustedFingerprints = Network.m_ssTrustedFingerprints;
205
206
    // Servers
207
0
    const vector<CServer*>& vServers = Network.GetServers();
208
0
    CString sServer;
209
0
    CServer* pCurServ = GetCurrentServer();
210
211
0
    if (pCurServ) {
212
0
        sServer = pCurServ->GetName();
213
0
    }
214
215
0
    DelServers();
216
217
0
    for (CServer* pServer : vServers) {
218
0
        AddServer(*pServer);
219
0
    }
220
221
0
    m_uServerIdx = 0;
222
0
    for (size_t a = 0; a < m_vServers.size(); a++) {
223
0
        if (sServer.Equals(m_vServers[a]->GetName())) {
224
0
            m_uServerIdx = a + 1;
225
0
            break;
226
0
        }
227
0
    }
228
0
    if (m_uServerIdx == 0) {
229
0
        m_uServerIdx = m_vServers.size();
230
0
        CIRCSock* pSock = GetIRCSock();
231
232
0
        if (pSock) {
233
0
            PutStatus(
234
0
                t_s("Jumping servers because this server is no longer in the "
235
0
                    "list"));
236
0
            pSock->Quit();
237
0
        }
238
0
    }
239
    // !Servers
240
241
    // Chans
242
0
    const vector<CChan*>& vChans = Network.GetChans();
243
0
    for (CChan* pNewChan : vChans) {
244
0
        CChan* pChan = FindChan(pNewChan->GetName());
245
246
0
        if (pChan) {
247
0
            pChan->SetInConfig(pNewChan->InConfig());
248
0
        } else {
249
0
            AddChan(pNewChan->GetName(), pNewChan->InConfig());
250
0
        }
251
0
    }
252
253
0
    for (CChan* pChan : m_vChans) {
254
0
        CChan* pNewChan = Network.FindChan(pChan->GetName());
255
256
0
        if (!pNewChan) {
257
0
            pChan->SetInConfig(false);
258
0
        } else {
259
0
            pChan->Clone(*pNewChan);
260
0
        }
261
0
    }
262
    // !Chans
263
264
    // Modules
265
0
    set<CString> ssUnloadMods;
266
0
    CModules& vCurMods = GetModules();
267
0
    const CModules& vNewMods = Network.GetModules();
268
269
0
    for (CModule* pNewMod : vNewMods) {
270
0
        CString sModRet;
271
0
        CModule* pCurMod = vCurMods.FindModule(pNewMod->GetModName());
272
273
0
        if (!pCurMod) {
274
0
            vCurMods.LoadModule(pNewMod->GetModName(), pNewMod->GetArgs(),
275
0
                                CModInfo::NetworkModule, m_pUser, this,
276
0
                                sModRet);
277
0
        } else if (pNewMod->GetArgs() != pCurMod->GetArgs()) {
278
0
            vCurMods.ReloadModule(pNewMod->GetModName(), pNewMod->GetArgs(),
279
0
                                  m_pUser, this, sModRet);
280
0
        }
281
0
    }
282
283
0
    for (CModule* pCurMod : vCurMods) {
284
0
        CModule* pNewMod = vNewMods.FindModule(pCurMod->GetModName());
285
286
0
        if (!pNewMod) {
287
0
            ssUnloadMods.insert(pCurMod->GetModName());
288
0
        }
289
0
    }
290
291
0
    for (const CString& sMod : ssUnloadMods) {
292
0
        vCurMods.UnloadModule(sMod);
293
0
    }
294
    // !Modules
295
296
0
    SetIRCConnectEnabled(Network.GetIRCConnectEnabled());
297
0
}
298
299
0
CIRCNetwork::~CIRCNetwork() {
300
0
    if (m_pIRCSock) {
301
0
        CZNC::Get().GetManager().DelSockByAddr(m_pIRCSock);
302
0
        m_pIRCSock = nullptr;
303
0
    }
304
305
    // Delete clients
306
0
    while (!m_vClients.empty()) {
307
0
        CZNC::Get().GetManager().DelSockByAddr(m_vClients[0]);
308
0
    }
309
0
    m_vClients.clear();
310
311
    // Delete servers
312
0
    DelServers();
313
314
    // Delete modules (this unloads all modules)
315
0
    delete m_pModules;
316
0
    m_pModules = nullptr;
317
318
    // Delete Channels
319
0
    for (CChan* pChan : m_vChans) {
320
0
        delete pChan;
321
0
    }
322
0
    m_vChans.clear();
323
324
    // Delete Queries
325
0
    for (CQuery* pQuery : m_vQueries) {
326
0
        delete pQuery;
327
0
    }
328
0
    m_vQueries.clear();
329
330
0
    CUser* pUser = GetUser();
331
0
    SetUser(nullptr);
332
333
    // Make sure we are not in the connection queue
334
0
    CZNC::Get().GetConnectionQueue().remove(this);
335
336
0
    CZNC::Get().GetManager().DelCronByAddr(m_pPingTimer);
337
0
    CZNC::Get().GetManager().DelCronByAddr(m_pJoinTimer);
338
339
0
    if (pUser) {
340
0
        pUser->AddBytesRead(m_uBytesRead);
341
0
        pUser->AddBytesWritten(m_uBytesWritten);
342
0
    } else {
343
0
        CZNC::Get().AddBytesRead(m_uBytesRead);
344
0
        CZNC::Get().AddBytesWritten(m_uBytesWritten);
345
0
    }
346
0
}
347
348
0
void CIRCNetwork::DelServers() {
349
0
    for (CServer* pServer : m_vServers) {
350
0
        delete pServer;
351
0
    }
352
0
    m_vServers.clear();
353
0
}
354
355
0
CString CIRCNetwork::GetNetworkPath() const {
356
0
    CString sNetworkPath = m_pUser->GetUserPath() + "/networks/" + m_sName;
357
358
0
    if (!CFile::Exists(sNetworkPath)) {
359
0
        CDir::MakeDir(sNetworkPath);
360
0
    }
361
362
0
    return sNetworkPath;
363
0
}
364
365
namespace {
366
template <class T>
367
struct TOption {
368
    const char* name;
369
    void (CIRCNetwork::*pSetter)(T);
370
};
371
}
372
373
bool CIRCNetwork::ParseConfig(CConfig* pConfig, CString& sError,
374
0
                              bool bUpgrade) {
375
0
    VCString vsList;
376
377
0
    if (!bUpgrade) {
378
0
        TOption<const CString&> StringOptions[] = {
379
0
            {"nick", &CIRCNetwork::SetNick},
380
0
            {"altnick", &CIRCNetwork::SetAltNick},
381
0
            {"ident", &CIRCNetwork::SetIdent},
382
0
            {"realname", &CIRCNetwork::SetRealName},
383
0
            {"bindhost", &CIRCNetwork::SetBindHost},
384
0
            {"encoding", &CIRCNetwork::SetEncoding},
385
0
            {"quitmsg", &CIRCNetwork::SetQuitMsg},
386
0
        };
387
0
        TOption<bool> BoolOptions[] = {
388
0
            {"ircconnectenabled", &CIRCNetwork::SetIRCConnectEnabled},
389
0
            {"trustallcerts", &CIRCNetwork::SetTrustAllCerts},
390
0
            {"trustpki", &CIRCNetwork::SetTrustPKI},
391
0
        };
392
0
        TOption<double> DoubleOptions[] = {
393
0
            {"floodrate", &CIRCNetwork::SetFloodRate},
394
0
        };
395
0
        TOption<short unsigned int> SUIntOptions[] = {
396
0
            {"floodburst", &CIRCNetwork::SetFloodBurst},
397
0
            {"joindelay", &CIRCNetwork::SetJoinDelay},
398
0
        };
399
400
0
        for (const auto& Option : StringOptions) {
401
0
            CString sValue;
402
0
            if (pConfig->FindStringEntry(Option.name, sValue))
403
0
                (this->*Option.pSetter)(sValue);
404
0
        }
405
406
0
        for (const auto& Option : BoolOptions) {
407
0
            CString sValue;
408
0
            if (pConfig->FindStringEntry(Option.name, sValue))
409
0
                (this->*Option.pSetter)(sValue.ToBool());
410
0
        }
411
412
0
        for (const auto& Option : DoubleOptions) {
413
0
            double fValue;
414
0
            if (pConfig->FindDoubleEntry(Option.name, fValue))
415
0
                (this->*Option.pSetter)(fValue);
416
0
        }
417
418
0
        for (const auto& Option : SUIntOptions) {
419
0
            unsigned short value;
420
0
            if (pConfig->FindUShortEntry(Option.name, value))
421
0
                (this->*Option.pSetter)(value);
422
0
        }
423
424
0
        pConfig->FindStringVector("loadmodule", vsList);
425
0
        for (const CString& sValue : vsList) {
426
0
            CString sModName = sValue.Token(0);
427
0
            CString sNotice = "Loading network module [" + sModName + "]";
428
429
            // XXX Legacy crap, added in ZNC 0.203, modified in 0.207
430
            // Note that 0.203 == 0.207
431
0
            if (sModName == "away") {
432
0
                sNotice =
433
0
                    "NOTICE: [away] was renamed, loading [awaystore] instead";
434
0
                sModName = "awaystore";
435
0
            }
436
437
            // XXX Legacy crap, added in ZNC 0.207
438
0
            if (sModName == "autoaway") {
439
0
                sNotice =
440
0
                    "NOTICE: [autoaway] was renamed, loading [awaystore] "
441
0
                    "instead";
442
0
                sModName = "awaystore";
443
0
            }
444
445
            // XXX Legacy crap, added in 1.1; fakeonline module was dropped in
446
            // 1.0 and returned in 1.1
447
0
            if (sModName == "fakeonline") {
448
0
                sNotice =
449
0
                    "NOTICE: [fakeonline] was renamed, loading "
450
0
                    "[modules_online] instead";
451
0
                sModName = "modules_online";
452
0
            }
453
454
0
            CString sModRet;
455
0
            CString sArgs = sValue.Token(1, true);
456
457
0
            bool bModRet = LoadModule(sModName, sArgs, sNotice, sModRet);
458
459
0
            if (!bModRet) {
460
                // Q is removed in znc 1.8
461
0
                if (sModName == "q") {
462
0
                    CUtils::PrintError(
463
0
                        "NOTICE: [q] is unavailable, cannot load.");
464
0
                    CUtils::PrintError(
465
0
                        "NOTICE: [q] is removed in this release of ZNC. Please "
466
0
                        "either remove it from your config or install it as a "
467
0
                        "third party module with the same name.");
468
0
                    CUtils::PrintError(
469
0
                        "NOTICE: More info can be found on "
470
0
                        "https://wiki.znc.in/Q");
471
0
                    return false;
472
0
                }
473
474
                // Partyline is removed in znc 1.8
475
0
                if (sModName == "partyline") {
476
0
                    CUtils::PrintError(
477
0
                        "NOTICE: [partyline] is unavailable, cannot load.");
478
0
                    CUtils::PrintError(
479
0
                        "NOTICE: [partyline] is removed in this release"
480
0
                        " of ZNC. Please either remove it from your config or "
481
0
                        "install it as a third party module with the same "
482
0
                        "name.");
483
0
                    CUtils::PrintError(
484
0
                        "NOTICE: More info can be found on "
485
0
                        "https://wiki.znc.in/Partyline");
486
0
                    return false;
487
0
                }
488
489
                // XXX The awaynick module was retired in 1.6 (still available
490
                // as external module)
491
0
                if (sModName == "awaynick") {
492
                    // load simple_away instead, unless it's already on the list
493
0
                    bool bFound = false;
494
0
                    for (const CString& sLoadMod : vsList) {
495
0
                        if (sLoadMod.Token(0).Equals("simple_away")) {
496
0
                            bFound = true;
497
0
                        }
498
0
                    }
499
0
                    if (!bFound) {
500
0
                        sNotice =
501
0
                            "NOTICE: awaynick was retired, loading network "
502
0
                            "module [simple_away] instead; if you still need "
503
0
                            "awaynick, install it as an external module";
504
0
                        sModName = "simple_away";
505
                        // not a fatal error if simple_away is not available
506
0
                        LoadModule(sModName, sArgs, sNotice, sModRet);
507
0
                    }
508
0
                } else {
509
0
                    sError = sModRet;
510
0
                    return false;
511
0
                }
512
0
            }
513
0
        }
514
0
    }
515
516
0
    pConfig->FindStringVector("server", vsList);
517
0
    CUtils::PrintAction("Adding " + CString(vsList.size()) + " servers");
518
0
    for (const CString& sServer : vsList) {
519
0
        CUtils::PrintStatus(AddServer(sServer));
520
0
    }
521
522
0
    pConfig->FindStringVector("trustedserverfingerprint", vsList);
523
0
    for (const CString& sFP : vsList) {
524
0
        AddTrustedFingerprint(sFP);
525
0
    }
526
527
0
    pConfig->FindStringVector("chan", vsList);
528
0
    for (const CString& sChan : vsList) {
529
0
        AddChan(sChan, true);
530
0
    }
531
532
0
    CConfig::SubConfig subConf;
533
0
    CConfig::SubConfig::const_iterator subIt;
534
535
0
    pConfig->FindSubConfig("chan", subConf);
536
0
    for (subIt = subConf.begin(); subIt != subConf.end(); ++subIt) {
537
0
        const CString& sChanName = subIt->first;
538
0
        CConfig* pSubConf = subIt->second.m_pSubConfig;
539
0
        CChan* pChan = new CChan(sChanName, this, true, pSubConf);
540
541
0
        if (!pSubConf->empty()) {
542
0
            sError = "Unhandled lines in config for User [" +
543
0
                     m_pUser->GetUsername() + "], Network [" + GetName() +
544
0
                     "], Channel [" + sChanName + "]!";
545
0
            CUtils::PrintError(sError);
546
547
0
            CZNC::DumpConfig(pSubConf);
548
0
            delete pChan;
549
0
            return false;
550
0
        }
551
552
        // Save the channel name, because AddChan
553
        // deletes the CChannel*, if adding fails
554
0
        sError = pChan->GetName();
555
0
        if (!AddChan(pChan)) {
556
0
            sError = "Channel [" + sError + "] defined more than once";
557
0
            CUtils::PrintError(sError);
558
0
            return false;
559
0
        }
560
0
        sError.clear();
561
0
    }
562
563
0
    return true;
564
0
}
565
566
0
CConfig CIRCNetwork::ToConfig() const {
567
0
    CConfig config;
568
569
0
    if (!m_sNick.empty()) {
570
0
        config.AddKeyValuePair("Nick", m_sNick);
571
0
    }
572
573
0
    if (!m_sAltNick.empty()) {
574
0
        config.AddKeyValuePair("AltNick", m_sAltNick);
575
0
    }
576
577
0
    if (!m_sIdent.empty()) {
578
0
        config.AddKeyValuePair("Ident", m_sIdent);
579
0
    }
580
581
0
    if (!m_sRealName.empty()) {
582
0
        config.AddKeyValuePair("RealName", m_sRealName);
583
0
    }
584
0
    if (!m_sBindHost.empty()) {
585
0
        config.AddKeyValuePair("BindHost", m_sBindHost);
586
0
    }
587
588
0
    config.AddKeyValuePair("IRCConnectEnabled",
589
0
                           CString(GetIRCConnectEnabled()));
590
0
    config.AddKeyValuePair("TrustAllCerts", CString(GetTrustAllCerts()));
591
0
    config.AddKeyValuePair("TrustPKI", CString(GetTrustPKI()));
592
0
    config.AddKeyValuePair("FloodRate", CString(GetFloodRate()));
593
0
    config.AddKeyValuePair("FloodBurst", CString(GetFloodBurst()));
594
0
    config.AddKeyValuePair("JoinDelay", CString(GetJoinDelay()));
595
0
    config.AddKeyValuePair("Encoding", m_sEncoding);
596
597
0
    if (!m_sQuitMsg.empty()) {
598
0
        config.AddKeyValuePair("QuitMsg", m_sQuitMsg);
599
0
    }
600
601
    // Modules
602
0
    const CModules& Mods = GetModules();
603
604
0
    if (!Mods.empty()) {
605
0
        for (CModule* pMod : Mods) {
606
0
            CString sArgs = pMod->GetArgs();
607
608
0
            if (!sArgs.empty()) {
609
0
                sArgs = " " + sArgs;
610
0
            }
611
612
0
            config.AddKeyValuePair("LoadModule", pMod->GetModName() + sArgs);
613
0
        }
614
0
    }
615
616
    // Servers
617
0
    for (CServer* pServer : m_vServers) {
618
0
        config.AddKeyValuePair("Server", pServer->GetString());
619
0
    }
620
621
0
    for (const CString& sFP : m_ssTrustedFingerprints) {
622
0
        config.AddKeyValuePair("TrustedServerFingerprint", sFP);
623
0
    }
624
625
    // Chans
626
0
    for (CChan* pChan : m_vChans) {
627
0
        if (pChan->InConfig()) {
628
0
            config.AddSubConfig("Chan", pChan->GetName(), pChan->ToConfig());
629
0
        }
630
0
    }
631
632
0
    return config;
633
0
}
634
635
0
void CIRCNetwork::BounceAllClients() {
636
0
    for (CClient* pClient : m_vClients) {
637
0
        pClient->BouncedOff();
638
0
    }
639
640
0
    m_vClients.clear();
641
0
}
642
643
0
bool CIRCNetwork::IsUserOnline() const {
644
0
    for (CClient* pClient : m_vClients) {
645
0
        if (!pClient->IsAway()) {
646
0
            return true;
647
0
        }
648
0
    }
649
650
0
    return false;
651
0
}
652
653
0
void CIRCNetwork::ClientConnected(CClient* pClient) {
654
0
    if (!m_pUser->MultiClients()) {
655
0
        BounceAllClients();
656
0
    }
657
658
0
    m_vClients.push_back(pClient);
659
660
0
    size_t uIdx, uSize;
661
662
0
    pClient->SetPlaybackActive(true);
663
664
0
    if (m_RawBuffer.IsEmpty()) {
665
0
        pClient->PutClient(":irc.znc.in 001 " + pClient->GetNick() +
666
0
                           " :" + t_s("Welcome to ZNC"));
667
0
    } else {
668
0
        const CString& sClientNick = pClient->GetNick(false);
669
0
        MCString msParams;
670
0
        msParams["target"] = sClientNick;
671
672
0
        uSize = m_RawBuffer.Size();
673
0
        for (uIdx = 0; uIdx < uSize; uIdx++) {
674
0
            pClient->PutClient(m_RawBuffer.GetLine(uIdx, *pClient, msParams));
675
0
        }
676
677
0
        const CNick& Nick = GetIRCNick();
678
0
        if (sClientNick != Nick.GetNick()) {  // case-sensitive match
679
0
            pClient->PutClient(":" + sClientNick + "!" + Nick.GetIdent() + "@" +
680
0
                               Nick.GetHost() + " NICK :" + Nick.GetNick());
681
0
            pClient->SetNick(Nick.GetNick());
682
0
        }
683
0
    }
684
685
0
    MCString msParams;
686
0
    msParams["target"] = GetIRCNick().GetNick();
687
688
    // Send the cached MOTD
689
0
    uSize = m_MotdBuffer.Size();
690
0
    if (uSize > 0) {
691
0
        for (uIdx = 0; uIdx < uSize; uIdx++) {
692
0
            pClient->PutClient(m_MotdBuffer.GetLine(uIdx, *pClient, msParams));
693
0
        }
694
0
    }
695
696
0
    if (GetIRCSock() != nullptr) {
697
0
        CString sUserMode("");
698
0
        const set<char>& scUserModes = GetIRCSock()->GetUserModes();
699
0
        for (char cMode : scUserModes) {
700
0
            sUserMode += cMode;
701
0
        }
702
0
        if (!sUserMode.empty()) {
703
0
            pClient->PutClient(":" + GetIRCNick().GetNickMask() + " MODE " +
704
0
                               GetIRCNick().GetNick() + " :+" + sUserMode);
705
0
        }
706
0
    }
707
708
0
    if (m_bIRCAway) {
709
        // If they want to know their away reason they'll have to whois
710
        // themselves. At least we can tell them their away status...
711
0
        pClient->PutClient(":irc.znc.in 306 " + GetIRCNick().GetNick() +
712
0
                           " :You have been marked as being away");
713
0
    }
714
715
0
    const vector<CChan*>& vChans = GetChans();
716
0
    for (CChan* pChan : vChans) {
717
0
        if ((pChan->IsOn()) && (!pChan->IsDetached())) {
718
0
            pChan->AttachUser(pClient);
719
0
        }
720
0
    }
721
722
0
    bool bClearQuery = m_pUser->AutoClearQueryBuffer();
723
0
    for (CQuery* pQuery : m_vQueries) {
724
0
        pQuery->SendBuffer(pClient);
725
0
        if (bClearQuery) {
726
0
            delete pQuery;
727
0
        }
728
0
    }
729
0
    if (bClearQuery) {
730
0
        m_vQueries.clear();
731
0
    }
732
733
0
    uSize = m_NoticeBuffer.Size();
734
0
    for (uIdx = 0; uIdx < uSize; uIdx++) {
735
0
        const CBufLine& BufLine = m_NoticeBuffer.GetBufLine(uIdx);
736
0
        CMessage Message(BufLine.GetLine(*pClient, msParams));
737
0
        Message.SetNetwork(this);
738
0
        Message.SetClient(pClient);
739
0
        Message.SetTime(BufLine.GetTime());
740
0
        Message.SetTags(BufLine.GetTags());
741
0
        bool bContinue = false;
742
0
        NETWORKMODULECALL(OnPrivBufferPlayMessage(Message), m_pUser, this,
743
0
                          nullptr, &bContinue);
744
0
        if (bContinue) continue;
745
0
        pClient->PutClient(Message);
746
0
    }
747
0
    m_NoticeBuffer.Clear();
748
749
0
    pClient->SetPlaybackActive(false);
750
751
    // Tell them why they won't connect
752
0
    if (!GetIRCConnectEnabled())
753
0
        pClient->PutStatus(
754
0
            t_s("You are currently disconnected from IRC. Use 'connect' to "
755
0
                "reconnect."));
756
0
}
757
758
0
void CIRCNetwork::ClientDisconnected(CClient* pClient) {
759
0
    auto it = std::find(m_vClients.begin(), m_vClients.end(), pClient);
760
0
    if (it != m_vClients.end()) {
761
0
        m_vClients.erase(it);
762
0
    }
763
0
}
764
765
0
CUser* CIRCNetwork::GetUser() const { return m_pUser; }
766
767
0
const CString& CIRCNetwork::GetName() const { return m_sName; }
768
769
std::vector<CClient*> CIRCNetwork::FindClients(
770
0
    const CString& sIdentifier) const {
771
0
    std::vector<CClient*> vClients;
772
0
    for (CClient* pClient : m_vClients) {
773
0
        if (pClient->GetIdentifier().Equals(sIdentifier)) {
774
0
            vClients.push_back(pClient);
775
0
        }
776
0
    }
777
778
0
    return vClients;
779
0
}
780
781
0
void CIRCNetwork::SetUser(CUser* pUser) {
782
0
    for (CClient* pClient : m_vClients) {
783
0
        pClient->PutStatus(
784
0
            t_s("This network is being deleted or moved to another user."));
785
0
        pClient->SetNetwork(nullptr);
786
0
    }
787
788
0
    m_vClients.clear();
789
790
0
    if (m_pUser) {
791
0
        m_pUser->RemoveNetwork(this);
792
0
    }
793
794
0
    m_pUser = pUser;
795
0
    if (m_pUser) {
796
0
        m_pUser->AddNetwork(this);
797
0
    }
798
0
}
799
800
0
bool CIRCNetwork::SetName(const CString& sName) {
801
0
    if (IsValidNetwork(sName)) {
802
0
        m_sName = sName;
803
0
        return true;
804
0
    }
805
806
0
    return false;
807
0
}
808
809
bool CIRCNetwork::PutUser(const CString& sLine, CClient* pClient,
810
0
                          CClient* pSkipClient) {
811
0
    for (CClient* pEachClient : m_vClients) {
812
0
        if ((!pClient || pClient == pEachClient) &&
813
0
            pSkipClient != pEachClient) {
814
0
            pEachClient->PutClient(sLine);
815
816
0
            if (pClient) {
817
0
                return true;
818
0
            }
819
0
        }
820
0
    }
821
822
0
    return (pClient == nullptr);
823
0
}
824
825
bool CIRCNetwork::PutUser(const CMessage& Message, CClient* pClient,
826
0
                          CClient* pSkipClient) {
827
0
    for (CClient* pEachClient : m_vClients) {
828
0
        if ((!pClient || pClient == pEachClient) &&
829
0
            pSkipClient != pEachClient) {
830
0
            pEachClient->PutClient(Message);
831
832
0
            if (pClient) {
833
0
                return true;
834
0
            }
835
0
        }
836
0
    }
837
838
0
    return (pClient == nullptr);
839
0
}
840
841
bool CIRCNetwork::PutStatus(const CString& sLine, CClient* pClient,
842
0
                            CClient* pSkipClient) {
843
0
    for (CClient* pEachClient : m_vClients) {
844
0
        if ((!pClient || pClient == pEachClient) &&
845
0
            pSkipClient != pEachClient) {
846
0
            pEachClient->PutStatus(sLine);
847
848
0
            if (pClient) {
849
0
                return true;
850
0
            }
851
0
        }
852
0
    }
853
854
0
    return (pClient == nullptr);
855
0
}
856
857
bool CIRCNetwork::PutModule(const CString& sModule, const CString& sLine,
858
0
                            CClient* pClient, CClient* pSkipClient) {
859
0
    for (CClient* pEachClient : m_vClients) {
860
0
        if ((!pClient || pClient == pEachClient) &&
861
0
            pSkipClient != pEachClient) {
862
0
            pEachClient->PutModule(sModule, sLine);
863
864
0
            if (pClient) {
865
0
                return true;
866
0
            }
867
0
        }
868
0
    }
869
870
0
    return (pClient == nullptr);
871
0
}
872
873
// Channels
874
875
0
const vector<CChan*>& CIRCNetwork::GetChans() const { return m_vChans; }
876
877
0
CChan* CIRCNetwork::FindChan(CString sName) const {
878
0
    if (GetIRCSock()) {
879
        // See
880
        // https://tools.ietf.org/html/draft-brocklesby-irc-isupport-03#section-3.16
881
0
        sName.TrimLeft(GetIRCSock()->GetISupport("STATUSMSG", ""));
882
0
    }
883
884
0
    for (CChan* pChan : m_vChans) {
885
0
        if (sName.Equals(pChan->GetName())) {
886
0
            return pChan;
887
0
        }
888
0
    }
889
890
0
    return nullptr;
891
0
}
892
893
0
std::vector<CChan*> CIRCNetwork::FindChans(const CString& sWild) const {
894
0
    std::vector<CChan*> vChans;
895
0
    vChans.reserve(m_vChans.size());
896
0
    const CString sLower = sWild.AsLower();
897
0
    for (CChan* pChan : m_vChans) {
898
0
        if (pChan->GetName().AsLower().WildCmp(sLower)) vChans.push_back(pChan);
899
0
    }
900
0
    return vChans;
901
0
}
902
903
0
bool CIRCNetwork::AddChan(CChan* pChan) {
904
0
    if (!pChan) {
905
0
        return false;
906
0
    }
907
908
0
    for (CChan* pEachChan : m_vChans) {
909
0
        if (pEachChan->GetName().Equals(pChan->GetName())) {
910
0
            delete pChan;
911
0
            return false;
912
0
        }
913
0
    }
914
915
0
    m_vChans.push_back(pChan);
916
0
    return true;
917
0
}
918
919
0
bool CIRCNetwork::AddChan(const CString& sName, bool bInConfig) {
920
0
    if (sName.empty() || FindChan(sName)) {
921
0
        return false;
922
0
    }
923
924
0
    CChan* pChan = new CChan(sName, this, bInConfig);
925
0
    m_vChans.push_back(pChan);
926
0
    return true;
927
0
}
928
929
0
bool CIRCNetwork::DelChan(const CString& sName) {
930
0
    for (vector<CChan*>::iterator a = m_vChans.begin(); a != m_vChans.end();
931
0
         ++a) {
932
0
        if (sName.Equals((*a)->GetName())) {
933
0
            delete *a;
934
0
            m_vChans.erase(a);
935
0
            return true;
936
0
        }
937
0
    }
938
939
0
    return false;
940
0
}
941
942
bool CIRCNetwork::MoveChan(const CString& sChan, unsigned int uIndex,
943
0
                           CString& sError) {
944
0
    if (uIndex >= m_vChans.size()) {
945
0
        sError = t_s("Invalid index");
946
0
        return false;
947
0
    }
948
949
0
    auto it = m_vChans.begin();
950
0
    for (; it != m_vChans.end(); ++it)
951
0
        if ((*it)->GetName().Equals(sChan)) break;
952
0
    if (it == m_vChans.end()) {
953
0
        sError = t_f("You are not on {1}")(sChan);
954
0
        return false;
955
0
    }
956
957
0
    const auto pChan = *it;
958
0
    m_vChans.erase(it);
959
0
    m_vChans.insert(m_vChans.begin() + uIndex, pChan);
960
0
    return true;
961
0
}
962
963
bool CIRCNetwork::SwapChans(const CString& sChan1, const CString& sChan2,
964
0
                            CString& sError) {
965
0
    auto it1 = m_vChans.begin();
966
0
    for (; it1 != m_vChans.end(); ++it1)
967
0
        if ((*it1)->GetName().Equals(sChan1)) break;
968
0
    if (it1 == m_vChans.end()) {
969
0
        sError = t_f("You are not on {1}")(sChan1);
970
0
        return false;
971
0
    }
972
973
0
    auto it2 = m_vChans.begin();
974
0
    for (; it2 != m_vChans.end(); ++it2)
975
0
        if ((*it2)->GetName().Equals(sChan2)) break;
976
0
    if (it2 == m_vChans.end()) {
977
0
        sError = t_f("You are not on {1}")(sChan2);
978
0
        return false;
979
0
    }
980
981
0
    std::swap(*it1, *it2);
982
0
    return true;
983
0
}
984
985
0
void CIRCNetwork::JoinChans() {
986
    // Avoid divsion by zero, it's bad!
987
0
    if (m_vChans.empty()) return;
988
989
    // We start at a random offset into the channel list so that if your
990
    // first 3 channels are invite-only and you got MaxJoins == 3, ZNC will
991
    // still be able to join the rest of your channels.
992
0
    unsigned int start = rand() % m_vChans.size();
993
0
    unsigned int uJoins = m_pUser->MaxJoins();
994
0
    set<CChan*> sChans;
995
0
    for (unsigned int a = 0; a < m_vChans.size(); a++) {
996
0
        unsigned int idx = (start + a) % m_vChans.size();
997
0
        CChan* pChan = m_vChans[idx];
998
0
        if (!pChan->IsOn() && !pChan->IsDisabled()) {
999
0
            if (!JoinChan(pChan)) continue;
1000
1001
0
            sChans.insert(pChan);
1002
1003
            // Limit the number of joins
1004
0
            if (uJoins != 0 && --uJoins == 0) {
1005
                // Reset the timer.
1006
0
                m_pJoinTimer->Reset();
1007
0
                break;
1008
0
            }
1009
0
        }
1010
0
    }
1011
1012
0
    while (!sChans.empty()) JoinChans(sChans);
1013
0
}
1014
1015
0
void CIRCNetwork::JoinChans(set<CChan*>& sChans) {
1016
0
    CString sKeys, sJoin;
1017
0
    bool bHaveKey = false;
1018
0
    size_t uiJoinLength = strlen("JOIN ");
1019
1020
0
    while (!sChans.empty()) {
1021
0
        set<CChan*>::iterator it = sChans.begin();
1022
0
        const CString& sName = (*it)->GetName();
1023
0
        const CString& sKey = (*it)->GetKey();
1024
0
        size_t len = sName.length() + sKey.length();
1025
0
        len += 2;  // two comma
1026
1027
0
        if (!sKeys.empty() && uiJoinLength + len >= 512) break;
1028
1029
0
        if (!sJoin.empty()) {
1030
0
            sJoin += ",";
1031
0
            sKeys += ",";
1032
0
        }
1033
0
        uiJoinLength += len;
1034
0
        sJoin += sName;
1035
0
        if (!sKey.empty()) {
1036
0
            sKeys += sKey;
1037
0
            bHaveKey = true;
1038
0
        }
1039
0
        sChans.erase(it);
1040
0
    }
1041
1042
0
    if (bHaveKey)
1043
0
        PutIRC("JOIN " + sJoin + " " + sKeys);
1044
0
    else
1045
0
        PutIRC("JOIN " + sJoin);
1046
0
}
1047
1048
0
bool CIRCNetwork::JoinChan(CChan* pChan) {
1049
0
    bool bReturn = false;
1050
0
    NETWORKMODULECALL(OnJoining(*pChan), m_pUser, this, nullptr, &bReturn);
1051
1052
0
    if (bReturn) return false;
1053
1054
0
    if (m_pUser->JoinTries() != 0 &&
1055
0
        pChan->GetJoinTries() >= m_pUser->JoinTries()) {
1056
0
        PutStatus(t_f("The channel {1} could not be joined, disabling it.")(
1057
0
            pChan->GetName()));
1058
0
        pChan->Disable();
1059
0
    } else {
1060
0
        pChan->IncJoinTries();
1061
0
        bool bFailed = false;
1062
0
        NETWORKMODULECALL(OnTimerAutoJoin(*pChan), m_pUser, this, nullptr,
1063
0
                          &bFailed);
1064
0
        if (bFailed) return false;
1065
0
        return true;
1066
0
    }
1067
0
    return false;
1068
0
}
1069
1070
0
bool CIRCNetwork::IsChan(const CString& sChan) const {
1071
0
    if (sChan.empty()) return false;  // There is no way this is a chan
1072
0
    if (GetChanPrefixes().empty())
1073
0
        return true;  // We can't know, so we allow everything
1074
    // Thanks to the above if (empty), we can do sChan[0]
1075
0
    return GetChanPrefixes().find(sChan[0]) != CString::npos;
1076
0
}
1077
1078
// Queries
1079
1080
0
const vector<CQuery*>& CIRCNetwork::GetQueries() const { return m_vQueries; }
1081
1082
0
CQuery* CIRCNetwork::FindQuery(const CString& sName) const {
1083
0
    for (CQuery* pQuery : m_vQueries) {
1084
0
        if (sName.Equals(pQuery->GetName())) {
1085
0
            return pQuery;
1086
0
        }
1087
0
    }
1088
1089
0
    return nullptr;
1090
0
}
1091
1092
0
std::vector<CQuery*> CIRCNetwork::FindQueries(const CString& sWild) const {
1093
0
    std::vector<CQuery*> vQueries;
1094
0
    vQueries.reserve(m_vQueries.size());
1095
0
    const CString sLower = sWild.AsLower();
1096
0
    for (CQuery* pQuery : m_vQueries) {
1097
0
        if (pQuery->GetName().AsLower().WildCmp(sLower))
1098
0
            vQueries.push_back(pQuery);
1099
0
    }
1100
0
    return vQueries;
1101
0
}
1102
1103
0
CQuery* CIRCNetwork::AddQuery(const CString& sName) {
1104
0
    if (sName.empty()) {
1105
0
        return nullptr;
1106
0
    }
1107
1108
0
    CQuery* pQuery = FindQuery(sName);
1109
0
    if (!pQuery) {
1110
0
        pQuery = new CQuery(sName, this);
1111
0
        m_vQueries.push_back(pQuery);
1112
1113
0
        if (m_pUser->MaxQueryBuffers() > 0) {
1114
0
            while (m_vQueries.size() > m_pUser->MaxQueryBuffers()) {
1115
0
                delete *m_vQueries.begin();
1116
0
                m_vQueries.erase(m_vQueries.begin());
1117
0
            }
1118
0
        }
1119
0
    }
1120
1121
0
    return pQuery;
1122
0
}
1123
1124
0
bool CIRCNetwork::DelQuery(const CString& sName) {
1125
0
    for (vector<CQuery*>::iterator a = m_vQueries.begin();
1126
0
         a != m_vQueries.end(); ++a) {
1127
0
        if (sName.Equals((*a)->GetName())) {
1128
0
            delete *a;
1129
0
            m_vQueries.erase(a);
1130
0
            return true;
1131
0
        }
1132
0
    }
1133
1134
0
    return false;
1135
0
}
1136
1137
// Server list
1138
1139
0
const vector<CServer*>& CIRCNetwork::GetServers() const { return m_vServers; }
1140
1141
0
CServer* CIRCNetwork::FindServer(const CString& sName) const {
1142
0
    for (CServer* pServer : m_vServers) {
1143
0
        if (sName.Equals(pServer->GetName())) {
1144
0
            return pServer;
1145
0
        }
1146
0
    }
1147
1148
0
    return nullptr;
1149
0
}
1150
1151
bool CIRCNetwork::DelServer(const CString& sName, unsigned short uPort,
1152
0
                            const CString& sPass) {
1153
0
    if (sName.empty()) {
1154
0
        return false;
1155
0
    }
1156
1157
0
    CServer Server(sName, uPort, sPass);
1158
0
    return DelServer(Server);
1159
0
}
1160
1161
0
bool CIRCNetwork::DelServer(const CServer& Server) {
1162
0
    unsigned int a = 0;
1163
0
    bool bSawCurrentServer = false;
1164
0
    CServer* pCurServer = GetCurrentServer();
1165
1166
0
    for (vector<CServer*>::iterator it = m_vServers.begin();
1167
0
         it != m_vServers.end(); ++it, a++) {
1168
0
        CServer* pServer = *it;
1169
1170
0
        if (pServer == pCurServer) bSawCurrentServer = true;
1171
1172
        // Unix sockets can be removed with "unix:" prefix and without, both
1173
        // work - that's not part of GetName()
1174
0
        if (!pServer->GetName().Equals(Server.GetName())) continue;
1175
1176
        // But it makes no sense to remove TCP server via "unix:hostname.com"
1177
0
        if (!pServer->IsUnixSocket() && Server.IsUnixSocket()) continue;
1178
1179
0
        if (Server.GetPort() != 6667 && pServer->GetPort() != Server.GetPort()) continue;
1180
1181
0
        if (!Server.GetPass().empty() && pServer->GetPass() != Server.GetPass()) continue;
1182
1183
0
        m_vServers.erase(it);
1184
1185
0
        if (pServer == pCurServer) {
1186
0
            CIRCSock* pIRCSock = GetIRCSock();
1187
1188
            // Make sure we don't skip the next server in the list!
1189
0
            if (m_uServerIdx) {
1190
0
                m_uServerIdx--;
1191
0
            }
1192
1193
0
            if (pIRCSock) {
1194
0
                pIRCSock->Quit();
1195
0
                PutStatus(t_s("Your current server was removed, jumping..."));
1196
0
            }
1197
0
        } else if (!bSawCurrentServer) {
1198
            // Our current server comes after the server which we
1199
            // are removing. This means that it now got a different
1200
            // index in m_vServers!
1201
0
            m_uServerIdx--;
1202
0
        }
1203
1204
0
        delete pServer;
1205
1206
0
        return true;
1207
0
    }
1208
1209
0
    return false;
1210
0
}
1211
1212
0
bool CIRCNetwork::AddServer(const CString& sName) {
1213
0
    if (sName.empty()) {
1214
0
        return false;
1215
0
    }
1216
1217
0
    return AddServer(CServer::Parse(sName));
1218
0
}
1219
1220
0
bool CIRCNetwork::AddServer(CServer Server) {
1221
0
    if (Server.GetName().empty()) return false;
1222
0
#ifndef HAVE_LIBSSL
1223
0
    if (Server.IsSSL()) return false;
1224
0
#endif
1225
1226
    // Check if server is already added
1227
0
    for (CServer* pServer : m_vServers) {
1228
0
        if (*pServer == Server) return false;
1229
0
    }
1230
1231
0
    m_vServers.push_back(new CServer(std::move(Server)));
1232
0
    CheckIRCConnect();
1233
0
    return true;
1234
0
}
1235
1236
bool CIRCNetwork::AddServer(const CString& sName, unsigned short uPort,
1237
0
                            const CString& sPass, bool bSSL) {
1238
0
#ifndef HAVE_LIBSSL
1239
0
    if (bSSL) {
1240
0
        return false;
1241
0
    }
1242
0
#endif
1243
1244
0
    if (sName.empty()) {
1245
0
        return false;
1246
0
    }
1247
1248
0
    return AddServer(CServer(sName, uPort, sPass, bSSL));
1249
0
}
1250
1251
0
CServer* CIRCNetwork::GetNextServer(bool bAdvance) {
1252
0
    if (m_vServers.empty()) {
1253
0
        return nullptr;
1254
0
    }
1255
1256
0
    if (m_uServerIdx >= m_vServers.size()) {
1257
0
        m_uServerIdx = 0;
1258
0
    }
1259
1260
0
    if (bAdvance) {
1261
0
        return m_vServers[m_uServerIdx++];
1262
0
    } else {
1263
0
        return m_vServers[m_uServerIdx];
1264
0
    }
1265
0
}
1266
1267
0
CServer* CIRCNetwork::GetCurrentServer() const {
1268
0
    size_t uIdx = (m_uServerIdx) ? m_uServerIdx - 1 : 0;
1269
1270
0
    if (uIdx >= m_vServers.size()) {
1271
0
        return nullptr;
1272
0
    }
1273
1274
0
    return m_vServers[uIdx];
1275
0
}
1276
1277
0
void CIRCNetwork::SetIRCServer(const CString& s) { m_sIRCServer = s; }
1278
1279
0
bool CIRCNetwork::SetNextServer(const CServer* pServer) {
1280
0
    for (unsigned int a = 0; a < m_vServers.size(); a++) {
1281
0
        if (m_vServers[a] == pServer) {
1282
0
            m_uServerIdx = a;
1283
0
            return true;
1284
0
        }
1285
0
    }
1286
1287
0
    return false;
1288
0
}
1289
1290
0
bool CIRCNetwork::IsLastServer() const {
1291
0
    return (m_uServerIdx >= m_vServers.size());
1292
0
}
1293
1294
0
const CString& CIRCNetwork::GetIRCServer() const { return m_sIRCServer; }
1295
0
const CNick& CIRCNetwork::GetIRCNick() const { return m_IRCNick; }
1296
1297
0
void CIRCNetwork::SetIRCNick(const CNick& n) {
1298
0
    m_IRCNick = n;
1299
1300
0
    for (CClient* pClient : m_vClients) {
1301
0
        pClient->SetNick(n.GetNick());
1302
0
    }
1303
0
}
1304
1305
0
CString CIRCNetwork::GetCurNick() const {
1306
0
    const CIRCSock* pIRCSock = GetIRCSock();
1307
1308
0
    if (pIRCSock) {
1309
0
        return pIRCSock->GetNick();
1310
0
    }
1311
1312
0
    if (!m_vClients.empty()) {
1313
0
        return m_vClients[0]->GetNick();
1314
0
    }
1315
1316
0
    return "";
1317
0
}
1318
1319
0
bool CIRCNetwork::Connect() {
1320
0
    if (!GetIRCConnectEnabled() || m_pIRCSock || !HasServers()) return false;
1321
1322
0
    CServer* pServer = GetNextServer();
1323
0
    if (!pServer) return false;
1324
1325
0
    if (CZNC::Get().GetServerThrottle(pServer->GetName())) {
1326
        // Can't connect right now, schedule retry later
1327
0
        CZNC::Get().AddNetworkToQueue(this);
1328
0
        return false;
1329
0
    }
1330
1331
0
    CZNC::Get().AddServerThrottle(pServer->GetName());
1332
1333
0
    bool bSSL = pServer->IsSSL();
1334
0
#ifndef HAVE_LIBSSL
1335
0
    if (bSSL) {
1336
0
        PutStatus(
1337
0
            t_f("Cannot connect to {1}, because ZNC is not compiled with SSL "
1338
0
                "support.")(pServer->GetString(false)));
1339
0
        CZNC::Get().AddNetworkToQueue(this);
1340
0
        return false;
1341
0
    }
1342
0
#endif
1343
1344
0
    CIRCSock* pIRCSock = new CIRCSock(this);
1345
0
    pIRCSock->SetPass(pServer->GetPass());
1346
0
    pIRCSock->SetSSLTrustedPeerFingerprints(m_ssTrustedFingerprints);
1347
0
    pIRCSock->SetTrustAllCerts(GetTrustAllCerts());
1348
0
    pIRCSock->SetTrustPKI(GetTrustPKI());
1349
1350
0
    DEBUG("Connecting user/network [" << m_pUser->GetUsername() << "/"
1351
0
                                      << m_sName << "]");
1352
1353
0
    bool bAbort = false;
1354
0
    NETWORKMODULECALL(OnIRCConnecting(pIRCSock), m_pUser, this, nullptr,
1355
0
                      &bAbort);
1356
0
    if (bAbort) {
1357
0
        DEBUG("Some module aborted the connection attempt");
1358
0
        PutStatus(t_s("Some module aborted the connection attempt"));
1359
0
        delete pIRCSock;
1360
0
        CZNC::Get().AddNetworkToQueue(this);
1361
0
        return false;
1362
0
    }
1363
1364
0
    CString sSockName = "IRC::" + m_pUser->GetUsername() + "::" + m_sName;
1365
1366
0
    if (pServer->IsUnixSocket()) {
1367
0
        pIRCSock->SetSSL(bSSL);
1368
0
        CZNC::Get().GetManager().ConnectUnix(sSockName, pServer->GetName(),
1369
0
                                             pIRCSock);
1370
0
    } else {
1371
0
        CZNC::Get().GetManager().Connect(pServer->GetName(), pServer->GetPort(),
1372
0
                                         sSockName, 120, bSSL, GetBindHost(),
1373
0
                                         pIRCSock);
1374
0
    }
1375
1376
0
    return true;
1377
0
}
1378
1379
0
bool CIRCNetwork::IsIRCConnected() const {
1380
0
    const CIRCSock* pSock = GetIRCSock();
1381
0
    return (pSock && pSock->IsAuthed());
1382
0
}
1383
1384
0
void CIRCNetwork::SetIRCSocket(CIRCSock* pIRCSock) { m_pIRCSock = pIRCSock; }
1385
1386
0
void CIRCNetwork::IRCConnected() {
1387
0
    if (m_uJoinDelay > 0) {
1388
0
        m_pJoinTimer->Delay(m_uJoinDelay);
1389
0
    } else {
1390
0
        JoinChans();
1391
0
    }
1392
0
}
1393
1394
0
void CIRCNetwork::IRCDisconnected() {
1395
0
    m_pIRCSock = nullptr;
1396
1397
0
    SetIRCServer("");
1398
0
    m_bIRCAway = false;
1399
1400
    // Get the reconnect going
1401
0
    CheckIRCConnect();
1402
0
}
1403
1404
0
void CIRCNetwork::NotifyClientsAboutServerDependentCap(const CString& sCap, bool bValue) {
1405
0
    CString sValue = GetIRCSock() ? GetIRCSock()->GetCapLsValue(sCap) : "";
1406
0
    for (CClient* pClient : m_vClients) {
1407
0
        pClient->NotifyServerDependentCap(sCap, bValue, sValue);
1408
0
    }
1409
0
}
1410
1411
0
bool CIRCNetwork::IsServerCapAccepted(const CString& sCap) const {
1412
0
    return m_pIRCSock && m_pIRCSock->IsCapAccepted(sCap);
1413
0
}
1414
1415
0
void CIRCNetwork::SetIRCConnectEnabled(bool b) {
1416
0
    m_bIRCConnectEnabled = b;
1417
1418
0
    if (m_bIRCConnectEnabled) {
1419
0
        CheckIRCConnect();
1420
0
    } else if (GetIRCSock()) {
1421
0
        if (GetIRCSock()->IsConnected()) {
1422
0
            GetIRCSock()->Quit();
1423
0
        } else {
1424
0
            GetIRCSock()->Close();
1425
0
        }
1426
0
    }
1427
0
}
1428
1429
0
void CIRCNetwork::CheckIRCConnect() {
1430
    // Do we want to connect?
1431
0
    if (GetIRCConnectEnabled() && GetIRCSock() == nullptr)
1432
0
        CZNC::Get().AddNetworkToQueue(this);
1433
0
}
1434
1435
0
bool CIRCNetwork::PutIRC(const CString& sLine) {
1436
0
    CIRCSock* pIRCSock = GetIRCSock();
1437
1438
0
    if (!pIRCSock) {
1439
0
        return false;
1440
0
    }
1441
1442
0
    pIRCSock->PutIRC(sLine);
1443
0
    return true;
1444
0
}
1445
1446
0
bool CIRCNetwork::PutIRC(const CMessage& Message) {
1447
0
    CIRCSock* pIRCSock = GetIRCSock();
1448
1449
0
    if (!pIRCSock) {
1450
0
        return false;
1451
0
    }
1452
1453
0
    pIRCSock->PutIRC(Message);
1454
0
    return true;
1455
0
}
1456
1457
0
void CIRCNetwork::ClearQueryBuffer() {
1458
0
    std::for_each(m_vQueries.begin(), m_vQueries.end(),
1459
0
                  std::default_delete<CQuery>());
1460
0
    m_vQueries.clear();
1461
0
}
1462
1463
0
const CString& CIRCNetwork::GetNick(const bool bAllowDefault) const {
1464
0
    if (m_sNick.empty()) {
1465
0
        return m_pUser->GetNick(bAllowDefault);
1466
0
    }
1467
1468
0
    return m_sNick;
1469
0
}
1470
1471
0
const CString& CIRCNetwork::GetAltNick(const bool bAllowDefault) const {
1472
0
    if (m_sAltNick.empty()) {
1473
0
        return m_pUser->GetAltNick(bAllowDefault);
1474
0
    }
1475
1476
0
    return m_sAltNick;
1477
0
}
1478
1479
0
const CString& CIRCNetwork::GetIdent(const bool bAllowDefault) const {
1480
0
    if (m_sIdent.empty()) {
1481
0
        return m_pUser->GetIdent(bAllowDefault);
1482
0
    }
1483
1484
0
    return m_sIdent;
1485
0
}
1486
1487
0
CString CIRCNetwork::GetRealName() const {
1488
0
    if (m_sRealName.empty()) {
1489
0
        return m_pUser->GetRealName();
1490
0
    }
1491
1492
0
    return m_sRealName;
1493
0
}
1494
1495
0
const CString& CIRCNetwork::GetBindHost() const {
1496
0
    if (m_sBindHost.empty()) {
1497
0
        return m_pUser->GetBindHost();
1498
0
    }
1499
1500
0
    return m_sBindHost;
1501
0
}
1502
1503
0
const CString& CIRCNetwork::GetEncoding() const { return m_sEncoding; }
1504
1505
0
CString CIRCNetwork::GetQuitMsg() const {
1506
0
    if (m_sQuitMsg.empty()) {
1507
0
        return m_pUser->GetQuitMsg();
1508
0
    }
1509
1510
0
    return m_sQuitMsg;
1511
0
}
1512
1513
0
void CIRCNetwork::SetNick(const CString& s) {
1514
0
    if (m_pUser->GetNick().Equals(s)) {
1515
0
        m_sNick = "";
1516
0
    } else {
1517
0
        m_sNick = s;
1518
0
    }
1519
0
}
1520
1521
0
void CIRCNetwork::SetAltNick(const CString& s) {
1522
0
    if (m_pUser->GetAltNick().Equals(s)) {
1523
0
        m_sAltNick = "";
1524
0
    } else {
1525
0
        m_sAltNick = s;
1526
0
    }
1527
0
}
1528
1529
0
void CIRCNetwork::SetIdent(const CString& s) {
1530
0
    if (m_pUser->GetIdent().Equals(s)) {
1531
0
        m_sIdent = "";
1532
0
    } else {
1533
0
        m_sIdent = s;
1534
0
    }
1535
0
}
1536
1537
0
void CIRCNetwork::SetRealName(const CString& s) {
1538
0
    if (m_pUser->GetRealName().Equals(s)) {
1539
0
        m_sRealName = "";
1540
0
    } else {
1541
0
        m_sRealName = s;
1542
0
    }
1543
0
}
1544
1545
0
void CIRCNetwork::SetBindHost(const CString& s) {
1546
0
    if (m_pUser->GetBindHost().Equals(s)) {
1547
0
        m_sBindHost = "";
1548
0
    } else {
1549
0
        m_sBindHost = s;
1550
0
    }
1551
0
}
1552
1553
0
void CIRCNetwork::SetEncoding(const CString& s) {
1554
0
    m_sEncoding = CZNC::Get().FixupEncoding(s);
1555
0
    if (GetIRCSock()) {
1556
0
        GetIRCSock()->SetEncoding(m_sEncoding);
1557
0
    }
1558
0
}
1559
1560
0
void CIRCNetwork::SetQuitMsg(const CString& s) {
1561
0
    if (m_pUser->GetQuitMsg().Equals(s)) {
1562
0
        m_sQuitMsg = "";
1563
0
    } else {
1564
0
        m_sQuitMsg = s;
1565
0
    }
1566
0
}
1567
1568
0
CString CIRCNetwork::ExpandString(const CString& sStr) const {
1569
0
    CString sRet;
1570
0
    return ExpandString(sStr, sRet);
1571
0
}
1572
1573
0
CString& CIRCNetwork::ExpandString(const CString& sStr, CString& sRet) const {
1574
0
    sRet = sStr;
1575
1576
0
    sRet.Replace("%altnick%", GetAltNick());
1577
0
    sRet.Replace("%bindhost%", GetBindHost());
1578
0
    sRet.Replace("%defnick%", GetNick());
1579
0
    sRet.Replace("%ident%", GetIdent());
1580
0
    sRet.Replace("%network%", GetName());
1581
0
    sRet.Replace("%nick%", GetCurNick());
1582
0
    sRet.Replace("%realname%", GetRealName());
1583
1584
0
    return m_pUser->ExpandString(sRet, sRet);
1585
0
}
1586
1587
bool CIRCNetwork::LoadModule(const CString& sModName, const CString& sArgs,
1588
0
                             const CString& sNotice, CString& sError) {
1589
0
    CUtils::PrintAction(sNotice);
1590
0
    CString sModRet;
1591
1592
0
    bool bModRet = GetModules().LoadModule(
1593
0
        sModName, sArgs, CModInfo::NetworkModule, GetUser(), this, sModRet);
1594
1595
0
    CUtils::PrintStatus(bModRet, sModRet);
1596
0
    if (!bModRet) {
1597
0
        sError = sModRet;
1598
0
    }
1599
0
    return bModRet;
1600
0
}