CClient: message handlers

This commit is contained in:
J-P Nurmi
2015-09-20 01:46:48 +02:00
parent 4d3b47f2b1
commit c83a41ce89
2 changed files with 429 additions and 334 deletions

View File

@@ -171,342 +171,46 @@ void CClient::ReadLine(const CString& sData) {
return;
}
if (sCommand.Equals("ZNC")) {
CString sTarget = Message.GetParam(0);
CString sModCommand;
if (sTarget.TrimPrefix(m_pUser->GetStatusPrefix())) {
sModCommand = Message.GetParams(1);
} else {
sTarget = "status";
sModCommand = Message.GetParams(0);
}
if (sTarget.Equals("status")) {
if (sModCommand.empty())
PutStatus("Hello. How may I help you?");
else
UserCommand(sModCommand);
} else {
if (sModCommand.empty())
CALLMOD(sTarget, this, m_pUser, m_pNetwork, PutModule("Hello. How may I help you?"))
else
CALLMOD(sTarget, this, m_pUser, m_pNetwork, OnModCommand(sModCommand))
}
return;
} else if (Message.GetType() == CMessage::Type::Ping) {
// All PONGs are generated by ZNC. We will still forward this to
// the ircd, but all PONGs from irc will be blocked.
if (!Message.GetParams().empty())
PutClient(":irc.znc.in PONG irc.znc.in " + Message.GetParams(0));
else
PutClient(":irc.znc.in PONG irc.znc.in");
} else if (Message.GetType() == CMessage::Type::Pong) {
// Block PONGs, we already responded to the pings
return;
} else if (Message.GetType() == CMessage::Type::Quit) {
CQuitMessage& QuitMsg = static_cast<CQuitMessage&>(Message);
NETWORKMODULECALL(OnUserQuitMessage(QuitMsg), m_pUser, m_pNetwork, this, &bReturn);
if (bReturn) return;
Close(Csock::CLT_AFTERWRITE); // Treat a client quit as a detach
return; // Don't forward this msg. We don't want the client getting us disconnected.
} else if (sCommand.Equals("PROTOCTL")) {
for (const CString& sParam : Message.GetParams()) {
if (sParam == "NAMESX") {
m_bNamesx = true;
} else if (sParam == "UHNAMES") {
m_bUHNames = true;
}
}
return; // If the server understands it, we already enabled namesx / uhnames
} else if (Message.GetType() == CMessage::Type::Notice) {
CNoticeMessage& NoticeMsg = static_cast<CNoticeMessage&>(Message);
CString sTargets = NoticeMsg.GetTarget();
VCString vTargets;
sTargets.Split(",", vTargets, false);
for (CString& sTarget : vTargets) {
NoticeMsg.SetTarget(sTarget);
if (sTarget.TrimPrefix(m_pUser->GetStatusPrefix())) {
if (!sTarget.Equals("status")) {
CALLMOD(sTarget, this, m_pUser, m_pNetwork, OnModNotice(NoticeMsg.GetText()));
}
continue;
}
bool bContinue = false;
NETWORKMODULECALL(OnUserNoticeMessage(NoticeMsg), m_pUser, m_pNetwork, this, &bContinue);
if (bContinue) continue;
if (!GetIRCSock()) {
// Some lagmeters do a NOTICE to their own nick, ignore those.
if (!sTarget.Equals(m_sNick))
PutStatus("Your notice to [" + NoticeMsg.GetTarget() + "] got lost, "
"you are not connected to IRC!");
continue;
}
if (m_pNetwork) {
AddBuffer(NoticeMsg);
EchoMessage(NoticeMsg);
PutIRC(NoticeMsg.ToString(CMessage::ExcludePrefix | CMessage::ExcludeTags));
}
}
return;
} else if (Message.GetType() == CMessage::Type::CTCP) {
CCTCPMessage& CTCPMsg = static_cast<CCTCPMessage&>(Message);
CString sTargets = CTCPMsg.GetTarget();
VCString vTargets;
sTargets.Split(",", vTargets, false);
if (CTCPMsg.IsReply()) {
CString sCTCP = CTCPMsg.GetText();
if (sCTCP.Token(0) == "VERSION") {
CTCPMsg.SetText(sCTCP + " via " + CZNC::GetTag(false));
}
}
for (CString& sTarget : vTargets) {
CTCPMsg.SetTarget(sTarget);
bool bContinue = false;
if (CTCPMsg.IsReply()) {
NETWORKMODULECALL(OnUserCTCPReplyMessage(CTCPMsg), m_pUser, m_pNetwork, this, &bContinue);
} else {
NETWORKMODULECALL(OnUserCTCPMessage(CTCPMsg), m_pUser, m_pNetwork, this, &bContinue);
}
if (bContinue) continue;
if (!GetIRCSock()) {
// Some lagmeters do a NOTICE to their own nick, ignore those.
if (!sTarget.Equals(m_sNick))
PutStatus("Your CTCP to [" + CTCPMsg.GetTarget() + "] got lost, "
"you are not connected to IRC!");
continue;
}
if (m_pNetwork) {
AddBuffer(CTCPMsg);
EchoMessage(CTCPMsg);
PutIRC(CTCPMsg.ToString(CMessage::ExcludePrefix | CMessage::ExcludeTags));
}
}
return;
} else if (Message.GetType() == CMessage::Type::Text) {
CTextMessage& TextMsg = static_cast<CTextMessage&>(Message);
CString sTargets = TextMsg.GetTarget();
VCString vTargets;
sTargets.Split(",", vTargets, false);
for (CString& sTarget : vTargets) {
TextMsg.SetTarget(sTarget);
if (sTarget.TrimPrefix(m_pUser->GetStatusPrefix())) {
EchoMessage(Message);
if (sTarget.Equals("status")) {
CString sMsg = TextMsg.GetText();
UserCommand(sMsg);
} else {
CALLMOD(sTarget, this, m_pUser, m_pNetwork, OnModCommand(TextMsg.GetText()));
}
continue;
}
bool bContinue = false;
NETWORKMODULECALL(OnUserTextMessage(TextMsg), m_pUser, m_pNetwork, this, &bContinue);
if (bContinue) continue;
if (!GetIRCSock()) {
// Some lagmeters do a PRIVMSG to their own nick, ignore those.
if (!sTarget.Equals(m_sNick))
PutStatus("Your message to [" + TextMsg.GetTarget() + "] got lost, "
"you are not connected to IRC!");
continue;
}
if (m_pNetwork) {
AddBuffer(TextMsg);
EchoMessage(TextMsg);
PutIRC(TextMsg.ToString(CMessage::ExcludePrefix | CMessage::ExcludeTags));
}
}
return;
} else if (Message.GetType() == CMessage::Type::Action) {
CActionMessage& ActionMsg = static_cast<CActionMessage&>(Message);
CString sTargets = ActionMsg.GetTarget();
VCString vTargets;
sTargets.Split(",", vTargets, false);
for (CString& sTarget : vTargets) {
ActionMsg.SetTarget(sTarget);
bool bContinue = false;
NETWORKMODULECALL(OnUserActionMessage(ActionMsg), m_pUser, m_pNetwork, this, &bContinue);
if (bContinue) continue;
if (m_pNetwork) {
AddBuffer(ActionMsg);
EchoMessage(ActionMsg);
PutIRC(ActionMsg.ToString(CMessage::ExcludePrefix | CMessage::ExcludeTags));
}
}
return;
switch (Message.GetType()) {
case CMessage::Type::Action:
bReturn = OnActionMessage(static_cast<CActionMessage&>(Message));
break;
case CMessage::Type::CTCP:
bReturn = OnCTCPMessage(static_cast<CCTCPMessage&>(Message));
break;
case CMessage::Type::Join:
bReturn = OnJoinMessage(static_cast<CJoinMessage&>(Message));
break;
case CMessage::Type::Mode:
bReturn = OnModeMessage(static_cast<CModeMessage&>(Message));
break;
case CMessage::Type::Notice:
bReturn = OnNoticeMessage(static_cast<CNoticeMessage&>(Message));
break;
case CMessage::Type::Part:
bReturn = OnPartMessage(static_cast<CPartMessage&>(Message));
break;
case CMessage::Type::Ping:
bReturn = OnPingMessage(Message);
break;
case CMessage::Type::Pong:
bReturn = OnPongMessage(Message);
break;
case CMessage::Type::Quit:
bReturn = OnQuitMessage(static_cast<CQuitMessage&>(Message));
break;
case CMessage::Type::Text:
bReturn = OnTextMessage(static_cast<CTextMessage&>(Message));
break;
case CMessage::Type::Topic:
bReturn = OnTopicMessage(static_cast<CTopicMessage&>(Message));
break;
default:
bReturn = OnOtherMessage(Message);
break;
}
if (sCommand.Equals("ATTACH")) {
CString sPatterns = sLine.Token(1, true);
if (sPatterns.empty()) {
PutStatusNotice("Usage: /attach <#chans|queries>");
return;
}
set<CChan*> sChans = MatchChans(sPatterns);
unsigned int uAttachedChans = AttachChans(sChans);
PutStatusNotice("There were [" + CString(sChans.size()) + "] channels matching [" + sPatterns + "]");
PutStatusNotice("Attached [" + CString(uAttachedChans) + "] channels");
return;
} else if (sCommand.Equals("DETACH")) {
if (!m_pNetwork) {
return;
}
CString sPatterns = Message.GetParams(0);
if (sPatterns.empty()) {
PutStatusNotice("Usage: /detach <#chans>");
return;
}
set<CChan*> sChans = MatchChans(sPatterns);
unsigned int uDetached = DetachChans(sChans);
PutStatusNotice("There were [" + CString(sChans.size()) + "] channels matching [" + sPatterns + "]");
PutStatusNotice("Detached [" + CString(uDetached) + "] channels");
return;
} else if (Message.GetType() == CMessage::Type::Join) {
CJoinMessage& JoinMsg = static_cast<CJoinMessage&>(Message);
CString sChans = JoinMsg.GetTarget();
CString sKeys = JoinMsg.GetKey();
VCString vsChans;
sChans.Split(",", vsChans, false);
sChans.clear();
VCString vsKeys;
sKeys.Split(",", vsKeys, true);
sKeys.clear();
for (unsigned int a = 0; a < vsChans.size(); a++) {
JoinMsg.SetTarget(vsChans[a]);
JoinMsg.SetKey((a < vsKeys.size()) ? vsKeys[a] : "");
bool bContinue = false;
NETWORKMODULECALL(OnUserJoinMessage(JoinMsg), m_pUser, m_pNetwork, this, &bContinue);
if (bContinue) continue;
CString sChannel = JoinMsg.GetTarget();
CString sKey = JoinMsg.GetKey();
CChan* pChan = m_pNetwork ? m_pNetwork->FindChan(sChannel) : nullptr;
if (pChan) {
if (pChan->IsDetached())
pChan->AttachUser(this);
else
pChan->JoinUser(sKey);
continue;
}
if (!sChannel.empty()) {
sChans += (sChans.empty()) ? sChannel : CString("," + sChannel);
if (!vsKeys.empty()) {
sKeys += (sKeys.empty()) ? sKey : CString("," + sKey);
}
}
}
if (sChans.empty()) {
return;
}
JoinMsg.SetTarget(sChans);
JoinMsg.SetKey(sKeys);
} else if (Message.GetType() == CMessage::Type::Part) {
CPartMessage& PartMsg = static_cast<CPartMessage&>(Message);
CString sChans = PartMsg.GetTarget();
VCString vsChans;
sChans.Split(",", vsChans, false);
sChans.clear();
for (CString& sChan : vsChans) {
bool bContinue = false;
PartMsg.SetTarget(sChan);
NETWORKMODULECALL(OnUserPartMessage(PartMsg), m_pUser, m_pNetwork, this, &bContinue);
if (bContinue) continue;
sChan = PartMsg.GetTarget();
CChan* pChan = m_pNetwork ? m_pNetwork->FindChan(sChan) : nullptr;
if (pChan && !pChan->IsOn()) {
PutStatusNotice("Removing channel [" + sChan + "]");
m_pNetwork->DelChan(sChan);
} else {
sChans += (sChans.empty()) ? sChan : CString("," + sChan);
}
}
if (sChans.empty()) {
return;
}
PartMsg.SetTarget(sChans);
} else if (Message.GetType() == CMessage::Type::Topic) {
CTopicMessage& TopicMsg = static_cast<CTopicMessage&>(Message);
CString sChan = TopicMsg.GetTarget();
CString sTopic = TopicMsg.GetTopic();
if (!sTopic.empty()) {
NETWORKMODULECALL(OnUserTopicMessage(TopicMsg), m_pUser, m_pNetwork, this, &bReturn);
if (bReturn) return;
} else {
NETWORKMODULECALL(OnUserTopicRequest(sChan), m_pUser, m_pNetwork, this, &bReturn);
if (bReturn) return;
TopicMsg.SetTarget(sChan);
}
} else if (Message.GetType() == CMessage::Type::Mode) {
CModeMessage& ModeMsg = static_cast<CModeMessage&>(Message);
CString sTarget = ModeMsg.GetTarget();
CString sModes = ModeMsg.GetModes();
if (m_pNetwork && m_pNetwork->IsChan(sTarget) && sModes.empty()) {
// If we are on that channel and already received a
// /mode reply from the server, we can answer this
// request ourself.
CChan *pChan = m_pNetwork->FindChan(sTarget);
if (pChan && pChan->IsOn() && !pChan->GetModeString().empty()) {
PutClient(":" + m_pNetwork->GetIRCServer() + " 324 " + GetNick() + " " + sTarget + " " + pChan->GetModeString());
if (pChan->GetCreationDate() > 0) {
PutClient(":" + m_pNetwork->GetIRCServer() + " 329 " + GetNick() + " " + sTarget + " " + CString(pChan->GetCreationDate()));
}
return;
}
}
}
if (bReturn) return;
PutIRC(Message.ToString(CMessage::ExcludePrefix | CMessage::ExcludeTags));
}
@@ -1174,3 +878,380 @@ unsigned int CClient::DetachChans(const std::set<CChan*>& sChans)
}
return uDetached;
}
bool CClient::OnActionMessage(CActionMessage& Message)
{
CString sTargets = Message.GetTarget();
VCString vTargets;
sTargets.Split(",", vTargets, false);
for (CString& sTarget : vTargets) {
Message.SetTarget(sTarget);
bool bContinue = false;
NETWORKMODULECALL(OnUserActionMessage(Message), m_pUser, m_pNetwork, this, &bContinue);
if (bContinue) continue;
if (m_pNetwork) {
AddBuffer(Message);
EchoMessage(Message);
PutIRC(Message.ToString(CMessage::ExcludePrefix | CMessage::ExcludeTags));
}
}
return true;
}
bool CClient::OnCTCPMessage(CCTCPMessage& Message)
{
CString sTargets = Message.GetTarget();
VCString vTargets;
sTargets.Split(",", vTargets, false);
if (Message.IsReply()) {
CString sCTCP = Message.GetText();
if (sCTCP.Token(0) == "VERSION") {
Message.SetText(sCTCP + " via " + CZNC::GetTag(false));
}
}
for (CString& sTarget : vTargets) {
Message.SetTarget(sTarget);
bool bContinue = false;
if (Message.IsReply()) {
NETWORKMODULECALL(OnUserCTCPReplyMessage(Message), m_pUser, m_pNetwork, this, &bContinue);
} else {
NETWORKMODULECALL(OnUserCTCPMessage(Message), m_pUser, m_pNetwork, this, &bContinue);
}
if (bContinue) continue;
if (!GetIRCSock()) {
// Some lagmeters do a NOTICE to their own nick, ignore those.
if (!sTarget.Equals(m_sNick))
PutStatus("Your CTCP to [" + Message.GetTarget() + "] got lost, "
"you are not connected to IRC!");
continue;
}
if (m_pNetwork) {
AddBuffer(Message);
EchoMessage(Message);
PutIRC(Message.ToString(CMessage::ExcludePrefix | CMessage::ExcludeTags));
}
}
return true;
}
bool CClient::OnJoinMessage(CJoinMessage& Message)
{
CString sChans = Message.GetTarget();
CString sKeys = Message.GetKey();
VCString vsChans;
sChans.Split(",", vsChans, false);
sChans.clear();
VCString vsKeys;
sKeys.Split(",", vsKeys, true);
sKeys.clear();
for (unsigned int a = 0; a < vsChans.size(); a++) {
Message.SetTarget(vsChans[a]);
Message.SetKey((a < vsKeys.size()) ? vsKeys[a] : "");
bool bContinue = false;
NETWORKMODULECALL(OnUserJoinMessage(Message), m_pUser, m_pNetwork, this, &bContinue);
if (bContinue) continue;
CString sChannel = Message.GetTarget();
CString sKey = Message.GetKey();
CChan* pChan = m_pNetwork ? m_pNetwork->FindChan(sChannel) : nullptr;
if (pChan) {
if (pChan->IsDetached())
pChan->AttachUser(this);
else
pChan->JoinUser(sKey);
continue;
}
if (!sChannel.empty()) {
sChans += (sChans.empty()) ? sChannel : CString("," + sChannel);
if (!vsKeys.empty()) {
sKeys += (sKeys.empty()) ? sKey : CString("," + sKey);
}
}
}
Message.SetTarget(sChans);
Message.SetKey(sKeys);
return sChans.empty();
}
bool CClient::OnModeMessage(CModeMessage& Message)
{
CString sTarget = Message.GetTarget();
CString sModes = Message.GetModes();
if (m_pNetwork && m_pNetwork->IsChan(sTarget) && sModes.empty()) {
// If we are on that channel and already received a
// /mode reply from the server, we can answer this
// request ourself.
CChan *pChan = m_pNetwork->FindChan(sTarget);
if (pChan && pChan->IsOn() && !pChan->GetModeString().empty()) {
PutClient(":" + m_pNetwork->GetIRCServer() + " 324 " + GetNick() + " " + sTarget + " " + pChan->GetModeString());
if (pChan->GetCreationDate() > 0) {
PutClient(":" + m_pNetwork->GetIRCServer() + " 329 " + GetNick() + " " + sTarget + " " + CString(pChan->GetCreationDate()));
}
return true;
}
}
return false;
}
bool CClient::OnNoticeMessage(CNoticeMessage& Message)
{
CString sTargets = Message.GetTarget();
VCString vTargets;
sTargets.Split(",", vTargets, false);
for (CString& sTarget : vTargets) {
Message.SetTarget(sTarget);
if (sTarget.TrimPrefix(m_pUser->GetStatusPrefix())) {
if (!sTarget.Equals("status")) {
CALLMOD(sTarget, this, m_pUser, m_pNetwork, OnModNotice(Message.GetText()));
}
continue;
}
bool bContinue = false;
NETWORKMODULECALL(OnUserNoticeMessage(Message), m_pUser, m_pNetwork, this, &bContinue);
if (bContinue) continue;
if (!GetIRCSock()) {
// Some lagmeters do a NOTICE to their own nick, ignore those.
if (!sTarget.Equals(m_sNick))
PutStatus("Your notice to [" + Message.GetTarget() + "] got lost, "
"you are not connected to IRC!");
continue;
}
if (m_pNetwork) {
AddBuffer(Message);
EchoMessage(Message);
PutIRC(Message.ToString(CMessage::ExcludePrefix | CMessage::ExcludeTags));
}
}
return true;
}
bool CClient::OnPartMessage(CPartMessage& Message)
{
CString sChans = Message.GetTarget();
VCString vsChans;
sChans.Split(",", vsChans, false);
sChans.clear();
for (CString& sChan : vsChans) {
bool bContinue = false;
Message.SetTarget(sChan);
NETWORKMODULECALL(OnUserPartMessage(Message), m_pUser, m_pNetwork, this, &bContinue);
if (bContinue) continue;
sChan = Message.GetTarget();
CChan* pChan = m_pNetwork ? m_pNetwork->FindChan(sChan) : nullptr;
if (pChan && !pChan->IsOn()) {
PutStatusNotice("Removing channel [" + sChan + "]");
m_pNetwork->DelChan(sChan);
} else {
sChans += (sChans.empty()) ? sChan : CString("," + sChan);
}
}
if (sChans.empty()) {
return true;
}
Message.SetTarget(sChans);
return false;
}
bool CClient::OnPingMessage(CMessage& Message)
{
// All PONGs are generated by ZNC. We will still forward this to
// the ircd, but all PONGs from irc will be blocked.
if (!Message.GetParams().empty())
PutClient(":irc.znc.in PONG irc.znc.in " + Message.GetParams(0));
else
PutClient(":irc.znc.in PONG irc.znc.in");
return false;
}
bool CClient::OnPongMessage(CMessage& Message)
{
// Block PONGs, we already responded to the pings
return true;
}
bool CClient::OnQuitMessage(CQuitMessage& Message)
{
bool bReturn = false;
NETWORKMODULECALL(OnUserQuitMessage(Message), m_pUser, m_pNetwork, this, &bReturn);
if (!bReturn) {
Close(Csock::CLT_AFTERWRITE); // Treat a client quit as a detach
}
// Don't forward this msg. We don't want the client getting us disconnected.
return true;
}
bool CClient::OnTextMessage(CTextMessage& Message)
{
CString sTargets = Message.GetTarget();
VCString vTargets;
sTargets.Split(",", vTargets, false);
for (CString& sTarget : vTargets) {
Message.SetTarget(sTarget);
if (sTarget.TrimPrefix(m_pUser->GetStatusPrefix())) {
EchoMessage(Message);
if (sTarget.Equals("status")) {
CString sMsg = Message.GetText();
UserCommand(sMsg);
} else {
CALLMOD(sTarget, this, m_pUser, m_pNetwork, OnModCommand(Message.GetText()));
}
continue;
}
bool bContinue = false;
NETWORKMODULECALL(OnUserTextMessage(Message), m_pUser, m_pNetwork, this, &bContinue);
if (bContinue) continue;
if (!GetIRCSock()) {
// Some lagmeters do a PRIVMSG to their own nick, ignore those.
if (!sTarget.Equals(m_sNick))
PutStatus("Your message to [" + Message.GetTarget() + "] got lost, "
"you are not connected to IRC!");
continue;
}
if (m_pNetwork) {
AddBuffer(Message);
EchoMessage(Message);
PutIRC(Message.ToString(CMessage::ExcludePrefix | CMessage::ExcludeTags));
}
}
return true;
}
bool CClient::OnTopicMessage(CTopicMessage& Message)
{
bool bReturn = false;
CString sChan = Message.GetTarget();
CString sTopic = Message.GetTopic();
if (!sTopic.empty()) {
NETWORKMODULECALL(OnUserTopicMessage(Message), m_pUser, m_pNetwork, this, &bReturn);
} else {
NETWORKMODULECALL(OnUserTopicRequest(sChan), m_pUser, m_pNetwork, this, &bReturn);
Message.SetTarget(sChan);
}
return bReturn;
}
bool CClient::OnOtherMessage(CMessage& Message)
{
const CString& sCommand = Message.GetCommand();
if (sCommand.Equals("ZNC")) {
CString sTarget = Message.GetParam(0);
CString sModCommand;
if (sTarget.TrimPrefix(m_pUser->GetStatusPrefix())) {
sModCommand = Message.GetParams(1);
} else {
sTarget = "status";
sModCommand = Message.GetParams(0);
}
if (sTarget.Equals("status")) {
if (sModCommand.empty())
PutStatus("Hello. How may I help you?");
else
UserCommand(sModCommand);
} else {
if (sModCommand.empty())
CALLMOD(sTarget, this, m_pUser, m_pNetwork, PutModule("Hello. How may I help you?"))
else
CALLMOD(sTarget, this, m_pUser, m_pNetwork, OnModCommand(sModCommand))
}
return true;
} else if (sCommand.Equals("ATTACH")) {
if (!m_pNetwork) {
return true;
}
CString sPatterns = Message.GetParams(0);
if (sPatterns.empty()) {
PutStatusNotice("Usage: /attach <#chans|queries>");
return true;
}
set<CChan*> sChans = MatchChans(sPatterns);
unsigned int uAttachedChans = AttachChans(sChans);
PutStatusNotice("There were [" + CString(sChans.size()) + "] channels matching [" + sPatterns + "]");
PutStatusNotice("Attached [" + CString(uAttachedChans) + "] channels");
return true;
} else if (sCommand.Equals("DETACH")) {
if (!m_pNetwork) {
return true;
}
CString sPatterns = Message.GetParams(0);
if (sPatterns.empty()) {
PutStatusNotice("Usage: /detach <#chans>");
return true;
}
set<CChan*> sChans = MatchChans(sPatterns);
unsigned int uDetached = DetachChans(sChans);
PutStatusNotice("There were [" + CString(sChans.size()) + "] channels matching [" + sPatterns + "]");
PutStatusNotice("Detached [" + CString(uDetached) + "] channels");
return true;
} else if (sCommand.Equals("PROTOCTL")) {
for (const CString& sParam : Message.GetParams()) {
if (sParam == "NAMESX") {
m_bNamesx = true;
} else if (sParam == "UHNAMES") {
m_bUHNames = true;
}
}
return true; // If the server understands it, we already enabled namesx / uhnames
}
return false;
}