bool MatchLine(Membership* memb, ChannelSettings* rs, std::string message) { // If the message is larger than whatever size it's set to, // let's pretend it isn't. If the first 512 (def. setting) match, it's probably spam. if (message.size() > ms.MaxMessageSize) message.erase(ms.MaxMessageSize); MemberInfo* rp = MemberInfoExt.get(memb); if (!rp) { rp = new MemberInfo; MemberInfoExt.set(memb, rp); } unsigned int matches = 0; if (!rs->Backlog) matches = rp->Counter; RepeatItemList& items = rp->ItemList; const unsigned int trigger = (message.size() * rs->Diff / 100); const time_t now = ServerInstance->Time(); std::transform(message.begin(), message.end(), message.begin(), ::tolower); for (std::deque<RepeatItem>::iterator it = items.begin(); it != items.end(); ++it) { if (it->ts < now) { items.erase(it, items.end()); matches = 0; break; } if (CompareLines(message, it->line, trigger)) { if (++matches >= rs->Lines) { if (rs->Action != ChannelSettings::ACT_BLOCK) rp->Counter = 0; return true; } } else if ((ms.MaxBacklog == 0) || (rs->Backlog == 0)) { matches = 0; items.clear(); break; } } unsigned int max_items = (rs->Backlog ? rs->Backlog : 1); if (items.size() >= max_items) items.pop_back(); items.push_front(RepeatItem(now + rs->Seconds, message)); rp->Counter = matches; return false; }
CmdResult add_watch(User* user, const char* nick) { if (!ServerInstance->IsNick(nick)) { user->WriteNumeric(942, "%s :Invalid nickname", nick); return CMD_FAILURE; } watchlist* wl = ext.get(user); if (!wl) { wl = new watchlist(); ext.set(user, wl); } if (wl->size() == MAX_WATCH) { user->WriteNumeric(512, "%s :Too many WATCH entries", nick); return CMD_FAILURE; } watchlist::iterator n = wl->find(nick); if (n == wl->end()) { /* Don't already have the user on my watch list, proceed */ watchentries::iterator x = whos_watching_me->find(nick); if (x != whos_watching_me->end()) { /* People are watching this user, add myself */ x->second.push_back(user); } else { std::deque<User*> newlist; newlist.push_back(user); (*(whos_watching_me))[nick] = newlist; } User* target = ServerInstance->FindNick(nick); if (target) { (*wl)[nick] = std::string(target->ident).append(" ").append(target->dhost).append(" ").append(ConvToStr(target->age)); user->WriteNumeric(604, "%s %s :is online", nick, (*wl)[nick].c_str()); if (target->IsAway()) { user->WriteNumeric(609, "%s %s %s %lu :is away", target->nick.c_str(), target->ident.c_str(), target->dhost.c_str(), (unsigned long) target->awaytime); } } else { (*wl)[nick].clear(); user->WriteNumeric(605, "%s * * 0 :is offline", nick); } } return CMD_SUCCESS; }
virtual void OnUserDisconnect(LocalUser *user) { /* User disconnect (generic socket detatch event) */ IdentRequestSocket *isock = ext.get(user); if (isock) { isock->Close(); ext.unset(user); } }
CmdResult remove_watch(User* user, const char* nick) { // removing an item from the list if (!ServerInstance->IsNick(nick)) { user->WriteNumeric(942, "%s :Invalid nickname", nick); return CMD_FAILURE; } watchlist* wl = ext.get(user); if (wl) { /* Yup, is on my list */ watchlist::iterator n = wl->find(nick); if (!wl) return CMD_FAILURE; if (n != wl->end()) { if (!n->second.empty()) user->WriteNumeric(602, "%s %s :stopped watching", n->first.c_str(), n->second.c_str()); else user->WriteNumeric(602, "%s * * 0 :stopped watching", nick); wl->erase(n); } if (wl->empty()) { ext.unset(user); } watchentries::iterator x = whos_watching_me->find(nick); if (x != whos_watching_me->end()) { /* People are watching this user, am i one of them? */ std::deque<User*>::iterator n2 = std::find(x->second.begin(), x->second.end(), user); if (n2 != x->second.end()) /* I'm no longer watching you... */ x->second.erase(n2); if (x->second.empty()) /* nobody else is, either. */ whos_watching_me->erase(nick); } } return CMD_SUCCESS; }
ModeAction OnModeChange(User* source, User* dest, Channel* channel, std::string ¶meter, bool adding) { ModeAction rv = ParamChannelModeHandler::OnModeChange(source, dest, channel, parameter, adding); if (rv == MODEACTION_ALLOW && !adding) ext.unset(channel); return rv; }
void OnUnset(User* source, Channel* chan) override { // Unset the per-membership extension when the mode is removed const Channel::MemberMap& users = chan->GetUsers(); for (Channel::MemberMap::const_iterator i = users.begin(); i != users.end(); ++i) MemberInfoExt.unset(i->second); }
/* This triggers pretty regularly, we can use it in preference to * creating a Timer object and especially better than creating a * Timer per ident lookup! */ virtual ModResult OnCheckReady(LocalUser *user) { /* Does user have an ident socket attached at all? */ IdentRequestSocket *isock = ext.get(user); if (!isock) { ServerInstance->Logs->Log("m_ident",DEBUG, "No ident socket :("); return MOD_RES_PASSTHRU; } ServerInstance->Logs->Log("m_ident",DEBUG, "Has ident_socket"); time_t compare = isock->age; compare += RequestTimeout; /* Check for timeout of the socket */ if (ServerInstance->Time() >= compare) { /* Ident timeout */ user->WriteServ("NOTICE Auth :*** Ident request timed out."); ServerInstance->Logs->Log("m_ident",DEBUG, "Timeout"); } else if (!isock->HasResult()) { // time still good, no result yet... hold the registration ServerInstance->Logs->Log("m_ident",DEBUG, "No result yet"); return MOD_RES_DENY; } ServerInstance->Logs->Log("m_ident",DEBUG, "Yay, result!"); /* wooo, got a result (it will be good, or bad) */ if (isock->result.empty()) { user->ident.insert(0, 1, '~'); user->WriteServ("NOTICE Auth :*** Could not find your ident, using %s instead.", user->ident.c_str()); } else { user->ident = isock->result; user->WriteServ("NOTICE Auth :*** Found your ident, '%s'", user->ident.c_str()); } isock->Close(); ext.unset(user); return MOD_RES_PASSTHRU; }
ModeAction OnModeChange(User* source, User* dest, Channel* channel, std::string ¶meter, bool adding) { if (adding) { std::string::size_type colon = parameter.find(':'); if ((colon == std::string::npos) || (parameter.find('-') != std::string::npos)) { source->WriteNumeric(608, "%s %s :Invalid flood parameter",source->nick.c_str(),channel->name.c_str()); return MODEACTION_DENY; } /* Set up the flood parameters for this channel */ unsigned int njoins = ConvToInt(parameter.substr(0, colon)); unsigned int nsecs = ConvToInt(parameter.substr(colon+1)); if ((njoins<1) || (nsecs<1)) { source->WriteNumeric(608, "%s %s :Invalid flood parameter",source->nick.c_str(),channel->name.c_str()); return MODEACTION_DENY; } joinfloodsettings jfs(nsecs, njoins); joinfloodsettings* f = ext.get(channel); if ((f) && (*f == jfs)) // mode params match return MODEACTION_DENY; ext.set(channel, jfs); parameter = ConvToStr(njoins) + ":" + ConvToStr(nsecs); channel->SetModeParam(this, parameter); return MODEACTION_ALLOW; } else { if (!channel->IsModeSet(this)) return MODEACTION_DENY; joinfloodsettings* f = ext.get(channel); if (f) { ext.unset(channel); channel->SetModeParam(this, ""); return MODEACTION_ALLOW; } } return MODEACTION_DENY; }
ModeAction OnModeChange(User* source, User* dest, Channel* channel, std::string ¶meter, bool adding) { if (adding) { std::string::size_type colon = parameter.find(':'); if (colon == std::string::npos) return MODEACTION_DENY; std::string duration = parameter.substr(colon+1); if ((IS_LOCAL(source)) && ((duration.length() > 10) || (!IsValidDuration(duration)))) return MODEACTION_DENY; unsigned int len = ConvToInt(parameter.substr(0, colon)); int time = InspIRCd::Duration(duration); if (len == 0 || time < 0) return MODEACTION_DENY; if (len > maxlines && IS_LOCAL(source)) return MODEACTION_DENY; if (len > maxlines) len = maxlines; if (parameter == channel->GetModeParameter(this)) return MODEACTION_DENY; HistoryList* history = ext.get(channel); if (history) { // Shrink the list if the new line number limit is lower than the old one if (len < history->lines.size()) history->lines.erase(history->lines.begin(), history->lines.begin() + (history->lines.size() - len)); history->maxlen = len; history->maxtime = time; } else { ext.set(channel, new HistoryList(len, time)); } } else { if (!channel->IsModeSet(this)) return MODEACTION_DENY; ext.unset(channel); } return MODEACTION_ALLOW; }
ModeAction OnModeChange(User* source, User* dest, Channel* channel, std::string ¶meter, bool adding) { if (adding) { std::string::size_type colon = parameter.find(':'); if ((colon == std::string::npos) || (parameter.find('-') != std::string::npos)) { source->WriteNumeric(608, "%s :Invalid flood parameter", channel->name.c_str()); return MODEACTION_DENY; } /* Set up the flood parameters for this channel */ bool ban = (parameter[0] == '*'); unsigned int nlines = ConvToInt(parameter.substr(ban ? 1 : 0, ban ? colon-1 : colon)); unsigned int nsecs = ConvToInt(parameter.substr(colon+1)); if ((nlines<2) || (nsecs<1)) { source->WriteNumeric(608, "%s :Invalid flood parameter", channel->name.c_str()); return MODEACTION_DENY; } floodsettings* f = ext.get(channel); if ((f) && (nlines == f->lines) && (nsecs == f->secs) && (ban == f->ban)) // mode params match return MODEACTION_DENY; ext.set(channel, new floodsettings(ban, nsecs, nlines)); parameter = std::string(ban ? "*" : "") + ConvToStr(nlines) + ":" + ConvToStr(nsecs); return MODEACTION_ALLOW; } else { if (!channel->IsModeSet(this)) return MODEACTION_DENY; ext.unset(channel); return MODEACTION_ALLOW; } }
ModeAction OnModeChange(User* source, User* dest, Channel* chan, std::string& parameter, bool adding) { if (adding) { std::string::size_type colon = parameter.find(':'); if (colon == std::string::npos || parameter.find('-') != std::string::npos) { source->WriteNumeric(608, "%s %s :Invalid join/part spam parameter", source->nick.c_str(), chan->name.c_str()); return MODEACTION_DENY; } unsigned int ncycles = ConvToInt(parameter.substr(0, colon)); unsigned int nsecs = ConvToInt(parameter.substr(colon+1)); if (ncycles < 2 || nsecs < 1) { source->WriteNumeric(608, "%s %s :Invalid join/part spam parameter", source->nick.c_str(), chan->name.c_str()); return MODEACTION_DENY; } joinpartspamsettings* jpss = ext.get(chan); if (jpss && ncycles == jpss->cycles && nsecs == jpss->secs) return MODEACTION_DENY; ext.set(chan, new joinpartspamsettings(ncycles, nsecs)); parameter = ConvToStr(ncycles) + ":" + ConvToStr(nsecs); chan->SetModeParam(GetModeChar(), parameter); return MODEACTION_ALLOW; } else { if (!chan->IsModeSet(GetModeChar())) return MODEACTION_DENY; ext.unset(chan); chan->SetModeParam(GetModeChar(), ""); return MODEACTION_ALLOW; } }
ModResult ProcessMessages(User* user, Channel* dest) { if (!IS_LOCAL(user) || !dest->IsModeSet(mf.GetModeChar())) return MOD_RES_PASSTHRU; if (ServerInstance->OnCheckExemption(user, dest, "slowmode") == MOD_RES_ALLOW) return MOD_RES_PASSTHRU; slowmodesettings *f = ext.get(dest); if (f == NULL || !f->addmessage()) return MOD_RES_PASSTHRU; user->WriteNumeric(404, "%s %s :Message throttled due to flood", user->nick.c_str(), dest->name.c_str()); return MOD_RES_DENY; }
void OnUserInit(LocalUser *user) { ConfigTag* tag = user->MyClass->config; if (!tag->getBool("useident", true)) return; user->WriteServ("NOTICE Auth :*** Looking up your ident..."); try { IdentRequestSocket *isock = new IdentRequestSocket(IS_LOCAL(user)); ext.set(user, isock); } catch (ModuleException &e) { ServerInstance->Logs->Log("m_ident",DEBUG,"Ident exception: %s", e.GetReason()); } }
CmdResult Handle (const std::vector<std::string> ¶meters, User *user) { if (parameters.empty()) { watchlist* wl = ext.get(user); if (wl) { for (watchlist::iterator q = wl->begin(); q != wl->end(); q++) { if (!q->second.empty()) user->WriteNumeric(604, "%s %s :is online", q->first.c_str(), q->second.c_str()); } } user->WriteNumeric(607, ":End of WATCH list"); } else if (parameters.size() > 0) { for (int x = 0; x < (int)parameters.size(); x++) { const char *nick = parameters[x].c_str(); if (!strcasecmp(nick,"C")) { // watch clear watchlist* wl = ext.get(user); if (wl) { for (watchlist::iterator i = wl->begin(); i != wl->end(); i++) { watchentries::iterator i2 = whos_watching_me->find(i->first); if (i2 != whos_watching_me->end()) { /* People are watching this user, am i one of them? */ std::deque<User*>::iterator n = std::find(i2->second.begin(), i2->second.end(), user); if (n != i2->second.end()) /* I'm no longer watching you... */ i2->second.erase(n); if (i2->second.empty()) /* nobody else is, either. */ whos_watching_me->erase(i2); } } ext.unset(user); } } else if (!strcasecmp(nick,"L")) { watchlist* wl = ext.get(user); if (wl) { for (watchlist::iterator q = wl->begin(); q != wl->end(); q++) { if (!q->second.empty()) { user->WriteNumeric(604, "%s %s :is online", q->first.c_str(), q->second.c_str()); User *targ = ServerInstance->FindNick(q->first.c_str()); if (targ->IsAway()) { user->WriteNumeric(609, "%s %s %s %lu :is away", targ->nick.c_str(), targ->ident.c_str(), targ->dhost.c_str(), (unsigned long) targ->awaytime); } } else user->WriteNumeric(605, "%s * * 0 :is offline", q->first.c_str()); } } user->WriteNumeric(607, ":End of WATCH list"); } else if (!strcasecmp(nick,"S")) { watchlist* wl = ext.get(user); int you_have = 0; int youre_on = 0; std::string list; if (wl) { for (watchlist::iterator q = wl->begin(); q != wl->end(); q++) list.append(q->first.c_str()).append(" "); you_have = wl->size(); } watchentries::iterator i2 = whos_watching_me->find(user->nick.c_str()); if (i2 != whos_watching_me->end()) youre_on = i2->second.size(); user->WriteNumeric(603, ":You have %d and are on %d WATCH entries", you_have, youre_on); user->WriteNumeric(606, ":%s", list.c_str()); user->WriteNumeric(607, ":End of WATCH S"); } else if (nick[0] == '-') { nick++; remove_watch(user, nick); } else if (nick[0] == '+') { nick++; add_watch(user, nick); } } } return CMD_SUCCESS; }
bool BeforeMode(User* source, User* dest, Channel* channel, std::string ¶m, bool adding) { /* nick!ident@host -> nick!ident@host * nick!ident@host#chan -> nick!ident@host#chan * nick@host#chan -> nick!*@host#chan * nick!ident#chan -> nick!ident@*#chan * nick#chan -> nick!*@*#chan */ if ((channel) && !param.empty()) { BanRedirectList* redirects; std::string mask[4]; enum { NICK, IDENT, HOST, CHAN } current = NICK; std::string::iterator start_pos = param.begin(); if (param.length() >= 2 && param[1] == ':') return true; if (param.find('#') == std::string::npos) return true; ListModeBase* banlm = static_cast<ListModeBase*>(*ban); unsigned int maxbans = banlm->GetLimit(channel); ListModeBase::ModeList* list = banlm->GetList(channel); if ((list) && (adding) && (maxbans <= list->size())) { source->WriteNumeric(ERR_BANLISTFULL, "%s :Channel ban list for %s is full (maximum entries for this channel is %u)", channel->name.c_str(), channel->name.c_str(), maxbans); return false; } for(std::string::iterator curr = start_pos; curr != param.end(); curr++) { switch(*curr) { case '!': if (current != NICK) break; mask[current].assign(start_pos, curr); current = IDENT; start_pos = curr+1; break; case '@': if (current != IDENT) break; mask[current].assign(start_pos, curr); current = HOST; start_pos = curr+1; break; case '#': if (current == CHAN) break; mask[current].assign(start_pos, curr); current = CHAN; start_pos = curr; break; } } if(mask[current].empty()) { mask[current].assign(start_pos, param.end()); } /* nick@host wants to be changed to *!nick@host rather than nick!*@host... */ if(mask[NICK].length() && mask[HOST].length() && mask[IDENT].empty()) { /* std::string::swap() is fast - it runs in constant time */ mask[NICK].swap(mask[IDENT]); } if (!mask[NICK].empty() && mask[IDENT].empty() && mask[HOST].empty()) { if (mask[NICK].find('.') != std::string::npos || mask[NICK].find(':') != std::string::npos) { mask[NICK].swap(mask[HOST]); } } for(int i = 0; i < 3; i++) { if(mask[i].empty()) { mask[i].assign("*"); } } param.assign(mask[NICK]).append(1, '!').append(mask[IDENT]).append(1, '@').append(mask[HOST]); if(mask[CHAN].length()) { if (adding && IS_LOCAL(source)) { if (!ServerInstance->IsChannel(mask[CHAN])) { source->WriteNumeric(ERR_NOSUCHCHANNEL, "%s :Invalid channel name in redirection (%s)", channel->name.c_str(), mask[CHAN].c_str()); return false; } Channel *c = ServerInstance->FindChan(mask[CHAN]); if (!c) { source->WriteNumeric(690, ":Target channel %s must exist to be set as a redirect.", mask[CHAN].c_str()); return false; } else if (adding && c->GetPrefixValue(source) < OP_VALUE) { source->WriteNumeric(690, ":You must be opped on %s to set it as a redirect.", mask[CHAN].c_str()); return false; } if (assign(channel->name) == mask[CHAN]) { source->WriteNumeric(690, "%s :You cannot set a ban redirection to the channel the ban is on", channel->name.c_str()); return false; } } if(adding) { /* It's a properly valid redirecting ban, and we're adding it */ redirects = extItem.get(channel); if (!redirects) { redirects = new BanRedirectList; extItem.set(channel, redirects); } /* Here 'param' doesn't have the channel on it yet */ redirects->push_back(BanRedirectEntry(mask[CHAN], param)); /* Now it does */ param.append(mask[CHAN]); } else { /* Removing a ban, if there's no extensible there are no redirecting bans and we're fine. */ redirects = extItem.get(channel); if (redirects) { /* But there were, so we need to remove the matching one if there is one */ for(BanRedirectList::iterator redir = redirects->begin(); redir != redirects->end(); redir++) { /* Ugly as f**k */ if((irc::string(redir->targetchan.c_str()) == irc::string(mask[CHAN].c_str())) && (irc::string(redir->banmask.c_str()) == irc::string(param.c_str()))) { redirects->erase(redir); if(redirects->empty()) { extItem.unset(channel); } break; } } } /* Append the channel so the default +b handler can remove the entry too */ param.append(mask[CHAN]); } } } return true; }
CmdResult Handle (const std::vector<std::string>& parameters, User *user) { if (!parameters.size()) { // no parameters, show the current silence list. silencelist* sl = ext.get(user); // if the user has a silence list associated with their user record, show it if (sl) { for (silencelist::const_iterator c = sl->begin(); c != sl->end(); c++) { std::string decomppattern = DecompPattern(c->second); user->WriteNumeric(271, "%s %s %s %s",user->nick.c_str(), user->nick.c_str(),c->first.c_str(), decomppattern.c_str()); } } user->WriteNumeric(272, "%s :End of Silence List",user->nick.c_str()); return CMD_SUCCESS; } else if (parameters.size() > 0) { // one or more parameters, add or delete entry from the list (only the first parameter is used) std::string mask = parameters[0].substr(1); char action = parameters[0][0]; // Default is private and notice so clients do not break int pattern = CompilePattern("pn"); // if pattern supplied, use it if (parameters.size() > 1) { pattern = CompilePattern(parameters[1].c_str()); } if (pattern == 0) { user->WriteServ("NOTICE %s :Bad SILENCE pattern",user->nick.c_str()); return CMD_INVALID; } if (!mask.length()) { // 'SILENCE +' or 'SILENCE -', assume *!*@* mask = "*!*@*"; } ModeParser::CleanMask(mask); if (action == '-') { std::string decomppattern = DecompPattern(pattern); // fetch their silence list silencelist* sl = ext.get(user); // does it contain any entries and does it exist? if (sl) { for (silencelist::iterator i = sl->begin(); i != sl->end(); i++) { // search through for the item irc::string listitem = i->first.c_str(); if (listitem == mask && i->second == pattern) { sl->erase(i); user->WriteNumeric(950, "%s %s :Removed %s %s from silence list",user->nick.c_str(), user->nick.c_str(), mask.c_str(), decomppattern.c_str()); if (!sl->size()) { ext.unset(user); } return CMD_SUCCESS; } } } user->WriteNumeric(952, "%s %s :%s %s does not exist on your silence list",user->nick.c_str(), user->nick.c_str(), mask.c_str(), decomppattern.c_str()); } else if (action == '+') { // fetch the user's current silence list silencelist* sl = ext.get(user); if (!sl) { sl = new silencelist; ext.set(user, sl); } if (sl->size() > maxsilence) { user->WriteNumeric(952, "%s %s :Your silence list is full",user->nick.c_str(), user->nick.c_str()); return CMD_FAILURE; } std::string decomppattern = DecompPattern(pattern); for (silencelist::iterator n = sl->begin(); n != sl->end(); n++) { irc::string listitem = n->first.c_str(); if (listitem == mask && n->second == pattern) { user->WriteNumeric(952, "%s %s :%s %s is already on your silence list",user->nick.c_str(), user->nick.c_str(), mask.c_str(), decomppattern.c_str()); return CMD_FAILURE; } } if (((pattern & SILENCE_EXCLUDE) > 0)) { sl->push_front(silenceset(mask,pattern)); } else { sl->push_back(silenceset(mask,pattern)); } user->WriteNumeric(951, "%s %s :Added %s %s to silence list",user->nick.c_str(), user->nick.c_str(), mask.c_str(), decomppattern.c_str()); return CMD_SUCCESS; } } return CMD_SUCCESS; }
virtual ModResult OnPreCommand(std::string &command, std::vector<std::string> ¶meters, LocalUser *user, bool validated, const std::string &original_line) { // Don't do anything with unregistered users if (user->registered != REG_ALL) return MOD_RES_PASSTHRU; if ((validated) && (parameters.size() >= 2) && ((command == "PRIVMSG") || (command == "NOTICE"))) { // parameters[0] should have the target(s) in it. // I think it will be faster to first check if there are any commas, and if there are then try and parse it out. // Most messages have a single target so... int targets = 1; int userchans = 0; if(*parameters[0].c_str() != '#') { // Decrement if the first target wasn't a channel. targets--; } for(const char* c = parameters[0].c_str(); *c; c++) if((*c == ',') && *(c+1) && (*(c+1) == '#')) targets++; /* targets should now contain the number of channel targets the msg/notice was pointed at. * If the msg/notice was a PM there should be no channel targets and 'targets' should = 0. * We don't want to block PMs so... */ if(targets == 0) { return MOD_RES_PASSTHRU; } userchans = user->chans.size(); // Check that this message wasn't already sent within a few seconds. BlockedMessage* m = blockamsg.get(user); // If the message is identical and within the time. // We check the target is *not* identical, that'd straying into the realms of flood control. Which isn't what we're doing... // OR // The number of target channels is equal to the number of channels the sender is on..a little suspicious. // Check it's more than 1 too, or else users on one channel would have fun. if((m && (m->message == parameters[1]) && (m->target != parameters[0]) && (ForgetDelay != -1) && (m->sent >= ServerInstance->Time()-ForgetDelay)) || ((targets > 1) && (targets == userchans))) { // Block it... if(action == IBLOCK_KILLOPERS || action == IBLOCK_NOTICEOPERS) ServerInstance->SNO->WriteToSnoMask('a', "%s had an /amsg or /ame denied", user->nick.c_str()); if(action == IBLOCK_KILL || action == IBLOCK_KILLOPERS) ServerInstance->Users->QuitUser(user, "You have been killed for use of /amsg or /ame"); else if(action == IBLOCK_NOTICE || action == IBLOCK_NOTICEOPERS) user->Write(":0ServerNotice@amsg-blocked. NOTICE %s :Global message (/amsg or /ame) denied", user->nick.c_str()); return MOD_RES_DENY; } if(m) { // If there's already a BlockedMessage allocated, use it. m->message = parameters[1]; m->target = parameters[0].c_str(); m->sent = ServerInstance->Time(); } else { m = new BlockedMessage(parameters[1], parameters[0].c_str(), ServerInstance->Time()); blockamsg.set(user, m); } } return MOD_RES_PASSTHRU; }