]> git.netwichtig.de Git - user/henk/code/inspircd.git/blob - src/coremods/core_mode.cpp
Add support for blocking tag messages with the deaf mode.
[user/henk/code/inspircd.git] / src / coremods / core_mode.cpp
1 /*
2  * InspIRCd -- Internet Relay Chat Daemon
3  *
4  *   Copyright (C) 2019 linuxdaemon <linuxdaemon.irc@gmail.com>
5  *   Copyright (C) 2019 Robby <robby@chatbelgie.be>
6  *   Copyright (C) 2018-2020 Sadie Powell <sadie@witchery.services>
7  *   Copyright (C) 2017 B00mX0r <b00mx0r@aureus.pw>
8  *   Copyright (C) 2014-2016 Attila Molnar <attilamolnar@hush.com>
9  *
10  * This file is part of InspIRCd.  InspIRCd is free software: you can
11  * redistribute it and/or modify it under the terms of the GNU General Public
12  * License as published by the Free Software Foundation, version 2.
13  *
14  * This program is distributed in the hope that it will be useful, but WITHOUT
15  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
16  * FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
17  * details.
18  *
19  * You should have received a copy of the GNU General Public License
20  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
21  */
22
23
24 #include "inspircd.h"
25
26 class CommandMode : public Command
27 {
28  private:
29         unsigned int sent[256];
30         unsigned int seq;
31         ChanModeReference secretmode;
32         ChanModeReference privatemode;
33
34         /** Show the list of one or more list modes to a user.
35          * @param user User to send to.
36          * @param chan Channel whose lists to show.
37          * @param mode_sequence Mode letters to show the lists of.
38          */
39         void DisplayListModes(User* user, Channel* chan, const std::string& mode_sequence);
40
41         /** Show the current modes of a channel or a user to a user.
42          * @param user User to show the modes to.
43          * @param targetuser User whose modes to show. NULL if showing the modes of a channel.
44          * @param targetchannel Channel whose modes to show. NULL if showing the modes of a user.
45          */
46         void DisplayCurrentModes(User* user, User* targetuser, Channel* targetchannel);
47
48         bool CanSeeChan(User* user, Channel* chan)
49         {
50                 // A user can always see the channel modes if they are:
51                 // (1) In the channel.
52                 // (2) An oper with the channels/auspex privilege.
53                 if (chan->HasUser(user) ||  user->HasPrivPermission("channels/auspex"))
54                         return true;
55
56                 // Otherwise, they can only see the modes when the channel is not +p or +s.
57                 return !chan->IsModeSet(secretmode) && !chan->IsModeSet(privatemode);
58         }
59
60  public:
61         /** Constructor for mode.
62          */
63         CommandMode(Module* parent);
64
65         /** Handle command.
66          * @param parameters The parameters to the command
67          * @param user The user issuing the command
68          * @return A value from CmdResult to indicate command success or failure.
69          */
70         CmdResult Handle(User* user, const Params& parameters) CXX11_OVERRIDE;
71
72         RouteDescriptor GetRouting(User* user, const Params& parameters) CXX11_OVERRIDE;
73 };
74
75 CommandMode::CommandMode(Module* parent)
76         : Command(parent, "MODE", 1)
77         , seq(0)
78         , secretmode(creator, "secret")
79         , privatemode(creator, "private")
80 {
81         syntax = "<target> [[(+|-)]<modes> [<mode-parameters>]]";
82         memset(&sent, 0, sizeof(sent));
83 }
84
85 CmdResult CommandMode::Handle(User* user, const Params& parameters)
86 {
87         const std::string& target = parameters[0];
88         Channel* targetchannel = ServerInstance->FindChan(target);
89         User* targetuser = NULL;
90         if (!targetchannel)
91         {
92                 if (IS_LOCAL(user))
93                         targetuser = ServerInstance->FindNickOnly(target);
94                 else
95                         targetuser = ServerInstance->FindNick(target);
96         }
97
98         if ((!targetchannel || !CanSeeChan(user, targetchannel)) && (!targetuser))
99         {
100                 if (target[0] == '#')
101                         user->WriteNumeric(Numerics::NoSuchChannel(target));
102                 else
103                         user->WriteNumeric(Numerics::NoSuchNick(target));
104                 return CMD_FAILURE;
105         }
106         if (parameters.size() == 1)
107         {
108                 this->DisplayCurrentModes(user, targetuser, targetchannel);
109                 return CMD_SUCCESS;
110         }
111
112         // Populate a temporary Modes::ChangeList with the parameters
113         Modes::ChangeList changelist;
114         ModeType type = targetchannel ? MODETYPE_CHANNEL : MODETYPE_USER;
115         ServerInstance->Modes.ModeParamsToChangeList(user, type, parameters, changelist);
116
117         ModResult MOD_RESULT;
118         FIRST_MOD_RESULT(OnPreMode, MOD_RESULT, (user, targetuser, targetchannel, changelist));
119
120         ModeParser::ModeProcessFlag flags = ModeParser::MODE_NONE;
121         if (IS_LOCAL(user))
122         {
123                 if (MOD_RESULT == MOD_RES_PASSTHRU)
124                 {
125                         if ((targetuser) && (user != targetuser))
126                         {
127                                 // Local users may only change the modes of other users if a module explicitly allows it
128                                 user->WriteNumeric(ERR_USERSDONTMATCH, "Can't change mode for other users");
129                                 return CMD_FAILURE;
130                         }
131
132                         // This is a mode change by a local user and modules didn't explicitly allow/deny.
133                         // Ensure access checks will happen for each mode being changed.
134                         flags |= ModeParser::MODE_CHECKACCESS;
135                 }
136                 else if (MOD_RESULT == MOD_RES_DENY)
137                         return CMD_FAILURE; // Entire mode change denied by a module
138         }
139         else
140                 flags |= ModeParser::MODE_LOCALONLY;
141
142         if (IS_LOCAL(user))
143                 ServerInstance->Modes->ProcessSingle(user, targetchannel, targetuser, changelist, flags);
144         else
145                 ServerInstance->Modes->Process(user, targetchannel, targetuser, changelist, flags);
146
147         if ((ServerInstance->Modes.GetLastChangeList().empty()) && (targetchannel) && (parameters.size() == 2))
148         {
149                 /* Special case for displaying the list for listmodes,
150                  * e.g. MODE #chan b, or MODE #chan +b without a parameter
151                  */
152                 this->DisplayListModes(user, targetchannel, parameters[1]);
153         }
154
155         return CMD_SUCCESS;
156 }
157
158 RouteDescriptor CommandMode::GetRouting(User* user, const Params& parameters)
159 {
160         return (IS_LOCAL(user) ? ROUTE_LOCALONLY : ROUTE_BROADCAST);
161 }
162
163 void CommandMode::DisplayListModes(User* user, Channel* chan, const std::string& mode_sequence)
164 {
165         seq++;
166
167         for (std::string::const_iterator i = mode_sequence.begin(); i != mode_sequence.end(); ++i)
168         {
169                 unsigned char mletter = *i;
170                 if (mletter == '+')
171                         continue;
172
173                 ModeHandler* mh = ServerInstance->Modes->FindMode(mletter, MODETYPE_CHANNEL);
174                 if (!mh || !mh->IsListMode())
175                         return;
176
177                 /* Ensure the user doesnt request the same mode twice,
178                  * so they can't flood themselves off out of idiocy.
179                  */
180                 if (sent[mletter] == seq)
181                         continue;
182
183                 sent[mletter] = seq;
184                 ServerInstance->Modes.ShowListModeList(user, chan, mh);
185         }
186 }
187
188 static std::string GetSnomasks(const User* user)
189 {
190         ModeHandler* const snomask = ServerInstance->Modes.FindMode('s', MODETYPE_USER);
191         std::string snomaskstr = snomask->GetUserParameter(user);
192         // snomaskstr is empty if the snomask mode isn't set, otherwise it begins with a '+'.
193         // In the former case output a "+", not an empty string.
194         if (snomaskstr.empty())
195                 snomaskstr.push_back('+');
196         return snomaskstr;
197 }
198
199 namespace
200 {
201         void GetModeList(Numeric::Numeric& num, Channel* chan, User* user)
202         {
203                 // We should only show the value of secret parameters (i.e. key) if
204                 // the user is a member of the channel.
205                 bool show_secret = chan->HasUser(user);
206
207                 size_t modepos = num.push("+").GetParams().size() - 1;
208                 std::string modes;
209                 std::string param;
210                 for (unsigned char chr = 65; chr < 123; ++chr)
211                 {
212                         // Check that the mode exists and is set.
213                         ModeHandler* mh = ServerInstance->Modes->FindMode(chr, MODETYPE_CHANNEL);
214                         if (!mh || !chan->IsModeSet(mh))
215                                 continue;
216
217                         // Add the mode to the set list.
218                         modes.push_back(mh->GetModeChar());
219
220                         // If the mode has a parameter we need to include that too.
221                         ParamModeBase* pm = mh->IsParameterMode();
222                         if (!pm)
223                                 continue;
224
225                         // If a mode has a secret parameter and the user is not privy to
226                         // the value of it then we use <name> instead of the value.
227                         if (pm->IsParameterSecret() && !show_secret)
228                         {
229                                 num.push("<" + pm->name + ">");
230                                 continue;
231                         }
232
233                         // Retrieve the parameter and add it to the mode list.
234                         pm->GetParameter(chan, param);
235                         num.push(param);
236                         param.clear();
237                 }
238                 num.GetParams()[modepos].append(modes);
239         }
240 }
241
242 void CommandMode::DisplayCurrentModes(User* user, User* targetuser, Channel* targetchannel)
243 {
244         if (targetchannel)
245         {
246                 // Display channel's current mode string
247                 Numeric::Numeric modenum(RPL_CHANNELMODEIS);
248                 modenum.push(targetchannel->name);
249                 GetModeList(modenum, targetchannel, user);
250                 user->WriteNumeric(modenum);
251                 user->WriteNumeric(RPL_CHANNELCREATED, targetchannel->name, (unsigned long)targetchannel->age);
252         }
253         else
254         {
255                 if (targetuser == user)
256                 {
257                         // Display user's current mode string
258                         user->WriteNumeric(RPL_UMODEIS, targetuser->GetModeLetters());
259                         if (targetuser->IsOper())
260                                 user->WriteNumeric(RPL_SNOMASKIS, GetSnomasks(targetuser), "Server notice mask");
261                 }
262                 else if (user->HasPrivPermission("users/auspex"))
263                 {
264                         // Querying the modes of another user.
265                         // We cannot use RPL_UMODEIS because that's only for showing the user's own modes.
266                         user->WriteNumeric(RPL_OTHERUMODEIS, targetuser->nick, targetuser->GetModeLetters());
267                         if (targetuser->IsOper())
268                                 user->WriteNumeric(RPL_OTHERSNOMASKIS, targetuser->nick, GetSnomasks(targetuser), "Server notice mask");
269                 }
270                 else
271                 {
272                         user->WriteNumeric(ERR_USERSDONTMATCH, "Can't view modes for other users");
273                 }
274         }
275 }
276
277 class CoreModMode : public Module
278 {
279  private:
280         CommandMode cmdmode;
281
282  public:
283         CoreModMode()
284                 : cmdmode(this)
285         {
286         }
287
288         void On005Numeric(std::map<std::string, std::string>& tokens) CXX11_OVERRIDE
289         {
290                 tokens["CHANMODES"] = ServerInstance->Modes->GiveModeList(MODETYPE_CHANNEL);
291                 tokens["USERMODES"] = ServerInstance->Modes->GiveModeList(MODETYPE_USER);
292         }
293
294         Version GetVersion() CXX11_OVERRIDE
295         {
296                 return Version("Provides the MODE command", VF_VENDOR|VF_CORE);
297         }
298 };
299
300 MODULE_INIT(CoreModMode)