EModRet OnUserNotice(CString& sTarget, CString& sMessage) override { sTarget.TrimPrefix(NickPrefix()); if (sMessage.TrimPrefix("``")) { return CONTINUE; } MCString::iterator it = FindNV(sTarget.AsLower()); if (it != EndNV()) { CChan* pChan = GetNetwork()->FindChan(sTarget); CString sNickMask = GetNetwork()->GetIRCNick().GetNickMask(); if (pChan) { if (!pChan->AutoClearChanBuffer()) pChan->AddBuffer(":" + NickPrefix() + _NAMEDFMT(sNickMask) + " NOTICE " + _NAMEDFMT(sTarget) + " :{text}", sMessage); GetUser()->PutUser(":" + NickPrefix() + sNickMask + " NOTICE " + sTarget + " :" + sMessage, NULL, GetClient()); } CString sMsg = MakeIvec() + sMessage; sMsg.Encrypt(it->second); sMsg.Base64Encode(); sMsg = "+OK *" + sMsg; PutIRC("NOTICE " + sTarget + " :" + sMsg); return HALTCORE; } return CONTINUE; }
virtual bool OnLoad(const CString& sArgs, CString& sMessage) { VCString vsChans; sArgs.Split(" ", vsChans, false); for (VCString::const_iterator it = vsChans.begin(); it != vsChans.end(); ++it) { CString sAdd = *it; bool bNegated = sAdd.TrimPrefix("!"); CString sChan = sAdd.Token(0); CString sHost = sAdd.Token(1, true); if (!Add(bNegated, sChan, sHost)) { PutModule("Unable to add [" + *it + "]"); } } // Load our saved settings, ignore errors MCString::iterator it; for (it = BeginNV(); it != EndNV(); ++it) { CString sAdd = it->first; bool bNegated = sAdd.TrimPrefix("!"); CString sChan = sAdd.Token(0); CString sHost = sAdd.Token(1, true); Add(bNegated, sChan, sHost); } return true; }
void CMessage::Parse(CString sMessage) { // <tags> m_mssTags.clear(); if (sMessage.StartsWith("@")) { VCString vsTags; sMessage.Token(0).TrimPrefix_n("@").Split(";", vsTags, false); for (const CString& sTag : vsTags) { CString sKey = sTag.Token(0, false, "=", true); CString sValue = sTag.Token(1, true, "=", true); m_mssTags[sKey] = sValue.Escape(CString::EMSGTAG, CString::CString::EASCII); } sMessage = sMessage.Token(1, true); } // <message> ::= [':' <prefix> <SPACE> ] <command> <params> <crlf> // <prefix> ::= <servername> | <nick> [ '!' <user> ] [ '@' <host> ] // <command> ::= <letter> { <letter> } | <number> <number> <number> // <SPACE> ::= ' ' { ' ' } // <params> ::= <SPACE> [ ':' <trailing> | <middle> <params> ] // <middle> ::= <Any *non-empty* sequence of octets not including SPACE // or NUL or CR or LF, the first of which may not be ':'> // <trailing> ::= <Any, possibly *empty*, sequence of octets not including // NUL or CR or LF> // <prefix> if (sMessage.TrimPrefix(":")) { m_Nick.Parse(sMessage.Token(0)); sMessage = sMessage.Token(1, true); } // <command> m_sCommand = sMessage.Token(0); sMessage = sMessage.Token(1, true); // <params> m_bColon = false; m_vsParams.clear(); while (!sMessage.empty()) { m_bColon = sMessage.TrimPrefix(":"); if (m_bColon) { m_vsParams.push_back(sMessage); sMessage.clear(); } else { m_vsParams.push_back(sMessage.Token(0)); sMessage = sMessage.Token(1, true); } } InitType(); }
int CZNCSock::ConvertAddress(const struct sockaddr_storage* pAddr, socklen_t iAddrLen, CString& sIP, u_short* piPort) const { int ret = Csock::ConvertAddress(pAddr, iAddrLen, sIP, piPort); if (ret == 0) sIP.TrimPrefix("::ffff:"); return ret; }
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")) {
EModRet OnUserTopic(CString& sTarget, CString& sMessage) override { sTarget.TrimPrefix(NickPrefix()); if (sMessage.TrimPrefix("``")) { return CONTINUE; } MCString::iterator it = FindNV(sTarget.AsLower()); if (it != EndNV()) { sMessage = MakeIvec() + sMessage; sMessage.Encrypt(it->second); sMessage.Base64Encode(); sMessage = "+OK *" + sMessage; } return CONTINUE; }
virtual void OnModCommand(const CString& sCommand) { CString sCmd = sCommand.Token(0); if (sCmd.Equals("DELKEY")) { CString sTarget = sCommand.Token(1); if (!sTarget.empty()) { if (DelNV(sTarget.AsLower())) { PutModule("Target [" + sTarget + "] deleted"); } else { PutModule("Target [" + sTarget + "] not found"); } } else { PutModule("Usage DelKey <#chan|Nick>"); } } else if (sCmd.Equals("SETKEY")) { CString sTarget = sCommand.Token(1); CString sKey = sCommand.Token(2, true); // Strip "cbc:" from beginning of string incase someone pastes directly from mircryption sKey.TrimPrefix("cbc:"); if (!sKey.empty()) { SetNV(sTarget.AsLower(), sKey); PutModule("Set encryption key for [" + sTarget + "] to [" + sKey + "]"); } else { PutModule("Usage: SetKey <#chan|Nick> <Key>"); } } else if (sCmd.Equals("LISTKEYS")) { if (BeginNV() == EndNV()) { PutModule("You have no encryption keys set."); } else { CTable Table; Table.AddColumn("Target"); Table.AddColumn("Key"); for (MCString::iterator it = BeginNV(); it != EndNV(); ++it) { Table.AddRow(); Table.SetCell("Target", it->first); Table.SetCell("Key", it->second); } MCString::iterator it = FindNV(NICK_PREFIX_KEY); if (it == EndNV()) { Table.AddRow(); Table.SetCell("Target", NICK_PREFIX_KEY); Table.SetCell("Key", NickPrefix()); } PutModule(Table); } } else if (sCmd.Equals("HELP")) { PutModule("Try: SetKey, DelKey, ListKeys"); } else { PutModule("Unknown command, try 'Help'"); } }
virtual EModRet OnUserRaw(CString& sLine) { CString sCmd = sLine.Token(0).AsUpper(); if (!m_pNetwork->GetIRCSock()) return CONTINUE; if (sCmd.Equals("MODE")) { // Check if this is a mode request that needs to be handled // If there are arguments to a mode change, // we must not route it. if (!sLine.Token(3, true).empty()) return CONTINUE; // Grab the mode change parameter CString sMode = sLine.Token(2); // If this is a channel mode request, znc core replies to it if (sMode.empty()) return CONTINUE; // Check if this is a mode change or a specific // mode request (the later needs to be routed). sMode.TrimPrefix("+"); if (sMode.length() != 1) return CONTINUE; // Now just check if it's one of the supported modes switch (sMode[0]) { case 'I': case 'b': case 'e': break; default: return CONTINUE; } // Ok, this looks like we should route it. // Fall through to the next loop } for (size_t i = 0; vRouteReplies[i].szRequest != NULL; i++) { if (vRouteReplies[i].szRequest == sCmd) { struct queued_req req = { sLine, vRouteReplies[i].vReplies }; m_vsPending[m_pClient].push_back(req); SendRequest(); return HALTCORE; } } return CONTINUE; }
void HandleDel(const CString& sLine) { CString sMsg = sLine.Token(1, true); bool bNegated = sMsg.TrimPrefix("!"); CString sChan = sMsg.Token(0); CString sHost = sMsg.Token(1); if (Del(bNegated, sChan, sHost)) { PutModule("Removed " + sChan + " from list"); } else { PutModule("Usage: Del [!]<#chan> <host>"); } }
EBufferType DecryptBuffer(const CString& sPath, CString& sBuffer, CString& sName) { CString sContent; sBuffer = ""; CFile File(sPath); if (sPath.empty() || !File.Open() || !File.ReadFile(sContent)) return EmptyBuffer; File.Close(); if (!sContent.empty()) { CBlowfish c(m_sPassword, BF_DECRYPT); sBuffer = c.Crypt(sContent); if (sBuffer.TrimPrefix(LEGACY_VERIFICATION_TOKEN)) { sName = FindLegacyBufferName(sPath); return ChanBuffer; } else if (sBuffer.TrimPrefix(CHAN_VERIFICATION_TOKEN)) { sName = sBuffer.FirstLine(); if (sBuffer.TrimLeft(sName + "\n")) return ChanBuffer; } else if (sBuffer.TrimPrefix(QUERY_VERIFICATION_TOKEN)) { sName = sBuffer.FirstLine(); if (sBuffer.TrimLeft(sName + "\n")) return QueryBuffer; } PutModule("Unable to decode Encrypted file [" + sPath + "]"); return InvalidBuffer; } return EmptyBuffer; }
void FilterIncoming(const CString& sTarget, CNick& Nick, CString& sMessage) { if (sMessage.TrimPrefix("+OK *")) { MCString::iterator it = FindNV(sTarget.AsLower()); if (it != EndNV()) { sMessage.Base64Decode(); sMessage.Decrypt(it->second); sMessage.LeftChomp(8); sMessage = sMessage.c_str(); Nick.SetNick(NickPrefix() + Nick.GetNick()); } } }
void OnSetKeyCommand(const CString& sCommand) { CString sTarget = sCommand.Token(1); CString sKey = sCommand.Token(2, true); // Strip "cbc:" from beginning of string incase someone pastes directly from mircryption sKey.TrimPrefix("cbc:"); if (!sKey.empty()) { SetNV(sTarget.AsLower(), sKey); PutModule("Set encryption key for [" + sTarget + "] to [" + sKey + "]"); } else { PutModule("Usage: SetKey <#chan|Nick> <Key>"); } }
void CMessage::InitType() { if (m_sCommand.length() == 3 && isdigit(m_sCommand[0]) && isdigit(m_sCommand[1]) && isdigit(m_sCommand[2])) { m_eType = Type::Numeric; } else if (m_sCommand.Equals("PRIVMSG")) { CString sParam = GetParam(1); if (sParam.TrimPrefix("\001") && sParam.EndsWith("\001")) { if (sParam.StartsWith("ACTION ")) { m_eType = Type::Action; } else { m_eType = Type::CTCP; } } else { m_eType = Type::Text; } } else if (m_sCommand.Equals("NOTICE")) { CString sParam = GetParam(1); if (sParam.StartsWith("\001") && sParam.EndsWith("\001")) { m_eType = Type::CTCP; } else { m_eType = Type::Notice; } } else { std::map<CString, Type> mTypes = { {"ACCOUNT", Type::Account}, {"AWAY", Type::Away}, {"CAP", Type::Capability}, {"ERROR", Type::Error}, {"INVITE", Type::Invite}, {"JOIN", Type::Join}, {"KICK", Type::Kick}, {"MODE", Type::Mode}, {"NICK", Type::Nick}, {"PART", Type::Part}, {"PING", Type::Ping}, {"PONG", Type::Pong}, {"QUIT", Type::Quit}, {"TOPIC", Type::Topic}, {"WALLOPS", Type::Wallops}, }; auto it = mTypes.find(m_sCommand.AsUpper()); if (it != mTypes.end()) { m_eType = it->second; } else { m_eType = Type::Unknown; } } }
EModRet OnRaw(CString& sLine) override { if (!sLine.Token(1).Equals("332")) { return CONTINUE; } CChan* pChan = GetNetwork()->FindChan(sLine.Token(3)); if (pChan) { CNick* Nick = pChan->FindNick(sLine.Token(2)); CString sTopic = sLine.Token(4, true); sTopic.TrimPrefix(":"); FilterIncoming(pChan->GetName(), *Nick, sTopic); sLine = sLine.Token(0) + " " + sLine.Token(1) + " " + sLine.Token(2) + " " + pChan->GetName() + " :" + sTopic; } return CONTINUE; }
void HandleAdd(const CString& sLine) { CString sMsg = sLine.Token(1, true); bool bHelp = false; bool bNegated = sMsg.TrimPrefix("!"); CString sChan = sMsg.Token(0); CString sHost = sMsg.Token(1); if (sChan.empty()) { bHelp = true; } else if (Add(bNegated, sChan, sHost)) { PutModule("Added to list"); } else { PutModule(sLine.Token(1, true) + " is already added"); bHelp = true; } if (bHelp) { PutModule("Usage: Add [!]<#chan> <host>"); PutModule("Wildcards are allowed"); } }
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.TrimPrefix("+")) { bSSL = true; } unsigned short uPort = sPort.ToUShort(); CString sPass = sLine.Token(2, true); return AddServer(sHost, uPort, sPass, bSSL); }
void CClient::HandleCap(const CMessage& Message) { CString sSubCmd = Message.GetParam(0); if (sSubCmd.Equals("LS")) { SCString ssOfferCaps; for (const auto& it : m_mCoreCaps) { bool bServerDependent = std::get<0>(it.second); if (!bServerDependent || m_ssServerDependentCaps.count(it.first) > 0) ssOfferCaps.insert(it.first); } GLOBALMODULECALL(OnClientCapLs(this, ssOfferCaps), NOTHING); CString sRes = CString(" ").Join(ssOfferCaps.begin(), ssOfferCaps.end()); RespondCap("LS :" + sRes); m_bInCap = true; if (Message.GetParam(1).ToInt() >= 302) { m_bCapNotify = true; } } else if (sSubCmd.Equals("END")) { m_bInCap = false; if (!IsAttached()) { if (!m_pUser && m_bGotUser && !m_bGotPass) { SendRequiredPasswordNotice(); } else { AuthUser(); } } } else if (sSubCmd.Equals("REQ")) { VCString vsTokens; Message.GetParam(1).Split(" ", vsTokens, false); for (const CString& sToken : vsTokens) { bool bVal = true; CString sCap = sToken; if (sCap.TrimPrefix("-")) bVal = false; bool bAccepted = false; const auto& it = m_mCoreCaps.find(sCap); if (m_mCoreCaps.end() != it) { bool bServerDependent = std::get<0>(it->second); bAccepted = !bServerDependent || m_ssServerDependentCaps.count(sCap) > 0; } GLOBALMODULECALL(IsClientCapSupported(this, sCap, bVal), &bAccepted); if (!bAccepted) { // Some unsupported capability is requested RespondCap("NAK :" + Message.GetParam(1)); return; } } // All is fine, we support what was requested for (const CString& sToken : vsTokens) { bool bVal = true; CString sCap = sToken; if (sCap.TrimPrefix("-")) bVal = false; auto handler_it = m_mCoreCaps.find(sCap); if (m_mCoreCaps.end() != handler_it) { const auto& handler = std::get<1>(handler_it->second); handler(bVal); } GLOBALMODULECALL(OnClientCapRequest(this, sCap, bVal), NOTHING); if (bVal) { m_ssAcceptedCaps.insert(sCap); } else { m_ssAcceptedCaps.erase(sCap); } } RespondCap("ACK :" + Message.GetParam(1)); } else if (sSubCmd.Equals("LIST")) { CString sList = CString(" ").Join(m_ssAcceptedCaps.begin(), m_ssAcceptedCaps.end()); RespondCap("LIST :" + sList); } else { PutClient(":irc.znc.in 410 " + GetNick() + " " + sSubCmd + " :Invalid CAP subcommand"); } }
void CClient::ReadLine(const CString& sData) { CString sLine = sData; sLine.TrimRight("\n\r"); DEBUG("(" << GetFullName() << ") CLI -> ZNC [" << sLine << "]"); if (sLine.Left(1) == "@") { // TODO support message-tags properly sLine = sLine.Token(1, true); } bool bReturn = false; if (IsAttached()) { NETWORKMODULECALL(OnUserRaw(sLine), m_pUser, m_pNetwork, this, &bReturn); } else { GLOBALMODULECALL(OnUnknownUserRaw(this, sLine), &bReturn); } if (bReturn) return; CString sCommand = sLine.Token(0); if (sCommand.Left(1) == ":") { // Evil client! Sending a nickmask prefix on client's command // is bad, bad, bad, bad, bad, bad, bad, bad, BAD, B A D! sLine = sLine.Token(1, true); sCommand = sLine.Token(0); } if (!IsAttached()) { // The following commands happen before authentication with ZNC if (sCommand.Equals("PASS")) { m_bGotPass = true; CString sAuthLine = sLine.Token(1, true).TrimPrefix_n(); // [user[/network]:]password if (sAuthLine.find(":") == CString::npos) { m_sPass = sAuthLine; sAuthLine = ""; } else { m_sPass = sAuthLine.Token(1, true, ":"); sAuthLine = sAuthLine.Token(0, false, ":"); } if (!sAuthLine.empty()) { m_sUser = sAuthLine.Token(0, false, "/"); m_sNetwork = sAuthLine.Token(1, true, "/"); } AuthUser(); return; // Don't forward this msg. ZNC has already registered us. } else if (sCommand.Equals("NICK")) { CString sNick = sLine.Token(1).TrimPrefix_n(); m_sNick = sNick; m_bGotNick = true; AuthUser(); return; // Don't forward this msg. ZNC will handle nick changes until auth is complete } else if (sCommand.Equals("USER")) { // user[/network] CString sAuthLine = sLine.Token(1); if (m_sUser.empty() && !sAuthLine.empty()) { m_sUser = sAuthLine.Token(0, false, "/"); m_sNetwork = sAuthLine.Token(1, true, "/"); } m_bGotUser = true; if (m_bGotPass) { AuthUser(); } else if (!m_bInCap) { SendRequiredPasswordNotice(); } return; // Don't forward this msg. ZNC has already registered us. } } if (sCommand.Equals("CAP")) { HandleCap(sLine); // Don't let the client talk to the server directly about CAP, // we don't want anything enabled that ZNC does not support. return; } if (!m_pUser) { // Only CAP, NICK, USER and PASS are allowed before login return; } if (sCommand.Equals("ZNC")) { CString sTarget = sLine.Token(1); CString sModCommand; if (sTarget.TrimPrefix(m_pUser->GetStatusPrefix())) { sModCommand = sLine.Token(2, true); } else { sTarget = "status"; sModCommand = sLine.Token(1, true); } 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 (sCommand.Equals("PING")) {
CS_STRING CZNCSock::ConvertAddress(void *addr, bool ipv6) { CString sRet = Csock::ConvertAddress(addr, ipv6); sRet.TrimPrefix("::ffff:"); return sRet; }