void FilterIncoming(const CString& sTarget, CNick& Nick, CString& sMessage) { if (sMessage.Left(5) == "+OK *") { MCString::iterator it = FindNV(sTarget.AsLower()); if (it != EndNV()) { sMessage.LeftChomp(5); sMessage.Base64Decode(); sMessage.Decrypt(it->second); sMessage.LeftChomp(8); sMessage = sMessage.c_str(); Nick.SetNick(NickPrefix() + Nick.GetNick()); } } }
virtual EModRet OnUserMsg(CString& sTarget, CString& sMessage) { sTarget.TrimLeft(NickPrefix()); if (sMessage.Left(2) == "``") { sMessage.LeftChomp(2); return CONTINUE; } MCString::iterator it = FindNV(sTarget.AsLower()); if (it != EndNV()) { CChan* pChan = m_pNetwork->FindChan(sTarget); if (pChan) { if (!pChan->AutoClearChanBuffer()) pChan->AddBuffer(":" + NickPrefix() + _NAMEDFMT(m_pNetwork->GetIRCNick().GetNickMask()) + " PRIVMSG " + _NAMEDFMT(sTarget) + " :{text}", sMessage); m_pUser->PutUser(":" + NickPrefix() + m_pNetwork->GetIRCNick().GetNickMask() + " PRIVMSG " + sTarget + " :" + sMessage, NULL, m_pClient); } CString sMsg = MakeIvec() + sMessage; sMsg.Encrypt(it->second); sMsg.Base64Encode(); sMsg = "+OK *" + sMsg; PutIRC("PRIVMSG " + sTarget + " :" + sMsg); return HALTCORE; } return CONTINUE; }
EModRet OnRaw(CString& sLine) { if(sLine.Left(m_look.size()).Equals(m_look)) { CString sChan = sLine.Token(2); CString sMsg = sLine.Token(3, true); sMsg.LeftChomp(); if(sMsg.empty() || sMsg[0] != '<') return CONTINUE; CString::size_type uPos = sMsg.find('>'); if(uPos == CString::npos) return CONTINUE; CString sTempNick = sMsg.substr(1, uPos - 1); CString sNick; for(size_t p = 0; p < sTempNick.size(); p++) { if(isalnum(sTempNick[p]) || std::string("[\\]^_-{|}").find(sTempNick[p]) != CString::npos) { sNick += sTempNick[p]; } } sNick += "|S"; sLine = ":" + sNick + "[email protected] PRIVMSG " + sChan + " :" + sMsg.substr(uPos + 2); } return CONTINUE; }
virtual EModRet OnUserRaw(CString& sLine) { // We dont care if we are not connected to IRC if (!m_pNetwork->IsIRCConnected()) return CONTINUE; // We are trying to get the config nick and this is a /nick? if (!m_pTimer || !sLine.Token(0).Equals("NICK")) return CONTINUE; // Is the nick change for the nick we are trying to get? CString sNick = sLine.Token(1); // Don't even think of using spaces in your nick! if (sNick.Left(1) == ":") sNick.LeftChomp(); if (!sNick.Equals(GetNick())) return CONTINUE; // Indeed trying to change to this nick, generate a 433 for it. // This way we can *always* block incoming 433s from the server. PutUser(":" + m_pNetwork->GetIRCServer() + " 433 " + m_pNetwork->GetIRCNick().GetNick() + " " + sNick + " :ZNC is already trying to get this nickname"); return CONTINUE; }
EModRet OnUserRaw(CString& sLine) override { // Handle ISON if (sLine.Token(0).Equals("ison")) { VCString vsNicks; // Get the list of nicks which are being asked for sLine.Token(1, true).TrimLeft_n(":").Split(" ", vsNicks, false); CString sBNCNicks; for (const CString& sNick : vsNicks) { if (IsOnlineModNick(sNick)) { sBNCNicks += " " + sNick; } } // Remove the leading space sBNCNicks.LeftChomp(); if (!GetNetwork()->GetIRCSock()) { // if we are not connected to any IRC server, send // an empty or module-nick filled response. PutUser(":irc.znc.in 303 " + GetClient()->GetNick() + " :" + sBNCNicks); } else { // We let the server handle this request and then act on // the 303 response from the IRC server. m_ISONRequests.push_back(sBNCNicks); } } // Handle WHOIS if (sLine.Token(0).Equals("whois")) { CString sNick = sLine.Token(1); if (IsOnlineModNick(sNick)) { CIRCNetwork* pNetwork = GetNetwork(); PutUser(":znc.in 311 " + pNetwork->GetCurNick() + " " + sNick + " znc znc.in * :" + sNick); PutUser(":znc.in 312 " + pNetwork->GetCurNick() + " " + sNick + " *.znc.in :Bouncer"); PutUser(":znc.in 318 " + pNetwork->GetCurNick() + " " + sNick + " :End of /WHOIS list."); return HALT; } } return CONTINUE; }
CString ParsePerform(const CString& sArg) const { CString sPerf = sArg; if (sPerf.Left(1) == "/") sPerf.LeftChomp(); if (sPerf.Token(0).Equals("MSG")) { sPerf = "PRIVMSG " + sPerf.Token(1, true); } if ((sPerf.Token(0).Equals("PRIVMSG") || sPerf.Token(0).Equals("NOTICE")) && sPerf.Token(2).Left(1) != ":") { sPerf = sPerf.Token(0) + " " + sPerf.Token(1) + " :" + sPerf.Token(2, true); } return sPerf; }
bool CIRCNetwork::AddServer(const CString& sName) { if (sName.empty()) { return false; } bool bSSL = false; CString sLine = sName; sLine.Trim(); CString sHost = sLine.Token(0); CString sPort = sLine.Token(1); if (sPort.Left(1) == "+") { bSSL = true; sPort.LeftChomp(); } unsigned short uPort = sPort.ToUShort(); CString sPass = sLine.Token(2, true); return AddServer(sHost, uPort, sPass, bSSL); }
virtual bool OnLoad(const CString& sArgs, CString& sMessage) { if (sArgs.Trim_n().empty()) { return true; // use defaults } m_sServer = sArgs.Token(0); CString sPort = sArgs.Token(1); m_sUserFormat = sArgs.Token(2); if (sPort.Left(1) == "+") { m_bSSL = true; sPort.LeftChomp(); } unsigned short uPort = sPort.ToUShort(); if (uPort) { m_uPort = uPort; } return true; }
// Config Parser. Might drop this. bool CConfig::Parse(CFile& file, CString& sErrorMsg) { CString sLine; unsigned int uLineNum = 0; CConfig *pActiveConfig = this; std::stack<ConfigStackEntry> ConfigStack; bool bCommented = false; // support for /**/ style comments if (!file.Seek(0)) { sErrorMsg = "Could not seek to the beginning of the config."; return false; } while (file.ReadLine(sLine)) { uLineNum++; #define ERROR(arg) do { \ std::stringstream stream; \ stream << "Error on line " << uLineNum << ": " << arg; \ sErrorMsg = stream.str(); \ m_SubConfigs.clear(); \ m_ConfigEntries.clear(); \ return false; \ } while (0) // Remove all leading spaces and trailing line endings sLine.TrimLeft(); sLine.TrimRight("\r\n"); if (bCommented || sLine.Left(2) == "/*") { /* Does this comment end on the same line again? */ bCommented = (sLine.Right(2) != "*/"); continue; } if ((sLine.empty()) || (sLine[0] == '#') || (sLine.Left(2) == "//")) { continue; } if ((sLine.Left(1) == "<") && (sLine.Right(1) == ">")) { sLine.LeftChomp(); sLine.RightChomp(); sLine.Trim(); CString sTag = sLine.Token(0); CString sValue = sLine.Token(1, true); sTag.Trim(); sValue.Trim(); if (sTag.Left(1) == "/") { sTag = sTag.substr(1); if (!sValue.empty()) ERROR("Malformated closing tag. Expected \"</" << sTag << ">\"."); if (ConfigStack.empty()) ERROR("Closing tag \"" << sTag << "\" which is not open."); const struct ConfigStackEntry& entry = ConfigStack.top(); CConfig myConfig(entry.Config); CString sName(entry.sName); if (!sTag.Equals(entry.sTag)) ERROR("Closing tag \"" << sTag << "\" which is not open."); // This breaks entry ConfigStack.pop(); if (ConfigStack.empty()) pActiveConfig = this; else pActiveConfig = &ConfigStack.top().Config; SubConfig &conf = pActiveConfig->m_SubConfigs[sTag.AsLower()]; SubConfig::const_iterator it = conf.find(sName); if (it != conf.end()) ERROR("Duplicate entry for tag \"" << sTag << "\" name \"" << sName << "\"."); conf[sName] = CConfigEntry(myConfig); } else { if (sValue.empty()) ERROR("Empty block name at begin of block."); ConfigStack.push(ConfigStackEntry(sTag.AsLower(), sValue)); pActiveConfig = &ConfigStack.top().Config; } continue; } // If we have a regular line, figure out where it goes CString sName = sLine.Token(0, false, "="); CString sValue = sLine.Token(1, true, "="); // Only remove the first space, people might want // leading spaces (e.g. in the MOTD). if (sValue.Left(1) == " ") sValue.LeftChomp(); // We don't have any names with spaces, trim all // leading/trailing spaces. sName.Trim(); if (sName.empty() || sValue.empty()) ERROR("Malformed line"); CString sNameLower = sName.AsLower(); pActiveConfig->m_ConfigEntries[sNameLower].push_back(sValue); } if (bCommented) ERROR("Comment not closed at end of file."); if (!ConfigStack.empty()) { const CString& sTag = ConfigStack.top().sTag; ERROR("Not all tags are closed at the end of the file. Inner-most open tag is \"" << sTag << "\"."); } return true; }
virtual void OnModCommand(const CString& sCommand) { CString sCmdName = sCommand.Token(0).AsLower(); if (sCmdName == "add") { CString sPerf = sCommand.Token(1, true); if (sPerf.empty()) { PutModule("Usage: add <command>"); return; } if (sPerf.Left(1) == "/") sPerf.LeftChomp(); if (sPerf.Token(0).Equals("MSG")) { sPerf = "PRIVMSG " + sPerf.Token(1, true); } if ((sPerf.Token(0).Equals("PRIVMSG") || sPerf.Token(0).Equals("NOTICE")) && sPerf.Token(2).Left(1) != ":") { sPerf = sPerf.Token(0) + " " + sPerf.Token(1) + " :" + sPerf.Token(2, true); } m_vPerform.push_back(sPerf); PutModule("Added!"); Save(); } else if (sCmdName == "del") { u_int iNum = sCommand.Token(1, true).ToUInt(); if (iNum > m_vPerform.size() || iNum <= 0) { PutModule("Illegal # Requested"); return; } else { m_vPerform.erase(m_vPerform.begin() + iNum - 1); PutModule("Command Erased."); } Save(); } else if (sCmdName == "list") { int i = 1; CString sExpanded; for (VCString::iterator it = m_vPerform.begin(); it != m_vPerform.end(); it++, i++) { sExpanded = GetUser()->ExpandString(*it); if (sExpanded != *it) PutModule(CString(i) + ": " + *it + " (" + sExpanded + ")"); else PutModule(CString(i) + ": " + *it); } PutModule(" -- End of List"); } else if (sCmdName == "execute") { OnIRCConnected(); PutModule("perform commands sent"); } else if (sCmdName == "swap") { u_int iNumA = sCommand.Token(1).ToUInt(); u_int iNumB = sCommand.Token(2).ToUInt(); if (iNumA > m_vPerform.size() || iNumA <= 0 || iNumB > m_vPerform.size() || iNumB <= 0) { PutModule("Illegal # Requested"); } else { std::iter_swap(m_vPerform.begin() + (iNumA - 1), m_vPerform.begin() + (iNumB - 1)); PutModule("Commands Swapped."); Save(); } } else { PutModule("Commands: add <command>, del <nr>, list, execute, swap <nr> <nr>"); } }
void CIRCSock::ReadLine(const CString& sData) { CString sLine = sData; sLine.TrimRight("\n\r"); DEBUG("(" << m_pUser->GetUserName() << ") IRC -> ZNC [" << sLine << "]"); MODULECALL(OnRaw(sLine), m_pUser, NULL, return); if (sLine.Equals("PING ", false, 5)) { // Generate a reply and don't forward this to any user, // we don't want any PING forwarded PutIRC("PONG " + sLine.substr(5)); return; } else if (sLine.Token(1).Equals("PONG")) { // Block PONGs, we already responded to the pings return; } else if (sLine.Equals("ERROR ", false, 6)) { //ERROR :Closing Link: nick[24.24.24.24] (Excess Flood) CString sError(sLine.substr(6)); if (sError.Left(1) == ":") { sError.LeftChomp(); } m_pUser->PutStatus("Error from Server [" + sError + "]"); return; } CString sCmd = sLine.Token(1); if ((sCmd.length() == 3) && (isdigit(sCmd[0])) && (isdigit(sCmd[1])) && (isdigit(sCmd[2]))) { CString sServer = sLine.Token(0); sServer.LeftChomp(); unsigned int uRaw = sCmd.ToUInt(); CString sNick = sLine.Token(2); CString sRest = sLine.Token(3, true); switch (uRaw) { case 1: { // :irc.server.com 001 nick :Welcome to the Internet Relay Network nick if (m_bAuthed && sServer == "irc.znc.in") { // m_bAuthed == true => we already received another 001 => we might be in a traffic loop m_pUser->PutStatus("ZNC seems to be connected to itself, disconnecting..."); Quit(); return; } m_pUser->SetIRCServer(sServer); SetTimeout(240, TMO_READ); // Now that we are connected, let nature take its course PutIRC("WHO " + sNick); m_bAuthed = true; m_pUser->PutStatus("Connected!"); vector<CClient*>& vClients = m_pUser->GetClients(); for (unsigned int a = 0; a < vClients.size(); a++) { CClient* pClient = vClients[a]; CString sClientNick = pClient->GetNick(false); if (!sClientNick.Equals(sNick)) { // If they connected with a nick that doesn't match the one we got on irc, then we need to update them pClient->PutClient(":" + sClientNick + "!" + m_Nick.GetIdent() + "@" + m_Nick.GetHost() + " NICK :" + sNick); } } SetNick(sNick); MODULECALL(OnIRCConnected(), m_pUser, NULL, ); m_pUser->ClearRawBuffer(); m_pUser->AddRawBuffer(":" + sServer + " " + sCmd + " ", " " + sRest); CZNC::Get().ReleaseISpoof(); m_bISpoofReleased = true; break; } case 5: ParseISupport(sRest); m_pUser->UpdateExactRawBuffer(":" + sServer + " " + sCmd + " ", " " + sRest); break; case 2: case 3: case 4: case 250: // highest connection count case 251: // user count case 252: // oper count case 254: // channel count case 255: // client count case 265: // local users case 266: // global users m_pUser->UpdateRawBuffer(":" + sServer + " " + sCmd + " ", " " + sRest); break; case 305: m_pUser->SetIRCAway(false); break; case 306: m_pUser->SetIRCAway(true); break; case 324: { // MODE sRest.Trim(); CChan* pChan = m_pUser->FindChan(sRest.Token(0)); if (pChan) { pChan->SetModes(sRest.Token(1, true)); } } break; case 329: { sRest.Trim(); CChan* pChan = m_pUser->FindChan(sRest.Token(0)); if (pChan) { unsigned long ulDate = sLine.Token(4).ToULong(); pChan->SetCreationDate(ulDate); } } break; case 331: { // :irc.server.com 331 yournick #chan :No topic is set. CChan* pChan = m_pUser->FindChan(sLine.Token(3)); if (pChan) { pChan->SetTopic(""); } break; } case 332: { // :irc.server.com 332 yournick #chan :This is a topic CChan* pChan = m_pUser->FindChan(sLine.Token(3)); if (pChan) { CString sTopic = sLine.Token(4, true); sTopic.LeftChomp(); pChan->SetTopic(sTopic); } break; } case 333: { // :irc.server.com 333 yournick #chan setternick 1112320796 CChan* pChan = m_pUser->FindChan(sLine.Token(3)); if (pChan) { sNick = sLine.Token(4); unsigned long ulDate = sLine.Token(5).ToULong(); pChan->SetTopicOwner(sNick); pChan->SetTopicDate(ulDate); } break; } case 352: { // :irc.yourserver.com 352 yournick #chan ident theirhost.com irc.theirserver.com theirnick H :0 Real Name sServer = sLine.Token(0); sNick = sLine.Token(7); CString sIdent = sLine.Token(4); CString sHost = sLine.Token(5); sServer.LeftChomp(); if (sNick.Equals(GetNick())) { m_Nick.SetIdent(sIdent); m_Nick.SetHost(sHost); } m_pUser->SetIRCNick(m_Nick); m_pUser->SetIRCServer(sServer); const vector<CChan*>& vChans = m_pUser->GetChans(); for (unsigned int a = 0; a < vChans.size(); a++) { vChans[a]->OnWho(sNick, sIdent, sHost); } break; } case 353: { // NAMES sRest.Trim(); // Todo: allow for non @+= server msgs CChan* pChan = m_pUser->FindChan(sRest.Token(1)); // If we don't know that channel, some client might have // requested a /names for it and we really should forward this. if (pChan) { CString sNicks = sRest.Token(2, true); if (sNicks.Left(1) == ":") { sNicks.LeftChomp(); } pChan->AddNicks(sNicks); } ForwardRaw353(sLine); // We forwarded it already, so return return; } case 366: { // end of names list m_pUser->PutUser(sLine); // First send them the raw // :irc.server.com 366 nick #chan :End of /NAMES list. CChan* pChan = m_pUser->FindChan(sRest.Token(0)); if (pChan) { if (pChan->IsOn()) { // If we are the only one in the chan, set our default modes if (pChan->GetNickCount() == 1) { CString sModes = pChan->GetDefaultModes(); if (sModes.empty()) { sModes = m_pUser->GetDefaultChanModes(); } if (!sModes.empty()) { PutIRC("MODE " + pChan->GetName() + " " + sModes); } } } } return; // return so we don't send them the raw twice } case 375: // begin motd case 422: // MOTD File is missing m_pUser->ClearMotdBuffer(); case 372: // motd case 376: // end motd m_pUser->AddMotdBuffer(":" + sServer + " " + sCmd + " ", " " + sRest); break; case 437: // :irc.server.net 437 * badnick :Nick/channel is temporarily unavailable // :irc.server.net 437 mynick badnick :Nick/channel is temporarily unavailable // :irc.server.net 437 mynick badnick :Cannot change nickname while banned on channel if (m_pUser->IsChan(sRest.Token(0)) || sNick != "*") break; case 432: // :irc.server.com 432 * nick :Erroneous Nickname: Illegal characters case 433: { CString sBadNick = sRest.Token(0); if (!m_bAuthed) { SendAltNick(sBadNick); return; } break; } case 451: // :irc.server.com 451 CAP :You have not registered // Servers that dont support CAP will give us this error, dont send it to the client if (sNick.Equals("CAP")) return; case 470: { // :irc.unreal.net 470 mynick [Link] #chan1 has become full, so you are automatically being transferred to the linked channel #chan2 // :mccaffrey.freenode.net 470 mynick #electronics ##electronics :Forwarding to another channel // freenode style numeric CChan* pChan = m_pUser->FindChan(sRest.Token(0)); if (!pChan) { // unreal style numeric pChan = m_pUser->FindChan(sRest.Token(1)); } if (pChan) { pChan->Disable(); m_pUser->PutStatus("Channel [" + pChan->GetName() + "] is linked to " "another channel and was thus disabled."); } break; } } } else {
void CIRCSock::ReadLine(const CString& sData) { CString sLine = sData; sLine.TrimRight("\n\r"); DEBUG("(" << m_pNetwork->GetUser()->GetUserName() << "/" << m_pNetwork->GetName() << ") IRC -> ZNC [" << sLine << "]"); NETWORKMODULECALL(OnRaw(sLine), m_pNetwork->GetUser(), m_pNetwork, NULL, return); if (sLine.Equals("PING ", false, 5)) { // Generate a reply and don't forward this to any user, // we don't want any PING forwarded PutIRC("PONG " + sLine.substr(5)); return; } else if (sLine.Token(1).Equals("PONG")) { // Block PONGs, we already responded to the pings return; } else if (sLine.Equals("ERROR ", false, 6)) { //ERROR :Closing Link: nick[24.24.24.24] (Excess Flood) CString sError(sLine.substr(6)); sError.TrimPrefix(); m_pNetwork->PutStatus("Error from Server [" + sError + "]"); return; } CString sCmd = sLine.Token(1); if ((sCmd.length() == 3) && (isdigit(sCmd[0])) && (isdigit(sCmd[1])) && (isdigit(sCmd[2]))) { CString sServer = sLine.Token(0).LeftChomp_n(); unsigned int uRaw = sCmd.ToUInt(); CString sNick = sLine.Token(2); CString sRest = sLine.Token(3, true); switch (uRaw) { case 1: { // :irc.server.com 001 nick :Welcome to the Internet Relay Network nick if (m_bAuthed && sServer == "irc.znc.in") { // m_bAuthed == true => we already received another 001 => we might be in a traffic loop m_pNetwork->PutStatus("ZNC seems to be connected to itself, disconnecting..."); Quit(); return; } m_pNetwork->SetIRCServer(sServer); SetTimeout(540, TMO_READ); // Now that we are connected, let nature take its course PutIRC("WHO " + sNick); m_bAuthed = true; m_pNetwork->PutStatus("Connected!"); vector<CClient*>& vClients = m_pNetwork->GetClients(); for (unsigned int a = 0; a < vClients.size(); a++) { CClient* pClient = vClients[a]; CString sClientNick = pClient->GetNick(false); if (!sClientNick.Equals(sNick)) { // If they connected with a nick that doesn't match the one we got on irc, then we need to update them pClient->PutClient(":" + sClientNick + "!" + m_Nick.GetIdent() + "@" + m_Nick.GetHost() + " NICK :" + sNick); } } SetNick(sNick); NETWORKMODULECALL(OnIRCConnected(), m_pNetwork->GetUser(), m_pNetwork, NULL, NOTHING); m_pNetwork->ClearRawBuffer(); m_pNetwork->AddRawBuffer(":" + sServer + " " + sCmd + " ", " " + sRest); break; } case 5: ParseISupport(sRest); m_pNetwork->UpdateExactRawBuffer(":" + sServer + " " + sCmd + " ", " " + sRest); break; case 10: { // :irc.server.com 010 nick <hostname> <port> :<info> CString sHost = sRest.Token(0); CString sPort = sRest.Token(1); CString sInfo = sRest.Token(2, true).TrimPrefix_n(); m_pNetwork->PutStatus("Server [" + m_pNetwork->GetCurrentServer()->GetString(false) + "] redirects us to [" + sHost + ":" + sPort + "] with reason [" + sInfo + "]"); m_pNetwork->PutStatus("Perhaps you want to add it as a new server."); // Don't send server redirects to the client return; } case 2: case 3: case 4: case 250: // highest connection count case 251: // user count case 252: // oper count case 254: // channel count case 255: // client count case 265: // local users case 266: // global users m_pNetwork->UpdateRawBuffer(":" + sServer + " " + sCmd + " ", " " + sRest); break; case 305: m_pNetwork->SetIRCAway(false); break; case 306: m_pNetwork->SetIRCAway(true); break; case 324: { // MODE sRest.Trim(); CChan* pChan = m_pNetwork->FindChan(sRest.Token(0)); if (pChan) { pChan->SetModes(sRest.Token(1, true)); // We don't SetModeKnown(true) here, // because a 329 will follow if (!pChan->IsModeKnown()) { // When we JOIN, we send a MODE // request. This makes sure the // reply isn't forwarded. return; } } } break; case 329: { sRest.Trim(); CChan* pChan = m_pNetwork->FindChan(sRest.Token(0)); if (pChan) { unsigned long ulDate = sLine.Token(4).ToULong(); pChan->SetCreationDate(ulDate); if (!pChan->IsModeKnown()) { pChan->SetModeKnown(true); // When we JOIN, we send a MODE // request. This makes sure the // reply isn't forwarded. return; } } } break; case 331: { // :irc.server.com 331 yournick #chan :No topic is set. CChan* pChan = m_pNetwork->FindChan(sLine.Token(3)); if (pChan) { pChan->SetTopic(""); } break; } case 332: { // :irc.server.com 332 yournick #chan :This is a topic CChan* pChan = m_pNetwork->FindChan(sLine.Token(3)); if (pChan) { CString sTopic = sLine.Token(4, true); sTopic.LeftChomp(); pChan->SetTopic(sTopic); } break; } case 333: { // :irc.server.com 333 yournick #chan setternick 1112320796 CChan* pChan = m_pNetwork->FindChan(sLine.Token(3)); if (pChan) { sNick = sLine.Token(4); unsigned long ulDate = sLine.Token(5).ToULong(); pChan->SetTopicOwner(sNick); pChan->SetTopicDate(ulDate); } break; } case 352: { // :irc.yourserver.com 352 yournick #chan ident theirhost.com irc.theirserver.com theirnick H :0 Real Name sServer = sLine.Token(0); sNick = sLine.Token(7); CString sIdent = sLine.Token(4); CString sHost = sLine.Token(5); sServer.LeftChomp(); if (sNick.Equals(GetNick())) { m_Nick.SetIdent(sIdent); m_Nick.SetHost(sHost); } m_pNetwork->SetIRCNick(m_Nick); m_pNetwork->SetIRCServer(sServer); const vector<CChan*>& vChans = m_pNetwork->GetChans(); for (unsigned int a = 0; a < vChans.size(); a++) { vChans[a]->OnWho(sNick, sIdent, sHost); } break; } case 353: { // NAMES sRest.Trim(); // Todo: allow for non @+= server msgs CChan* pChan = m_pNetwork->FindChan(sRest.Token(1)); // If we don't know that channel, some client might have // requested a /names for it and we really should forward this. if (pChan) { CString sNicks = sRest.Token(2, true).TrimPrefix_n(); pChan->AddNicks(sNicks); } ForwardRaw353(sLine); // We forwarded it already, so return return; } case 366: { // end of names list m_pNetwork->PutUser(sLine); // First send them the raw // :irc.server.com 366 nick #chan :End of /NAMES list. CChan* pChan = m_pNetwork->FindChan(sRest.Token(0)); if (pChan) { if (pChan->IsOn()) { // If we are the only one in the chan, set our default modes if (pChan->GetNickCount() == 1) { CString sModes = pChan->GetDefaultModes(); if (sModes.empty()) { sModes = m_pNetwork->GetUser()->GetDefaultChanModes(); } if (!sModes.empty()) { PutIRC("MODE " + pChan->GetName() + " " + sModes); } } } } return; // return so we don't send them the raw twice } case 375: // begin motd case 422: // MOTD File is missing m_pNetwork->ClearMotdBuffer(); case 372: // motd case 376: // end motd m_pNetwork->AddMotdBuffer(":" + sServer + " " + sCmd + " ", " " + sRest); break; case 437: // :irc.server.net 437 * badnick :Nick/channel is temporarily unavailable // :irc.server.net 437 mynick badnick :Nick/channel is temporarily unavailable // :irc.server.net 437 mynick badnick :Cannot change nickname while banned on channel if (m_pNetwork->IsChan(sRest.Token(0)) || sNick != "*") break; case 432: // :irc.server.com 432 * nick :Erroneous Nickname: Illegal characters case 433: { CString sBadNick = sRest.Token(0); if (!m_bAuthed) { SendAltNick(sBadNick); return; } break; } case 451: // :irc.server.com 451 CAP :You have not registered // Servers that dont support CAP will give us this error, dont send it to the client if (sNick.Equals("CAP")) return; case 470: { // :irc.unreal.net 470 mynick [Link] #chan1 has become full, so you are automatically being transferred to the linked channel #chan2 // :mccaffrey.freenode.net 470 mynick #electronics ##electronics :Forwarding to another channel // freenode style numeric CChan* pChan = m_pNetwork->FindChan(sRest.Token(0)); if (!pChan) { // unreal style numeric pChan = m_pNetwork->FindChan(sRest.Token(1)); } if (pChan) { pChan->Disable(); m_pNetwork->PutStatus("Channel [" + pChan->GetName() + "] is linked to " "another channel and was thus disabled."); } break; } } } else { CNick Nick(sLine.Token(0).TrimPrefix_n()); sCmd = sLine.Token(1); CString sRest = sLine.Token(2, true); if (sCmd.Equals("NICK")) { CString sNewNick = sRest.TrimPrefix_n(); bool bIsVisible = false; vector<CChan*> vFoundChans; const vector<CChan*>& vChans = m_pNetwork->GetChans(); for (unsigned int a = 0; a < vChans.size(); a++) { CChan* pChan = vChans[a]; if (pChan->ChangeNick(Nick.GetNick(), sNewNick)) { vFoundChans.push_back(pChan); if (!pChan->IsDetached()) { bIsVisible = true; } } } // Todo: use nick compare function here if (Nick.GetNick().Equals(GetNick())) { // We are changing our own nick, the clients always must see this! bIsVisible = true; SetNick(sNewNick); } NETWORKMODULECALL(OnNick(Nick, sNewNick, vFoundChans), m_pNetwork->GetUser(), m_pNetwork, NULL, NOTHING); if (!bIsVisible) { return; } } else if (sCmd.Equals("QUIT")) { CString sMessage = sRest.TrimPrefix_n(); bool bIsVisible = false; // :[email protected] QUIT :message if (Nick.GetNick().Equals(GetNick())) { m_pNetwork->PutStatus("You quit [" + sMessage + "]"); // We don't call module hooks and we don't // forward this quit to clients (Some clients // disconnect if they receive such a QUIT) return; } vector<CChan*> vFoundChans; const vector<CChan*>& vChans = m_pNetwork->GetChans(); for (unsigned int a = 0; a < vChans.size(); a++) { CChan* pChan = vChans[a]; if (pChan->RemNick(Nick.GetNick())) { vFoundChans.push_back(pChan); if (!pChan->IsDetached()) { bIsVisible = true; } } } NETWORKMODULECALL(OnQuit(Nick, sMessage, vFoundChans), m_pNetwork->GetUser(), m_pNetwork, NULL, NOTHING); if (!bIsVisible) { return; } } else if (sCmd.Equals("JOIN")) { CString sChan = sRest.Token(0).TrimPrefix_n(); CChan* pChan; // Todo: use nick compare function if (Nick.GetNick().Equals(GetNick())) { m_pNetwork->AddChan(sChan, false); pChan = m_pNetwork->FindChan(sChan); if (pChan) { pChan->ResetJoinTries(); pChan->Enable(); pChan->SetIsOn(true); PutIRC("MODE " + sChan); } } else { pChan = m_pNetwork->FindChan(sChan); } if (pChan) { pChan->AddNick(Nick.GetNickMask()); NETWORKMODULECALL(OnJoin(Nick.GetNickMask(), *pChan), m_pNetwork->GetUser(), m_pNetwork, NULL, NOTHING); if (pChan->IsDetached()) { return; } } } else if (sCmd.Equals("PART")) { CString sChan = sRest.Token(0).TrimPrefix_n(); CString sMsg = sRest.Token(1, true).TrimPrefix_n(); CChan* pChan = m_pNetwork->FindChan(sChan); bool bDetached = false; if (pChan) { pChan->RemNick(Nick.GetNick()); NETWORKMODULECALL(OnPart(Nick.GetNickMask(), *pChan, sMsg), m_pNetwork->GetUser(), m_pNetwork, NULL, NOTHING); if (pChan->IsDetached()) bDetached = true; } // Todo: use nick compare function if (Nick.GetNick().Equals(GetNick())) { m_pNetwork->DelChan(sChan); } /* * We use this boolean because * m_pNetwork->DelChan() will delete this channel * and thus we would dereference an * already-freed pointer! */ if (bDetached) { return; } } else if (sCmd.Equals("MODE")) { CString sTarget = sRest.Token(0); CString sModes = sRest.Token(1, true); if (sModes.Left(1) == ":") sModes = sModes.substr(1); CChan* pChan = m_pNetwork->FindChan(sTarget); if (pChan) { pChan->ModeChange(sModes, &Nick); if (pChan->IsDetached()) { return; } } else if (sTarget == m_Nick.GetNick()) { CString sModeArg = sModes.Token(0); bool bAdd = true; /* no module call defined (yet?) MODULECALL(OnRawUserMode(*pOpNick, *this, sModeArg, sArgs), m_pNetwork->GetUser(), NULL, ); */ for (unsigned int a = 0; a < sModeArg.size(); a++) { const unsigned char& uMode = sModeArg[a]; if (uMode == '+') { bAdd = true; } else if (uMode == '-') { bAdd = false; } else { if (bAdd) { m_scUserModes.insert(uMode); } else { m_scUserModes.erase(uMode); } } } } } else if (sCmd.Equals("KICK")) { // :[email protected] KICK #chan nick :msg CString sChan = sRest.Token(0); CString sKickedNick = sRest.Token(1); CString sMsg = sRest.Token(2, true); sMsg.LeftChomp(); CChan* pChan = m_pNetwork->FindChan(sChan); if (pChan) { NETWORKMODULECALL(OnKick(Nick, sKickedNick, *pChan, sMsg), m_pNetwork->GetUser(), m_pNetwork, NULL, NOTHING); // do not remove the nick till after the OnKick call, so modules // can do Chan.FindNick or something to get more info. pChan->RemNick(sKickedNick); } if (GetNick().Equals(sKickedNick) && pChan) { pChan->SetIsOn(false); // Don't try to rejoin! pChan->Disable(); } if ((pChan) && (pChan->IsDetached())) { return; } } else if (sCmd.Equals("NOTICE")) { // :[email protected] NOTICE #chan :Message CString sTarget = sRest.Token(0); CString sMsg = sRest.Token(1, true); sMsg.LeftChomp(); if (sMsg.WildCmp("\001*\001")) { sMsg.LeftChomp(); sMsg.RightChomp(); if (sTarget.Equals(GetNick())) { if (OnCTCPReply(Nick, sMsg)) { return; } } m_pNetwork->PutUser(":" + Nick.GetNickMask() + " NOTICE " + sTarget + " :\001" + sMsg + "\001"); return; } else { if (sTarget.Equals(GetNick())) { if (OnPrivNotice(Nick, sMsg)) { return; } } else { if (OnChanNotice(Nick, sTarget, sMsg)) { return; } } } if (Nick.GetNick().Equals(m_pNetwork->GetIRCServer())) { m_pNetwork->PutUser(":" + Nick.GetNick() + " NOTICE " + sTarget + " :" + sMsg); } else { m_pNetwork->PutUser(":" + Nick.GetNickMask() + " NOTICE " + sTarget + " :" + sMsg); } return; } else if (sCmd.Equals("TOPIC")) { // :[email protected] TOPIC #chan :This is a topic CChan* pChan = m_pNetwork->FindChan(sLine.Token(2)); if (pChan) { CString sTopic = sLine.Token(3, true); sTopic.LeftChomp(); NETWORKMODULECALL(OnTopic(Nick, *pChan, sTopic), m_pNetwork->GetUser(), m_pNetwork, NULL, return); pChan->SetTopicOwner(Nick.GetNick()); pChan->SetTopicDate((unsigned long) time(NULL)); pChan->SetTopic(sTopic); if (pChan->IsDetached()) { return; // Don't forward this } sLine = ":" + Nick.GetNickMask() + " TOPIC " + pChan->GetName() + " :" + sTopic; } } else if (sCmd.Equals("PRIVMSG")) {
CString CTemplate::GetValue(const CString& sArgs, bool bFromIf) { CTemplateLoopContext* pContext = GetCurLoopContext(); CString sName = sArgs.Token(0); CString sRest = sArgs.Token(1, true); CString sRet; while (sRest.Replace(" =", "=", "\"", "\"")) {} while (sRest.Replace("= ", "=", "\"", "\"")) {} VCString vArgs; MCString msArgs; //sRest.Split(" ", vArgs, false, "\"", "\""); sRest.QuoteSplit(vArgs); for (unsigned int a = 0; a < vArgs.size(); a++) { const CString& sArg = vArgs[a]; msArgs[sArg.Token(0, false, "=").AsUpper()] = sArg.Token(1, true, "="); } /* We have no CConfig in ZNC land * Hmm... Actually, we do have it now. if (msArgs.find("CONFIG") != msArgs.end()) { sRet = CConfig::GetValue(sName); } else*/ if (msArgs.find("ROWS") != msArgs.end()) { vector<CTemplate*>* pLoop = GetLoop(sName); sRet = CString((pLoop) ? pLoop->size() : 0); } else if (msArgs.find("TOP") == msArgs.end() && pContext) { sRet = pContext->GetValue(sArgs, bFromIf); if (!sRet.empty()) { return sRet; } } else { if (sName.Left(1) == "*") { sName.LeftChomp(1); MCString::iterator it = find(sName); sName = (it != end()) ? it->second : ""; } MCString::iterator it = find(sName); sRet = (it != end()) ? it->second : ""; } vector<CSmartPtr<CTemplateTagHandler> >& vspTagHandlers = GetTagHandlers(); if (!vspTagHandlers.empty()) { // @todo this should go up to the top to grab handlers CTemplate* pTmpl = GetCurTemplate(); if (sRet.empty()) { for (unsigned int j = 0; j < vspTagHandlers.size(); j++) { CSmartPtr<CTemplateTagHandler> spTagHandler = vspTagHandlers[j]; CString sCustomOutput; if (!bFromIf && spTagHandler->HandleVar(*pTmpl, sArgs.Token(0), sArgs.Token(1, true), sCustomOutput)) { sRet = sCustomOutput; break; } else if (bFromIf && spTagHandler->HandleIf(*pTmpl, sArgs.Token(0), sArgs.Token(1, true), sCustomOutput)) { sRet = sCustomOutput; break; } } } for (unsigned int j = 0; j < vspTagHandlers.size(); j++) { CSmartPtr<CTemplateTagHandler> spTagHandler = vspTagHandlers[j]; if (spTagHandler->HandleValue(*pTmpl, sRet, msArgs)) { break; } } } if (!bFromIf) { if (sRet.empty()) { sRet = ResolveLiteral(msArgs["DEFAULT"]); } MCString::iterator it = msArgs.find("ESC"); if (it != msArgs.end()) { VCString vsEscs; it->second.Split(",", vsEscs, false); for (unsigned int a = 0; a < vsEscs.size(); a++) { sRet.Escape(CString::ToEscape(vsEscs[a])); } } else { sRet.Escape(m_spOptions->GetEscapeFrom(), m_spOptions->GetEscapeTo()); } } return sRet; }