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