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())
42 ConfigTagList tags = ServerInstance->Config->ConfTags("hidemode");
43 for (ConfigIter i = tags.first; i != tags.second; ++i)
45 ConfigTag* tag = i->second;
46 std::string modename = tag->getString("mode");
47 unsigned int rank = tag->getInt("rank", 0, 0);
48 if (!modename.empty() && rank)
50 ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "Hiding the %s mode from users below rank %u", modename.c_str(), rank);
51 rankstosee.insert(std::make_pair(modename, rank));
57 class ModeHook : public ClientProtocol::EventHook
59 typedef insp::flat_map<unsigned int, const ClientProtocol::MessageList*> FilteredModeMap;
61 std::vector<Modes::ChangeList> modechangelists;
62 std::list<ClientProtocol::Messages::Mode> filteredmodelist;
63 std::list<ClientProtocol::MessageList> filteredmsgplists;
64 FilteredModeMap cache;
66 static ModResult HandleResult(const ClientProtocol::MessageList* filteredmessagelist, ClientProtocol::MessageList& messagelist)
68 // Deny if member isn't allowed to see even a single mode change from this mode event
69 if (!filteredmessagelist)
72 // Member is allowed to see at least one mode change, replace list
73 if (filteredmessagelist != &messagelist)
74 messagelist = *filteredmessagelist;
76 return MOD_RES_PASSTHRU;
79 Modes::ChangeList* FilterModeChangeList(const ClientProtocol::Events::Mode& mode, unsigned int rank)
81 Modes::ChangeList* modechangelist = NULL;
82 for (Modes::ChangeList::List::const_iterator i = mode.GetChangeList().getlist().begin(); i != mode.GetChangeList().getlist().end(); ++i)
84 const Modes::Change& curr = *i;
85 if (settings.GetRequiredRank(*curr.mh) <= rank)
87 // No restriction on who can see this mode or there is one but the member's rank is sufficient
89 modechangelist->push(curr);
94 // Member cannot see the current mode change
98 // Create new mode change list or reuse the last one if it's empty
99 if ((modechangelists.empty()) || (!modechangelists.back().empty()))
100 modechangelists.push_back(Modes::ChangeList());
102 // Add all modes to it which we've accepted so far
103 modechangelists.back().push(mode.GetChangeList().getlist().begin(), i);
104 modechangelist = &modechangelists.back();
107 return modechangelist;
110 void OnEventInit(const ClientProtocol::Event& ev) CXX11_OVERRIDE
113 filteredmsgplists.clear();
114 filteredmodelist.clear();
115 modechangelists.clear();
117 // Ensure no reallocations will happen
118 const size_t numprefixmodes = ServerInstance->Modes.GetPrefixModes().size();
119 modechangelists.reserve(numprefixmodes);
122 ModResult OnPreEventSend(LocalUser* user, const ClientProtocol::Event& ev, ClientProtocol::MessageList& messagelist) CXX11_OVERRIDE
124 const ClientProtocol::Events::Mode& mode = static_cast<const ClientProtocol::Events::Mode&>(ev);
125 Channel* const chan = mode.GetMessages().front().GetChanTarget();
127 return MOD_RES_PASSTHRU;
129 Membership* const memb = chan->GetUser(user);
131 return MOD_RES_PASSTHRU;
134 const FilteredModeMap::const_iterator it = cache.find(memb->getRank());
135 if (it != cache.end())
136 return HandleResult(it->second, messagelist);
138 // Message for this rank isn't cached, generate it now
139 const Modes::ChangeList* const filteredchangelist = FilterModeChangeList(mode, memb->getRank());
141 // If no new change list was generated (above method returned NULL) it means the member and everyone else
142 // with the same rank can see everything in the original change list.
143 ClientProtocol::MessageList* finalmsgplist = &messagelist;
144 if (filteredchangelist)
146 if (filteredchangelist->empty())
148 // This rank cannot see any mode changes in the original change list
149 finalmsgplist = NULL;
153 // This rank can see some of the mode changes in the filtered mode change list.
154 // Create and store a new protocol message from it.
155 filteredmsgplists.push_back(ClientProtocol::MessageList());
156 ClientProtocol::Events::Mode::BuildMessages(mode.GetMessages().front().GetSourceUser(), chan, NULL, *filteredchangelist, filteredmodelist, filteredmsgplists.back());
157 finalmsgplist = &filteredmsgplists.back();
161 // Cache the result in all cases so it can be reused for further members with the same rank
162 cache.insert(std::make_pair(memb->getRank(), finalmsgplist));
163 return HandleResult(finalmsgplist, messagelist);
169 ModeHook(Module* mod)
170 : ClientProtocol::EventHook(mod, "MODE", 10)
176 class ModuleHideMode : public Module
187 void ReadConfig(ConfigStatus& status) CXX11_OVERRIDE
189 modehook.settings.Load();
192 Version GetVersion() CXX11_OVERRIDE
194 return Version("Provides support for hiding mode changes", VF_VENDOR);
198 MODULE_INIT(ModuleHideMode)