2 * InspIRCd -- Internet Relay Chat Daemon
4 * Copyright (C) 2016 Attila Molnar <attilamolnar@hush.com>
6 * This file is part of InspIRCd. InspIRCd is free software: you can
7 * redistribute it and/or modify it under the terms of the GNU General Public
8 * License as published by the Free Software Foundation, version 2.
10 * This program is distributed in the hope that it will be useful, but WITHOUT
11 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
12 * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
15 * You should have received a copy of the GNU General Public License
16 * along with this program. If not, see <http://www.gnu.org/licenses/>.
26 typedef insp::flat_map<std::string, unsigned int> RanksToSeeMap;
27 RanksToSeeMap rankstosee;
30 unsigned int GetRequiredRank(const ModeHandler& mh) const
32 RanksToSeeMap::const_iterator it = rankstosee.find(mh.name);
33 if (it != rankstosee.end())
40 RanksToSeeMap newranks;
42 ConfigTagList tags = ServerInstance->Config->ConfTags("hidemode");
43 for (ConfigIter i = tags.first; i != tags.second; ++i)
45 ConfigTag* tag = i->second;
46 const std::string modename = tag->getString("mode");
48 throw ModuleException("<hidemode:mode> is empty at " + tag->getTagLocation());
50 unsigned int rank = tag->getUInt("rank", 0);
52 throw ModuleException("<hidemode:rank> must be greater than 0 at " + tag->getTagLocation());
54 ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "Hiding the %s mode from users below rank %u", modename.c_str(), rank);
55 newranks.insert(std::make_pair(modename, rank));
57 rankstosee.swap(newranks);
61 class ModeHook : public ClientProtocol::EventHook
63 typedef insp::flat_map<unsigned int, const ClientProtocol::MessageList*> FilteredModeMap;
65 std::vector<Modes::ChangeList> modechangelists;
66 std::list<ClientProtocol::Messages::Mode> filteredmodelist;
67 std::list<ClientProtocol::MessageList> filteredmsgplists;
68 FilteredModeMap cache;
70 static ModResult HandleResult(const ClientProtocol::MessageList* filteredmessagelist, ClientProtocol::MessageList& messagelist)
72 // Deny if member isn't allowed to see even a single mode change from this mode event
73 if (!filteredmessagelist)
76 // Member is allowed to see at least one mode change, replace list
77 if (filteredmessagelist != &messagelist)
78 messagelist = *filteredmessagelist;
80 return MOD_RES_PASSTHRU;
83 Modes::ChangeList* FilterModeChangeList(const ClientProtocol::Events::Mode& mode, unsigned int rank)
85 Modes::ChangeList* modechangelist = NULL;
86 for (Modes::ChangeList::List::const_iterator i = mode.GetChangeList().getlist().begin(); i != mode.GetChangeList().getlist().end(); ++i)
88 const Modes::Change& curr = *i;
89 if (settings.GetRequiredRank(*curr.mh) <= rank)
91 // No restriction on who can see this mode or there is one but the member's rank is sufficient
93 modechangelist->push(curr);
98 // Member cannot see the current mode change
102 // Create new mode change list or reuse the last one if it's empty
103 if ((modechangelists.empty()) || (!modechangelists.back().empty()))
104 modechangelists.push_back(Modes::ChangeList());
106 // Add all modes to it which we've accepted so far
107 modechangelists.back().push(mode.GetChangeList().getlist().begin(), i);
108 modechangelist = &modechangelists.back();
111 return modechangelist;
114 void OnEventInit(const ClientProtocol::Event& ev) CXX11_OVERRIDE
117 filteredmsgplists.clear();
118 filteredmodelist.clear();
119 modechangelists.clear();
121 // Ensure no reallocations will happen
122 const size_t numprefixmodes = ServerInstance->Modes.GetPrefixModes().size();
123 modechangelists.reserve(numprefixmodes);
126 ModResult OnPreEventSend(LocalUser* user, const ClientProtocol::Event& ev, ClientProtocol::MessageList& messagelist) CXX11_OVERRIDE
128 const ClientProtocol::Events::Mode& mode = static_cast<const ClientProtocol::Events::Mode&>(ev);
129 Channel* const chan = mode.GetMessages().front().GetChanTarget();
131 return MOD_RES_PASSTHRU;
133 Membership* const memb = chan->GetUser(user);
135 return MOD_RES_PASSTHRU;
138 const FilteredModeMap::const_iterator it = cache.find(memb->getRank());
139 if (it != cache.end())
140 return HandleResult(it->second, messagelist);
142 // Message for this rank isn't cached, generate it now
143 const Modes::ChangeList* const filteredchangelist = FilterModeChangeList(mode, memb->getRank());
145 // If no new change list was generated (above method returned NULL) it means the member and everyone else
146 // with the same rank can see everything in the original change list.
147 ClientProtocol::MessageList* finalmsgplist = &messagelist;
148 if (filteredchangelist)
150 if (filteredchangelist->empty())
152 // This rank cannot see any mode changes in the original change list
153 finalmsgplist = NULL;
157 // This rank can see some of the mode changes in the filtered mode change list.
158 // Create and store a new protocol message from it.
159 filteredmsgplists.push_back(ClientProtocol::MessageList());
160 ClientProtocol::Events::Mode::BuildMessages(mode.GetMessages().front().GetSourceUser(), chan, NULL, *filteredchangelist, filteredmodelist, filteredmsgplists.back());
161 finalmsgplist = &filteredmsgplists.back();
165 // Cache the result in all cases so it can be reused for further members with the same rank
166 cache.insert(std::make_pair(memb->getRank(), finalmsgplist));
167 return HandleResult(finalmsgplist, messagelist);
173 ModeHook(Module* mod)
174 : ClientProtocol::EventHook(mod, "MODE", 10)
180 class ModuleHideMode : public Module
191 void ReadConfig(ConfigStatus& status) CXX11_OVERRIDE
193 modehook.settings.Load();
196 Version GetVersion() CXX11_OVERRIDE
198 return Version("Provides support for hiding mode changes", VF_VENDOR);
202 MODULE_INIT(ModuleHideMode)