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