]> jfr.im git - irc/rizon/znc.git/blob - IRCSock.cpp
Write forceserver and webircpassword to conf
[irc/rizon/znc.git] / IRCSock.cpp
1 /*
2 * Copyright (C) 2004-2011 See the AUTHORS file for details.
3 *
4 * This program is free software; you can redistribute it and/or modify it
5 * under the terms of the GNU General Public License version 2 as published
6 * by the Free Software Foundation.
7 */
8
9 #include "IRCSock.h"
10 #include "Chan.h"
11 #include "Client.h"
12 #include "User.h"
13 #include "znc.h"
14 #include "Server.h"
15
16 // These are used in OnGeneralCTCP()
17 const time_t CIRCSock::m_uCTCPFloodTime = 5;
18 const unsigned int CIRCSock::m_uCTCPFloodCount = 5;
19
20 CIRCSock::CIRCSock(CUser* pUser) : CZNCSock() {
21 m_pUser = pUser;
22 m_bAuthed = false;
23 m_bNamesx = false;
24 m_bUHNames = false;
25 EnableReadLine();
26 m_Nick.SetIdent(pUser->GetIdent());
27 m_Nick.SetHost(pUser->GetBindHost());
28
29 m_uMaxNickLen = 9;
30 m_uCapPaused = 0;
31 m_lastCTCP = 0;
32 m_uNumCTCP = 0;
33 m_sPerms = "*!@%+";
34 m_sPermModes = "qaohv";
35 m_mueChanModes['b'] = ListArg;
36 m_mueChanModes['e'] = ListArg;
37 m_mueChanModes['I'] = ListArg;
38 m_mueChanModes['k'] = HasArg;
39 m_mueChanModes['l'] = ArgWhenSet;
40 m_mueChanModes['p'] = NoArg;
41 m_mueChanModes['s'] = NoArg;
42 m_mueChanModes['t'] = NoArg;
43 m_mueChanModes['i'] = NoArg;
44 m_mueChanModes['n'] = NoArg;
45
46 pUser->SetIRCSocket(this);
47
48 // RFC says a line can have 512 chars max, but we don't care ;)
49 SetMaxBufferThreshold(1024);
50 }
51
52 CIRCSock::~CIRCSock() {
53 if (!m_bAuthed) {
54 MODULECALL(OnIRCConnectionError(this), m_pUser, NULL, NOTHING);
55 }
56
57 const vector<CChan*>& vChans = m_pUser->GetChans();
58 for (unsigned int a = 0; a < vChans.size(); a++) {
59 vChans[a]->Reset();
60 }
61
62 m_pUser->IRCDisconnected();
63
64 for (map<CString, CChan*>::iterator a = m_msChans.begin(); a != m_msChans.end(); ++a) {
65 delete a->second;
66 }
67
68 Quit();
69 m_msChans.clear();
70 GetUser()->AddBytesRead(GetBytesRead());
71 GetUser()->AddBytesWritten(GetBytesWritten());
72 }
73
74 void CIRCSock::Quit(const CString& sQuitMsg) {
75 if (!m_bAuthed) {
76 Close(CLT_NOW);
77 return;
78 }
79 if (!sQuitMsg.empty()) {
80 PutIRC("QUIT :" + sQuitMsg);
81 } else {
82 PutIRC("QUIT :" + m_pUser->ExpandString(m_pUser->GetQuitMsg()));
83 }
84 Close(CLT_AFTERWRITE);
85 }
86
87 void CIRCSock::ReadLine(const CString& sData) {
88 CString sLine = sData;
89
90 sLine.TrimRight("\n\r");
91
92 DEBUG("(" << m_pUser->GetUserName() << ") IRC -> ZNC [" << sLine << "]");
93
94 MODULECALL(OnRaw(sLine), m_pUser, NULL, return);
95
96 if (sLine.Equals("PING ", false, 5)) {
97 // Generate a reply and don't forward this to any user,
98 // we don't want any PING forwarded
99 PutIRC("PONG " + sLine.substr(5));
100 return;
101 } else if (sLine.Token(1).Equals("PONG")) {
102 // Block PONGs, we already responded to the pings
103 return;
104 } else if (sLine.Equals("ERROR ", false, 6)) {
105 //ERROR :Closing Link: nick[24.24.24.24] (Excess Flood)
106 CString sError(sLine.substr(6));
107
108 if (sError.Left(1) == ":") {
109 sError.LeftChomp();
110 }
111
112 m_pUser->PutStatus("Error from Server [" + sError + "]");
113 return;
114 }
115
116 CString sCmd = sLine.Token(1);
117
118 if ((sCmd.length() == 3) && (isdigit(sCmd[0])) && (isdigit(sCmd[1])) && (isdigit(sCmd[2]))) {
119 CString sServer = sLine.Token(0); sServer.LeftChomp();
120 unsigned int uRaw = sCmd.ToUInt();
121 CString sNick = sLine.Token(2);
122 CString sRest = sLine.Token(3, true);
123
124 switch (uRaw) {
125 case 1: { // :irc.server.com 001 nick :Welcome to the Internet Relay Network nick
126 if (m_bAuthed && sServer == "irc.znc.in") {
127 // m_bAuthed == true => we already received another 001 => we might be in a traffic loop
128 m_pUser->PutStatus("ZNC seems to be connected to itself, disconnecting...");
129 Quit();
130 return;
131 }
132
133 m_pUser->SetIRCServer(sServer);
134 SetTimeout(540, TMO_READ); // Now that we are connected, let nature take its course
135 PutIRC("WHO " + sNick);
136
137 m_bAuthed = true;
138 m_pUser->PutStatus("Connected!");
139
140 vector<CClient*>& vClients = m_pUser->GetClients();
141
142 for (unsigned int a = 0; a < vClients.size(); a++) {
143 CClient* pClient = vClients[a];
144 CString sClientNick = pClient->GetNick(false);
145
146 if (!sClientNick.Equals(sNick)) {
147 // If they connected with a nick that doesn't match the one we got on irc, then we need to update them
148 pClient->PutClient(":" + sClientNick + "!" + m_Nick.GetIdent() + "@" + m_Nick.GetHost() + " NICK :" + sNick);
149 }
150 }
151
152 SetNick(sNick);
153
154 MODULECALL(OnIRCConnected(), m_pUser, NULL, NOTHING);
155
156 m_pUser->ClearRawBuffer();
157 m_pUser->AddRawBuffer(":" + sServer + " " + sCmd + " ", " " + sRest);
158
159 break;
160 }
161 case 5:
162 ParseISupport(sRest);
163 m_pUser->UpdateExactRawBuffer(":" + sServer + " " + sCmd + " ", " " + sRest);
164 break;
165 case 10: { // :irc.server.com 010 nick <hostname> <port> :<info>
166 CString sHost = sRest.Token(0);
167 CString sPort = sRest.Token(1);
168 CString sInfo = sRest.Token(2, true).TrimPrefix_n(":");
169 m_pUser->PutStatus("Server [" + m_pUser->GetCurrentServer()->GetString(false) +
170 "] redirects us to [" + sHost + ":" + sPort + "] with reason [" + sInfo + "]");
171 m_pUser->PutStatus("Perhaps you want to add it as a new server.");
172 // Don't send server redirects to the client
173 return;
174 }
175 case 2:
176 case 3:
177 case 4:
178 case 250: // highest connection count
179 case 251: // user count
180 case 252: // oper count
181 case 254: // channel count
182 case 255: // client count
183 case 265: // local users
184 case 266: // global users
185 m_pUser->UpdateRawBuffer(":" + sServer + " " + sCmd + " ", " " + sRest);
186 break;
187 case 305:
188 m_pUser->SetIRCAway(false);
189 break;
190 case 306:
191 m_pUser->SetIRCAway(true);
192 break;
193 case 324: { // MODE
194 sRest.Trim();
195 CChan* pChan = m_pUser->FindChan(sRest.Token(0));
196
197 if (pChan) {
198 pChan->SetModes(sRest.Token(1, true));
199
200 // We don't SetModeKnown(true) here,
201 // because a 329 will follow
202 if (!pChan->IsModeKnown()) {
203 // When we JOIN, we send a MODE
204 // request. This makes sure the
205 // reply isn't forwarded.
206 return;
207 }
208 }
209 }
210 break;
211 case 329: {
212 sRest.Trim();
213 CChan* pChan = m_pUser->FindChan(sRest.Token(0));
214
215 if (pChan) {
216 unsigned long ulDate = sLine.Token(4).ToULong();
217 pChan->SetCreationDate(ulDate);
218
219 if (!pChan->IsModeKnown()) {
220 pChan->SetModeKnown(true);
221 // When we JOIN, we send a MODE
222 // request. This makes sure the
223 // reply isn't forwarded.
224 return;
225 }
226 }
227 }
228 break;
229 case 331: {
230 // :irc.server.com 331 yournick #chan :No topic is set.
231 CChan* pChan = m_pUser->FindChan(sLine.Token(3));
232
233 if (pChan) {
234 pChan->SetTopic("");
235 }
236
237 break;
238 }
239 case 332: {
240 // :irc.server.com 332 yournick #chan :This is a topic
241 CChan* pChan = m_pUser->FindChan(sLine.Token(3));
242
243 if (pChan) {
244 CString sTopic = sLine.Token(4, true);
245 sTopic.LeftChomp();
246 pChan->SetTopic(sTopic);
247 }
248
249 break;
250 }
251 case 333: {
252 // :irc.server.com 333 yournick #chan setternick 1112320796
253 CChan* pChan = m_pUser->FindChan(sLine.Token(3));
254
255 if (pChan) {
256 sNick = sLine.Token(4);
257 unsigned long ulDate = sLine.Token(5).ToULong();
258
259 pChan->SetTopicOwner(sNick);
260 pChan->SetTopicDate(ulDate);
261 }
262
263 break;
264 }
265 case 352: {
266 // :irc.yourserver.com 352 yournick #chan ident theirhost.com irc.theirserver.com theirnick H :0 Real Name
267 sServer = sLine.Token(0);
268 sNick = sLine.Token(7);
269 CString sIdent = sLine.Token(4);
270 CString sHost = sLine.Token(5);
271
272 sServer.LeftChomp();
273
274 if (sNick.Equals(GetNick())) {
275 m_Nick.SetIdent(sIdent);
276 m_Nick.SetHost(sHost);
277 }
278
279 m_pUser->SetIRCNick(m_Nick);
280 m_pUser->SetIRCServer(sServer);
281
282 const vector<CChan*>& vChans = m_pUser->GetChans();
283
284 for (unsigned int a = 0; a < vChans.size(); a++) {
285 vChans[a]->OnWho(sNick, sIdent, sHost);
286 }
287
288 break;
289 }
290 case 353: { // NAMES
291 sRest.Trim();
292 // Todo: allow for non @+= server msgs
293 CChan* pChan = m_pUser->FindChan(sRest.Token(1));
294 // If we don't know that channel, some client might have
295 // requested a /names for it and we really should forward this.
296 if (pChan) {
297 CString sNicks = sRest.Token(2, true);
298 if (sNicks.Left(1) == ":") {
299 sNicks.LeftChomp();
300 }
301
302 pChan->AddNicks(sNicks);
303 }
304
305 ForwardRaw353(sLine);
306
307 // We forwarded it already, so return
308 return;
309 }
310 case 366: { // end of names list
311 m_pUser->PutUser(sLine); // First send them the raw
312
313 // :irc.server.com 366 nick #chan :End of /NAMES list.
314 CChan* pChan = m_pUser->FindChan(sRest.Token(0));
315
316 if (pChan) {
317 if (pChan->IsOn()) {
318 // If we are the only one in the chan, set our default modes
319 if (pChan->GetNickCount() == 1) {
320 CString sModes = pChan->GetDefaultModes();
321
322 if (sModes.empty()) {
323 sModes = m_pUser->GetDefaultChanModes();
324 }
325
326 if (!sModes.empty()) {
327 PutIRC("MODE " + pChan->GetName() + " " + sModes);
328 }
329 }
330 }
331 }
332
333 return; // return so we don't send them the raw twice
334 }
335 case 375: // begin motd
336 case 422: // MOTD File is missing
337 m_pUser->ClearMotdBuffer();
338 case 372: // motd
339 case 376: // end motd
340 m_pUser->AddMotdBuffer(":" + sServer + " " + sCmd + " ", " " + sRest);
341 break;
342 case 437:
343 // :irc.server.net 437 * badnick :Nick/channel is temporarily unavailable
344 // :irc.server.net 437 mynick badnick :Nick/channel is temporarily unavailable
345 // :irc.server.net 437 mynick badnick :Cannot change nickname while banned on channel
346 if (m_pUser->IsChan(sRest.Token(0)) || sNick != "*")
347 break;
348 case 432: // :irc.server.com 432 * nick :Erroneous Nickname: Illegal characters
349 case 433: {
350 CString sBadNick = sRest.Token(0);
351
352 if (!m_bAuthed) {
353 SendAltNick(sBadNick);
354 return;
355 }
356 break;
357 }
358 case 451:
359 // :irc.server.com 451 CAP :You have not registered
360 // Servers that dont support CAP will give us this error, dont send it to the client
361 if (sNick.Equals("CAP"))
362 return;
363 case 470: {
364 // :irc.unreal.net 470 mynick [Link] #chan1 has become full, so you are automatically being transferred to the linked channel #chan2
365 // :mccaffrey.freenode.net 470 mynick #electronics ##electronics :Forwarding to another channel
366
367 // freenode style numeric
368 CChan* pChan = m_pUser->FindChan(sRest.Token(0));
369 if (!pChan) {
370 // unreal style numeric
371 pChan = m_pUser->FindChan(sRest.Token(1));
372 }
373 if (pChan) {
374 pChan->Disable();
375 m_pUser->PutStatus("Channel [" + pChan->GetName() + "] is linked to "
376 "another channel and was thus disabled.");
377 }
378 break;
379 }
380 }
381 } else {
382 CNick Nick(sLine.Token(0).LeftChomp_n());
383 sCmd = sLine.Token(1);
384 CString sRest = sLine.Token(2, true);
385
386 if (sCmd.Equals("NICK")) {
387 CString sNewNick = sRest;
388 bool bIsVisible = false;
389
390 if (sNewNick.Left(1) == ":") {
391 sNewNick.LeftChomp();
392 }
393
394 vector<CChan*> vFoundChans;
395 const vector<CChan*>& vChans = m_pUser->GetChans();
396
397 for (unsigned int a = 0; a < vChans.size(); a++) {
398 CChan* pChan = vChans[a];
399
400 if (pChan->ChangeNick(Nick.GetNick(), sNewNick)) {
401 vFoundChans.push_back(pChan);
402
403 if (!pChan->IsDetached()) {
404 bIsVisible = true;
405 }
406 }
407 }
408
409 // Todo: use nick compare function here
410 if (Nick.GetNick().Equals(GetNick())) {
411 // We are changing our own nick, the clients always must see this!
412 bIsVisible = true;
413 SetNick(sNewNick);
414 }
415
416 MODULECALL(OnNick(Nick, sNewNick, vFoundChans), m_pUser, NULL, NOTHING);
417
418 if (!bIsVisible) {
419 return;
420 }
421 } else if (sCmd.Equals("QUIT")) {
422 CString sMessage = sRest;
423 bool bIsVisible = false;
424
425 if (sMessage.Left(1) == ":") {
426 sMessage.LeftChomp();
427 }
428
429 // :nick!ident@host.com QUIT :message
430
431 if (Nick.GetNick().Equals(GetNick())) {
432 m_pUser->PutStatus("You quit [" + sMessage + "]");
433 // We don't call module hooks and we don't
434 // forward this quit to clients (Some clients
435 // disconnect if they receive such a QUIT)
436 return;
437 }
438
439 vector<CChan*> vFoundChans;
440 const vector<CChan*>& vChans = m_pUser->GetChans();
441
442 for (unsigned int a = 0; a < vChans.size(); a++) {
443 CChan* pChan = vChans[a];
444
445 if (pChan->RemNick(Nick.GetNick())) {
446 vFoundChans.push_back(pChan);
447
448 if (!pChan->IsDetached()) {
449 bIsVisible = true;
450 }
451 }
452 }
453
454 MODULECALL(OnQuit(Nick, sMessage, vFoundChans), m_pUser, NULL, NOTHING);
455
456 if (!bIsVisible) {
457 return;
458 }
459 } else if (sCmd.Equals("JOIN")) {
460 CString sChan = sRest.Token(0);
461 if (sChan.Left(1) == ":") {
462 sChan.LeftChomp();
463 }
464
465 CChan* pChan;
466
467 // Todo: use nick compare function
468 if (Nick.GetNick().Equals(GetNick())) {
469 m_pUser->AddChan(sChan, false);
470 pChan = m_pUser->FindChan(sChan);
471 if (pChan) {
472 pChan->Enable();
473 pChan->SetIsOn(true);
474 PutIRC("MODE " + sChan);
475 }
476 } else {
477 pChan = m_pUser->FindChan(sChan);
478 }
479
480 if (pChan) {
481 pChan->AddNick(Nick.GetNickMask());
482 MODULECALL(OnJoin(Nick.GetNickMask(), *pChan), m_pUser, NULL, NOTHING);
483
484 if (pChan->IsDetached()) {
485 return;
486 }
487 }
488 } else if (sCmd.Equals("PART")) {
489 CString sChan = sRest.Token(0);
490 if (sChan.Left(1) == ":") {
491 sChan.LeftChomp();
492 }
493 CString sMsg = sRest.Token(1, true).TrimPrefix_n(":");
494
495 CChan* pChan = m_pUser->FindChan(sChan);
496 bool bDetached = false;
497 if (pChan) {
498 pChan->RemNick(Nick.GetNick());
499 MODULECALL(OnPart(Nick.GetNickMask(), *pChan, sMsg), m_pUser, NULL, NOTHING);
500
501 if (pChan->IsDetached())
502 bDetached = true;
503 }
504
505 // Todo: use nick compare function
506 if (Nick.GetNick().Equals(GetNick())) {
507 m_pUser->DelChan(sChan);
508 }
509
510 /*
511 * We use this boolean because
512 * m_pUser->DelChan() will delete this channel
513 * and thus we would dereference an
514 * already-freed pointer!
515 */
516 if (bDetached) {
517 return;
518 }
519 } else if (sCmd.Equals("MODE")) {
520 CString sTarget = sRest.Token(0);
521 CString sModes = sRest.Token(1, true);
522 if (sModes.Left(1) == ":")
523 sModes = sModes.substr(1);
524
525 CChan* pChan = m_pUser->FindChan(sTarget);
526 if (pChan) {
527 pChan->ModeChange(sModes, &Nick);
528
529 if (pChan->IsDetached()) {
530 return;
531 }
532 } else if (sTarget == m_Nick.GetNick()) {
533 CString sModeArg = sModes.Token(0);
534 bool bAdd = true;
535 /* no module call defined (yet?)
536 MODULECALL(OnRawUserMode(*pOpNick, *this, sModeArg, sArgs), m_pUser, NULL, );
537 */
538 for (unsigned int a = 0; a < sModeArg.size(); a++) {
539 const unsigned char& uMode = sModeArg[a];
540
541 if (uMode == '+') {
542 bAdd = true;
543 } else if (uMode == '-') {
544 bAdd = false;
545 } else {
546 if (bAdd) {
547 m_scUserModes.insert(uMode);
548 } else {
549 m_scUserModes.erase(uMode);
550 }
551 }
552 }
553 }
554 } else if (sCmd.Equals("KICK")) {
555 // :opnick!ident@host.com KICK #chan nick :msg
556 CString sChan = sRest.Token(0);
557 CString sKickedNick = sRest.Token(1);
558 CString sMsg = sRest.Token(2, true);
559 sMsg.LeftChomp();
560
561 CChan* pChan = m_pUser->FindChan(sChan);
562
563 if (pChan) {
564 MODULECALL(OnKick(Nick, sKickedNick, *pChan, sMsg), m_pUser, NULL, NOTHING);
565 // do not remove the nick till after the OnKick call, so modules
566 // can do Chan.FindNick or something to get more info.
567 pChan->RemNick(sKickedNick);
568 }
569
570 if (GetNick().Equals(sKickedNick) && pChan) {
571 pChan->SetIsOn(false);
572
573 // Don't try to rejoin!
574 pChan->Disable();
575 }
576
577 if ((pChan) && (pChan->IsDetached())) {
578 return;
579 }
580 } else if (sCmd.Equals("NOTICE")) {
581 // :nick!ident@host.com NOTICE #chan :Message
582 CString sTarget = sRest.Token(0);
583 CString sMsg = sRest.Token(1, true);
584 sMsg.LeftChomp();
585
586 if (sMsg.WildCmp("\001*\001")) {
587 sMsg.LeftChomp();
588 sMsg.RightChomp();
589
590 if (sTarget.Equals(GetNick())) {
591 if (OnCTCPReply(Nick, sMsg)) {
592 return;
593 }
594 }
595
596 m_pUser->PutUser(":" + Nick.GetNickMask() + " NOTICE " + sTarget + " :\001" + sMsg + "\001");
597 return;
598 } else {
599 if (sTarget.Equals(GetNick())) {
600 if (OnPrivNotice(Nick, sMsg)) {
601 return;
602 }
603 } else {
604 if (OnChanNotice(Nick, sTarget, sMsg)) {
605 return;
606 }
607 }
608 }
609
610 if (Nick.GetNick().Equals(m_pUser->GetIRCServer())) {
611 m_pUser->PutUser(":" + Nick.GetNick() + " NOTICE " + sTarget + " :" + sMsg);
612 } else {
613 m_pUser->PutUser(":" + Nick.GetNickMask() + " NOTICE " + sTarget + " :" + sMsg);
614 }
615
616 return;
617 } else if (sCmd.Equals("TOPIC")) {
618 // :nick!ident@host.com TOPIC #chan :This is a topic
619 CChan* pChan = m_pUser->FindChan(sLine.Token(2));
620
621 if (pChan) {
622 CString sTopic = sLine.Token(3, true);
623 sTopic.LeftChomp();
624
625 MODULECALL(OnTopic(Nick, *pChan, sTopic), m_pUser, NULL, return)
626
627 pChan->SetTopicOwner(Nick.GetNick());
628 pChan->SetTopicDate((unsigned long) time(NULL));
629 pChan->SetTopic(sTopic);
630
631 if (pChan->IsDetached()) {
632 return; // Don't forward this
633 }
634
635 sLine = ":" + Nick.GetNickMask() + " TOPIC " + pChan->GetName() + " :" + sTopic;
636 }
637 } else if (sCmd.Equals("PRIVMSG")) {
638 // :nick!ident@host.com PRIVMSG #chan :Message
639 CString sTarget = sRest.Token(0);
640 CString sMsg = sRest.Token(1, true);
641
642 if (sMsg.Left(1) == ":") {
643 sMsg.LeftChomp();
644 }
645
646 if (sMsg.WildCmp("\001*\001")) {
647 sMsg.LeftChomp();
648 sMsg.RightChomp();
649
650 if (sTarget.Equals(GetNick())) {
651 if (OnPrivCTCP(Nick, sMsg)) {
652 return;
653 }
654 } else {
655 if (OnChanCTCP(Nick, sTarget, sMsg)) {
656 return;
657 }
658 }
659
660 m_pUser->PutUser(":" + Nick.GetNickMask() + " PRIVMSG " + sTarget + " :\001" + sMsg + "\001");
661 return;
662 } else {
663 if (sTarget.Equals(GetNick())) {
664 if (OnPrivMsg(Nick, sMsg)) {
665 return;
666 }
667 } else {
668 if (OnChanMsg(Nick, sTarget, sMsg)) {
669 return;
670 }
671 }
672
673 m_pUser->PutUser(":" + Nick.GetNickMask() + " PRIVMSG " + sTarget + " :" + sMsg);
674 return;
675 }
676 } else if (sCmd.Equals("WALLOPS")) {
677 // :blub!dummy@rox-8DBEFE92 WALLOPS :this is a test
678 CString sMsg = sRest.Token(0, true);
679
680 if (sMsg.Left(1) == ":") {
681 sMsg.LeftChomp();
682 }
683
684 if (!m_pUser->IsUserAttached()) {
685 m_pUser->AddQueryBuffer(":" + Nick.GetNickMask() + " WALLOPS ", ":" + m_pUser->AddTimestamp(sMsg), false);
686 }
687 } else if (sCmd.Equals("CAP")) {
688 // CAPs are supported only before authorization.
689 if (!m_bAuthed) {
690 // sRest.Token(0) is most likely "*". No idea why, the
691 // CAP spec don't mention this, but all implementations
692 // I've seen add this extra asterisk
693 CString sSubCmd = sRest.Token(1);
694
695 // If the caplist of a reply is too long, it's split
696 // into multiple replies. A "*" is prepended to show
697 // that the list was split into multiple replies.
698 // This is useful mainly for LS. For ACK and NAK
699 // replies, there's no real need for this, because
700 // we request only 1 capability per line.
701 // If we will need to support broken servers or will
702 // send several requests per line, need to delay ACK
703 // actions until all ACK lines are received and
704 // to recognize past request of NAK by 100 chars
705 // of this reply.
706 CString sArgs;
707 if (sRest.Token(2) == "*") {
708 sArgs = sRest.Token(3, true).TrimPrefix_n(":");
709 } else {
710 sArgs = sRest.Token(2, true).TrimPrefix_n(":");
711 }
712
713 if (sSubCmd == "LS") {
714 VCString vsTokens;
715 VCString::iterator it;
716 sArgs.Split(" ", vsTokens, false);
717
718 for (it = vsTokens.begin(); it != vsTokens.end(); ++it) {
719 if (OnServerCapAvailable(*it) || *it == "multi-prefix" || *it == "userhost-in-names") {
720 m_ssPendingCaps.insert(*it);
721 }
722 }
723 } else if (sSubCmd == "ACK") {
724 sArgs.Trim();
725 MODULECALL(OnServerCapResult(sArgs, true), m_pUser, NULL, NOTHING);
726 if ("multi-prefix" == sArgs) {
727 m_bNamesx = true;
728 } else if ("userhost-in-names" == sArgs) {
729 m_bUHNames = true;
730 }
731 m_ssAcceptedCaps.insert(sArgs);
732 } else if (sSubCmd == "NAK") {
733 // This should work because there's no [known]
734 // capability with length of name more than 100 characters.
735 sArgs.Trim();
736 MODULECALL(OnServerCapResult(sArgs, false), m_pUser, NULL, NOTHING);
737 }
738
739 SendNextCap();
740 }
741 // Don't forward any CAP stuff to the client
742 return;
743 }
744 }
745
746 m_pUser->PutUser(sLine);
747 }
748
749 void CIRCSock::SendNextCap() {
750 if (!m_uCapPaused) {
751 if (m_ssPendingCaps.empty()) {
752 // We already got all needed ACK/NAK replies.
753 PutIRC("CAP END");
754 } else {
755 CString sCap = *m_ssPendingCaps.begin();
756 m_ssPendingCaps.erase(m_ssPendingCaps.begin());
757 PutIRC("CAP REQ :" + sCap);
758 }
759 }
760 }
761
762 void CIRCSock::PauseCap() {
763 ++m_uCapPaused;
764 }
765
766 void CIRCSock::ResumeCap() {
767 --m_uCapPaused;
768 SendNextCap();
769 }
770
771 bool CIRCSock::OnServerCapAvailable(const CString& sCap) {
772 MODULECALL(OnServerCapAvailable(sCap), m_pUser, NULL, return true);
773 return false;
774 }
775
776 bool CIRCSock::OnCTCPReply(CNick& Nick, CString& sMessage) {
777 MODULECALL(OnCTCPReply(Nick, sMessage), m_pUser, NULL, return true);
778
779 return false;
780 }
781
782 bool CIRCSock::OnPrivCTCP(CNick& Nick, CString& sMessage) {
783 MODULECALL(OnPrivCTCP(Nick, sMessage), m_pUser, NULL, return true);
784
785 if (sMessage.TrimPrefix("ACTION ")) {
786 MODULECALL(OnPrivAction(Nick, sMessage), m_pUser, NULL, return true);
787
788 if (!m_pUser->IsUserAttached()) {
789 // If the user is detached, add to the buffer
790 m_pUser->AddQueryBuffer(":" + Nick.GetNickMask() + " PRIVMSG ", " :\001ACTION " + m_pUser->AddTimestamp(sMessage) + "\001");
791 }
792
793 sMessage = "ACTION " + sMessage;
794 }
795
796 // This handles everything which wasn't handled yet
797 return OnGeneralCTCP(Nick, sMessage);
798 }
799
800 bool CIRCSock::OnGeneralCTCP(CNick& Nick, CString& sMessage) {
801 const MCString& mssCTCPReplies = m_pUser->GetCTCPReplies();
802 CString sQuery = sMessage.Token(0).AsUpper();
803 MCString::const_iterator it = mssCTCPReplies.find(sQuery);
804 bool bHaveReply = false;
805 CString sReply;
806
807 if (it != mssCTCPReplies.end()) {
808 sReply = m_pUser->ExpandString(it->second);
809 bHaveReply = true;
810
811 if (sReply.empty()) {
812 return true;
813 }
814 }
815
816 if (!bHaveReply && !m_pUser->IsUserAttached()) {
817 if (sQuery == "VERSION") {
818 sReply = CZNC::GetTag();
819 } else if (sQuery == "PING") {
820 sReply = sMessage.Token(1, true);
821 }
822 }
823
824 if (!sReply.empty()) {
825 time_t now = time(NULL);
826 // If the last CTCP is older than m_uCTCPFloodTime, reset the counter
827 if (m_lastCTCP + m_uCTCPFloodTime < now)
828 m_uNumCTCP = 0;
829 m_lastCTCP = now;
830 // If we are over the limit, don't reply to this CTCP
831 if (m_uNumCTCP >= m_uCTCPFloodCount) {
832 DEBUG("CTCP flood detected - not replying to query");
833 return false;
834 }
835 m_uNumCTCP++;
836
837 PutIRC("NOTICE " + Nick.GetNick() + " :\001" + sQuery + " " + sReply + "\001");
838 return true;
839 }
840
841 return false;
842 }
843
844 bool CIRCSock::OnPrivNotice(CNick& Nick, CString& sMessage) {
845 MODULECALL(OnPrivNotice(Nick, sMessage), m_pUser, NULL, return true);
846
847 if (!m_pUser->IsUserAttached()) {
848 // If the user is detached, add to the buffer
849 m_pUser->AddQueryBuffer(":" + Nick.GetNickMask() + " NOTICE ", " :" + m_pUser->AddTimestamp(sMessage));
850 }
851
852 return false;
853 }
854
855 bool CIRCSock::OnPrivMsg(CNick& Nick, CString& sMessage) {
856 MODULECALL(OnPrivMsg(Nick, sMessage), m_pUser, NULL, return true);
857
858 if (!m_pUser->IsUserAttached()) {
859 // If the user is detached, add to the buffer
860 m_pUser->AddQueryBuffer(":" + Nick.GetNickMask() + " PRIVMSG ", " :" + m_pUser->AddTimestamp(sMessage));
861 }
862
863 return false;
864 }
865
866 bool CIRCSock::OnChanCTCP(CNick& Nick, const CString& sChan, CString& sMessage) {
867 CChan* pChan = m_pUser->FindChan(sChan);
868 if (pChan) {
869 MODULECALL(OnChanCTCP(Nick, *pChan, sMessage), m_pUser, NULL, return true);
870
871 // Record a /me
872 if (sMessage.TrimPrefix("ACTION ")) {
873 MODULECALL(OnChanAction(Nick, *pChan, sMessage), m_pUser, NULL, return true);
874 if (pChan->KeepBuffer() || !m_pUser->IsUserAttached() || pChan->IsDetached()) {
875 pChan->AddBuffer(":" + Nick.GetNickMask() + " PRIVMSG " + sChan + " :\001ACTION " + m_pUser->AddTimestamp(sMessage) + "\001");
876 }
877 sMessage = "ACTION " + sMessage;
878 }
879 }
880
881 if (OnGeneralCTCP(Nick, sMessage))
882 return true;
883
884 return (pChan && pChan->IsDetached());
885 }
886
887 bool CIRCSock::OnChanNotice(CNick& Nick, const CString& sChan, CString& sMessage) {
888 CChan* pChan = m_pUser->FindChan(sChan);
889 if (pChan) {
890 MODULECALL(OnChanNotice(Nick, *pChan, sMessage), m_pUser, NULL, return true);
891
892 if (pChan->KeepBuffer() || !m_pUser->IsUserAttached() || pChan->IsDetached()) {
893 pChan->AddBuffer(":" + Nick.GetNickMask() + " NOTICE " + sChan + " :" + m_pUser->AddTimestamp(sMessage));
894 }
895 }
896
897 return ((pChan) && (pChan->IsDetached()));
898 }
899
900 bool CIRCSock::OnChanMsg(CNick& Nick, const CString& sChan, CString& sMessage) {
901 CChan* pChan = m_pUser->FindChan(sChan);
902 if (pChan) {
903 MODULECALL(OnChanMsg(Nick, *pChan, sMessage), m_pUser, NULL, return true);
904
905 if (pChan->KeepBuffer() || !m_pUser->IsUserAttached() || pChan->IsDetached()) {
906 pChan->AddBuffer(":" + Nick.GetNickMask() + " PRIVMSG " + sChan + " :" + m_pUser->AddTimestamp(sMessage));
907 }
908 }
909
910 return ((pChan) && (pChan->IsDetached()));
911 }
912
913 void CIRCSock::PutIRC(const CString& sLine) {
914 DEBUG("(" << m_pUser->GetUserName() << ") ZNC -> IRC [" << sLine << "]");
915 Write(sLine + "\r\n");
916 }
917
918 void CIRCSock::SetNick(const CString& sNick) {
919 m_Nick.SetNick(sNick);
920 m_pUser->SetIRCNick(m_Nick);
921 }
922
923 void CIRCSock::Connected() {
924 DEBUG(GetSockName() << " == Connected()");
925
926 CString sPass = m_sPass;
927 CString sNick = m_pUser->GetNick();
928 CString sIdent = m_pUser->GetIdent();
929 CString sRealName = m_pUser->GetRealName();
930
931 MODULECALL(OnIRCRegistration(sPass, sNick, sIdent, sRealName), m_pUser, NULL, return);
932
933 PutIRC("CAP LS");
934
935 if (!sPass.empty()) {
936 PutIRC("PASS " + sPass);
937 }
938
939 PutIRC("NICK " + sNick);
940 PutIRC("USER " + sIdent + " \"" + sIdent + "\" \"" + sIdent + "\" :" + sRealName);
941
942 // SendAltNick() needs this
943 m_Nick.SetNick(sNick);
944 }
945
946 void CIRCSock::Disconnected() {
947 MODULECALL(OnIRCDisconnected(), m_pUser, NULL, NOTHING);
948
949 DEBUG(GetSockName() << " == Disconnected()");
950 if (!m_pUser->IsBeingDeleted() && m_pUser->GetIRCConnectEnabled() &&
951 m_pUser->GetServers().size() != 0) {
952 m_pUser->PutStatus("Disconnected from IRC. Reconnecting...");
953 }
954 m_pUser->ClearRawBuffer();
955 m_pUser->ClearMotdBuffer();
956
957 ResetChans();
958
959 // send a "reset user modes" cmd to the client.
960 // otherwise, on reconnect, it might think it still
961 // had user modes that it actually doesn't have.
962 CString sUserMode;
963 for (set<unsigned char>::const_iterator it = m_scUserModes.begin(); it != m_scUserModes.end(); ++it) {
964 sUserMode += *it;
965 }
966 if (!sUserMode.empty()) {
967 m_pUser->PutUser(":" + m_pUser->GetIRCNick().GetNickMask() + " MODE " + m_pUser->GetIRCNick().GetNick() + " :-" + sUserMode);
968 }
969
970 // also clear the user modes in our space:
971 m_scUserModes.clear();
972 }
973
974 void CIRCSock::SockError(int iErrno) {
975 CString sError;
976
977 if (iErrno == EDOM) {
978 sError = "Your bind host could not be resolved";
979 } else if (iErrno == EADDRNOTAVAIL) {
980 // Csocket uses this if it can't resolve the dest host name
981 // ...but it also does generate this if bind() fails -.-
982 sError = strerror(iErrno);
983 if (GetBindHost().empty())
984 sError += " (Is your IRC server's host name valid?)";
985 else
986 sError += " (Is your IRC server's host name and ZNC bind host valid?)";
987 } else {
988 sError = strerror(iErrno);
989 }
990
991 DEBUG(GetSockName() << " == SockError(" << iErrno << " "
992 << sError << ")");
993 if (!m_pUser->IsBeingDeleted()) {
994 if (GetConState() != CST_OK)
995 m_pUser->PutStatus("Cannot connect to IRC (" +
996 sError + "). Retrying...");
997 else
998 m_pUser->PutStatus("Disconnected from IRC (" +
999 sError + "). Reconnecting...");
1000 }
1001 m_pUser->ClearRawBuffer();
1002 m_pUser->ClearMotdBuffer();
1003
1004 ResetChans();
1005 m_scUserModes.clear();
1006 }
1007
1008 void CIRCSock::Timeout() {
1009 DEBUG(GetSockName() << " == Timeout()");
1010 if (!m_pUser->IsBeingDeleted()) {
1011 m_pUser->PutStatus("IRC connection timed out. Reconnecting...");
1012 }
1013 m_pUser->ClearRawBuffer();
1014 m_pUser->ClearMotdBuffer();
1015
1016 ResetChans();
1017 m_scUserModes.empty();
1018 }
1019
1020 void CIRCSock::ConnectionRefused() {
1021 DEBUG(GetSockName() << " == ConnectionRefused()");
1022 if (!m_pUser->IsBeingDeleted()) {
1023 m_pUser->PutStatus("Connection Refused. Reconnecting...");
1024 }
1025 m_pUser->ClearRawBuffer();
1026 m_pUser->ClearMotdBuffer();
1027 }
1028
1029 void CIRCSock::ReachedMaxBuffer() {
1030 DEBUG(GetSockName() << " == ReachedMaxBuffer()");
1031 m_pUser->PutStatus("Received a too long line from the IRC server!");
1032 Quit();
1033 }
1034
1035 void CIRCSock::ParseISupport(const CString& sLine) {
1036 VCString vsTokens;
1037 VCString::iterator it;
1038
1039 sLine.Split(" ", vsTokens, false);
1040
1041 for (it = vsTokens.begin(); it != vsTokens.end(); ++it) {
1042 CString sName = it->Token(0, false, "=");
1043 CString sValue = it->Token(1, true, "=");
1044
1045 if (sName.Equals("PREFIX")) {
1046 CString sPrefixes = sValue.Token(1, false, ")");
1047 CString sPermModes = sValue.Token(0, false, ")");
1048 sPermModes.TrimLeft("(");
1049
1050 if (!sPrefixes.empty() && sPermModes.size() == sPrefixes.size()) {
1051 m_sPerms = sPrefixes;
1052 m_sPermModes = sPermModes;
1053 }
1054 } else if (sName.Equals("CHANTYPES")) {
1055 m_pUser->SetChanPrefixes(sValue);
1056 } else if (sName.Equals("NICKLEN")) {
1057 unsigned int uMax = sValue.ToUInt();
1058
1059 if (uMax) {
1060 m_uMaxNickLen = uMax;
1061 }
1062 } else if (sName.Equals("CHANMODES")) {
1063 if (!sValue.empty()) {
1064 m_mueChanModes.clear();
1065
1066 for (unsigned int a = 0; a < 4; a++) {
1067 CString sModes = sValue.Token(a, false, ",");
1068
1069 for (unsigned int b = 0; b < sModes.size(); b++) {
1070 m_mueChanModes[sModes[b]] = (EChanModeArgs) a;
1071 }
1072 }
1073 }
1074 } else if (sName.Equals("NAMESX")) {
1075 if (m_bNamesx)
1076 continue;
1077 m_bNamesx = true;
1078 PutIRC("PROTOCTL NAMESX");
1079 } else if (sName.Equals("UHNAMES")) {
1080 if (m_bUHNames)
1081 continue;
1082 m_bUHNames = true;
1083 PutIRC("PROTOCTL UHNAMES");
1084 }
1085 }
1086 }
1087
1088 void CIRCSock::ForwardRaw353(const CString& sLine) const {
1089 vector<CClient*>& vClients = m_pUser->GetClients();
1090 vector<CClient*>::iterator it;
1091
1092 for (it = vClients.begin(); it != vClients.end(); ++it) {
1093 ForwardRaw353(sLine, *it);
1094 }
1095 }
1096
1097 void CIRCSock::ForwardRaw353(const CString& sLine, CClient* pClient) const {
1098 CString sNicks = sLine.Token(5, true);
1099 if (sNicks.Left(1) == ":")
1100 sNicks.LeftChomp();
1101
1102 if ((!m_bNamesx || pClient->HasNamesx()) && (!m_bUHNames || pClient->HasUHNames())) {
1103 // Client and server have both the same UHNames and Namesx stuff enabled
1104 m_pUser->PutUser(sLine, pClient);
1105 } else {
1106 // Get everything except the actual user list
1107 CString sTmp = sLine.Token(0, false, " :") + " :";
1108
1109 VCString vsNicks;
1110 VCString::const_iterator it;
1111
1112 // This loop runs once for every nick on the channel
1113 sNicks.Split(" ", vsNicks, false);
1114 for (it = vsNicks.begin(); it != vsNicks.end(); ++it) {
1115 CString sNick = *it;
1116 if (sNick.empty())
1117 break;
1118
1119 if (m_bNamesx && !pClient->HasNamesx() && IsPermChar(sNick[0])) {
1120 // Server has, client doesn't have NAMESX, so we just use the first perm char
1121 size_t pos = sNick.find_first_not_of(GetPerms());
1122 if (pos >= 2 && pos != CString::npos) {
1123 sNick = sNick[0] + sNick.substr(pos);
1124 }
1125 }
1126
1127 if (m_bUHNames && !pClient->HasUHNames()) {
1128 // Server has, client hasnt UHNAMES,
1129 // so we strip away ident and host.
1130 sNick = sNick.Token(0, false, "!");
1131 }
1132
1133 sTmp += sNick + " ";
1134 }
1135 // Strip away the spaces we inserted at the end
1136 sTmp.TrimRight(" ");
1137 m_pUser->PutUser(sTmp, pClient);
1138 }
1139 }
1140
1141 void CIRCSock::SendAltNick(const CString& sBadNick) {
1142 const CString& sLastNick = m_Nick.GetNick();
1143
1144 // We don't know the maximum allowed nick length yet, but we know which
1145 // nick we sent last. If sBadNick is shorter than that, we assume the
1146 // server truncated our nick.
1147 if (sBadNick.length() < sLastNick.length())
1148 m_uMaxNickLen = sBadNick.length();
1149
1150 unsigned int uMax = m_uMaxNickLen;
1151
1152 const CString& sConfNick = m_pUser->GetNick();
1153 const CString& sAltNick = m_pUser->GetAltNick();
1154 CString sNewNick;
1155
1156 if (sLastNick.Equals(sConfNick)) {
1157 if ((!sAltNick.empty()) && (!sConfNick.Equals(sAltNick))) {
1158 sNewNick = sAltNick;
1159 } else {
1160 sNewNick = sConfNick.Left(uMax - 1) + "-";
1161 }
1162 } else if (sLastNick.Equals(sAltNick)) {
1163 sNewNick = sConfNick.Left(uMax -1) + "-";
1164 } else if (sLastNick.Equals(CString(sConfNick.Left(uMax -1) + "-"))) {
1165 sNewNick = sConfNick.Left(uMax -1) + "|";
1166 } else if (sLastNick.Equals(CString(sConfNick.Left(uMax -1) + "|"))) {
1167 sNewNick = sConfNick.Left(uMax -1) + "^";
1168 } else if (sLastNick.Equals(CString(sConfNick.Left(uMax -1) + "^"))) {
1169 sNewNick = sConfNick.Left(uMax -1) + "a";
1170 } else {
1171 char cLetter = 0;
1172 if (sBadNick.empty()) {
1173 m_pUser->PutUser("No free nick available");
1174 Quit();
1175 return;
1176 }
1177
1178 cLetter = sBadNick.Right(1)[0];
1179
1180 if (cLetter == 'z') {
1181 m_pUser->PutUser("No free nick found");
1182 Quit();
1183 return;
1184 }
1185
1186 sNewNick = sConfNick.Left(uMax -1) + ++cLetter;
1187 }
1188 PutIRC("NICK " + sNewNick);
1189 m_Nick.SetNick(sNewNick);
1190 }
1191
1192 unsigned char CIRCSock::GetPermFromMode(unsigned char uMode) const {
1193 if (m_sPermModes.size() == m_sPerms.size()) {
1194 for (unsigned int a = 0; a < m_sPermModes.size(); a++) {
1195 if (m_sPermModes[a] == uMode) {
1196 return m_sPerms[a];
1197 }
1198 }
1199 }
1200
1201 return 0;
1202 }
1203
1204 CIRCSock::EChanModeArgs CIRCSock::GetModeType(unsigned char uMode) const {
1205 map<unsigned char, EChanModeArgs>::const_iterator it = m_mueChanModes.find(uMode);
1206
1207 if (it == m_mueChanModes.end()) {
1208 return NoArg;
1209 }
1210
1211 return it->second;
1212 }
1213
1214 void CIRCSock::ResetChans() {
1215 for (map<CString, CChan*>::iterator a = m_msChans.begin(); a != m_msChans.end(); ++a) {
1216 a->second->Reset();
1217 }
1218 }
1219