]> git.netwichtig.de Git - user/henk/code/inspircd.git/blob - src/modules/m_hidemode.cpp
Fix a bunch of weird indentation and spacing issues.
[user/henk/code/inspircd.git] / src / modules / m_hidemode.cpp
1 /*
2  * InspIRCd -- Internet Relay Chat Daemon
3  *
4  *   Copyright (C) 2019 Matt Schatz <genius3000@g3k.solutions>
5  *   Copyright (C) 2018 linuxdaemon <linuxdaemon.irc@gmail.com>
6  *   Copyright (C) 2018 Sadie Powell <sadie@witchery.services>
7  *   Copyright (C) 2018 Attila Molnar <attilamolnar@hush.com>
8  *
9  * This file is part of InspIRCd.  InspIRCd is free software: you can
10  * redistribute it and/or modify it under the terms of the GNU General Public
11  * License as published by the Free Software Foundation, version 2.
12  *
13  * This program is distributed in the hope that it will be useful, but WITHOUT
14  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
15  * FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
16  * details.
17  *
18  * You should have received a copy of the GNU General Public License
19  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
20  */
21
22
23 #include "inspircd.h"
24
25 namespace
26 {
27 class Settings
28 {
29         typedef insp::flat_map<std::string, unsigned int> RanksToSeeMap;
30         RanksToSeeMap rankstosee;
31
32  public:
33         unsigned int GetRequiredRank(const ModeHandler& mh) const
34         {
35                 RanksToSeeMap::const_iterator it = rankstosee.find(mh.name);
36                 if (it != rankstosee.end())
37                         return it->second;
38                 return 0;
39         }
40
41         void Load()
42         {
43                 RanksToSeeMap newranks;
44
45                 ConfigTagList tags = ServerInstance->Config->ConfTags("hidemode");
46                 for (ConfigIter i = tags.first; i != tags.second; ++i)
47                 {
48                         ConfigTag* tag = i->second;
49                         const std::string modename = tag->getString("mode");
50                         if (modename.empty())
51                                 throw ModuleException("<hidemode:mode> is empty at " + tag->getTagLocation());
52
53                         unsigned int rank = tag->getUInt("rank", 0);
54                         if (!rank)
55                                 throw ModuleException("<hidemode:rank> must be greater than 0 at " + tag->getTagLocation());
56
57                         ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "Hiding the %s mode from users below rank %u", modename.c_str(), rank);
58                         newranks.insert(std::make_pair(modename, rank));
59                 }
60                 rankstosee.swap(newranks);
61         }
62 };
63
64 class ModeHook : public ClientProtocol::EventHook
65 {
66         typedef insp::flat_map<unsigned int, const ClientProtocol::MessageList*> FilteredModeMap;
67
68         std::vector<Modes::ChangeList> modechangelists;
69         std::list<ClientProtocol::Messages::Mode> filteredmodelist;
70         std::list<ClientProtocol::MessageList> filteredmsgplists;
71         FilteredModeMap cache;
72
73         static ModResult HandleResult(const ClientProtocol::MessageList* filteredmessagelist, ClientProtocol::MessageList& messagelist)
74         {
75                 // Deny if member isn't allowed to see even a single mode change from this mode event
76                 if (!filteredmessagelist)
77                         return MOD_RES_DENY;
78
79                 // Member is allowed to see at least one mode change, replace list
80                 if (filteredmessagelist != &messagelist)
81                         messagelist = *filteredmessagelist;
82
83                 return MOD_RES_PASSTHRU;
84         }
85
86         Modes::ChangeList* FilterModeChangeList(const ClientProtocol::Events::Mode& mode, unsigned int rank)
87         {
88                 Modes::ChangeList* modechangelist = NULL;
89                 for (Modes::ChangeList::List::const_iterator i = mode.GetChangeList().getlist().begin(); i != mode.GetChangeList().getlist().end(); ++i)
90                 {
91                         const Modes::Change& curr = *i;
92                         if (settings.GetRequiredRank(*curr.mh) <= rank)
93                         {
94                                 // No restriction on who can see this mode or there is one but the member's rank is sufficient
95                                 if (modechangelist)
96                                         modechangelist->push(curr);
97
98                                 continue;
99                         }
100
101                         // Member cannot see the current mode change
102
103                         if (!modechangelist)
104                         {
105                                 // Create new mode change list or reuse the last one if it's empty
106                                 if ((modechangelists.empty()) || (!modechangelists.back().empty()))
107                                         modechangelists.push_back(Modes::ChangeList());
108
109                                 // Add all modes to it which we've accepted so far
110                                 modechangelists.back().push(mode.GetChangeList().getlist().begin(), i);
111                                 modechangelist = &modechangelists.back();
112                         }
113                 }
114                 return modechangelist;
115         }
116
117         void OnEventInit(const ClientProtocol::Event& ev) CXX11_OVERRIDE
118         {
119                 cache.clear();
120                 filteredmsgplists.clear();
121                 filteredmodelist.clear();
122                 modechangelists.clear();
123
124                 // Ensure no reallocations will happen
125                 const size_t numprefixmodes = ServerInstance->Modes.GetPrefixModes().size();
126                 modechangelists.reserve(numprefixmodes);
127         }
128
129         ModResult OnPreEventSend(LocalUser* user, const ClientProtocol::Event& ev, ClientProtocol::MessageList& messagelist) CXX11_OVERRIDE
130         {
131                 const ClientProtocol::Events::Mode& mode = static_cast<const ClientProtocol::Events::Mode&>(ev);
132                 Channel* const chan = mode.GetMessages().front().GetChanTarget();
133                 if (!chan)
134                         return MOD_RES_PASSTHRU;
135
136                 if (user->HasPrivPermission("channels/auspex"))
137                         return MOD_RES_PASSTHRU;
138
139                 Membership* const memb = chan->GetUser(user);
140                 if (!memb)
141                         return MOD_RES_PASSTHRU;
142
143                 // Check cache first
144                 const FilteredModeMap::const_iterator it = cache.find(memb->getRank());
145                 if (it != cache.end())
146                         return HandleResult(it->second, messagelist);
147
148                 // Message for this rank isn't cached, generate it now
149                 const Modes::ChangeList* const filteredchangelist = FilterModeChangeList(mode, memb->getRank());
150
151                 // If no new change list was generated (above method returned NULL) it means the member and everyone else
152                 // with the same rank can see everything in the original change list.
153                 ClientProtocol::MessageList* finalmsgplist = &messagelist;
154                 if (filteredchangelist)
155                 {
156                         if (filteredchangelist->empty())
157                         {
158                                 // This rank cannot see any mode changes in the original change list
159                                 finalmsgplist = NULL;
160                         }
161                         else
162                         {
163                                 // This rank can see some of the mode changes in the filtered mode change list.
164                                 // Create and store a new protocol message from it.
165                                 filteredmsgplists.push_back(ClientProtocol::MessageList());
166                                 ClientProtocol::Events::Mode::BuildMessages(mode.GetMessages().front().GetSourceUser(), chan, NULL, *filteredchangelist, filteredmodelist, filteredmsgplists.back());
167                                 finalmsgplist = &filteredmsgplists.back();
168                         }
169                 }
170
171                 // Cache the result in all cases so it can be reused for further members with the same rank
172                 cache.insert(std::make_pair(memb->getRank(), finalmsgplist));
173                 return HandleResult(finalmsgplist, messagelist);
174         }
175
176  public:
177         Settings settings;
178
179         ModeHook(Module* mod)
180                 : ClientProtocol::EventHook(mod, "MODE", 10)
181         {
182         }
183 };
184 }
185
186 class ModuleHideMode : public Module
187 {
188  private:
189         ModeHook modehook;
190
191  public:
192         ModuleHideMode()
193                 : modehook(this)
194         {
195         }
196
197         void ReadConfig(ConfigStatus& status) CXX11_OVERRIDE
198         {
199                 modehook.settings.Load();
200         }
201
202         Version GetVersion() CXX11_OVERRIDE
203         {
204                 return Version("Allows mode changes to be hidden from users without a prefix mode ranked equal to or higher than a defined level.", VF_VENDOR);
205         }
206 };
207
208 MODULE_INIT(ModuleHideMode)