Coverage Report

Created: 2023-09-25 06:17

/src/znc/src/IRCSock.cpp
Line
Count
Source (jump to first uncovered line)
1
/*
2
 * Copyright (C) 2004-2023 ZNC, see the NOTICE file for details.
3
 *
4
 * Licensed under the Apache License, Version 2.0 (the "License");
5
 * you may not use this file except in compliance with the License.
6
 * You may obtain a copy of the License at
7
 *
8
 *     http://www.apache.org/licenses/LICENSE-2.0
9
 *
10
 * Unless required by applicable law or agreed to in writing, software
11
 * distributed under the License is distributed on an "AS IS" BASIS,
12
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
 * See the License for the specific language governing permissions and
14
 * limitations under the License.
15
 */
16
17
#include <znc/IRCSock.h>
18
#include <znc/Chan.h>
19
#include <znc/User.h>
20
#include <znc/IRCNetwork.h>
21
#include <znc/Server.h>
22
#include <znc/Query.h>
23
#include <znc/ZNCDebug.h>
24
#include <time.h>
25
26
using std::set;
27
using std::vector;
28
using std::map;
29
30
#define IRCSOCKMODULECALL(macFUNC, macEXITER)                              \
31
0
    NETWORKMODULECALL(macFUNC, m_pNetwork->GetUser(), m_pNetwork, nullptr, \
32
0
                      macEXITER)
33
// These are used in OnGeneralCTCP()
34
const time_t CIRCSock::m_uCTCPFloodTime = 5;
35
const unsigned int CIRCSock::m_uCTCPFloodCount = 5;
36
37
// It will be bad if user sets it to 0.00000000000001
38
// If you want no flood protection, set network's flood rate to -1
39
// TODO move this constant to CIRCNetwork?
40
static const double FLOOD_MINIMAL_RATE = 0.3;
41
42
class CIRCFloodTimer : public CCron {
43
    CIRCSock* m_pSock;
44
45
  public:
46
0
    CIRCFloodTimer(CIRCSock* pSock) : m_pSock(pSock) {
47
0
        StartMaxCycles(m_pSock->m_fFloodRate, 0);
48
0
    }
49
    CIRCFloodTimer(const CIRCFloodTimer&) = delete;
50
    CIRCFloodTimer& operator=(const CIRCFloodTimer&) = delete;
51
0
    void RunJob() override {
52
0
        if (m_pSock->m_iSendsAllowed < m_pSock->m_uFloodBurst) {
53
0
            m_pSock->m_iSendsAllowed++;
54
0
        }
55
0
        m_pSock->TrySend();
56
0
    }
57
};
58
59
0
bool CIRCSock::IsFloodProtected(double fRate) {
60
0
    return fRate > FLOOD_MINIMAL_RATE;
61
0
}
62
63
CIRCSock::CIRCSock(CIRCNetwork* pNetwork)
64
    : CIRCSocket(),
65
      m_bAuthed(false),
66
      m_bNamesx(false),
67
      m_bUHNames(false),
68
      m_bAwayNotify(false),
69
      m_bAccountNotify(false),
70
      m_bAccountTag(false),
71
      m_bExtendedJoin(false),
72
      m_bServerTime(false),
73
      m_sPerms("*!@%+"),
74
      m_sPermModes("qaohv"),
75
      m_scUserModes(),
76
      m_mceChanModes(),
77
      m_pNetwork(pNetwork),
78
      m_Nick(),
79
      m_sPass(""),
80
      m_msChans(),
81
      m_uMaxNickLen(9),
82
      m_uCapPaused(0),
83
      m_ssAcceptedCaps(),
84
      m_ssPendingCaps(),
85
      m_lastCTCP(0),
86
      m_uNumCTCP(0),
87
      m_mISupport(),
88
      m_vSendQueue(),
89
      m_iSendsAllowed(pNetwork->GetFloodBurst()),
90
      m_uFloodBurst(pNetwork->GetFloodBurst()),
91
      m_fFloodRate(pNetwork->GetFloodRate()),
