]> jfr.im git - irc/rizon/znc.git/blame - Client.cpp
Write forceserver and webircpassword to conf
[irc/rizon/znc.git] / Client.cpp
CommitLineData
a09a7e79 1/*
b9b0fd4c 2 * Copyright (C) 2004-2011 See the AUTHORS file for details.
a09a7e79 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 */
6dcacaa7 8
712d4006 9#include "Client.h"
e72c4456 10#include "Chan.h"
3f24f287 11#include "FileUtils.h"
e72c4456 12#include "IRCSock.h"
e72c4456 13#include "User.h"
9e6d05a0 14#include "znc.h"
ad92c58c 15#include "WebModules.h"
538d3ece 16
99f1efc8 17#define CALLMOD(MOD, CLIENT, USER, FUNC) { \
18 CModule* pModule = CZNC::Get().GetModules().FindModule(MOD); \
19 if (pModule) { \
20 try { \
21 pModule->SetClient(CLIENT); \
22 pModule->SetUser(USER); \
23 pModule->FUNC; \
24 pModule->SetClient(NULL); \
25 pModule->SetUser(NULL); \
26 } catch (CModule::EModException e) { \
27 if (e == CModule::UNLOAD) { \
28 CZNC::Get().GetModules().UnloadModule(MOD); \
29 } \
30 } \
31 } else { \
32 pModule = (USER)->GetModules().FindModule(MOD); \
33 if (pModule) { \
34 try { \
35 pModule->SetClient(CLIENT); \
36 pModule->FUNC; \
37 pModule->SetClient(NULL); \
38 } catch (CModule::EModException e) { \
39 if (e == CModule::UNLOAD) { \
40 (USER)->GetModules().UnloadModule(MOD); \
41 } \
42 } \
43 } else { \
44 PutStatus("No such module [" + MOD + "]"); \
45 } \
46 } \
d6c34d24 47}
ecabbd2b 48
b772e266 49CClient::~CClient() {
50 if (!m_spAuth.IsNull()) {
51 CClientAuth* pAuth = (CClientAuth*) &(*m_spAuth);
b0e59f12 52 pAuth->Invalidate();
b772e266 53 }
54 if (m_pUser != NULL) {
55 m_pUser->AddBytesRead(GetBytesRead());
56 m_pUser->AddBytesWritten(GetBytesWritten());
57 }
58}
59
40b5a5cd
KF
60void CClient::SendRequiredPasswordNotice() {
61 PutClient(":irc.znc.in 464 " + GetNick() + " :Password required");
62 PutClient(":irc.znc.in NOTICE AUTH :*** "
63 "You need to send your password. "
64 "Try /quote PASS <username>:<password>");
65}
66
712d4006 67void CClient::ReadLine(const CString& sData) {
beb5b49b 68 CString sLine = sData;
538d3ece 69
9933ba9c 70 sLine.TrimRight("\n\r");
538d3ece 71
235b10c2 72 DEBUG("(" << ((m_pUser) ? m_pUser->GetUserName() : GetRemoteIP()) << ") CLI -> ZNC [" << sLine << "]");
538d3ece 73
e7bb3e5d 74 if (IsAttached()) {
b740d7cc 75 MODULECALL(OnUserRaw(sLine), m_pUser, this, return);
d84b9c6e 76 } else {
47a5ab37 77 GLOBALMODULECALL(OnUnknownUserRaw(sLine), m_pUser, this, return);
538d3ece 78 }
538d3ece 79
24950d24 80 CString sCommand = sLine.Token(0);
cd831837 81 if (sCommand.Left(1) == ":") {
82 // Evil client! Sending a nickmask prefix on client's command
83 // is bad, bad, bad, bad, bad, bad, bad, bad, BAD, B A D!
84 sLine = sLine.Token(1, true);
85 sCommand = sLine.Token(0);
86 }
538d3ece 87
5237a247 88 if (sCommand.Equals("PASS")) {
e7bb3e5d 89 if (!IsAttached()) {
57f66b5e 90 m_bGotPass = true;
24950d24 91 m_sPass = sLine.Token(1);
35394275 92 if (m_sPass.Left(1) == ":")
93 m_sPass.LeftChomp();
538d3ece 94
beb5b49b 95 if (m_sPass.find(":") != CString::npos) {
3e0c33b0 96 m_sUser = m_sPass.Token(0, false, ":");
97 m_sPass = m_sPass.Token(1, true, ":");
57f66b5e 98 }
538d3ece 99
f6f4e6a6 100 AuthUser();
99f1efc8 101 return; // Don't forward this msg. ZNC has already registered us.
7f1feb3c 102 }
5237a247 103 } else if (sCommand.Equals("NICK")) {
24950d24 104 CString sNick = sLine.Token(1);
078bbcf0 105 if (sNick.Left(1) == ":") {
106 sNick.LeftChomp();
538d3ece 107 }
108
e7bb3e5d 109 if (!IsAttached()) {
538d3ece 110 m_sNick = sNick;
111 m_bGotNick = true;
112
f6f4e6a6 113 AuthUser();
99f1efc8 114 return; // Don't forward this msg. ZNC will handle nick changes until auth is complete
538d3ece 115 }
5237a247 116 } else if (sCommand.Equals("USER")) {
e7bb3e5d 117 if (!IsAttached()) {
7f1feb3c 118 if (m_sUser.empty()) {
119 m_sUser = sLine.Token(1);
120 }
538d3ece 121
7f1feb3c 122 m_bGotUser = true;
57f66b5e 123
f6f4e6a6 124 if (m_bGotPass) {
7f1feb3c 125 AuthUser();
40b5a5cd
KF
126 } else if (!m_bInCap) {
127 SendRequiredPasswordNotice();
7f1feb3c 128 }
538d3ece 129
99f1efc8 130 return; // Don't forward this msg. ZNC has already registered us.
7f1feb3c 131 }
99ea9c6f 132 } else if (sCommand.Equals("CAP")) {
133 HandleCap(sLine);
134
135 // Don't let the client talk to the server directly about CAP,
7bb4ed34 136 // we don't want anything enabled that ZNC does not support.
99ea9c6f 137 return;
fb9a062f 138 }
139
140 if (!m_pUser) {
99ea9c6f 141 // Only CAP, NICK, USER and PASS are allowed before login
2390a315 142 return;
143 }
144
5237a247 145 if (sCommand.Equals("ZNC")) {
051e1f81 146 CString sTarget = sLine.Token(1);
147 CString sModCommand;
148
149 if (sTarget.TrimPrefix(m_pUser->GetStatusPrefix())) {
150 sModCommand = sLine.Token(2, true);
151 } else {
152 sTarget = "status";
153 sModCommand = sLine.Token(1, true);
154 }
155
5237a247 156 if (sTarget.Equals("status")) {
051e1f81 157 if (sModCommand.empty())
158 PutStatus("Hello. How may I help you?");
159 else
160 UserCommand(sModCommand);
161 } else {
051e1f81 162 if (sModCommand.empty())
163 CALLMOD(sTarget, this, m_pUser, PutModule("Hello. How may I help you?"))
164 else
165 CALLMOD(sTarget, this, m_pUser, OnModCommand(sModCommand))
051e1f81 166 }
2390a315 167 return;
5237a247 168 } else if (sCommand.Equals("DETACH")) {
9fecae45 169 CString sChannels = sLine.Token(1).TrimPrefix_n(":");
2390a315 170
9fecae45 171 if (sChannels.empty()) {
cc00aa23 172 PutStatusNotice("Usage: /detach <#chan>");
173 return;
174 }
2390a315 175
9fecae45
NL
176 VCString vChans;
177 sChannels.Split(",", vChans, false);
178 sChannels.clear();
179
180 for (VCString::const_iterator channelIterator = vChans.begin();
181 channelIterator != vChans.end();
182 ++channelIterator)
183 {
184 CString sChannel = *channelIterator;
185
186 CChan *pChannel = m_pUser->FindChan(sChannel);
187 if (pChannel) {
188 pChannel->DetachUser();
189 } else {
190 PutStatusNotice("You are not on [" + sChannel + "]");
191 }
2390a315 192 }
cc00aa23 193
cc00aa23 194 return;
5237a247 195 } else if (sCommand.Equals("PING")) {
bc40713b 196 // All PONGs are generated by znc. We will still forward this to
197 // the ircd, but all PONGs from irc will be blocked.
e86008ff 198 if (sLine.length() >= 5)
199 PutClient(":irc.znc.in PONG irc.znc.in " + sLine.substr(5));
200 else
201 PutClient(":irc.znc.in PONG irc.znc.in");
5237a247 202 } else if (sCommand.Equals("PONG")) {
bc40713b 203 // Block PONGs, we already responded to the pings
204 return;
5237a247 205 } else if (sCommand.Equals("JOIN")) {
fb9a062f 206 CString sChans = sLine.Token(1);
41b5ea43 207 CString sKey = sLine.Token(2);
208
fb9a062f 209 if (sChans.Left(1) == ":") {
210 sChans.LeftChomp();
538d3ece 211 }
212
cc00aa23 213 VCString vChans;
214 sChans.Split(",", vChans, false);
215 sChans.clear();
fb9a062f 216
cc00aa23 217 for (unsigned int a = 0; a < vChans.size(); a++) {
218 CString sChannel = vChans[a];
219 MODULECALL(OnUserJoin(sChannel, sKey), m_pUser, this, continue);
fb9a062f 220
cc00aa23 221 CChan* pChan = m_pUser->FindChan(sChannel);
fb9a062f 222
cc00aa23 223 if (pChan) {
224 pChan->JoinUser(false, sKey);
225 continue;
fb9a062f 226 }
538d3ece 227
cc00aa23 228 if (!sChannel.empty()) {
229 sChans += (sChans.empty()) ? sChannel : CString("," + sChannel);
538d3ece 230 }
cc00aa23 231 }
fb9a062f 232
cc00aa23 233 if (sChans.empty()) {
234 return;
235 }
fb9a062f 236
cc00aa23 237 sLine = "JOIN " + sChans;
238
239 if (!sKey.empty()) {
240 sLine += " " + sKey;
538d3ece 241 }
5237a247 242 } else if (sCommand.Equals("PART")) {
7ae31aad
NL
243 CString sChans = sLine.Token(1).TrimPrefix_n(":");
244 CString sMessage = sLine.Token(2, true).TrimPrefix_n(":");
fb9a062f 245
7ae31aad
NL
246 VCString vChans;
247 sChans.Split(",", vChans, false);
248 sChans.clear();
fb9a062f 249
7ae31aad
NL
250 for (VCString::const_iterator it = vChans.begin(); it != vChans.end(); ++it) {
251 CString sChan = *it;
252 MODULECALL(OnUserPart(sChan, sMessage), m_pUser, this, continue)
fb9a062f 253
7ae31aad
NL
254 CChan* pChan = m_pUser->FindChan(sChan);
255
256 if (pChan && !pChan->IsOn()) {
257 PutStatusNotice("Removing channel [" + sChan + "]");
258 m_pUser->DelChan(sChan);
259 return;
260 } else {
261 sChans += (sChans.empty()) ? sChan : CString("," + sChan);
262 }
263 }
fc06004d 264
7ae31aad 265 if (sChans.empty()) {
cc00aa23 266 return;
fc06004d 267 }
fb9a062f 268
7ae31aad 269 sLine = "PART " + sChans;
fb9a062f 270
271 if (!sMessage.empty()) {
272 sLine += " :" + sMessage;
273 }
5237a247 274 } else if (sCommand.Equals("TOPIC")) {
442ef47c 275 CString sChan = sLine.Token(1);
276 CString sTopic = sLine.Token(2, true);
442ef47c 277
0316c6a1 278 if (!sTopic.empty()) {
279 if (sTopic.Left(1) == ":")
280 sTopic.LeftChomp();
281 MODULECALL(OnUserTopic(sChan, sTopic), m_pUser, this, return);
282 sLine = "TOPIC " + sChan + " :" + sTopic;
283 } else {
284 MODULECALL(OnUserTopicRequest(sChan), m_pUser, this, return);
442ef47c 285 }
5237a247 286 } else if (sCommand.Equals("MODE")) {
0a622749 287 CString sTarget = sLine.Token(1);
288 CString sModes = sLine.Token(2, true);
289
cc00aa23 290 if (m_pUser->IsChan(sTarget)) {
3b8134c3 291 CChan *pChan = m_pUser->FindChan(sTarget);
0a622749 292
bc77d8b2 293 // If we are on that channel and already received a
294 // /mode reply from the server, we can answer this
295 // request ourself.
296 if (pChan && pChan->IsOn() && sModes.empty() && !pChan->GetModeString().empty()) {
3b8134c3 297 PutClient(":" + m_pUser->GetIRCServer() + " 324 " + GetNick() + " " + sTarget + " " + pChan->GetModeString());
298 if (pChan->GetCreationDate() > 0) {
299 PutClient(":" + m_pUser->GetIRCServer() + " 329 " + GetNick() + " " + sTarget + " " + CString(pChan->GetCreationDate()));
300 }
d97ef37d 301 return;
3b8134c3 302 }
0a622749 303 }
5237a247 304 } else if (sCommand.Equals("QUIT")) {
cc00aa23 305 m_pUser->UserDisconnected(this);
538d3ece 306
99f1efc8 307 Close(Csock::CLT_AFTERWRITE); // Treat a client quit as a detach
308 return; // Don't forward this msg. We don't want the client getting us disconnected.
5237a247 309 } else if (sCommand.Equals("PROTOCTL")) {
5d9a22f6 310 VCString vsTokens;
311 VCString::const_iterator it;
312 sLine.Token(1, true).Split(" ", vsTokens, false);
313
314 for (it = vsTokens.begin(); it != vsTokens.end(); ++it) {
315 if (*it == "NAMESX") {
0a622749 316 m_bNamesx = true;
5d9a22f6 317 } else if (*it == "UHNAMES") {
0a622749 318 m_bUHNames = true;
319 }
0a622749 320 }
99f1efc8 321 return; // If the server understands it, we already enabled namesx / uhnames
5237a247 322 } else if (sCommand.Equals("NOTICE")) {
24950d24 323 CString sTarget = sLine.Token(1);
324 CString sMsg = sLine.Token(2, true);
538d3ece 325
078bbcf0 326 if (sMsg.Left(1) == ":") {
327 sMsg.LeftChomp();
538d3ece 328 }
329
b757a318 330 if (sTarget.TrimPrefix(m_pUser->GetStatusPrefix())) {
5237a247 331 if (!sTarget.Equals("status")) {
b757a318 332 CALLMOD(sTarget, this, m_pUser, OnModNotice(sMsg));
333 }
538d3ece 334 return;
335 }
336
0823b27f 337 if (sMsg.WildCmp("\001*\001")) {
beb5b49b 338 CString sCTCP = sMsg;
078bbcf0 339 sCTCP.LeftChomp();
340 sCTCP.RightChomp();
538d3ece 341
b740d7cc 342 MODULECALL(OnUserCTCPReply(sTarget, sCTCP), m_pUser, this, return);
538d3ece 343
344 sMsg = "\001" + sCTCP + "\001";
345 } else {
b740d7cc 346 MODULECALL(OnUserNotice(sTarget, sMsg), m_pUser, this, return);
538d3ece 347 }
538d3ece 348
b50d944b 349 if (!GetIRCSock()) {
f1e6a41d 350 // Some lagmeters do a NOTICE to their own nick, ignore those.
5237a247 351 if (!sTarget.Equals(m_sNick))
f1e6a41d 352 PutStatus("Your notice to [" + sTarget + "] got lost, "
353 "you are not connected to IRC!");
c4f1eb3d 354 return;
355 }
356
4c33f794 357 CChan* pChan = m_pUser->FindChan(sTarget);
358
6d27d1c0 359 if ((pChan) && (pChan->KeepBuffer())) {
f601db2c 360 pChan->AddBuffer(":" + GetNickMask() + " NOTICE " + sTarget + " :" + m_pUser->AddTimestamp(sMsg));
6d27d1c0 361 }
362
9170991a 363 // Relay to the rest of the clients that may be connected to this user
cc00aa23 364 if (m_pUser->IsChan(sTarget)) {
9170991a 365 vector<CClient*>& vClients = m_pUser->GetClients();
366
367 for (unsigned int a = 0; a < vClients.size(); a++) {
368 CClient* pClient = vClients[a];
369
370 if (pClient != this) {
371 pClient->PutClient(":" + GetNickMask() + " NOTICE " + sTarget + " :" + sMsg);
372 }
373 }
374 }
375
538d3ece 376 PutIRC("NOTICE " + sTarget + " :" + sMsg);
377 return;
5237a247 378 } else if (sCommand.Equals("PRIVMSG")) {
24950d24 379 CString sTarget = sLine.Token(1);
380 CString sMsg = sLine.Token(2, true);
538d3ece 381
078bbcf0 382 if (sMsg.Left(1) == ":") {
383 sMsg.LeftChomp();
538d3ece 384 }
385
0823b27f 386 if (sMsg.WildCmp("\001*\001")) {
beb5b49b 387 CString sCTCP = sMsg;
078bbcf0 388 sCTCP.LeftChomp();
389 sCTCP.RightChomp();
538d3ece 390
8f508cb4 391 if (sTarget.TrimPrefix(m_pUser->GetStatusPrefix())) {
5237a247 392 if (sTarget.Equals("status")) {
f74f501d 393 StatusCTCP(sCTCP);
b757a318 394 } else {
b757a318 395 CALLMOD(sTarget, this, m_pUser, OnModCTCP(sCTCP));
b757a318 396 }
538d3ece 397 return;
398 }
399
2cd52ad3 400 CChan* pChan = m_pUser->FindChan(sTarget);
401
5237a247 402 if (sCTCP.Token(0).Equals("ACTION")) {
f601db2c 403 CString sMessage = sCTCP.Token(1, true);
404 MODULECALL(OnUserAction(sTarget, sMessage), m_pUser, this, return);
a3b405bd 405 sCTCP = "ACTION " + sMessage;
f601db2c 406
2cd52ad3 407 if (pChan && pChan->KeepBuffer()) {
f601db2c 408 pChan->AddBuffer(":" + GetNickMask() + " PRIVMSG " + sTarget + " :\001ACTION " + m_pUser->AddTimestamp(sMessage) + "\001");
2cd52ad3 409 }
9170991a 410
411 // Relay to the rest of the clients that may be connected to this user
cc00aa23 412 if (m_pUser->IsChan(sTarget)) {
9170991a 413 vector<CClient*>& vClients = m_pUser->GetClients();
414
415 for (unsigned int a = 0; a < vClients.size(); a++) {
416 CClient* pClient = vClients[a];
417
418 if (pClient != this) {
419 pClient->PutClient(":" + GetNickMask() + " PRIVMSG " + sTarget + " :\001" + sCTCP + "\001");
420 }
421 }
422 }
2cd52ad3 423 } else {
b740d7cc 424 MODULECALL(OnUserCTCP(sTarget, sCTCP), m_pUser, this, return);
2cd52ad3 425 }
426
538d3ece 427 PutIRC("PRIVMSG " + sTarget + " :\001" + sCTCP + "\001");
428 return;
429 }
430
b757a318 431 if (sTarget.TrimPrefix(m_pUser->GetStatusPrefix())) {
5237a247 432 if (sTarget.Equals("status")) {
b757a318 433 UserCommand(sMsg);
434 } else {
b757a318 435 CALLMOD(sTarget, this, m_pUser, OnModCommand(sMsg));
b757a318 436 }
538d3ece 437 return;
438 }
439
b740d7cc 440 MODULECALL(OnUserMsg(sTarget, sMsg), m_pUser, this, return);
4c33f794 441
b50d944b 442 if (!GetIRCSock()) {
f1e6a41d 443 // Some lagmeters do a PRIVMSG to their own nick, ignore those.
5237a247 444 if (!sTarget.Equals(m_sNick))
f1e6a41d 445 PutStatus("Your message to [" + sTarget + "] got lost, "
446 "you are not connected to IRC!");
c4f1eb3d 447 return;
448 }
449
4c33f794 450 CChan* pChan = m_pUser->FindChan(sTarget);
451
538d3ece 452 if ((pChan) && (pChan->KeepBuffer())) {
f601db2c 453 pChan->AddBuffer(":" + GetNickMask() + " PRIVMSG " + sTarget + " :" + m_pUser->AddTimestamp(sMsg));
538d3ece 454 }
455
538d3ece 456 PutIRC("PRIVMSG " + sTarget + " :" + sMsg);
aaec84a3 457
458 // Relay to the rest of the clients that may be connected to this user
459
cc00aa23 460 if (m_pUser->IsChan(sTarget)) {
712d4006 461 vector<CClient*>& vClients = m_pUser->GetClients();
aaec84a3 462
712d4006 463 for (unsigned int a = 0; a < vClients.size(); a++) {
464 CClient* pClient = vClients[a];
aaec84a3 465
712d4006 466 if (pClient != this) {
467 pClient->PutClient(":" + GetNickMask() + " PRIVMSG " + sTarget + " :" + sMsg);
aaec84a3 468 }
469 }
470 }
471
538d3ece 472 return;
473 }
474
475 PutIRC(sLine);
476}
477
712d4006 478void CClient::SetNick(const CString& s) {
03e34ac6 479 m_sNick = s;
538d3ece 480}
481
f99e5190 482const CIRCSock* CClient::GetIRCSock() const {
483 return m_pUser->GetIRCSock();
484}
485
486CIRCSock* CClient::GetIRCSock() {
487 return m_pUser->GetIRCSock();
488}
489
f74f501d 490void CClient::StatusCTCP(const CString& sLine) {
491 CString sCommand = sLine.Token(0);
492
5237a247 493 if (sCommand.Equals("PING")) {
f74f501d 494 PutStatusNotice("\001PING " + sLine.Token(1, true) + "\001");
5237a247 495 } else if (sCommand.Equals("VERSION")) {
f74f501d 496 PutStatusNotice("\001VERSION " + CZNC::GetTag() + "\001");
497 }
498}
499
712d4006 500bool CClient::SendMotd() {
d2e72850 501 const VCString& vsMotd = CZNC::Get().GetMotd();
502
503 if (!vsMotd.size()) {
504 return false;
505 }
506
507 for (unsigned int a = 0; a < vsMotd.size(); a++) {
82129aa2 508 PutStatusNotice(m_pUser->ExpandString(vsMotd[a]));
d2e72850 509 }
510
511 return true;
512}
513
712d4006 514void CClient::AuthUser() {
8af157cc 515 if (!m_bGotNick || !m_bGotUser || !m_bGotPass || m_bInCap || IsAttached())
f6f4e6a6 516 return;
517
be354f11 518 m_spAuth = new CClientAuth(this, m_sUser, m_sPass);
519
31feec2c 520 CZNC::Get().AuthUser(m_spAuth);
be354f11 521}
522
5e0c652b 523CClientAuth::CClientAuth(CClient* pClient, const CString& sUsername, const CString& sPassword)
4e31d492 524 : CAuthBase(sUsername, sPassword, pClient) {
5e0c652b 525 m_pClient = pClient;
526}
527
cbc27f5b 528void CClientAuth::RefusedLogin(const CString& sReason) {
be354f11 529 if (m_pClient) {
530 m_pClient->RefuseLogin(sReason);
531 }
cbc27f5b 532}
533
4e31d492 534CString CAuthBase::GetRemoteIP() const {
535 if (m_pSock)
536 return m_pSock->GetRemoteIP();
537 return "";
538}
539
b0e59f12 540void CAuthBase::Invalidate() {
541 m_pSock = NULL;
542}
543
544void CAuthBase::AcceptLogin(CUser& User) {
545 if (m_pSock) {
546 AcceptedLogin(User);
547 Invalidate();
548 }
549}
550
cbc27f5b 551void CAuthBase::RefuseLogin(const CString& sReason) {
b0e59f12 552 if (!m_pSock)
553 return;
554
9c92d93a 555 CUser* pUser = CZNC::Get().FindUser(GetUsername());
e5ecd9c9 556
0015098a 557 // If the username is valid, notify that user that someone tried to
558 // login. Use sReason because there are other reasons than "wrong
559 // password" for a login to be rejected (e.g. fail2ban).
560 if (pUser) {
d66698e9 561 pUser->PutStatus("A client from [" + GetRemoteIP() + "] attempted "
562 "to login as you, but was rejected [" + sReason + "].");
0015098a 563 }
564
1023d868 565 GLOBALMODULECALL(OnFailedLogin(GetUsername(), GetRemoteIP()), NULL, NULL, NOTHING);
cbc27f5b 566 RefusedLogin(sReason);
b0e59f12 567 Invalidate();
be354f11 568}
569
570void CClient::RefuseLogin(const CString& sReason) {
571 PutStatus("Bad username and/or password.");
02beef2e 572 PutClient(":irc.znc.in 464 " + GetNick() + " :" + sReason);
1a859f38 573 Close(Csock::CLT_AFTERWRITE);
be354f11 574}
538d3ece 575
cbc27f5b 576void CClientAuth::AcceptedLogin(CUser& User) {
be354f11 577 if (m_pClient) {
578 m_pClient->AcceptLogin(User);
579 }
580}
538d3ece 581
be354f11 582void CClient::AcceptLogin(CUser& User) {
583 m_sPass = "";
584 m_pUser = &User;
585
edb6c42d 586 // Set our proper timeout and set back our proper timeout mode
587 // (constructor set a different timeout and mode)
d2f3b8c5 588 SetTimeout(540, TMO_READ);
e1bf2d21 589
be354f11 590 SetSockName("USR::" + m_pUser->GetUserName());
591
be354f11 592 m_pUser->UserConnected(this);
593
594 SendMotd();
d2e72850 595
1023d868 596 MODULECALL(OnClientLogin(), m_pUser, this, NOTHING);
538d3ece 597}
598
edb6c42d 599void CClient::Timeout() {
e1bf2d21 600 PutClient("ERROR :Closing link [Timeout]");
e1bf2d21 601}
602
712d4006 603void CClient::Connected() {
235b10c2 604 DEBUG(GetSockName() << " == Connected();");
538d3ece 605}
606
712d4006 607void CClient::ConnectionRefused() {
235b10c2 608 DEBUG(GetSockName() << " == ConnectionRefused()");
538d3ece 609}
610
712d4006 611void CClient::Disconnected() {
235b10c2 612 DEBUG(GetSockName() << " == Disconnected()");
e03604de 613 if (m_pUser) {
aaec84a3 614 m_pUser->UserDisconnected(this);
538d3ece 615 }
616
1023d868 617 MODULECALL(OnClientDisconnect(), m_pUser, this, NOTHING);
538d3ece 618}
619
18ce52e3 620void CClient::ReachedMaxBuffer() {
235b10c2 621 DEBUG(GetSockName() << " == ReachedMaxBuffer()");
18ce52e3 622 if (IsAttached()) {
623 PutClient("ERROR :Closing link [Too long raw line]");
624 }
625 Close();
626}
627
712d4006 628void CClient::BouncedOff() {
57f66b5e 629 PutStatusNotice("You are being disconnected because another user just authenticated as you.");
1a859f38 630 Close(Csock::CLT_AFTERWRITE);
538d3ece 631}
632
712d4006 633void CClient::PutIRC(const CString& sLine) {
e87f440d 634 m_pUser->PutIRC(sLine);
538d3ece 635}
636
712d4006 637void CClient::PutClient(const CString& sLine) {
235b10c2 638 DEBUG("(" << ((m_pUser) ? m_pUser->GetUserName() : GetRemoteIP()) << ") ZNC -> CLI [" << sLine << "]");
79aaf3d5 639 Write(sLine + "\r\n");
538d3ece 640}
641
712d4006 642void CClient::PutStatusNotice(const CString& sLine) {
66389db9 643 PutModNotice("status", sLine);
644}
645
fd92e65b 646unsigned int CClient::PutStatus(const CTable& table) {
647 unsigned int idx = 0;
648 CString sLine;
649 while (table.GetLine(idx++, sLine))
650 PutStatus(sLine);
651 return idx - 1;
652}
653
712d4006 654void CClient::PutStatus(const CString& sLine) {
538d3ece 655 PutModule("status", sLine);
656}
657
712d4006 658void CClient::PutModNotice(const CString& sModule, const CString& sLine) {
66389db9 659 if (!m_pUser) {
660 return;
661 }
662
235b10c2 663 DEBUG("(" << m_pUser->GetUserName() << ") ZNC -> CLI [:" + m_pUser->GetStatusPrefix() + ((sModule.empty()) ? "status" : sModule) + "!znc@znc.in NOTICE " << GetNick() << " :" << sLine << "]");
02beef2e 664 Write(":" + m_pUser->GetStatusPrefix() + ((sModule.empty()) ? "status" : sModule) + "!znc@znc.in NOTICE " + GetNick() + " :" + sLine + "\r\n");
66389db9 665}
666
712d4006 667void CClient::PutModule(const CString& sModule, const CString& sLine) {
538d3ece 668 if (!m_pUser) {
669 return;
670 }
671
235b10c2 672 DEBUG("(" << m_pUser->GetUserName() << ") ZNC -> CLI [:" + m_pUser->GetStatusPrefix() + ((sModule.empty()) ? "status" : sModule) + "!znc@znc.in PRIVMSG " << GetNick() << " :" << sLine << "]");
02beef2e 673 Write(":" + m_pUser->GetStatusPrefix() + ((sModule.empty()) ? "status" : sModule) + "!znc@znc.in PRIVMSG " + GetNick() + " :" + sLine + "\r\n");
538d3ece 674}
675
9b671ee9 676CString CClient::GetNick(bool bAllowIRCNick) const {
beb5b49b 677 CString sRet;
538d3ece 678
b50d944b 679 if ((bAllowIRCNick) && (IsAttached()) && (GetIRCSock())) {
680 sRet = GetIRCSock()->GetNick();
538d3ece 681 }
682
683 return (sRet.empty()) ? m_sNick : sRet;
684}
685
712d4006 686CString CClient::GetNickMask() const {
b50d944b 687 if (GetIRCSock() && GetIRCSock()->IsAuthed()) {
688 return GetIRCSock()->GetNickMask();
538d3ece 689 }
690
341263f9 691 CString sHost = m_pUser->GetBindHost();
3c1e610c 692 if (sHost.empty()) {
02beef2e 693 sHost = "irc.znc.in";
3c1e610c 694 }
695
696 return GetNick() + "!" + m_pUser->GetIdent() + "@" + sHost;
538d3ece 697}
99ea9c6f 698
699void CClient::RespondCap(const CString& sResponse)
700{
701 PutClient(":irc.znc.in CAP " + GetNick() + " " + sResponse);
702}
703
704void CClient::HandleCap(const CString& sLine)
705{
706 CString sSubCmd = sLine.Token(1);
707
708 if (sSubCmd.Equals("LS")) {
9d99e4cc 709 SCString ssOfferCaps;
1023d868 710 GLOBALMODULECALL(OnClientCapLs(ssOfferCaps), m_pUser, this, NOTHING);
9d99e4cc 711 CString sRes;
712 for (SCString::iterator i = ssOfferCaps.begin(); i != ssOfferCaps.end(); ++i) {
713 sRes += *i + " ";
714 }
715 RespondCap("LS :" + sRes + "userhost-in-names multi-prefix");
99ea9c6f 716 m_bInCap = true;
717 } else if (sSubCmd.Equals("END")) {
718 m_bInCap = false;
40b5a5cd
KF
719 if (!IsAttached()) {
720 if (!m_pUser && m_bGotUser && !m_bGotPass) {
721 SendRequiredPasswordNotice();
722 } else {
723 AuthUser();
724 }
725 }
99ea9c6f 726 } else if (sSubCmd.Equals("REQ")) {
d7393315 727 VCString vsTokens;
728 VCString::iterator it;
22a641a0 729 sLine.Token(2, true).TrimPrefix_n(":").Split(" ", vsTokens, false);
d7393315 730
731 for (it = vsTokens.begin(); it != vsTokens.end(); ++it) {
e00fa217 732 bool bVal = true;
9d99e4cc 733 CString sCap = *it;
734 if (sCap.TrimPrefix("-"))
e00fa217 735 bVal = false;
736
9ae959b8 737 bool bAccepted = ("multi-prefix" == sCap) || ("userhost-in-names" == sCap);
3dd57bdf 738 GLOBALMODULECALL(IsClientCapSupported(sCap, bVal), m_pUser, this, bAccepted = true);
9ae959b8 739
740 if (!bAccepted) {
d7393315 741 // Some unsupported capability is requested
742 RespondCap("NAK :" + sLine.Token(2, true).TrimPrefix_n(":"));
743 return;
744 }
745 }
746
747 // All is fine, we support what was requested
9d99e4cc 748 for (it = vsTokens.begin(); it != vsTokens.end(); ++it) {
749 bool bVal = true;
750 if (it->TrimPrefix("-"))
751 bVal = false;
752
753 if ("multi-prefix" == *it) {
754 m_bNamesx = bVal;
755 } else if ("userhost-in-names" == *it) {
756 m_bUHNames = bVal;
9d99e4cc 757 }
1023d868 758 GLOBALMODULECALL(OnClientCapRequest(*it, bVal), m_pUser, this, NOTHING);
9d99e4cc 759
760 if (bVal) {
761 m_ssAcceptedCaps.insert(*it);
762 } else {
763 m_ssAcceptedCaps.erase(*it);
764 }
765 }
766
d7393315 767 RespondCap("ACK :" + sLine.Token(2, true).TrimPrefix_n(":"));
99ea9c6f 768 } else if (sSubCmd.Equals("LIST")) {
d7393315 769 CString sList = "";
9d99e4cc 770 for (SCString::iterator i = m_ssAcceptedCaps.begin(); i != m_ssAcceptedCaps.end(); ++i) {
771 sList += *i + " ";
772 }
d7393315 773 RespondCap("LIST :" + sList.TrimSuffix_n(" "));
88daf2fa 774 } else if (sSubCmd.Equals("CLEAR")) {
775 SCString ssRemoved;
776 for (SCString::iterator i = m_ssAcceptedCaps.begin(); i != m_ssAcceptedCaps.end(); ++i) {
777 bool bRemoving = false;
778 GLOBALMODULECALL(IsClientCapSupported(*i, false), m_pUser, this, bRemoving = true);
779 if (bRemoving) {
1023d868 780 GLOBALMODULECALL(OnClientCapRequest(*i, false), m_pUser, this, NOTHING);
88daf2fa 781 ssRemoved.insert(*i);
782 }
783 }
784 if (m_bNamesx) {
785 m_bNamesx = false;
786 ssRemoved.insert("multi-prefix");
787 }
788 if (m_bUHNames) {
789 m_bUHNames = false;
790 ssRemoved.insert("userhost-in-names");
791 }
792 CString sList = "";
793 for (SCString::iterator i = ssRemoved.begin(); i != ssRemoved.end(); ++i) {
794 m_ssAcceptedCaps.erase(*i);
795 sList += "-" + *i + " ";
796 }
797 RespondCap("ACK :" + sList.TrimSuffix_n(" "));
99ea9c6f 798 }
799}