]> git.netwichtig.de Git - user/henk/code/inspircd.git/blob - src/coremods/core_user/cmd_mode.cpp
ffeb9a2e3ec1897f91a41fd2ac067961876d6152
[user/henk/code/inspircd.git] / src / coremods / core_user / cmd_mode.cpp
1 /*
2  * InspIRCd -- Internet Relay Chat Daemon
3  *
4  *   Copyright (C) 2014 Attila Molnar <attilamolnar@hush.com>
5  *   Copyright (C) 2009-2010 Daniel De Graaf <danieldg@inspircd.org>
6  *   Copyright (C) 2006-2008 Robin Burchell <robin+git@viroteck.net>
7  *   Copyright (C) 2004-2008 Craig Edwards <craigedwards@brainbox.cc>
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 #include "inspircd.h"
23 #include "core_user.h"
24
25 CommandMode::CommandMode(Module* parent)
26         : Command(parent, "MODE", 1)
27         , seq(0)
28 {
29         syntax = "<target> <modes> {<mode-parameters>}";
30         memset(&sent, 0, sizeof(sent));
31 }
32
33 CmdResult CommandMode::Handle(User* user, const Params& parameters)
34 {
35         const std::string& target = parameters[0];
36         Channel* targetchannel = ServerInstance->FindChan(target);
37         User* targetuser = NULL;
38         if (!targetchannel)
39         {
40                 if (IS_LOCAL(user))
41                         targetuser = ServerInstance->FindNickOnly(target);
42                 else
43                         targetuser = ServerInstance->FindNick(target);
44         }
45
46         if ((!targetchannel) && (!targetuser))
47         {
48                 if (target[0] == '#')
49                         user->WriteNumeric(Numerics::NoSuchChannel(target));
50                 else
51                         user->WriteNumeric(Numerics::NoSuchNick(target));
52                 return CMD_FAILURE;
53         }
54         if (parameters.size() == 1)
55         {
56                 this->DisplayCurrentModes(user, targetuser, targetchannel);
57                 return CMD_SUCCESS;
58         }
59
60         // Populate a temporary Modes::ChangeList with the parameters
61         Modes::ChangeList changelist;
62         ModeType type = targetchannel ? MODETYPE_CHANNEL : MODETYPE_USER;
63         ServerInstance->Modes.ModeParamsToChangeList(user, type, parameters, changelist);
64
65         ModResult MOD_RESULT;
66         FIRST_MOD_RESULT(OnPreMode, MOD_RESULT, (user, targetuser, targetchannel, changelist));
67
68         ModeParser::ModeProcessFlag flags = ModeParser::MODE_NONE;
69         if (IS_LOCAL(user))
70         {
71                 if (MOD_RESULT == MOD_RES_PASSTHRU)
72                 {
73                         if ((targetuser) && (user != targetuser))
74                         {
75                                 // Local users may only change the modes of other users if a module explicitly allows it
76                                 user->WriteNumeric(ERR_USERSDONTMATCH, "Can't change mode for other users");
77                                 return CMD_FAILURE;
78                         }
79
80                         // This is a mode change by a local user and modules didn't explicitly allow/deny.
81                         // Ensure access checks will happen for each mode being changed.
82                         flags |= ModeParser::MODE_CHECKACCESS;
83                 }
84                 else if (MOD_RESULT == MOD_RES_DENY)
85                         return CMD_FAILURE; // Entire mode change denied by a module
86         }
87         else
88                 flags |= ModeParser::MODE_LOCALONLY;
89
90         if (IS_LOCAL(user))
91                 ServerInstance->Modes->ProcessSingle(user, targetchannel, targetuser, changelist, flags);
92         else
93                 ServerInstance->Modes->Process(user, targetchannel, targetuser, changelist, flags);
94
95         if ((ServerInstance->Modes.GetLastChangeList().empty()) && (targetchannel) && (parameters.size() == 2))
96         {
97                 /* Special case for displaying the list for listmodes,
98                  * e.g. MODE #chan b, or MODE #chan +b without a parameter
99                  */
100                 this->DisplayListModes(user, targetchannel, parameters[1]);
101         }
102
103         return CMD_SUCCESS;
104 }
105
106 RouteDescriptor CommandMode::GetRouting(User* user, const Params& parameters)
107 {
108         return (IS_LOCAL(user) ? ROUTE_LOCALONLY : ROUTE_BROADCAST);
109 }
110
111 void CommandMode::DisplayListModes(User* user, Channel* chan, const std::string& mode_sequence)
112 {
113         seq++;
114
115         for (std::string::const_iterator i = mode_sequence.begin(); i != mode_sequence.end(); ++i)
116         {
117                 unsigned char mletter = *i;
118                 if (mletter == '+')
119                         continue;
120
121                 ModeHandler* mh = ServerInstance->Modes->FindMode(mletter, MODETYPE_CHANNEL);
122                 if (!mh || !mh->IsListMode())
123                         return;
124
125                 /* Ensure the user doesnt request the same mode twice,
126                  * so they can't flood themselves off out of idiocy.
127                  */
128                 if (sent[mletter] == seq)
129                         continue;
130
131                 sent[mletter] = seq;
132                 ServerInstance->Modes.ShowListModeList(user, chan, mh);
133         }
134 }
135
136 static std::string GetSnomasks(const User* user)
137 {
138         ModeHandler* const snomask = ServerInstance->Modes.FindMode('s', MODETYPE_USER);
139         std::string snomaskstr = snomask->GetUserParameter(user);
140         // snomaskstr is empty if the snomask mode isn't set, otherwise it begins with a '+'.
141         // In the former case output a "+", not an empty string.
142         if (snomaskstr.empty())
143                 snomaskstr.push_back('+');
144         return snomaskstr;
145 }
146
147 namespace
148 {
149         void GetModeList(Numeric::Numeric& num, Channel* chan, User* user)
150         {
151                 // We should only show the value of secret parameters (i.e. key) if
152                 // the user is a member of the channel.
153                 bool show_secret = chan->HasUser(user);
154
155                 std::string& modes = num.push("+").GetParams().back();
156                 std::string param;
157                 for (unsigned char chr = 65; chr < 123; ++chr)
158                 {
159                         // Check that the mode exists and is set.
160                         ModeHandler* mh = ServerInstance->Modes->FindMode(chr, MODETYPE_CHANNEL);
161                         if (!mh || !chan->IsModeSet(mh))
162                                 continue;
163
164                         // Add the mode to the set list.
165                         modes.push_back(mh->GetModeChar());
166
167                         // If the mode has a parameter we need to include that too.
168                         ParamModeBase* pm = mh->IsParameterMode();
169                         if (!pm)
170                                 continue;
171
172                         // If a mode has a secret parameter and the user is not privy to
173                         // the value of it then we use <name> instead of the value.
174                         if (pm->IsParameterSecret() && !show_secret)
175                         {
176                                 num.push("<" + pm->name + ">");
177                                 continue;
178                         }
179
180                         // Retrieve the parameter and add it to the mode list.
181                         pm->GetParameter(chan, param);
182                         num.push(param);
183                         param.clear();
184                 }
185         }
186 }
187
188 void CommandMode::DisplayCurrentModes(User* user, User* targetuser, Channel* targetchannel)
189 {
190         if (targetchannel)
191         {
192                 // Display channel's current mode string
193                 Numeric::Numeric modenum(RPL_CHANNELMODEIS);
194                 modenum.push(targetchannel->name);
195                 GetModeList(modenum, targetchannel, user);
196                 user->WriteNumeric(modenum);
197                 user->WriteNumeric(RPL_CHANNELCREATED, targetchannel->name, (unsigned long)targetchannel->age);
198         }
199         else
200         {
201                 if (targetuser == user)
202                 {
203                         // Display user's current mode string
204                         user->WriteNumeric(RPL_UMODEIS, targetuser->GetModeLetters());
205                         if (targetuser->IsOper())
206                                 user->WriteNumeric(RPL_SNOMASKIS, GetSnomasks(targetuser), "Server notice mask");
207                 }
208                 else if (user->HasPrivPermission("users/auspex"))
209                 {
210                         // Querying the modes of another user.
211                         // We cannot use RPL_UMODEIS because that's only for showing the user's own modes.
212                         user->WriteNumeric(RPL_OTHERUMODEIS, targetuser->nick, targetuser->GetModeLetters());
213                         if (targetuser->IsOper())
214                                 user->WriteNumeric(RPL_OTHERSNOMASKIS, targetuser->nick, GetSnomasks(targetuser), "Server notice mask");
215                 }
216                 else
217                 {
218                         user->WriteNumeric(ERR_USERSDONTMATCH, "Can't view modes for other users");
219                 }
220         }
221 }