92
0
      m_bFloodProtection(IsFloodProtected(pNetwork->GetFloodRate())) {
93
0
    EnableReadLine();
94
0
    m_Nick.SetIdent(m_pNetwork->GetIdent());
95
0
    m_Nick.SetHost(m_pNetwork->GetBindHost());
96
0
    SetEncoding(m_pNetwork->GetEncoding());
97
98
0
    m_mceChanModes['b'] = ListArg;
99
0
    m_mceChanModes['e'] = ListArg;
100
0
    m_mceChanModes['I'] = ListArg;
101
0
    m_mceChanModes['k'] = HasArg;
102
0
    m_mceChanModes['l'] = ArgWhenSet;
103
0
    m_mceChanModes['p'] = NoArg;
104
0
    m_mceChanModes['s'] = NoArg;
105
0
    m_mceChanModes['t'] = NoArg;
106
0
    m_mceChanModes['i'] = NoArg;
107
0
    m_mceChanModes['n'] = NoArg;
108
109
0
    pNetwork->SetIRCSocket(this);
110
111
    // RFC says a line can have 512 chars max + 512 chars for message tags, but
112
    // we don't care ;)
113
0
    SetMaxBufferThreshold(2048);
114
0
    if (m_bFloodProtection) {
115
0
        AddCron(new CIRCFloodTimer(this));
116
0
    }
117
0
}
118
119
0
CIRCSock::~CIRCSock() {
120
0
    if (!m_bAuthed) {
121
0
        IRCSOCKMODULECALL(OnIRCConnectionError(this), NOTHING);
122
0
    }
123
124
0
    const vector<CChan*>& vChans = m_pNetwork->GetChans();
125
0
    for (CChan* pChan : vChans) {
126
0
        pChan->Reset();
127
0
    }
128
129
0
    m_pNetwork->IRCDisconnected();
130
131
0
    for (const auto& it : m_msChans) {
132
0
        delete it.second;
133
0
    }
134
135
0
    Quit();
136
0
    m_msChans.clear();
137
0
    m_pNetwork->AddBytesRead(GetBytesRead());
138
0
    m_pNetwork->AddBytesWritten(GetBytesWritten());
139
0
}
140
141
0
void CIRCSock::Quit(const CString& sQuitMsg) {
142
0
    if (IsClosed()) {
143
0
        return;
144
0
    }
145
0
    if (!m_bAuthed) {
146
0
        Close(CLT_NOW);
147
0
        return;
148
0
    }
149
0
    if (!sQuitMsg.empty()) {
150
0
        PutIRC("QUIT :" + sQuitMsg);
151
0
    } else {
152
0
        PutIRC("QUIT :" + m_pNetwork->ExpandString(m_pNetwork->GetQuitMsg()));
153
0
    }
154
0
    Close(CLT_AFTERWRITE);
155
0
}
156
157
0
void CIRCSock::ReadLine(const CString& sData) {
158
0
    CString sLine = sData;
159
160
0
    sLine.Replace("\n", "");
161
0
    sLine.Replace("\r", "");
162
163
0
    DEBUG("(" << m_pNetwork->GetUser()->GetUsername() << "/"
164
0
              << m_pNetwork->GetName() << ") IRC -> ZNC [" << sLine << "]");
165
166
0
    bool bReturn = false;
167
0
    IRCSOCKMODULECALL(OnRaw(sLine), &bReturn);
168
0
    if (bReturn) return;
169
170
0
    CMessage Message(sLine);
171
0
    Message.SetNetwork(m_pNetwork);
172
173
0
    IRCSOCKMODULECALL(OnRawMessage(Message), &bReturn);
174
0
    if (bReturn) return;
175
176
0
    switch (Message.GetType()) {
177
0
        case CMessage::Type::Account:
178
0
            bReturn = OnAccountMessage(Message);
179
0
            break;
180
0
        case CMessage::Type::Action:
181
0
            bReturn = OnActionMessage(Message);
182
0
            break;
183
0
        case CMessage::Type::Away:
184
0
            bReturn = OnAwayMessage(Message);
185
0
            break;
186
0
        case CMessage::Type::Capability:
187
0
            bReturn = OnCapabilityMessage(Message);
188
0
            break;
189
0
        case CMessage::Type::CTCP:
190
0
            bReturn = OnCTCPMessage(Message);
191
0
            break;
192
0
        case CMessage::Type::Error:
193
0
            bReturn = OnErrorMessage(Message);
194
0
            break;
195
0
        case CMessage::Type::Invite:
196
0
            bReturn = OnInviteMessage(Message);
197
0
            break;
198
0
        case CMessage::Type::Join:
199
0
            bReturn = OnJoinMessage(Message);
200
0
            break;
201
0
        case CMessage::Type::Kick:
202
0
            bReturn = OnKickMessage(Message);
203
0
            break;
204
0
        case CMessage::Type::Mode:
205
0
            bReturn = OnModeMessage(Message);
206
0
            break;
207
0
        case CMessage::Type::Nick:
208
0
            bReturn = OnNickMessage(Message);
209
0
            break;
210
0
        case CMessage::Type::Notice:
211
0
            bReturn = OnNoticeMessage(Message);
212
0
            break;
213
0
        case CMessage::Type::Numeric:
214
0
            bReturn = OnNumericMessage(Message);
215
0
            break;
216
0
        case CMessage::Type::Part:
217
0
            bReturn = OnPartMessage(Message);
218
0
            break;
219
0
        case CMessage::Type::Ping:
220
0
            bReturn = OnPingMessage(Message);
221
0
            break;
222
0
        case CMessage::Type::Pong:
223
0
            bReturn = OnPongMessage(Message);
224
0
            break;
225
0
        case CMessage::Type::Quit:
226
0
            bReturn = OnQuitMessage(Message);
227
0
            break;
228
0
        case CMessage::Type::Text:
229
0
            bReturn = OnTextMessage(Message);
230
0
            break;
231
0
        case CMessage::Type::Topic:
232
0
            bReturn = OnTopicMessage(Message);
233
0
            break;
234
0
        case CMessage::Type::Wallops:
235
0
            bReturn = OnWallopsMessage(Message);
236
0
            break;
237
0
        default:
238
0
            break;
239
0
    }
240
0
    if (bReturn) return;
241
242
0
    m_pNetwork->PutUser(Message);
243
0
}
244
245
0
void CIRCSock::SendNextCap() {
246
0
    if (!m_uCapPaused) {
247
0
        if (m_ssPendingCaps.empty()) {
248
            // We already got all needed ACK/NAK replies.
249
0
            PutIRC("CAP END");
250
0
        } else {
251
0
            CString sCap = *m_ssPendingCaps.begin();
252
0
            m_ssPendingCaps.erase(m_ssPendingCaps.begin());
253
0
            PutIRC("CAP REQ :" + sCap);
254
0
        }
255
0
    }
256
0
}
257
258
0
void CIRCSock::PauseCap() { ++m_uCapPaused; }
259
260
0
void CIRCSock::ResumeCap() {
261
0
    --m_uCapPaused;
262
0
    SendNextCap();
263
0
}
264
265
0
bool CIRCSock::OnServerCapAvailable(const CString& sCap) {
266
0
    bool bResult = false;
267
0
    IRCSOCKMODULECALL(OnServerCapAvailable(sCap), &bResult);
268
0
    return bResult;
269
0
}
270
271
// #124: OnChanMsg(): nick doesn't have perms
272
0
static void FixupChanNick(CNick& Nick, CChan* pChan) {
273
    // A channel nick has up-to-date channel perms, but might be
274
    // lacking (usernames-in-host) the associated ident & host.
275
    // An incoming message, on the other hand, has normally a full
276
    // nick!ident@host prefix. Sync the two so that channel nicks
277
    // get the potentially missing piece of info and module hooks
278
    // get the perms.
279
0
    CNick* pChanNick = pChan->FindNick(Nick.GetNick());
280
0
    if (pChanNick) {
281
0
        if (!Nick.GetIdent().empty()) {
282
0
            pChanNick->SetIdent(Nick.GetIdent());
283
0
        }
284
0
        if (!Nick.GetHost().empty()) {
285
0
            pChanNick->SetHost(Nick.GetHost());
286
0
        }
287
0
        Nick.Clone(*pChanNick);
288
0
    }
289
0
}
290
291
0
bool CIRCSock::OnAccountMessage(CMessage& Message) {
292
    // TODO: IRCSOCKMODULECALL(OnAccountMessage(Message)) ?
293
0
    return false;
294
0
}
295
296
0
bool CIRCSock::OnActionMessage(CActionMessage& Message) {
297
0
    bool bResult = false;
298
0
    CChan* pChan = nullptr;
299
0
    CString sTarget = Message.GetTarget();
300
0
    if (sTarget.Equals(GetNick())) {
301
0
        IRCSOCKMODULECALL(OnPrivCTCPMessage(Message), &bResult);
302
0
        if (bResult) return true;
303
0
        IRCSOCKMODULECALL(OnPrivActionMessage(Message), &bResult);
304
0
        if (bResult) return true;
305
306
0
        if (!m_pNetwork->IsUserOnline() ||
307
0
            !m_pNetwork->GetUser()->AutoClearQueryBuffer()) {
308
0
            const CNick& Nick = Message.GetNick();
309
0
            CQuery* pQuery = m_pNetwork->AddQuery(Nick.GetNick());
310
0
            if (pQuery) {
311
0
                CActionMessage Format;
312
0
                Format.Clone(Message);
313
0
                Format.SetNick(_NAMEDFMT(Nick.GetNickMask()));
314
0
                Format.SetTarget("{target}");
315
0
                Format.SetText("{text}");
316
0
                pQuery->AddBuffer(Format, Message.GetText());
317
0
            }
318
0
        }
319
0
    } else {
320
0
        pChan = m_pNetwork->FindChan(sTarget);
321
0
        if (pChan) {
322
0
            Message.SetChan(pChan);
323
0
            FixupChanNick(Message.GetNick(), pChan);
324
0
            IRCSOCKMODULECALL(OnChanCTCPMessage(Message), &bResult);
325
0
            if (bResult) return true;
326
0
            IRCSOCKMODULECALL(OnChanActionMessage(Message), &bResult);
327
0
            if (bResult) return true;
328
329
0
            if (!pChan->AutoClearChanBuffer() || !m_pNetwork->IsUserOnline() ||
330
0
                pChan->IsDetached()) {
331
0
                CActionMessage Format;
332
0
                Format.Clone(Message);
333
0
                Format.SetNick(_NAMEDFMT(Message.GetNick().GetNickMask()));
334
0
                Format.SetTarget(_NAMEDFMT(Message.GetTarget()));
335
0
                Format.SetText("{text}");
336
0
                pChan->AddBuffer(Format, Message.GetText());
337
0
            }
338
0
        }
339
0
    }
340
341
0
    return (pChan && pChan->IsDetached());
342
0
}
343
344
0
bool CIRCSock::OnAwayMessage(CMessage& Message) {
345
    // TODO: IRCSOCKMODULECALL(OnAwayMessage(Message)) ?
346
0
    return false;
347
0
}
348
349
0
bool CIRCSock::OnCapabilityMessage(CMessage& Message) {
350
    // CAPs are supported only before authorization.
351
0
    if (!m_bAuthed) {
352
        // The first parameter is most likely "*". No idea why, the
353
        // CAP spec don't mention this, but all implementations
354
        // I've seen add this extra asterisk
355
0
        CString sSubCmd = Message.GetParam(1);
356
357
        // If the caplist of a reply is too long, it's split
358
        // into multiple replies. A "*" is prepended to show
359
        // that the list was split into multiple replies.
360
        // This is useful mainly for LS. For ACK and NAK
361
        // replies, there's no real need for this, because
362
        // we request only 1 capability per line.
363
        // If we will need to support broken servers or will
364
        // send several requests per line, need to delay ACK
365
        // actions until all ACK lines are received and
366
        // to recognize past request of NAK by 100 chars
367
        // of this reply.
368
0
        CString sArgs;
369
0
        if (Message.GetParam(2) == "*") {
370
0
            sArgs = Message.GetParam(3);
371
0
        } else {
372
0
            sArgs = Message.GetParam(2);
373
0
        }
374
375
0
        std::map<CString, std::function<void(bool bVal)>> mSupportedCaps = {
376
0
            {"multi-prefix", [this](bool bVal) { m_bNamesx = bVal; }},
377
0
            {"userhost-in-names", [this](bool bVal) { m_bUHNames = bVal; }},
378
0
            {"away-notify", [this](bool bVal) { m_bAwayNotify = bVal; }},
379
0
            {"account-notify", [this](bool bVal) { m_bAccountNotify = bVal; }},
380
0
            {"account-tag", [this](bool bVal) { m_bAccountTag = bVal; }},
381
0
            {"extended-join", [this](bool bVal) { m_bExtendedJoin = bVal; }},
382
0
            {"server-time", [this](bool bVal) { m_bServerTime = bVal; }},
383
0
            {"znc.in/server-time-iso",
384
0
             [this](bool bVal) { m_bServerTime = bVal; }},
385
0
        };
386
387
0
        if (sSubCmd == "LS") {
388
0
            VCString vsTokens;
389
0
            sArgs.Split(" ", vsTokens, false);
390
391
0
            for (const CString& sCap : vsTokens) {
392
0
                if (OnServerCapAvailable(sCap) || mSupportedCaps.count(sCap)) {
393
0
                    m_ssPendingCaps.insert(sCap);
394
0
                }
395
0
            }
396
0
        } else if (sSubCmd == "ACK") {
397
0
            sArgs.Trim();
398
0
            IRCSOCKMODULECALL(OnServerCapResult(sArgs, true), NOTHING);
399
0
            const auto& it = mSupportedCaps.find(sArgs);
400
0
            if (it != mSupportedCaps.end()) {
401
0
                it->second(true);
402
0
            }
403
0
            m_ssAcceptedCaps.insert(sArgs);
404
0
        } else if (sSubCmd == "NAK") {
405
            // This should work because there's no [known]
406
            // capability with length of name more than 100 characters.
407
0
            sArgs.Trim();
408
0
            IRCSOCKMODULECALL(OnServerCapResult(sArgs, false), NOTHING);
409
0
        }
410
411
0
        SendNextCap();
412
0
    }
413
    // Don't forward any CAP stuff to the client
414
0
    return true;
415
0
}
416
417
0
bool CIRCSock::OnCTCPMessage(CCTCPMessage& Message) {
418
0
    bool bResult = false;
419
0
    CChan* pChan = nullptr;
420
0
    CString sTarget = Message.GetTarget();
421
0
    if (sTarget.Equals(GetNick())) {
422
0
        if (Message.IsReply()) {
423
0
            IRCSOCKMODULECALL(OnCTCPReplyMessage(Message), &bResult);
424
0
            return bResult;
425
0
        } else {
426
0
            IRCSOCKMODULECALL(OnPrivCTCPMessage(Message), &bResult);
427
0
            if (bResult) return true;
428
0
        }
429
0
    } else {
430
0
        pChan = m_pNetwork->FindChan(sTarget);
431
0
        if (pChan) {
432
0
            Message.SetChan(pChan);
433
0
            FixupChanNick(Message.GetNick(), pChan);
434
0
            if (Message.IsReply()) {
435
0
                IRCSOCKMODULECALL(OnCTCPReplyMessage(Message), &bResult);
436
0
                return bResult;
437
0
            } else {
438
0
                IRCSOCKMODULECALL(OnChanCTCPMessage(Message), &bResult);
439
0
            }
440
0
            if (bResult) return true;
441
0
        }
442
0
    }
443
444
0
    const CNick& Nick = Message.GetNick();
445
0
    const CString& sMessage = Message.GetText();
446
0
    const MCString& mssCTCPReplies = m_pNetwork->GetUser()->GetCTCPReplies();
447
0
    CString sQuery = sMessage.Token(0).AsUpper();
448
0
    MCString::const_iterator it = mssCTCPReplies.find(sQuery);
449
0
    bool bHaveReply = false;
450
0
    CString sReply;
451
452
0
    if (it != mssCTCPReplies.end()) {
453
0
        sReply = m_pNetwork->ExpandString(it->second);
454
0
        bHaveReply = true;
455
456
0
        if (sReply.empty()) {
457
0
            return true;
458
0
        }
459
0
    }
460
461
0
    if (!bHaveReply && !m_pNetwork->IsUserAttached()) {
462
0
        if (sQuery == "VERSION") {
463
0
            sReply = CZNC::GetTag(false);
464
0
        } else if (sQuery == "PING") {
465
0
            sReply = sMessage.Token(1, true);
466
0
        }
467
0
    }
468
469
0
    if (!sReply.empty()) {
470
0
        time_t now = time(nullptr);
471
        // If the last CTCP is older than m_uCTCPFloodTime, reset the counter
472
0
        if (m_lastCTCP + m_uCTCPFloodTime < now) m_uNumCTCP = 0;
473
0
        m_lastCTCP = now;
474
        // If we are over the limit, don't reply to this CTCP
475
0
        if (m_uNumCTCP >= m_uCTCPFloodCount) {
476
0
            DEBUG("CTCP flood detected - not replying to query");
477
0
            return true;
478
0
        }
479
0
        m_uNumCTCP++;
480
481
0
        PutIRC("NOTICE " + Nick.GetNick() + " :\001" + sQuery + " " + sReply +
482
0
               "\001");
483
0
        return true;
484
0
    }
485
486
0
    return (pChan && pChan->IsDetached());
487
0
}
488
489
0
bool CIRCSock::OnErrorMessage(CMessage& Message) {
490
    // ERROR :Closing Link: nick[24.24.24.24] (Excess Flood)
491
0
    CString sError = Message.GetParam(0);
492
0
    m_pNetwork->PutStatus(t_f("Error from server: {1}")(sError));
493
0
    return true;
494
0
}
495
496
0
bool CIRCSock::OnInviteMessage(CMessage& Message) {
497
0
    bool bResult = false;
498
0
    IRCSOCKMODULECALL(OnInvite(Message.GetNick(), Message.GetParam(1)),
499
0
                      &bResult);
500
0
    return bResult;
501
0
}
502
503
0
bool CIRCSock::OnJoinMessage(CJoinMessage& Message) {
504
0
    const CNick& Nick = Message.GetNick();
505
0
    CString sChan = Message.GetParam(0);
506
0
    CChan* pChan = nullptr;
507
508
0
    if (Nick.NickEquals(GetNick())) {
509
0
        m_pNetwork->AddChan(sChan, false);
510
0
        pChan = m_pNetwork->FindChan(sChan);
511
0
        if (pChan) {
512
0
            pChan->Enable();
513
0
            pChan->SetIsOn(true);
514
0
            PutIRC("MODE " + sChan);
515
0
        }
516
0
    } else {
517
0
        pChan = m_pNetwork->FindChan(sChan);
518
0
    }
519
520
0
    if (pChan) {
521
0
        pChan->AddNick(Nick.GetNickMask());
522
0
        Message.SetChan(pChan);
523
0
        IRCSOCKMODULECALL(OnJoinMessage(Message), NOTHING);
524
525
0
        if (pChan->IsDetached()) {
526
0
            return true;
527
0
        }
528
0
    }
529
530
0
    return false;
531
0
}
532
533
0
bool CIRCSock::OnKickMessage(CKickMessage& Message) {
534
0
    CString sChan = Message.GetParam(0);
535
0
    CString sKickedNick = Message.GetKickedNick();
536
537
0
    CChan* pChan = m_pNetwork->FindChan(sChan);
538
539
0
    if (pChan) {
540
0
        Message.SetChan(pChan);
541
0
        IRCSOCKMODULECALL(OnKickMessage(Message), NOTHING);
542
        // do not remove the nick till after the OnKick call, so modules
543
        // can do Chan.FindNick or something to get more info.
544
0
        pChan->RemNick(sKickedNick);
545
0
    }
546
547
0
    if (GetNick().Equals(sKickedNick) && pChan) {
548
0
        pChan->SetIsOn(false);
549
550
        // Don't try to rejoin!
551
0
        pChan->Disable();
552
0
    }
553
554
0
    return (pChan && pChan->IsDetached());
555
0
}
556
557
0
bool CIRCSock::OnModeMessage(CModeMessage& Message) {
558
0
    const CNick& Nick = Message.GetNick();
559
0
    CString sTarget = Message.GetTarget();
560
0
    VCString vsModes = Message.GetModeParams();
561
0
    CString sModes = Message.GetModeList();
562
563
0
    CChan* pChan = m_pNetwork->FindChan(sTarget);
564
0
    if (pChan) {
565
0
        pChan->ModeChange(sModes, vsModes, &Nick);
566
567
0
        if (pChan->IsDetached()) {
568
0
            return true;
569
0
        }
570
0
    } else if (sTarget == m_Nick.GetNick()) {
571
0
        bool bAdd = true;
572
        /* no module call defined (yet?)
573
                MODULECALL(OnRawUserMode(*pOpNick, *this, sModeArg, sArgs),
574
           m_pNetwork->GetUser(), nullptr, );
575
        */
576
0
        for (unsigned int a = 0; a < sModes.size(); a++) {
577
0
            const char& cMode = sModes[a];
578
579
0
            if (cMode == '+') {
580
0
                bAdd = true;
581
0
            } else if (cMode == '-') {
582
0
                bAdd = false;
583
0
            } else {
584
0
                if (bAdd) {
585
0
                    m_scUserModes.insert(cMode);
586
0
                } else {
587
0
                    m_scUserModes.erase(cMode);
588
0
                }
589
0
            }
590
0
        }
591
0
    }
592
0
    return false;
593
0
}
594
595
0
bool CIRCSock::OnNickMessage(CNickMessage& Message) {
596
0
    const CNick& Nick = Message.GetNick();
597
0
    CString sNewNick = Message.GetNewNick();
598
0
    bool bIsVisible = false;
599
600
0
    vector<CChan*> vFoundChans;
601
0
    const vector<CChan*>& vChans = m_pNetwork->GetChans();
602
603
0
    for (CChan* pChan : vChans) {
604
0
        if (pChan->ChangeNick(Nick.GetNick(), sNewNick)) {
605
0
            vFoundChans.push_back(pChan);
606
607
0
            if (!pChan->IsDetached()) {
608
0
                bIsVisible = true;
609
0
            }
610
0
        }
611
0
    }
612
613
0
    if (Nick.NickEquals(GetNick())) {
614
        // We are changing our own nick, the clients always must see this!
615
0
        bIsVisible = false;
616
0
        SetNick(sNewNick);
617
0
        m_pNetwork->PutUser(Message);
618
0
    }
619
620
0
    IRCSOCKMODULECALL(OnNickMessage(Message, vFoundChans), NOTHING);
621
622
0
    return !bIsVisible;
623
0
}
624
625
0
bool CIRCSock::OnNoticeMessage(CNoticeMessage& Message) {
626
0
    CString sTarget = Message.GetTarget();
627
0
    bool bResult = false;
628
629
0
    if (sTarget.Equals(GetNick())) {
630
0
        IRCSOCKMODULECALL(OnPrivNoticeMessage(Message), &bResult);
631
0
        if (bResult) return true;
632
633
0
        if (!m_pNetwork->IsUserOnline()) {
634
            // If the user is detached, add to the buffer
635
0
            CNoticeMessage Format;
636
0
            Format.Clone(Message);
637
0
            Format.SetNick(CNick(_NAMEDFMT(Message.GetNick().GetNickMask())));
638
0
            Format.SetTarget("{target}");
639
0
            Format.SetText("{text}");
640
0
            m_pNetwork->AddNoticeBuffer(Format, Message.GetText());
641
0
        }
642
643
0
        return false;
644
0
    } else {
645
0
        CChan* pChan = m_pNetwork->FindChan(sTarget);
646
0
        if (pChan) {
647
0
            Message.SetChan(pChan);
648
0
            FixupChanNick(Message.GetNick(), pChan);
649
0
            IRCSOCKMODULECALL(OnChanNoticeMessage(Message), &bResult);
650
0
            if (bResult) return true;
651
652
0
            if (!pChan->AutoClearChanBuffer() || !m_pNetwork->IsUserOnline() ||
653
0
                pChan->IsDetached()) {
654
0
                CNoticeMessage Format;
655
0
                Format.Clone(Message);
656
0
                Format.SetNick(_NAMEDFMT(Message.GetNick().GetNickMask()));
657
0
                Format.SetTarget(_NAMEDFMT(Message.GetTarget()));
658
0
                Format.SetText("{text}");
659
0
                pChan->AddBuffer(Format, Message.GetText());
660
0
            }
661
0
        }
662
663
0
        return (pChan && pChan->IsDetached());
664
0
    }
665
0
}
666
667
0
static CMessage BufferMessage(const CNumericMessage& Message) {
668
0
    CMessage Format(Message);
669
0
    Format.SetNick(CNick(_NAMEDFMT(Message.GetNick().GetHostMask())));
670
0
    Format.SetParam(0, "{target}");
671
0
    unsigned uParams = Format.GetParams().size();
672
0
    for (unsigned int i = 1; i < uParams; ++i) {
673
0
        Format.SetParam(i, _NAMEDFMT(Format.GetParam(i)));
674
0
    }
675
0
    return Format;
676
0
}
677
678
0
bool CIRCSock::OnNumericMessage(CNumericMessage& Message) {
679
0
    const CString& sCmd = Message.GetCommand();
680
0
    CString sServer = Message.GetNick().GetHostMask();
681
0
    unsigned int uRaw = Message.GetCode();
682
0
    CString sNick = Message.GetParam(0);
683
684
0
    bool bResult = false;
685
0
    IRCSOCKMODULECALL(OnNumericMessage(Message), &bResult);
686
0
    if (bResult) return true;
687
688
0
    switch (uRaw) {
689
0
        case 1: {  // :irc.server.com 001 nick :Welcome to the Internet Relay
690
0
            if (m_bAuthed && sServer == "irc.znc.in") {
691
                // m_bAuthed == true => we already received another 001 => we
692
                // might be in a traffic loop
693
0
                m_pNetwork->PutStatus(t_s(
694
0
                    "ZNC seems to be connected to itself, disconnecting..."));
695
0
                Quit();
696
0
                return true;
697
0
            }
698
699
0
            m_pNetwork->SetIRCServer(sServer);
700
            // Now that we are connected, let nature take its course
701
0
            SetTimeout(m_pNetwork->GetUser()->GetNoTrafficTimeout(), TMO_READ);
702
0
            PutIRC("WHO " + sNick);
703
704
0
            m_bAuthed = true;
705
706
0
            const vector<CClient*>& vClients = m_pNetwork->GetClients();
707
708
0
            for (CClient* pClient : vClients) {
709
0
                CString sClientNick = pClient->GetNick(false);
710
711
0
                if (!sClientNick.Equals(sNick)) {
712
                    // If they connected with a nick that doesn't match the one
713
                    // we got on irc, then we need to update them
714
0
                    pClient->PutClient(":" + sClientNick + "!" +
715
0
                                       m_Nick.GetIdent() + "@" +
716
0
                                       m_Nick.GetHost() + " NICK :" + sNick);
717
0
                }
718
0
            }
719
720
0
            SetNick(sNick);
721
722
0
            m_pNetwork->PutStatus("Connected!");
723
0
            IRCSOCKMODULECALL(OnIRCConnected(), NOTHING);
724
725
0
            m_pNetwork->ClearRawBuffer();
726
0
            m_pNetwork->AddRawBuffer(BufferMessage(Message));
727
728
0
            m_pNetwork->IRCConnected();
729
730
0
            break;
731
0
        }
732
0
        case 5:
733
0
            ParseISupport(Message);
734
0
            m_pNetwork->UpdateExactRawBuffer(BufferMessage(Message));
735
0
            break;
736
0
        case 10: {  // :irc.server.com 010 nick <hostname> <port> :<info>
737
0
            CString sHost = Message.GetParam(1);
738
0
            CString sPort = Message.GetParam(2);
739
0
            CString sInfo = Message.GetParam(3);
740
0
            m_pNetwork->PutStatus(
741
0
                t_f("Server {1} redirects us to {2}:{3} with reason: {4}")(
742
0
                    m_pNetwork->GetCurrentServer()->GetString(false), sHost,
743
0
                    sPort, sInfo));
744
0
            m_pNetwork->PutStatus(
745
0
                t_s("Perhaps you want to add it as a new server."));
746
            // Don't send server redirects to the client
747
0
            return true;
748
0
        }
749
0
        case 2:
750
0
        case 3:
751
0
        case 4:
752
0
        case 250:  // highest connection count
753
0
        case 251:  // user count
754
0
        case 252:  // oper count
755
0
        case 254:  // channel count
756
0
        case 255:  // client count
757
0
        case 265:  // local users
758
0
        case 266:  // global users
759
0
            m_pNetwork->UpdateRawBuffer(sCmd, BufferMessage(Message));
760
0
            break;
761
0
        case 305:
762
0
            m_pNetwork->SetIRCAway(false);
763
0
            break;
764
0
        case 306:
765
0
            m_pNetwork->SetIRCAway(true);
766
0
            break;
767
0
        case 324: {  // MODE
768
            // :irc.server.com 324 nick #chan +nstk key
769
0
            CChan* pChan = m_pNetwork->FindChan(Message.GetParam(1));
770
771
0
            if (pChan) {
772
0
                pChan->SetModes(Message.GetParam(2), Message.GetParamsSplit(3));
773
774
                // We don't SetModeKnown(true) here,
775
                // because a 329 will follow
776
0
                if (!pChan->IsModeKnown()) {
777
                    // When we JOIN, we send a MODE
778
                    // request. This makes sure the
779
                    // reply isn't forwarded.
780
0
                    return true;
781
0
                }
782
0
                if (pChan->IsDetached()) {
783
0
                    return true;
784
0
                }
785
0
            }
786
0
        } break;
787
0
        case 329: {
788
            // :irc.server.com 329 nick #chan 1234567890
789
0
            CChan* pChan = m_pNetwork->FindChan(Message.GetParam(1));
790
791
0
            if (pChan) {
792
0
                unsigned long ulDate = Message.GetParam(2).ToULong();
793
0
                pChan->SetCreationDate(ulDate);
794
795
0
                if (!pChan->IsModeKnown()) {
796
0
                    pChan->SetModeKnown(true);
797
                    // When we JOIN, we send a MODE
798
                    // request. This makes sure the
799
                    // reply isn't forwarded.
800
0
                    return true;
801
0
                }
802
0
                if (pChan->IsDetached()) {
803
0
                    return true;
804
0
                }
805
0
            }
806
0
        } break;
807
0
        case 331: {
808
            // :irc.server.com 331 yournick #chan :No topic is set.
809
0
            CChan* pChan = m_pNetwork->FindChan(Message.GetParam(1));
810
811
0
            if (pChan) {
812
0
                pChan->SetTopic("");
813
0
                if (pChan->IsDetached()) {
814
0
                    return true;
815
0
                }
816
0
            }
817
818
0
            break;
819
0
        }
820
0
        case 332: {
821
            // :irc.server.com 332 yournick #chan :This is a topic
822
0
            CChan* pChan = m_pNetwork->FindChan(Message.GetParam(1));
823
824
0
            if (pChan) {
825
0
                CString sTopic = Message.GetParam(2);
826
0
                pChan->SetTopic(sTopic);
827
0
                if (pChan->IsDetached()) {
828
0
                    return true;
829
0
                }
830
0
            }
831
832
0
            break;
833
0
        }
834
0
        case 333: {
835
            // :irc.server.com 333 yournick #chan setternick 1112320796
836
0
            CChan* pChan = m_pNetwork->FindChan(Message.GetParam(1));
837
838
0
            if (pChan) {
839
0
                sNick = Message.GetParam(2);
840
0
                unsigned long ulDate = Message.GetParam(3).ToULong();
841
842
0
                pChan->SetTopicOwner(sNick);
843
0
                pChan->SetTopicDate(ulDate);
844
845
0
                if (pChan->IsDetached()) {
846
0
                    return true;
847
0
                }
848
0
            }
849
850
0
            break;
851
0
        }
852
0
        case 352: {  // WHO
853
            // :irc.yourserver.com 352 yournick #chan ident theirhost.com irc.theirserver.com theirnick H :0 Real Name
854
0
            sNick = Message.GetParam(5);
855
0
            CString sChan = Message.GetParam(1);
856
0
            CString sIdent = Message.GetParam(2);
857
0
            CString sHost = Message.GetParam(3);
858
859
0
            if (sNick.Equals(GetNick())) {
860
0
                m_Nick.SetIdent(sIdent);
861
0
                m_Nick.SetHost(sHost);
862
0
            }
863
864
0
            m_pNetwork->SetIRCNick(m_Nick);
865
0
            m_pNetwork->SetIRCServer(sServer);
866
867
            // A nick can only have one ident and hostname. Yes, you can query
868
            // this information per-channel, but it is still global. For
869
            // example, if the client supports UHNAMES, but the IRC server does
870
            // not, then AFAIR "passive snooping of WHO replies" is the only way
871
            // that ZNC can figure out the ident and host for the UHNAMES
872
            // replies.
873
0
            const vector<CChan*>& vChans = m_pNetwork->GetChans();
874
875
0
            for (CChan* pChan : vChans) {
876
0
                pChan->OnWho(sNick, sIdent, sHost);
877
0
            }
878
879
0
            CChan* pChan = m_pNetwork->FindChan(sChan);
880
0
            if (pChan && pChan->IsDetached()) {
881
0
                return true;
882
0
            }
883
884
0
            break;
885
0
        }
886
0
        case 353: {  // NAMES
887
            // :irc.server.com 353 nick @ #chan :nick1 nick2
888
            // Todo: allow for non @+= server msgs
889
0
            CChan* pChan = m_pNetwork->FindChan(Message.GetParam(2));
890
            // If we don't know that channel, some client might have
891
            // requested a /names for it and we really should forward this.
892
0
            if (pChan) {
893
0
                CString sNicks = Message.GetParam(3);
894
0
                pChan->AddNicks(sNicks);
895
0
                if (pChan->IsDetached()) {
896
0
                    return true;
897
0
                }
898
0
            }
899
900
0
            break;
901
0
        }
902
0
        case 366: {  // end of names list
903
            // :irc.server.com 366 nick #chan :End of /NAMES list.
904
0
            CChan* pChan = m_pNetwork->FindChan(Message.GetParam(1));
905
906
0
            if (pChan) {
907
0
                if (pChan->IsOn()) {
908
                    // If we are the only one in the chan, set our default modes
909
0
                    if (pChan->GetNickCount() == 1) {
910
0
                        CString sModes = pChan->GetDefaultModes();
911
912
0
                        if (sModes.empty()) {
913
0
                            sModes =
914
0
                                m_pNetwork->GetUser()->GetDefaultChanModes();
915
0
                        }
916
917
0
                        if (!sModes.empty()) {
918
0
                            PutIRC("MODE " + pChan->GetName() + " " + sModes);
919
0
                        }
920
0
                    }
921
0
                }
922
0
                if (pChan->IsDetached()) {
923
                    // don't put it to clients
924
0
                    return true;
925
0
                }
926
0
            }
927
928
0
            break;
929
0
        }
930
0
        case 375:  // begin motd
931
0
        case 422:  // MOTD File is missing
932
0
            if (m_pNetwork->GetIRCServer().Equals(sServer)) {
933
0
                m_pNetwork->ClearMotdBuffer();
934
0
            }
935
0
        case 372:  // motd
936
0
        case 376:  // end motd
937
0
            if (m_pNetwork->GetIRCServer().Equals(sServer)) {
938
0
                m_pNetwork->AddMotdBuffer(BufferMessage(Message));
939
0
            }
940
0
            break;
941
0
        case 437:
942
            // :irc.server.net 437 * badnick :Nick/channel is temporarily unavailable
943
            // :irc.server.net 437 mynick badnick :Nick/channel is temporarily unavailable
944
            // :irc.server.net 437 mynick badnick :Cannot change nickname while banned on channel
945
0
            if (m_pNetwork->IsChan(Message.GetParam(1)) || sNick != "*") break;
946
0
        case 432:
947
        // :irc.server.com 432 * nick :Erroneous Nickname: Illegal chars
948
0
        case 433: {
949
0
            CString sBadNick = Message.GetParam(1);
950
951
0
            if (!m_bAuthed) {
952
0
                SendAltNick(sBadNick);
953
0
                return true;
954
0
            }
955
0
            break;
956
0
        }
957
0
        case 451:
958
            // :irc.server.com 451 CAP :You have not registered
959
            // Servers that don't support CAP will give us this error, don't send
960
            // it to the client
961
0
            if (sNick.Equals("CAP")) return true;
962
0
        case 470: {
963
            // :irc.unreal.net 470 mynick [Link] #chan1 has become full, so you are automatically being transferred to the linked channel #chan2
964
            // :mccaffrey.freenode.net 470 mynick #electronics ##electronics :Forwarding to another channel
965
966
            // freenode style numeric
967
0
            CChan* pChan = m_pNetwork->FindChan(Message.GetParam(1));
968
0
            if (!pChan) {
969
                // unreal style numeric
970
0
                pChan = m_pNetwork->FindChan(Message.GetParam(2));
971
0
            }
972
0
            if (pChan) {
973
0
                pChan->Disable();
974
0
                m_pNetwork->PutStatus(
975
0
                    t_f("Channel {1} is linked to another channel and was thus "
976
0
                        "disabled.")(pChan->GetName()));
977
0
            }
978
0
            break;
979
0
        }
980
0
        case 670:
981
            // :hydra.sector5d.org 670 kylef :STARTTLS successful, go ahead with TLS handshake
982
            //
983
            // 670 is a response to `STARTTLS` telling the client to switch to
984
            // TLS
985
0
            if (!GetSSL()) {
986
0
                StartTLS();
987
0
                m_pNetwork->PutStatus(t_s("Switched to SSL (STARTTLS)"));
988
0
            }
989
990
0
            return true;
991
0
    }
992
993
0
    return false;
994
0
}
995
996
0
bool CIRCSock::OnPartMessage(CPartMessage& Message) {
997
0
    const CNick& Nick = Message.GetNick();
998
0
    CString sChan = Message.GetTarget();
999
1000
0
    CChan* pChan = m_pNetwork->FindChan(sChan);
1001
0
    bool bDetached = false;
1002
0
    if (pChan) {
1003
0
        pChan->RemNick(Nick.GetNick());
1004
0
        Message.SetChan(pChan);
1005
0
        IRCSOCKMODULECALL(OnPartMessage(Message), NOTHING);
1006
1007
0
        if (pChan->IsDetached()) bDetached = true;
1008
0
    }
1009
1010
0
    if (Nick.NickEquals(GetNick())) {
1011
0
        m_pNetwork->DelChan(sChan);
1012
0
    }
1013
1014
    /*
1015
     * We use this boolean because
1016
     * m_pNetwork->DelChan() will delete this channel
1017
     * and thus we would dereference an
1018
     * already-freed pointer!
1019
     */
1020
0
    return bDetached;
1021
0
}
1022
1023
0
bool CIRCSock::OnPingMessage(CMessage& Message) {
1024
    // Generate a reply and don't forward this to any user,
1025
    // we don't want any PING forwarded
1026
0
    PutIRCQuick("PONG " + Message.GetParam(0));
1027
0
    return true;
1028
0
}
1029
1030
0
bool CIRCSock::OnPongMessage(CMessage& Message) {
1031
    // Block PONGs, we already responded to the pings
1032
0
    return true;
1033
0
}
1034
1035
0
bool CIRCSock::OnQuitMessage(CQuitMessage& Message) {
1036
0
    const CNick& Nick = Message.GetNick();
1037
0
    bool bIsVisible = false;
1038
1039
0
    if (Nick.NickEquals(GetNick())) {
1040
0
        m_pNetwork->PutStatus(t_f("You quit: {1}")(Message.GetReason()));
1041
        // We don't call module hooks and we don't
1042
        // forward this quit to clients (Some clients
1043
        // disconnect if they receive such a QUIT)
1044
0
        return true;
1045
0
    }
1046
1047
0
    vector<CChan*> vFoundChans;
1048
0
    const vector<CChan*>& vChans = m_pNetwork->GetChans();
1049
1050
0
    for (CChan* pChan : vChans) {
1051
0
        if (pChan->RemNick(Nick.GetNick())) {
1052
0
            vFoundChans.push_back(pChan);
1053
1054
0
            if (!pChan->IsDetached()) {
1055
0
                bIsVisible = true;
1056
0
            }
1057
0
        }
1058
0
    }
1059
1060
0
    IRCSOCKMODULECALL(OnQuitMessage(Message, vFoundChans), NOTHING);
1061
1062
0
    return !bIsVisible;
1063
0
}
1064
1065
0
bool CIRCSock::OnTextMessage(CTextMessage& Message) {
1066
0
    bool bResult = false;
1067
0
    CChan* pChan = nullptr;
1068
0
    CString sTarget = Message.GetTarget();
1069
1070
0
    if (sTarget.Equals(GetNick())) {
1071
0
        IRCSOCKMODULECALL(OnPrivTextMessage(Message), &bResult);
1072
0
        if (bResult) return true;
1073
1074
0
        if (!m_pNetwork->IsUserOnline() ||
1075
0
            !m_pNetwork->GetUser()->AutoClearQueryBuffer()) {
1076
0
            const CNick& Nick = Message.GetNick();
1077
0
            CQuery* pQuery = m_pNetwork->AddQuery(Nick.GetNick());
1078
0
            if (pQuery) {
1079
0
                CTextMessage Format;
1080
0
                Format.Clone(Message);
1081
0
                Format.SetNick(_NAMEDFMT(Nick.GetNickMask()));
1082
0
                Format.SetTarget("{target}");
1083
0
                Format.SetText("{text}");
1084
0
                pQuery->AddBuffer(Format, Message.GetText());
1085
0
            }
1086
0
        }
1087
0
    } else {
1088
0
        pChan = m_pNetwork->FindChan(sTarget);
1089
0
        if (pChan) {
1090
0
            Message.SetChan(pChan);
1091
0
            FixupChanNick(Message.GetNick(), pChan);
1092
0
            IRCSOCKMODULECALL(OnChanTextMessage(Message), &bResult);
1093
0
            if (bResult) return true;
1094
1095
0
            if (!pChan->AutoClearChanBuffer() || !m_pNetwork->IsUserOnline() ||
1096
0
                pChan->IsDetached()) {
1097
0
                CTextMessage Format;
1098
0
                Format.Clone(Message);
1099
0
                Format.SetNick(_NAMEDFMT(Message.GetNick().GetNickMask()));
1100
0
                Format.SetTarget(_NAMEDFMT(Message.GetTarget()));
1101
0
                Format.SetText("{text}");
1102
0
                pChan->AddBuffer(Format, Message.GetText());
1103
0
            }
1104
0
        }
1105
0
    }
1106
1107
0
    return (pChan && pChan->IsDetached());
1108
0
}
1109
1110
0
bool CIRCSock::OnTopicMessage(CTopicMessage& Message) {
1111
0
    const CNick& Nick = Message.GetNick();
1112
0
    CChan* pChan = m_pNetwork->FindChan(Message.GetParam(0));
1113
1114
0
    if (pChan) {
1115
0
        Message.SetChan(pChan);
1116
0
        bool bReturn = false;
1117
0
        IRCSOCKMODULECALL(OnTopicMessage(Message), &bReturn);
1118
0
        if (bReturn) return true;
1119
1120
0
        pChan->SetTopicOwner(Nick.GetNick());
1121
0
        pChan->SetTopicDate((unsigned long)time(nullptr));
1122
0
        pChan->SetTopic(Message.GetTopic());
1123
0
    }
1124
1125
0
    return (pChan && pChan->IsDetached());
1126
0
}
1127
1128
0
bool CIRCSock::OnWallopsMessage(CMessage& Message) {
1129
    // :blub!dummy@rox-8DBEFE92 WALLOPS :this is a test
1130
0
    CString sMsg = Message.GetParam(0);
1131
1132
0
    if (!m_pNetwork->IsUserOnline()) {
1133
0
        CMessage Format(Message);
1134
0
        Format.SetNick(CNick(_NAMEDFMT(Message.GetNick().GetHostMask())));
1135
0
        Format.SetParam(0, "{text}");
1136
0
        m_pNetwork->AddNoticeBuffer(Format, sMsg);
1137
0
    }
1138
0
    return false;
1139
0
}
1140
1141
0
void CIRCSock::PutIRC(const CString& sLine) {
1142
0
    PutIRC(CMessage(sLine));
1143
0
}
1144
1145
0
void CIRCSock::PutIRC(const CMessage& Message) {
1146
    // Only print if the line won't get sent immediately (same condition as in
1147
    // TrySend()!)
1148
0
    if (m_bFloodProtection && m_iSendsAllowed <= 0) {
1149
0
        DEBUG("(" << m_pNetwork->GetUser()->GetUsername() << "/"
1150
0
                  << m_pNetwork->GetName() << ") ZNC -> IRC ["
1151
0
                  << CDebug::Filter(Message.ToString()) << "] (queued)");
1152
0
    }
1153
0
    m_vSendQueue.push_back(Message);
1154
0
    TrySend();
1155
0
}
1156
1157
0
void CIRCSock::PutIRCQuick(const CString& sLine) {
1158
    // Only print if the line won't get sent immediately (same condition as in
1159
    // TrySend()!)
1160
0
    if (m_bFloodProtection && m_iSendsAllowed <= 0) {
1161
0
        DEBUG("(" << m_pNetwork->GetUser()->GetUsername() << "/"
1162
0
                  << m_pNetwork->GetName() << ") ZNC -> IRC ["
1163
0
                  << CDebug::Filter(sLine) << "] (queued to front)");
1164
0
    }
1165
0
    m_vSendQueue.emplace_front(sLine);
1166
0
    TrySend();
1167
0
}
1168
1169
0
void CIRCSock::TrySend() {
1170
    // This condition must be the same as in PutIRC() and PutIRCQuick()!
1171
0
    while (!m_vSendQueue.empty() &&
1172
0
           (!m_bFloodProtection || m_iSendsAllowed > 0)) {
1173
0
        m_iSendsAllowed--;
1174
0
        CMessage& Message = m_vSendQueue.front();
1175
1176
0
        MCString mssTags;
1177
0
        for (const auto& it : Message.GetTags()) {
1178
0
            if (IsTagEnabled(it.first)) {
1179
0
                mssTags[it.first] = it.second;
1180
0
            }
1181
0
        }
1182
0
        Message.SetTags(mssTags);
1183
0
        Message.SetNetwork(m_pNetwork);
1184
1185
0
        bool bSkip = false;
1186
0
        IRCSOCKMODULECALL(OnSendToIRCMessage(Message), &bSkip);
1187
1188
0
        if (!bSkip) {
1189
0
            PutIRCRaw(Message.ToString());
1190
0
        }
1191
0
        m_vSendQueue.pop_front();
1192
0
    }
1193
0
}
1194
1195
0
void CIRCSock::PutIRCRaw(const CString& sLine) {
1196
0
    CString sCopy = sLine;
1197
0
    bool bSkip = false;
1198
0
    IRCSOCKMODULECALL(OnSendToIRC(sCopy), &bSkip);
1199
0
    if (!bSkip) {
1200
0
        DEBUG("(" << m_pNetwork->GetUser()->GetUsername() << "/"
1201
0
                  << m_pNetwork->GetName() << ") ZNC -> IRC ["
1202
0
                  << CDebug::Filter(sCopy) << "]");
1203
0
        Write(sCopy + "\r\n");
1204
0
    }
1205
0
}
1206
1207
0
void CIRCSock::SetNick(const CString& sNick) {
1208
0
    m_Nick.SetNick(sNick);
1209
0
    m_pNetwork->SetIRCNick(m_Nick);
1210
0
}
1211
1212
0
void CIRCSock::Connected() {
1213
0
    DEBUG(GetSockName() << " == Connected()");
1214
1215
0
    CString sPass = m_sPass;
1216
0
    CString sNick = m_pNetwork->GetNick();
1217
0
    CString sIdent = m_pNetwork->GetIdent();
1218
0
    CString sRealName = m_pNetwork->GetRealName();
1219
1220
0
    bool bReturn = false;
1221
0
    IRCSOCKMODULECALL(OnIRCRegistration(sPass, sNick, sIdent, sRealName),
1222
0
                      &bReturn);
1223
0
    if (bReturn) return;
1224
1225
0
    PutIRC("CAP LS");
1226
1227
0
    if (!sPass.empty()) {
1228
0
        PutIRC("PASS " + sPass);
1229
0
    }
1230
1231
0
    PutIRC("NICK " + sNick);
1232
0
    PutIRC("USER " + sIdent + " \"" + sIdent + "\" \"" + sIdent + "\" :" +
1233
0
           sRealName);
1234
1235
    // SendAltNick() needs this
1236
0
    m_Nick.SetNick(sNick);
1237
0
}
1238
1239
0
void CIRCSock::Disconnected() {
1240
0
    IRCSOCKMODULECALL(OnIRCDisconnected(), NOTHING);
1241
1242
0
    DEBUG(GetSockName() << " == Disconnected()");
1243
0
    if (!m_pNetwork->GetUser()->IsBeingDeleted() &&
1244
0
        m_pNetwork->GetIRCConnectEnabled() &&
1245
0
        m_pNetwork->GetServers().size() != 0) {
1246
0
        m_pNetwork->PutStatus(t_s("Disconnected from IRC. Reconnecting..."));
1247
0
    }
1248
0
    m_pNetwork->ClearRawBuffer();
1249
0
    m_pNetwork->ClearMotdBuffer();
1250
1251
0
    ResetChans();
1252
1253
    // send a "reset user modes" cmd to the client.
1254
    // otherwise, on reconnect, it might think it still
1255
    // had user modes that it actually doesn't have.
1256
0
    CString sUserMode;
1257
0
    for (char cMode : m_scUserModes) {
1258
0
        sUserMode += cMode;
1259
0
    }
1260
0
    if (!sUserMode.empty()) {
1261
0
        m_pNetwork->PutUser(":" + m_pNetwork->GetIRCNick().GetNickMask() +
1262
0
                            " MODE " + m_pNetwork->GetIRCNick().GetNick() +
1263
0
                            " :-" + sUserMode);
1264
0
    }
1265
1266
    // also clear the user modes in our space:
1267
0
    m_scUserModes.clear();
1268
0
}
1269
1270
0
void CIRCSock::SockError(int iErrno, const CString& sDescription) {
1271
0
    CString sError = sDescription;
1272
1273
0
    DEBUG(GetSockName() << " == SockError(" << iErrno << " " << sError << ")");
1274
0
    if (!m_pNetwork->GetUser()->IsBeingDeleted()) {
1275
0
        if (GetConState() != CST_OK) {
1276
0
            m_pNetwork->PutStatus(
1277
0
                t_f("Cannot connect to IRC ({1}). Retrying...")(sError));
1278
0
        } else {
1279
0
            m_pNetwork->PutStatus(
1280
0
                t_f("Disconnected from IRC ({1}). Reconnecting...")(sError));
1281
0
        }
1282
0
    }
1283
0
    for (const CString& s : m_vsSSLError) {
1284
0
        m_pNetwork->PutStatus(s);
1285
0
    }
1286
0
    m_pNetwork->ClearRawBuffer();
1287
0
    m_pNetwork->ClearMotdBuffer();
1288
1289
0
    ResetChans();
1290
0
    m_scUserModes.clear();
1291
0
}
1292
1293
#ifdef HAVE_LIBSSL
1294
void CIRCSock::SSLCertError(X509* pCert) {
1295
    BIO* mem = BIO_new(BIO_s_mem());
1296
    X509_print(mem, pCert);
1297
    char* pCertStr = nullptr;
1298
    long iLen = BIO_get_mem_data(mem, &pCertStr);
1299
    CString sCert(pCertStr, iLen);
1300
    BIO_free(mem);
1301
1302
    VCString vsCert;
1303
    sCert.Split("\n", vsCert);
1304
    for (const CString& s : vsCert) {
1305
        // It shouldn't contain any bad characters, but let's be
1306
        // safe...
1307
        m_vsSSLError.push_back("|" + s.Escape_n(CString::EDEBUG));
1308
    }
1309
    CString sSHA1;
1310
    if (GetPeerFingerprint(sSHA1))
1311
        m_vsSSLError.push_back(
1312
            "SHA1: " + sSHA1.Escape_n(CString::EHEXCOLON, CString::EHEXCOLON));
1313
    CString sSHA256 = GetSSLPeerFingerprint(pCert);
1314
    m_vsSSLError.push_back("SHA-256: " + sSHA256);
1315
    m_vsSSLError.push_back(
1316
        t_f("If you trust this certificate, do /znc "
1317
            "AddTrustedServerFingerprint {1}")(sSHA256));
1318
}
1319
#endif
1320
1321
0
void CIRCSock::Timeout() {
1322
0
    DEBUG(GetSockName() << " == Timeout()");
1323
0
    if (!m_pNetwork->GetUser()->IsBeingDeleted()) {
1324
0
        m_pNetwork->PutStatus(
1325
0
            t_s("IRC connection timed out.  Reconnecting..."));
1326
0
    }
1327
0
    m_pNetwork->ClearRawBuffer();
1328
0
    m_pNetwork->ClearMotdBuffer();
1329
1330
0
    ResetChans();
1331
0
    m_scUserModes.clear();
1332
0
}
1333
1334
0
void CIRCSock::ConnectionRefused() {
1335
0
    DEBUG(GetSockName() << " == ConnectionRefused()");
1336
0
    if (!m_pNetwork->GetUser()->IsBeingDeleted()) {
1337
0
        m_pNetwork->PutStatus(t_s("Connection Refused.  Reconnecting..."));
1338
0
    }
1339
0
    m_pNetwork->ClearRawBuffer();
1340
0
    m_pNetwork->ClearMotdBuffer();
1341
0
}
1342
1343
0
void CIRCSock::ReachedMaxBuffer() {
1344
0
    DEBUG(GetSockName() << " == ReachedMaxBuffer()");
1345
0
    m_pNetwork->PutStatus(t_s("Received a too long line from the IRC server!"));
1346
0
    Quit();
1347
0
}
1348
1349
0
void CIRCSock::ParseISupport(const CMessage& Message) {
1350
0
    const VCString vsParams = Message.GetParams();
1351
1352
0
    for (size_t i = 1; i + 1 < vsParams.size(); ++i) {
1353
0
        const CString& sParam = vsParams[i];
1354
0
        CString sName = sParam.Token(0, false, "=");
1355
0
        CString sValue = sParam.Token(1, true, "=");
1356
1357
0
        if (0 < sName.length() && ':' == sName[0]) {
1358
0
            break;
1359
0
        }
1360
1361
0
        m_mISupport[sName] = sValue;
1362
1363
0
        if (sName.Equals("PREFIX")) {
1364
0
            CString sPrefixes = sValue.Token(1, false, ")");
1365
0
            CString sPermModes = sValue.Token(0, false, ")");
1366
0
            sPermModes.TrimLeft("(");
1367
1368
0
            if (!sPrefixes.empty() && sPermModes.size() == sPrefixes.size()) {
1369
0
                m_sPerms = sPrefixes;
1370
0
                m_sPermModes = sPermModes;
1371
0
            }
1372
0
        } else if (sName.Equals("CHANTYPES")) {
1373
0
            m_pNetwork->SetChanPrefixes(sValue);
1374
0
        } else if (sName.Equals("NICKLEN")) {
1375
0
            unsigned int uMax = sValue.ToUInt();
1376
1377
0
            if (uMax) {
1378
0
                m_uMaxNickLen = uMax;
1379
0
            }
1380
0
        } else if (sName.Equals("CHANMODES")) {
1381
0
            if (!sValue.empty()) {
1382
0
                m_mceChanModes.clear();
1383
1384
0
                for (unsigned int a = 0; a < 4; a++) {
1385
0
                    CString sModes = sValue.Token(a, false, ",");
1386
1387
0
                    for (unsigned int b = 0; b < sModes.size(); b++) {
1388
0
                        m_mceChanModes[sModes[b]] = (EChanModeArgs)a;
1389
0
                    }
1390
0
                }
1391
0
            }
1392
0
        } else if (sName.Equals("NAMESX")) {
1393
0
            if (m_bNamesx) continue;
1394
0
            m_bNamesx = true;
1395
0
            PutIRC("PROTOCTL NAMESX");
1396
0
        } else if (sName.Equals("UHNAMES")) {
1397
0
            if (m_bUHNames) continue;
1398
0
            m_bUHNames = true;
1399
0
            PutIRC("PROTOCTL UHNAMES");
1400
0
        }
1401
0
    }
1402
0
}
1403
1404
CString CIRCSock::GetISupport(const CString& sKey,
1405
0
                              const CString& sDefault) const {
1406
0
    MCString::const_iterator i = m_mISupport.find(sKey.AsUpper());
1407
0
    if (i == m_mISupport.end()) {
1408
0
        return sDefault;
1409
0
    } else {
1410
0
        return i->second;
1411
0
    }
1412
0
}
1413
1414
0
void CIRCSock::SendAltNick(const CString& sBadNick) {
1415
0
    const CString& sLastNick = m_Nick.GetNick();
1416
1417
    // We don't know the maximum allowed nick length yet, but we know which
1418
    // nick we sent last. If sBadNick is shorter than that, we assume the
1419
    // server truncated our nick.
1420
0
    if (sBadNick.length() < sLastNick.length())
1421
0
        m_uMaxNickLen = (unsigned int)sBadNick.length();
1422
1423
0
    unsigned int uMax = m_uMaxNickLen;
1424
1425
0
    const CString& sConfNick = m_pNetwork->GetNick();
1426
0
    const CString& sAltNick = m_pNetwork->GetAltNick();
1427
0
    CString sNewNick = sConfNick.Left(uMax - 1);
1428
1429
0
    if (sLastNick.Equals(sConfNick)) {
1430
0
        if ((!sAltNick.empty()) && (!sConfNick.Equals(sAltNick))) {
1431
0
            sNewNick = sAltNick;
1432
0
        } else {
1433
0
            sNewNick += "-";
1434
0
        }
1435
0
    } else if (sLastNick.Equals(sAltNick) && !sAltNick.Equals(sNewNick + "-")) {
1436
0
        sNewNick += "-";
1437
0
    } else if (sLastNick.Equals(sNewNick + "-") &&
1438
0
               !sAltNick.Equals(sNewNick + "|")) {
1439
0
        sNewNick += "|";
1440
0
    } else if (sLastNick.Equals(sNewNick + "|") &&
1441
0
               !sAltNick.Equals(sNewNick + "^")) {
1442
0
        sNewNick += "^";
1443
0
    } else if (sLastNick.Equals(sNewNick + "^") &&
1444
0
               !sAltNick.Equals(sNewNick + "a")) {
1445
0
        sNewNick += "a";
1446
0
    } else {
1447
0
        char cLetter = 0;
1448
0
        if (sBadNick.empty()) {
1449
0
            m_pNetwork->PutUser(t_s("No free nick available"));
1450
0
            Quit();
1451
0
            return;
1452
0
        }
1453
1454
0
        cLetter = sBadNick.back();
1455
1456
0
        if (cLetter == 'z') {
1457
0
            m_pNetwork->PutUser(t_s("No free nick found"));
1458
0
            Quit();
1459
0
            return;
1460
0
        }
1461
1462
0
        sNewNick = sConfNick.Left(uMax - 1) + ++cLetter;
1463
0
        if (sNewNick.Equals(sAltNick))
1464
0
            sNewNick = sConfNick.Left(uMax - 1) + ++cLetter;
1465
0
    }
1466
0
    PutIRC("NICK " + sNewNick);
1467
0
    m_Nick.SetNick(sNewNick);
1468
0
}
1469
1470
0
char CIRCSock::GetPermFromMode(char cMode) const {
1471
0
    if (m_sPermModes.size() == m_sPerms.size()) {
1472
0
        for (unsigned int a = 0; a < m_sPermModes.size(); a++) {
1473
0
            if (m_sPermModes[a] == cMode) {
1474
0
                return m_sPerms[a];
1475
0
            }
1476
0
        }
1477
0
    }
1478
1479
0
    return 0;
1480
0
}
1481
1482
0
CIRCSock::EChanModeArgs CIRCSock::GetModeType(char cMode) const {
1483
0
    map<char, EChanModeArgs>::const_iterator it =
1484
0
        m_mceChanModes.find(cMode);
1485
1486
0
    if (it == m_mceChanModes.end()) {
1487
0
        return NoArg;
1488
0
    }
1489
1490
0
    return it->second;
1491
0
}
1492
1493
0
void CIRCSock::ResetChans() {
1494
0
    for (const auto& it : m_msChans) {
1495
0
        it.second->Reset();
1496
0
    }
1497
0
}
1498
1499
0
void CIRCSock::SetTagSupport(const CString& sTag, bool bState) {
1500
0
    if (bState) {
1501
0
        m_ssSupportedTags.insert(sTag);
1502
0
    } else {
1503
0
        m_ssSupportedTags.erase(sTag);
1504
0
    }
1505
0
}
1506