6
CIRCSock::CIRCSock(CZNC* pZNC, CUser* pUser) : Csock() {
10
m_uQueryBufferCount = 50;
11
m_bISpoofReleased = false;
15
m_RawBuffer.SetLineCount(100); // This should be more than enough raws, especially since we are asking the server to resend MOTD
16
m_Nick.SetIdent(pUser->GetIdent());
17
m_Nick.SetHost(pUser->GetVHost());
20
CIRCSock::~CIRCSock() {
21
for (map<string, CChan*>::iterator a = m_msChans.begin(); a != m_msChans.end(); a++) {
25
if (!m_bISpoofReleased) {
26
m_pZNC->ReleaseISpoof();
33
void CIRCSock::ReadLine(const string& sData) {
36
while ((CUtils::Right(sLine, 1) == "\r") || (CUtils::Right(sLine, 1) == "\n")) {
37
CUtils::RightChomp(sLine);
40
DEBUG_ONLY(cout << GetSockName() << " <- [" << sLine << "]" << endl);
43
if (m_pUser->GetModules().OnRaw(sLine)) {
48
if (strncasecmp(sLine.c_str(), "PING ", 5) == 0) {
49
PutServ("PONG " + sLine.substr(5));
50
} else if (CUtils::WildCmp(":* * *", sLine.c_str())) { //"^:(\\S+) (\\d\\d\\d) (.*?) (.*)$", vCap)) {
51
string sCmd = CUtils::Token(sLine, 1);
53
if ((sCmd.length() == 3) && (isdigit(sCmd[0])) && (isdigit(sCmd[1])) && (isdigit(sCmd[2]))) {
54
string sServer = CUtils::Token(sLine, 0); CUtils::LeftChomp(sServer);
55
unsigned int uRaw = strtoul(sCmd.c_str(), NULL, 10);
56
string sNick = CUtils::Token(sLine, 2);
57
string sRest = CUtils::Token(sLine, 3, true);
60
case 1: {// :irc.server.com 001 nick :Welcome to the Internet Relay Network nick
61
SetTimeout(900); // Now that we are connected, let nature take its course
62
PutServ("WHO " + sNick);
64
m_pUser->GetModules().OnIRCConnected();
67
m_pUser->PutStatus("Connected!");
70
string sClientNick = m_pUserSock->GetNick();
71
if (strcasecmp(sClientNick.c_str(), sNick.c_str()) != 0) {
72
// If they connected with a nick that doesn't match the one we got on irc, then we need to update them
73
PutUser(":" + sClientNick + "!" + m_Nick.GetIdent() + "@" + m_Nick.GetHost() + " NICK :" + sNick);
80
m_RawBuffer.AddLine(":" + sServer + " " + sCmd + " ", " " + sRest);
82
// Now that we are connected, we need to join our chans
83
const vector<CChan*>& vChans = m_pUser->GetChans();
85
for (unsigned int a = 0; a < vChans.size(); a++) {
86
PutServ("JOIN " + vChans[a]->GetName() + " " + vChans[a]->GetKey());
89
m_pZNC->ReleaseISpoof();
90
m_bISpoofReleased = true;
98
case 250: // highest connection count
99
case 251: // user count
100
case 252: // oper count
101
case 254: // channel count
102
case 255: // client count
103
case 265: // local users
104
case 266: // global users
105
m_RawBuffer.AddLine(":" + sServer + " " + sCmd + " ", " " + sRest);
108
case 375: // begin motd
109
case 376: // end motd
111
case 471: // :irc.server.net 471 nick #chan :Cannot join channel (+l)
112
case 473: // :irc.server.net 473 nick #chan :Cannot join channel (+i)
113
case 475: // :irc.server.net 475 nick #chan :Cannot join channel (+k)
115
CChan* pChan = m_pUser->FindChan(sRest.substr(0, sRest.find(' ')));
117
if ((pChan) && (!pChan->IsOn())) {
118
if (!pChan->DecClientRequests()) {
126
string sBadNick = CUtils::Token(sRest, 0);
127
string sConfNick = m_pUser->GetNick();
130
string sAltNick = m_pUser->GetAltNick();
132
if (strcasecmp(sBadNick.c_str(), sConfNick.c_str()) == 0) {
133
if ((!sAltNick.empty()) && (strcasecmp(sConfNick.c_str(), sAltNick.c_str()) != 0)) {
134
PutServ("NICK " + sAltNick);
136
PutServ("NICK " + CUtils::Left(sConfNick, 8) + "-");
138
} else if (strcasecmp(sBadNick.c_str(), sAltNick.c_str()) == 0) {
139
PutServ("NICK " + CUtils::Left(sConfNick, 8) + "-");
140
} else if (strcasecmp(sBadNick.c_str(), string(CUtils::Left(sConfNick, 8) + "-").c_str()) == 0) {
141
PutServ("NICK " + CUtils::Left(sConfNick, 8) + "|");
142
} else if (strcasecmp(sBadNick.c_str(), string(CUtils::Left(sConfNick, 8) + "|").c_str()) == 0) {
143
PutServ("NICK " + CUtils::Left(sConfNick, 8) + "^");
146
if (sBadNick.empty()) {
151
cLetter = CUtils::Right(sBadNick, 1)[0];
153
if (cLetter == 'z') {
158
string sSend = "NICK " + CUtils::Left(sConfNick, 8) + cLetter++;
164
// :irc.server.net 433 mynick badnick :Nickname is already in use.
165
if ((m_bKeepNick) && (m_pUser->KeepNick())) {
166
if (strcasecmp(sBadNick.c_str(), sConfNick.c_str()) == 0) {
167
if ((!m_pUserSock) || (!m_pUserSock->DecKeepNickCounter())) {
176
// :irc.server.com 315 yournick #chan :End of /WHO list.
177
CChan* pChan = m_pUser->FindChan(CUtils::Token(sLine, 3));
184
// :irc.yourserver.com 352 yournick #chan ident theirhost.com irc.theirserver.com theirnick H :0 Real Name
185
string sNick = CUtils::Token(sLine, 7);
186
string sIdent = CUtils::Token(sLine, 4);
187
string sHost = CUtils::Token(sLine, 5);
189
if (strcasecmp(sNick.c_str(), GetNick().c_str()) == 0) {
190
m_Nick.SetIdent(sIdent);
191
m_Nick.SetHost(sHost);
194
const vector<CChan*>& vChans = m_pUser->GetChans();
196
for (unsigned int a = 0; a < vChans.size(); a++) {
197
vChans[a]->OnWho(sNick, sIdent, sHost);
204
CChan* pChan = m_pUser->FindChan(CUtils::Token(sRest, 0));
207
pChan->SetModes(CUtils::Token(sRest, 1, true));
213
// Todo: allow for non @+= server msgs
214
CChan* pChan = m_pUser->FindChan(CUtils::Token(sRest, 1));
216
string sNicks = CUtils::Token(sRest, 2, true);
217
if (CUtils::Left(sNicks, 1) == ":") {
218
CUtils::LeftChomp(sNicks);
221
pChan->AddNicks(sNicks);
225
case 366: { // end of names list
226
PutUser(sLine); // First send them the raw
228
// :irc.server.com 366 nick #chan :End of /NAMES list.
229
CChan* pChan = m_pUser->FindChan(CUtils::Token(sRest, 0));
232
if (IsUserAttached()) {
233
const vector<string>& vsBuffer = pChan->GetBuffer();
235
if (vsBuffer.size()) {
236
PutUser(":***!znc@znc.com PRIVMSG " + pChan->GetName() + " :Buffer Playback...");
238
for (unsigned int a = 0; a < vsBuffer.size(); a++) {
239
PutUser(vsBuffer[a]);
242
if (!pChan->KeepBuffer()) {
243
pChan->ClearBuffer();
246
PutUser(":***!znc@znc.com PRIVMSG " + pChan->GetName() + " :Playback Complete.");
250
if (!pChan->IsOn()) {
251
pChan->SetIsOn(true);
252
PutServ("MODE " + pChan->GetName());
254
// If we are the only one in the chan, set our default modes
255
if (pChan->GetNickCount() == 1) {
256
string sModes = pChan->GetDefaultModes();
258
if (sModes.empty()) {
259
sModes = m_pUser->GetDefaultChanModes();
262
if (!sModes.empty()) {
263
PutServ("MODE " + pChan->GetName() + " " + sModes);
269
return; // return so we don't send them the raw twice
272
} else { //if (CUtils::WildCmp(":*!*@* * *", sLine.c_str())) {
273
string sNickMask = CUtils::Token(sLine, 0);
274
CUtils::LeftChomp(sNickMask);
276
string sNick = CUtils::Token(sNickMask, 0, false, '!');
277
string sCmd = CUtils::Token(sLine, 1);
278
string sRest = CUtils::Token(sLine, 2, true);
280
if (strcasecmp(sCmd.c_str(), "NICK") == 0) {
281
string sNewNick = sRest;
282
if (CUtils::Left(sNewNick, 1) == ":") {
283
CUtils::LeftChomp(sNewNick);
286
const vector<CChan*>& vChans = m_pUser->GetChans();
287
for (unsigned int a = 0; a < vChans.size(); a++) {
288
vChans[a]->ChangeNick(sNick, sNewNick);
291
if (strcasecmp(sNick.c_str(), GetNick().c_str()) == 0) {
293
if (strcasecmp(sNick.c_str(), m_pUser->GetNick().c_str()) == 0) {
294
// If the user changes his nick away from the config nick, we shut off keepnick for this session
297
} else if (strcasecmp(sNick.c_str(), m_pUser->GetNick().c_str()) == 0) {
301
m_pUser->GetModules().OnNick(sNickMask, sNewNick);
303
} else if (strcasecmp(sCmd.c_str(), "QUIT") == 0) {
304
string sChan = sRest;
305
if (CUtils::Left(sChan, 1) == ":") {
306
CUtils::LeftChomp(sChan);
309
// :nick!ident@host.com QUIT :message
310
CNick Nick(sNickMask);
312
const vector<CChan*>& vChans = m_pUser->GetChans();
313
for (unsigned int a = 0; a < vChans.size(); a++) {
314
vChans[a]->RemNick(sNick);
317
if (strcasecmp(Nick.GetNick().c_str(), m_pUser->GetNick().c_str()) == 0) {
322
m_pUser->GetModules().OnQuit(Nick, sChan);
324
} else if (strcasecmp(sCmd.c_str(), "JOIN") == 0) {
325
string sChan = sRest;
326
if (CUtils::Left(sChan, 1) == ":") {
327
CUtils::LeftChomp(sChan);
330
if (strcasecmp(sNick.c_str(), GetNick().c_str()) == 0) {
331
m_pUser->AddChan(sChan);
334
CChan* pChan = m_pUser->FindChan(sChan);
336
pChan->AddNick(sNickMask);
338
m_pUser->GetModules().OnJoin(sNickMask, *pChan);
341
} else if (strcasecmp(sCmd.c_str(), "PART") == 0) {
342
string sChan = sRest;
343
if (CUtils::Left(sChan, 1) == ":") {
344
CUtils::LeftChomp(sChan);
347
CChan* pChan = m_pUser->FindChan(sChan);
349
pChan->RemNick(sNick);
351
m_pUser->GetModules().OnPart(sNickMask, *pChan);
355
if (strcasecmp(sNick.c_str(), GetNick().c_str()) == 0) {
356
m_pUser->DelChan(sChan);
358
} else if (strcasecmp(sCmd.c_str(), "MODE") == 0) {
359
string sChan = CUtils::Token(sRest, 0);
360
string sModes = CUtils::Token(sRest, 1, true);
362
CChan* pChan = m_pUser->FindChan(sChan);
364
pChan->ModeChange(sModes, sNick);
366
} else if (strcasecmp(sCmd.c_str(), "KICK") == 0) {
367
// :opnick!ident@host.com KICK #chan nick :msg
368
string sChan = CUtils::Token(sRest, 0);
369
string sKickedNick = CUtils::Token(sRest, 1);
370
string sMsg = CUtils::Token(sRest, 2, true);
371
CUtils::LeftChomp(sMsg);
373
CChan* pChan = m_pUser->FindChan(sChan);
376
pChan->RemNick(sKickedNick);
378
m_pUser->GetModules().OnKick(sNickMask, sKickedNick, *pChan, sMsg);
382
if (strcasecmp(GetNick().c_str(), sKickedNick.c_str()) == 0) {
386
sKey = pChan->GetKey();
387
pChan->SetIsOn(false);
390
PutServ("JOIN " + sChan + " " + sKey);
392
} else if (strcasecmp(sCmd.c_str(), "NOTICE") == 0) {
393
// :nick!ident@host.com NOTICE #chan :Message
395
CNick Nick(sNickMask);
397
string sTarget = CUtils::Token(sRest, 0);
398
string sMsg = CUtils::Token(sRest, 1, true);
399
CUtils::LeftChomp(sMsg);
401
if (CUtils::WildCmp("\001*\001", sMsg.c_str())) {
402
CUtils::LeftChomp(sMsg);
403
CUtils::RightChomp(sMsg);
405
if (strcasecmp(sTarget.c_str(), GetNick().c_str()) == 0) {
406
if (OnCTCPReply(sNickMask, sMsg)) {
411
PutUser(":" + sNickMask + " NOTICE " + sTarget + " :\001" + sMsg + "\001");
414
if (strcasecmp(sTarget.c_str(), GetNick().c_str()) == 0) {
415
if (OnPrivNotice(sNickMask, sMsg)) {
419
if (OnChanNotice(sNickMask, sTarget, sMsg)) {
425
PutUser(":" + sNickMask + " NOTICE " + sTarget + " :" + sMsg);
427
} else if (strcasecmp(sCmd.c_str(), "PRIVMSG") == 0) {
428
// :nick!ident@host.com PRIVMSG #chan :Message
430
CNick Nick(sNickMask);
431
string sTarget = CUtils::Token(sRest, 0);
432
string sMsg = CUtils::Token(sRest, 1, true);
434
if (CUtils::Left(sMsg, 1) == ":") {
435
CUtils::LeftChomp(sMsg);
438
if (CUtils::WildCmp("\001*\001", sMsg.c_str())) {
439
CUtils::LeftChomp(sMsg);
440
CUtils::RightChomp(sMsg);
442
if (strcasecmp(sTarget.c_str(), GetNick().c_str()) == 0) {
443
if (OnPrivCTCP(sNickMask, sMsg)) {
447
if (OnChanCTCP(sNickMask, sTarget, sMsg)) {
452
PutUser(":" + sNickMask + " PRIVMSG " + sTarget + " :\001" + sMsg + "\001");
455
if (strcasecmp(sTarget.c_str(), GetNick().c_str()) == 0) {
456
if (OnPrivMsg(sNickMask, sMsg)) {
460
if (OnChanMsg(sNickMask, sTarget, sMsg)) {
465
PutUser(":" + sNickMask + " PRIVMSG " + sTarget + " :" + sMsg);
475
void CIRCSock::KeepNick() {
476
const string& sConfNick = m_pUser->GetNick();
478
if ((m_bAuthed) && (m_bKeepNick) && (m_pUser->KeepNick()) && (strcasecmp(GetNick().c_str(), sConfNick.c_str()) != 0)) {
479
PutServ("NICK " + sConfNick);
483
bool CIRCSock::OnCTCPReply(const string& sNickMask, string& sMessage) {
485
if (m_pUser->GetModules().OnCTCPReply(sNickMask, sMessage)) {
493
bool CIRCSock::OnPrivCTCP(const string& sNickMask, string& sMessage) {
495
if (m_pUser->GetModules().OnPrivCTCP(sNickMask, sMessage)) {
500
// DCC CHAT chat 2453612361 44592
501
if (strncasecmp(sMessage.c_str(), "DCC ", 4) == 0) {
502
string sType = CUtils::Token(sMessage, 1);
503
string sFile = CUtils::Token(sMessage, 2);
504
unsigned long uLongIP = strtoul(CUtils::Token(sMessage, 3).c_str(), NULL, 10);
505
unsigned short uPort = strtoul(CUtils::Token(sMessage, 4).c_str(), NULL, 10);
506
unsigned long uFileSize = strtoul(CUtils::Token(sMessage, 5).c_str(), NULL, 10);
508
if (strcasecmp(sType.c_str(), "CHAT") == 0) {
510
CNick FromNick(sNickMask);
511
unsigned short uBNCPort = CDCCBounce::DCCRequest(FromNick.GetNick(), uLongIP, uPort, "", true, m_pUser, GetLocalIP(), CUtils::GetIP(uLongIP));
514
PutUser(":" + sNickMask + " PRIVMSG " + GetNick() + " :\001DCC CHAT chat " + CUtils::ToString(CUtils::GetLongIP(GetLocalIP())) + " " + CUtils::ToString(uBNCPort) + "\001");
517
} else if (strcasecmp(sType.c_str(), "SEND") == 0) {
518
// DCC SEND readme.txt 403120438 5550 1104
519
CNick FromNick(sNickMask);
521
unsigned short uBNCPort = CDCCBounce::DCCRequest(FromNick.GetNick(), uLongIP, uPort, sFile, false, m_pUser, GetLocalIP(), CUtils::GetIP(uLongIP));
523
PutUser(":" + sNickMask + " PRIVMSG " + GetNick() + " :\001DCC SEND " + sFile + " " + CUtils::ToString(CUtils::GetLongIP(GetLocalIP())) + " " + CUtils::ToString(uBNCPort) + " " + CUtils::ToString(uFileSize));
525
} else if (strcasecmp(sType.c_str(), "RESUME") == 0) {
526
// Need to lookup the connection by port, filter the port, and forward to the user
527
CDCCBounce* pSock = (CDCCBounce*) m_pZNC->GetManager().FindSockByLocalPort(atoi(CUtils::Token(sMessage, 3).c_str()));
529
if ((pSock) && (strncasecmp(pSock->GetSockName().c_str(), "DCC::", 5) == 0)) {
530
PutUser(":" + sNickMask + " PRIVMSG " + GetNick() + " :\001DCC " + sType + " " + sFile + " " + CUtils::ToString(pSock->GetUserPort()) + " " + CUtils::Token(sMessage, 4) + "\001");
532
} else if (strcasecmp(sType.c_str(), "ACCEPT") == 0) {
533
// Need to lookup the connection by port, filter the port, and forward to the user
534
TSocketManager<Csock>& Manager = m_pZNC->GetManager();
536
for (unsigned int a = 0; a < Manager.size(); a++) {
537
CDCCBounce* pSock = (CDCCBounce*) Manager[a];
539
if ((pSock) && (strncasecmp(pSock->GetSockName().c_str(), "DCC::", 5) == 0)) {
540
if (pSock->GetUserPort() == atoi(CUtils::Token(sMessage, 3).c_str())) {
541
PutUser(":" + sNickMask + " PRIVMSG " + GetNick() + " :\001DCC " + sType + " " + sFile + " " + CUtils::ToString(pSock->GetLocalPort()) + " " + CUtils::Token(sMessage, 4) + "\001");
553
bool CIRCSock::OnPrivNotice(const string& sNickMask, string& sMessage) {
555
if (m_pUser->GetModules().OnPrivNotice(sNickMask, sMessage)) {
560
// If the user is detached, add to the buffer
561
m_QueryBuffer.AddLine(":" + sNickMask + " NOTICE ", " :" + sMessage);
567
bool CIRCSock::OnPrivMsg(const string& sNickMask, string& sMessage) {
569
if (m_pUser->GetModules().OnPrivMsg(sNickMask, sMessage)) {
574
// If the user is detached, add to the buffer
575
m_QueryBuffer.AddLine(":" + sNickMask + " PRIVMSG ", " :" + sMessage);
581
bool CIRCSock::OnChanCTCP(const string& sNickMask, const string& sChan, string& sMessage) {
582
CChan* pChan = m_pUser->FindChan(sChan);
585
if (m_pUser->GetModules().OnChanCTCP(sNickMask, *pChan, sMessage)) {
594
bool CIRCSock::OnChanNotice(const string& sNickMask, const string& sChan, string& sMessage) {
595
CChan* pChan = m_pUser->FindChan(sChan);
598
if (m_pUser->GetModules().OnChanNotice(sNickMask, *pChan, sMessage)) {
602
if ((pChan->KeepBuffer()) || (!m_pUserSock)) {
603
pChan->AddBuffer(":" + sNickMask + " NOTICE " + sChan + " :" + sMessage);
610
bool CIRCSock::OnChanMsg(const string& sNickMask, const string& sChan, string& sMessage) {
611
CChan* pChan = m_pUser->FindChan(sChan);
614
if (m_pUser->GetModules().OnChanMsg(sNickMask, *pChan, sMessage)) {
618
if ((pChan->KeepBuffer()) || (!m_pUserSock)) {
619
pChan->AddBuffer(":" + sNickMask + " PRIVMSG " + sChan + " :" + sMessage);
626
void CIRCSock::UserConnected(CUserSock* pUserSock) {
628
m_pUserSock->BouncedOff();
631
m_pUserSock = pUserSock;
633
if (m_RawBuffer.IsEmpty()) {
634
PutUser(":irc.znc.com 001 " + m_pUserSock->GetNick() + " :- Welcome to ZNC -");
636
unsigned int uIdx = 0;
639
while (m_RawBuffer.GetLine(GetNick(), sLine, uIdx++)) {
646
const vector<CChan*>& vChans = m_pUser->GetChans();
647
for (unsigned int a = 0; a < vChans.size(); a++) {
648
if (vChans[a]->IsOn()) {
649
PutUser(":" + m_Nick.GetNickMask() + " JOIN :" + vChans[a]->GetName());
650
PutServ("NAMES " + vChans[a]->GetName());
657
while (m_QueryBuffer.GetNextLine(GetNick(), sBufLine)) {
662
void CIRCSock::UserDisconnected() {
666
void CIRCSock::PutServ(const string& sLine) {
667
DEBUG_ONLY(cout << GetSockName() << " -> [" << sLine << "]" << endl);
668
Write(sLine + "\r\n");
671
void CIRCSock::PutUser(const string& sLine) {
673
m_pUserSock->PutServ(sLine);
677
void CIRCSock::PutStatus(const string& sLine) {
679
m_pUserSock->PutStatus(sLine);
683
void CIRCSock::SetNick(const string& sNick) {
684
m_Nick.SetNick(sNick);
687
m_pUserSock->SetNick(sNick);
691
void CIRCSock::Connected() {
692
DEBUG_ONLY(cout << GetSockName() << " == Connected()" << endl);
694
CUserSock* pUserSock = (CUserSock*) m_pZNC->FindSockByName("USR::" + m_pUser->GetUserName());
697
m_pUserSock = pUserSock;
698
pUserSock->IRCConnected(this);
701
if (!m_sPass.empty()) {
702
PutServ("PASS " + m_sPass);
705
PutServ("NICK " + m_pUser->GetNick());
706
PutServ("USER " + m_pUser->GetIdent() + " \"" + m_pUser->GetIdent() + "\" \"" + m_pUser->GetIdent() + "\" :" + m_pUser->GetRealName());
709
void CIRCSock::Disconnected() {
710
DEBUG_ONLY(cout << GetSockName() << " == Disconnected()" << endl);
711
m_pUser->PutStatus("Disconnected from IRC. Reconnecting...");
714
m_pUser->GetModules().OnIRCDisconnected();
717
const vector<CChan*>& vChans = m_pUser->GetChans();
718
for (unsigned int a = 0; a < vChans.size(); a++) {
719
vChans[a]->SetIsOn(false);
723
m_pUserSock->IRCDisconnected();
728
void CIRCSock::SockError(int iErrno) {
729
DEBUG_ONLY(cout << GetSockName() << " == SockError(" << iErrno << ")" << endl);
730
m_pUser->PutStatus("Disconnected from IRC. Reconnecting...");
733
void CIRCSock::Timeout() {
734
DEBUG_ONLY(cout << GetSockName() << " == Timeout()" << endl);
735
m_pUser->PutStatus("IRC connection timed out. Reconnecting...");
738
void CIRCSock::ConnectionRefused() {
739
DEBUG_ONLY(cout << GetSockName() << " == ConnectionRefused()" << endl);
740
m_pUser->PutStatus("Connection Refused. Reconnecting...");