void SetMechanismCommand(const CString& sLine) { CString sMechanisms = sLine.Token(1, true).AsUpper(); if (!sMechanisms.empty()) { VCString vsMechanisms; sMechanisms.Split(" ", vsMechanisms); for (VCString::const_iterator it = vsMechanisms.begin(); it != vsMechanisms.end(); ++it) { if (!SupportsMechanism(*it)) { PutModule("Unsupported mechanism: " + *it); return; } } SetNV(NV_MECHANISMS, sMechanisms); } PutModule("Current mechanisms set: " + GetMechanismsString()); }
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); m_pNetwork->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); m_pNetwork->PutIRC(Message.ToString(CMessage::ExcludePrefix | CMessage::ExcludeTags)); } } return true; }
void OnModCommand(const CString& sCommand) override { CString sResult; VCString vsResult; CString sCmd = sCommand; if (sCmd.Token(0).CaseCmp(".tcl") == 0) sCmd = sCmd.Token(1, true); if (sCmd.Left(1).CaseCmp(".") == 0) sCmd = "Binds::ProcessDcc - - {" + sCmd + "}"; Tcl_Eval(interp, sCmd.c_str()); sResult = CString(Tcl_GetStringResult(interp)); if (!sResult.empty()) { sResult.Split("\n", vsResult); unsigned int a = 0; for (a = 0; a < vsResult.size(); a++) PutModule(vsResult[a].TrimRight_n()); } }
void Replay(const CString & sBuffer) { CString sFile; CString sName; PutUser(":***[email protected] PRIVMSG " + sBuffer + " :Buffer Playback..."); if (DecryptBuffer(GetPath(sBuffer), sFile, sName)) { VCString vsLines; VCString::iterator it; sFile.Split("\n", vsLines); for (it = vsLines.begin(); it != vsLines.end(); ++it) { CString sLine(*it); sLine.Trim(); PutUser(sLine); } } PutUser(":***[email protected] PRIVMSG " + sBuffer + " :Playback Complete."); }
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) { if (!Add(*it)) { PutModule("Unable to add [" + *it + "]"); } } // Load our saved settings, ignore errors MCString::iterator it; for (it = BeginNV(); it != EndNV(); ++it) { Add(it->first); } // Default is auto cycle for all channels if (m_vsChans.empty()) Add("*"); return true; }
virtual void OnModCommand(const CString& sCmdLine) { CString sCommand = sCmdLine.Token(0); CString sArgs = sCmdLine.Token(1, true); if (sCommand.Equals("setpass")) { PutModule("Password set to [" + sArgs + "]"); m_sPassword = CBlowfish::MD5(sArgs); } else if (sCommand.Equals("dumpbuff")) { CString sFile; if (DecryptChannel(sArgs, sFile)) { VCString vsLines; VCString::iterator it; sFile.Split("\n", vsLines); for (it = vsLines.begin(); it != vsLines.end(); ++it) { CString sLine(*it); sLine.Trim(); PutModule("[" + sLine + "]"); } } PutModule("//!-- EOF " + sArgs); } else if (sCommand.Equals("replay")) { Replay(sArgs); PutModule("Replayed " + sArgs); } else if (sCommand.Equals("save")) { SaveBufferToDisk(); PutModule("Done."); } else PutModule("Unknown command [" + sCommand + "]"); }
bool CDir::MakeDir(const CString& sPath, mode_t iMode) { CString sDir; VCString dirs; VCString::iterator it; // Just in case someone tries this... if (sPath.empty()) { errno = ENOENT; return false; } // If this is an absolute path, we need to handle this now! if (sPath.StartsWith("/")) sDir = "/"; // For every single subpath, do... sPath.Split("/", dirs, false); for (it = dirs.begin(); it != dirs.end(); ++it) { // Add this to the path we already created sDir += *it; int i = mkdir(sDir.c_str(), iMode); if (i != 0) { // All errors except EEXIST are fatal if (errno != EEXIST) return false; // If it's EEXIST we have to make sure it's a dir if (!CFile::IsDir(sDir)) return false; } sDir += "/"; } // All went well 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); if (m_pNetwork) { // May be nullptr. Message.SetChan(m_pNetwork->FindChan(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(t_f("Removing channel {1}")(sChan)); m_pNetwork->DelChan(sChan); } else { sChans += (sChans.empty()) ? sChan : CString("," + sChan); } } if (sChans.empty()) { return true; } Message.SetTarget(sChans); return false; }
bool OnLoad(const CString& sArgs, CString& sMessage) override { VCString vArgs; VCString::iterator it; MCString::iterator it2; // Load saved settings for (it2 = BeginNV(); it2 != EndNV(); ++it2) { // Ignore errors Block(it2->first); } // Parse arguments, each argument is a user name to block sArgs.Split(" ", vArgs, false); for (it = vArgs.begin(); it != vArgs.end(); ++it) { if (!Block(*it)) { sMessage = "Could not block [" + *it + "]"; return false; } } return true; }
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); m_pNetwork->PutIRC(Message.ToString(CMessage::ExcludePrefix | CMessage::ExcludeTags)); } } return true; }
bool BootStrap() { CString sFile; if (DecryptMessages(sFile)) { VCString vsLines; VCString::iterator it; sFile.Split("\n", vsLines); for (it = vsLines.begin(); it != vsLines.end(); ++it) { CString sLine(*it); sLine.Trim(); AddMessage(sLine); } } else { m_sPassword = ""; CUtils::PrintError("[" + GetModName() + ".so] Failed to Decrypt Messages"); return(false); } return(true); }
void CUrlBufferModule::CheckLineForTrigger(const CString& sMessage, const CString& sTarget) { if(GetNV("enablepublic").ToBool()) { VCString words; sMessage.Split(" ", words, false, "", "", true, true); for (size_t a = 0; a < words.size(); a++) { CString& word = words[a]; if(word.AsLower() == "!showlinks") { if(lastUrls.empty()) PutIRC("PRIVMSG " + sTarget + " :No links were found..."); else { unsigned int maxLinks = GetNV("buffersize").ToUInt(); if (a+1 < words.size()) { unsigned int size = words[a+1].ToUInt(); if(size!=0 && size<UINT_MAX) //if it was a valid number maxLinks = size; } unsigned int maxSize = lastUrls.size()-1; for(unsigned int i=0; i<=maxSize && i<maxLinks; i++) { sleep(1); PutIRC("PRIVMSG " + sTarget + " :" + nicks[maxSize-i] + ": "+ lastUrls[maxSize-i]); } } } } } }
bool CClient::PutClient(const CMessage& Message) { if (!m_bAwayNotify && Message.GetType() == CMessage::Type::Away) { return false; } else if (!m_bAccountNotify && Message.GetType() == CMessage::Type::Account) { return false; } CMessage Msg(Message); const CIRCSock* pIRCSock = GetIRCSock(); if (pIRCSock) { if (Msg.GetType() == CMessage::Type::Numeric) { unsigned int uCode = Msg.As<CNumericMessage>().GetCode(); if (uCode == 352) { // RPL_WHOREPLY if (!m_bNamesx && pIRCSock->HasNamesx()) { // The server has NAMESX, but the client doesn't, so we need // to remove extra prefixes CString sNick = Msg.GetParam(6); if (sNick.size() > 1 && pIRCSock->IsPermChar(sNick[1])) { CString sNewNick = sNick; size_t pos = sNick.find_first_not_of(pIRCSock->GetPerms()); if (pos >= 2 && pos != CString::npos) { sNewNick = sNick[0] + sNick.substr(pos); } Msg.SetParam(6, sNewNick); } } } else if (uCode == 353) { // RPL_NAMES if ((!m_bNamesx && pIRCSock->HasNamesx()) || (!m_bUHNames && pIRCSock->HasUHNames())) { // The server has either UHNAMES or NAMESX, but the client // is missing either or both CString sNicks = Msg.GetParam(3); VCString vsNicks; sNicks.Split(" ", vsNicks, false); for (CString& sNick : vsNicks) { if (sNick.empty()) break; if (!m_bNamesx && pIRCSock->HasNamesx() && pIRCSock->IsPermChar(sNick[0])) { // The server has NAMESX, but the client doesn't, so // we just use the first perm char size_t pos = sNick.find_first_not_of(pIRCSock->GetPerms()); if (pos >= 2 && pos != CString::npos) { sNick = sNick[0] + sNick.substr(pos); } } if (!m_bUHNames && pIRCSock->HasUHNames()) { // The server has UHNAMES, but the client doesn't, // so we strip away ident and host sNick = sNick.Token(0, false, "!"); } } Msg.SetParam( 3, CString(" ").Join(vsNicks.begin(), vsNicks.end())); } } } else if (Msg.GetType() == CMessage::Type::Join) { if (!m_bExtendedJoin && pIRCSock->HasExtendedJoin()) { Msg.SetParams({Msg.As<CJoinMessage>().GetTarget()}); } } } MCString mssTags; for (const auto& it : Msg.GetTags()) { if (IsTagEnabled(it.first)) { mssTags[it.first] = it.second; } } if (HasServerTime()) { // If the server didn't set the time tag, manually set it mssTags.emplace("time", CUtils::FormatServerTime(Msg.GetTime())); } Msg.SetTags(mssTags); Msg.SetClient(this); Msg.SetNetwork(m_pNetwork); bool bReturn = false; NETWORKMODULECALL(OnSendToClientMessage(Msg), m_pUser, m_pNetwork, this, &bReturn); if (bReturn) return false; return PutClientRaw(Msg.ToString()); }
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") { // There are 2 different scenarios: // // a) CTCP reply for VERSION is not set. // 1. ZNC receives CTCP VERSION from someone // 2. ZNC forwards CTCP VERSION to client // 3. Client replies with something // 4. ZNC adds itself to the reply // 5. ZNC sends the modified reply to whoever asked // // b) CTCP reply for VERSION is set. // 1. ZNC receives CTCP VERSION from someone // 2. ZNC replies with the configured reply (or just drops it if // empty), without forwarding anything to client // 3. Client does not see any CTCP request, and does not reply // // So, if user doesn't want "via ZNC" in CTCP VERSION reply, they // can set custom reply. // // See more bikeshedding at github issues #820 and #1012 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::PutClient(const CMessage& Message) { if (!m_bAwayNotify && Message.GetType() == CMessage::Type::Away) { return false; } else if (!m_bAccountNotify && Message.GetType() == CMessage::Type::Account) { return false; } CMessage Msg(Message); const CIRCSock* pIRCSock = GetIRCSock(); if (pIRCSock) { if (Msg.GetType() == CMessage::Type::Numeric) { unsigned int uCode = Msg.As<CNumericMessage>().GetCode(); if (uCode == 352) { // RPL_WHOREPLY if (!m_bNamesx && pIRCSock->HasNamesx()) { // The server has NAMESX, but the client doesn't, so we need // to remove extra prefixes CString sNick = Msg.GetParam(6); if (sNick.size() > 1 && pIRCSock->IsPermChar(sNick[1])) { CString sNewNick = sNick; size_t pos = sNick.find_first_not_of(pIRCSock->GetPerms()); if (pos >= 2 && pos != CString::npos) { sNewNick = sNick[0] + sNick.substr(pos); } Msg.SetParam(6, sNewNick); } } } else if (uCode == 353) { // RPL_NAMES if ((!m_bNamesx && pIRCSock->HasNamesx()) || (!m_bUHNames && pIRCSock->HasUHNames())) { // The server has either UHNAMES or NAMESX, but the client // is missing either or both CString sNicks = Msg.GetParam(3); VCString vsNicks; sNicks.Split(" ", vsNicks, false); for (CString& sNick : vsNicks) { if (sNick.empty()) break; if (!m_bNamesx && pIRCSock->HasNamesx() && pIRCSock->IsPermChar(sNick[0])) { // The server has NAMESX, but the client doesn't, so // we just use the first perm char size_t pos = sNick.find_first_not_of(pIRCSock->GetPerms()); if (pos >= 2 && pos != CString::npos) { sNick = sNick[0] + sNick.substr(pos); } } if (!m_bUHNames && pIRCSock->HasUHNames()) { // The server has UHNAMES, but the client doesn't, // so we strip away ident and host sNick = sNick.Token(0, false, "!"); } } Msg.SetParam( 3, CString(" ").Join(vsNicks.begin(), vsNicks.end())); } } } else if (Msg.GetType() == CMessage::Type::Join) { if (!m_bExtendedJoin && pIRCSock->HasExtendedJoin()) { Msg.SetParams({Msg.As<CJoinMessage>().GetTarget()}); } } } CString sLine = Msg.ToString(CMessage::ExcludeTags); // TODO: introduce a module hook that gives control over the tags that are // sent MCString mssTags; if (HasServerTime()) { CString sServerTime = Msg.GetTag("time"); if (!sServerTime.empty()) { mssTags["time"] = sServerTime; } else { mssTags["time"] = CUtils::FormatServerTime(Msg.GetTime()); } } if (HasBatch()) { CString sBatch = Msg.GetTag("batch"); if (!sBatch.empty()) { mssTags["batch"] = sBatch; } } if (!mssTags.empty()) { CUtils::SetMessageTags(sLine, mssTags); } PutClient(sLine); return true; }
void OnModCommand(const CString& command) { VCString tokens; int token_count = command.Split(" ", tokens, false); if (token_count < 1) { return; } CString action = tokens[0].AsLower(); if (action == "email"){ if (token_count < 2) { PutModule("Email: " + NotificationEmail); PutModule("Usage: email <email address>"); return; } NotificationEmail = tokens[1].AsLower(); SetNV("email", NotificationEmail); PutModule("email address set to : "+ NotificationEmail); } else if (action == "subject"){ if (token_count < 2) { PutModule("Subject: " + NotificationSubject); PutModule("Usage: subject <email subject>"); return; } NotificationSubject = tokens[1]; for (int i = 2; i < token_count; i++) { NotificationSubject += " " + tokens[i]; } SetNV("subject", NotificationSubject); PutModule("email subject set to : "+ NotificationSubject); } else if (action == "interval") { if (token_count < 2) { PutModule("Interval: " + GetNV("interval")); PutModule("Usage: interval <seconds>"); return; } SetNV("interval", tokens[1]); PutModule("interval set to : "+ tokens[1] + " , will be effective after reloading mailer : '/msg *status reloadmod mailer'"); } else if (action == "notifmax") { if (token_count < 2) { PutModule("maxNotifications: " + intToString(MaxNotifications)); PutModule("Usage: notifmax <number>"); return; } MaxNotifications = atoi(tokens[1].c_str()); SetNV("maxNotifications", tokens[1]); PutModule("maxNotifications set to : "+ tokens[1]); } else if (action == "help") { PutModule("Commands :"); PutModule(" email <email address>"); PutModule(" set the email to sent notifications to."); PutModule(" subject <email subject>"); PutModule(" set the subject of the notification, default IRC Notification."); PutModule(" interval <seconds>"); PutModule(" set the time between the first message and the mail notification, default 1200 (20 minutes)."); PutModule(" Reload module required ('/msg *status reloadmod mailer')"); PutModule(" notifmax <number>"); PutModule(" set the number of notification in one mail, default 50."); PutModule(" "); PutModule("View the documentation at http://znc-mailer.readthedocs.org/"); } else { if (!DebugMode){ PutModule("Error: invalid command, try `help`"); } } DebugCommands(command); }
virtual bool OnLoad(const CString& sArgs, CString& sErrorMsg) { /* * On module load, check for debug being passed in as an arguement. * * Then setup the timer for batch emails and prompt the user for their * email address. */ VCString tokens; int token_count = sArgs.Split(" ", tokens, false); if (token_count >= 1){ CString action = tokens[0].AsLower(); if (action == "debug"){ PutModule("DEBUG ON"); DebugMode = true; } } // Recover config vars. NotificationEmail = GetNV("email"); NotificationSubject = GetNV("subject"); unsigned int interval = atoi(GetNV("interval").c_str()); MaxNotifications = atoi(GetNV("maxNotifications").c_str()); if (DebugMode) { PutModule("NV: email: " + NotificationEmail); PutModule("NV: subject: " + NotificationSubject); PutModule("NV: interval: " + intToString(interval)); PutModule("NV: maxNotifications: " + intToString(MaxNotifications)); } // Set to default if vars are missing. if (interval <= 0) { // Default to 20 mins, debug to 60 seconds. interval = 1200; if (DebugMode){ interval = 60; } SetNV("interval", "1200"); } if (MaxNotifications <= 0) { // Default to 50. MaxNotifications = 50; SetNV("maxNotifications", "50"); } AddTimer(new CMailerTimer(this, interval, 0, "Mail", "Send emails every "+intToString(interval)+" mins")); if (NotificationSubject == "") { NotificationSubject = "IRC Notification"; SetNV("subject", "IRC Notification"); PutModule("Default subject : 'IRC Notification' (change whith 'subject <subject>')"); } if (NotificationEmail == "") { PutModule("Please tell me what email address you want notifications to be sent to with 'email <address>'"); } return true; }
bool CIRCSock::OnCapabilityMessage(CMessage& Message) { // CAPs are supported only before authorization. if (!m_bAuthed) { // The first parameter is most likely "*". No idea why, the // CAP spec don't mention this, but all implementations // I've seen add this extra asterisk CString sSubCmd = Message.GetParam(1); // If the caplist of a reply is too long, it's split // into multiple replies. A "*" is prepended to show // that the list was split into multiple replies. // This is useful mainly for LS. For ACK and NAK // replies, there's no real need for this, because // we request only 1 capability per line. // If we will need to support broken servers or will // send several requests per line, need to delay ACK // actions until all ACK lines are received and // to recognize past request of NAK by 100 chars // of this reply. CString sArgs; if (Message.GetParam(2) == "*") { sArgs = Message.GetParam(3); } else { sArgs = Message.GetParam(2); } std::map<CString, std::function<void(bool bVal)>> mSupportedCaps = { {"multi-prefix", [this](bool bVal) { m_bNamesx = bVal; } }, {"userhost-in-names", [this](bool bVal) { m_bUHNames = bVal; } }, {"away-notify", [this](bool bVal) { m_bAwayNotify = bVal; } }, {"account-notify", [this](bool bVal) { m_bAccountNotify = bVal; } }, {"extended-join", [this](bool bVal) { m_bExtendedJoin = bVal; } }, {"server-time", [this](bool bVal) { m_bServerTime = bVal; } }, { "znc.in/server-time-iso", [this](bool bVal) { m_bServerTime = bVal; } }, }; if (sSubCmd == "LS") { VCString vsTokens; sArgs.Split(" ", vsTokens, false); for (const CString& sCap : vsTokens) { if (OnServerCapAvailable(sCap) || mSupportedCaps.count(sCap)) { m_ssPendingCaps.insert(sCap); } } } else if (sSubCmd == "ACK") { sArgs.Trim(); IRCSOCKMODULECALL(OnServerCapResult(sArgs, true), NOTHING); const auto& it = mSupportedCaps.find(sArgs); if (it != mSupportedCaps.end()) { it->second(true); } m_ssAcceptedCaps.insert(sArgs); } else if (sSubCmd == "NAK") { // This should work because there's no [known] // capability with length of name more than 100 characters. sArgs.Trim(); IRCSOCKMODULECALL(OnServerCapResult(sArgs, false), NOTHING); } SendNextCap(); } // Don't forward any CAP stuff to the client return true; }