]> git.netwichtig.de Git - user/henk/code/inspircd.git/blob - src/coremods/core_privmsg.cpp
Use CommandBase::Params instead of std::vector<std::string>.
[user/henk/code/inspircd.git] / src / coremods / core_privmsg.cpp
1 /*
2  * InspIRCd -- Internet Relay Chat Daemon
3  *
4  *   Copyright (C) 2009 Daniel De Graaf <danieldg@inspircd.org>
5  *   Copyright (C) 2007-2008 Craig Edwards <craigedwards@brainbox.cc>
6  *   Copyright (C) 2007 Robin Burchell <robin+git@viroteck.net>
7  *
8  * This file is part of InspIRCd.  InspIRCd is free software: you can
9  * redistribute it and/or modify it under the terms of the GNU General Public
10  * License as published by the Free Software Foundation, version 2.
11  *
12  * This program is distributed in the hope that it will be useful, but WITHOUT
13  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
14  * FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
15  * details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
19  */
20
21
22 #include "inspircd.h"
23
24 namespace
25 {
26         const char* MessageTypeString[] = { "PRIVMSG", "NOTICE" };
27 }
28
29 class MessageCommandBase : public Command
30 {
31         ChanModeReference moderatedmode;
32         ChanModeReference noextmsgmode;
33
34         /** Send a PRIVMSG or NOTICE message to all local users from the given user
35          * @param user User sending the message
36          * @param msg The message to send
37          * @param mt Type of the message (MSG_PRIVMSG or MSG_NOTICE)
38          */
39         static void SendAll(User* user, const std::string& msg, MessageType mt);
40
41  public:
42         MessageCommandBase(Module* parent, MessageType mt)
43                 : Command(parent, MessageTypeString[mt], 2, 2)
44                 , moderatedmode(parent, "moderated")
45                 , noextmsgmode(parent, "noextmsg")
46         {
47                 syntax = "<target>{,<target>} <message>";
48         }
49
50         /** Handle command.
51          * @param parameters The parameters to the command
52          * @param user The user issuing the command
53          * @return A value from CmdResult to indicate command success or failure.
54          */
55         CmdResult HandleMessage(User* user, const CommandBase::Params& parameters, MessageType mt);
56
57         RouteDescriptor GetRouting(User* user, const Params& parameters) CXX11_OVERRIDE
58         {
59                 if (IS_LOCAL(user))
60                         // This is handled by the OnUserPostMessage hook to split the LoopCall pieces
61                         return ROUTE_LOCALONLY;
62                 else
63                         return ROUTE_MESSAGE(parameters[0]);
64         }
65 };
66
67 void MessageCommandBase::SendAll(User* user, const std::string& msg, MessageType mt)
68 {
69         const std::string message = ":" + user->GetFullHost() + " " + MessageTypeString[mt] + " $* :" + msg;
70         const UserManager::LocalList& list = ServerInstance->Users.GetLocalUsers();
71         for (UserManager::LocalList::const_iterator i = list.begin(); i != list.end(); ++i)
72         {
73                 if ((*i)->registered == REG_ALL)
74                         (*i)->Write(message);
75         }
76 }
77
78 CmdResult MessageCommandBase::HandleMessage(User* user, const CommandBase::Params& parameters, MessageType mt)
79 {
80         User *dest;
81         Channel *chan;
82
83         LocalUser* localuser = IS_LOCAL(user);
84         if (localuser)
85                 localuser->idle_lastmsg = ServerInstance->Time();
86
87         if (CommandParser::LoopCall(user, this, parameters, 0))
88                 return CMD_SUCCESS;
89
90         if (parameters[0][0] == '$')
91         {
92                 if (!user->HasPrivPermission("users/mass-message"))
93                         return CMD_SUCCESS;
94
95                 std::string servername(parameters[0], 1);
96                 MessageTarget msgtarget(&servername);
97                 MessageDetails msgdetails(mt, parameters[1]);
98
99                 ModResult MOD_RESULT;
100                 FIRST_MOD_RESULT(OnUserPreMessage, MOD_RESULT, (user, msgtarget, msgdetails));
101                 if (MOD_RESULT == MOD_RES_DENY)
102                 {
103                         FOREACH_MOD(OnUserMessageBlocked, (user, msgtarget, msgdetails));
104                         return CMD_FAILURE;
105                 }
106
107                 FOREACH_MOD(OnUserMessage, (user, msgtarget, msgdetails));
108                 if (InspIRCd::Match(ServerInstance->Config->ServerName, servername, NULL))
109                 {
110                         SendAll(user, msgdetails.text, mt);
111                 }
112                 FOREACH_MOD(OnUserPostMessage, (user, msgtarget, msgdetails));
113                 return CMD_SUCCESS;
114         }
115
116         char status = 0;
117         const char* target = parameters[0].c_str();
118
119         if (ServerInstance->Modes->FindPrefix(*target))
120         {
121                 status = *target;
122                 target++;
123         }
124         if (*target == '#')
125         {
126                 chan = ServerInstance->FindChan(target);
127
128                 if (chan)
129                 {
130                         if (localuser && chan->GetPrefixValue(user) < VOICE_VALUE)
131                         {
132                                 if (chan->IsModeSet(noextmsgmode) && !chan->HasUser(user))
133                                 {
134                                         user->WriteNumeric(ERR_CANNOTSENDTOCHAN, chan->name, "Cannot send to channel (no external messages)");
135                                         return CMD_FAILURE;
136                                 }
137
138                                 if (chan->IsModeSet(moderatedmode))
139                                 {
140                                         user->WriteNumeric(ERR_CANNOTSENDTOCHAN, chan->name, "Cannot send to channel (+m)");
141                                         return CMD_FAILURE;
142                                 }
143
144                                 if (ServerInstance->Config->RestrictBannedUsers != ServerConfig::BUT_NORMAL)
145                                 {
146                                         if (chan->IsBanned(user))
147                                         {
148                                                 if (ServerInstance->Config->RestrictBannedUsers == ServerConfig::BUT_RESTRICT_NOTIFY)
149                                                         user->WriteNumeric(ERR_CANNOTSENDTOCHAN, chan->name, "Cannot send to channel (you're banned)");
150                                                 return CMD_FAILURE;
151                                         }
152                                 }
153                         }
154
155                         MessageTarget msgtarget(chan, status);
156                         MessageDetails msgdetails(mt, parameters[1]);
157                         msgdetails.exemptions.insert(user);
158
159                         ModResult MOD_RESULT;
160                         FIRST_MOD_RESULT(OnUserPreMessage, MOD_RESULT, (user, msgtarget, msgdetails));
161                         if (MOD_RESULT == MOD_RES_DENY)
162                         {
163                                 FOREACH_MOD(OnUserMessageBlocked, (user, msgtarget, msgdetails));
164                                 return CMD_FAILURE;
165                         }
166
167                         /* Check again, a module may have zapped the input string */
168                         if (msgdetails.text.empty())
169                         {
170                                 user->WriteNumeric(ERR_NOTEXTTOSEND, "No text to send");
171                                 return CMD_FAILURE;
172                         }
173
174                         FOREACH_MOD(OnUserMessage, (user, msgtarget, msgdetails));
175
176                         if (status)
177                         {
178                                 chan->WriteAllExcept(user, false, status, msgdetails.exemptions, "%s %c%s :%s", MessageTypeString[mt], status, chan->name.c_str(), msgdetails.text.c_str());
179                         }
180                         else
181                         {
182                                 chan->WriteAllExcept(user, false, status, msgdetails.exemptions, "%s %s :%s", MessageTypeString[mt], chan->name.c_str(), msgdetails.text.c_str());
183                         }
184
185                         FOREACH_MOD(OnUserPostMessage, (user, msgtarget, msgdetails));
186                 }
187                 else
188                 {
189                         /* channel does not exist */
190                         user->WriteNumeric(Numerics::NoSuchChannel(parameters[0]));
191                         return CMD_FAILURE;
192                 }
193                 return CMD_SUCCESS;
194         }
195
196         const char* destnick = parameters[0].c_str();
197
198         if (localuser)
199         {
200                 const char* targetserver = strchr(destnick, '@');
201
202                 if (targetserver)
203                 {
204                         std::string nickonly;
205
206                         nickonly.assign(destnick, 0, targetserver - destnick);
207                         dest = ServerInstance->FindNickOnly(nickonly);
208                         if (dest && strcasecmp(dest->server->GetName().c_str(), targetserver + 1))
209                         {
210                                 /* Incorrect server for user */
211                                 user->WriteNumeric(Numerics::NoSuchNick(parameters[0]));
212                                 return CMD_FAILURE;
213                         }
214                 }
215                 else
216                         dest = ServerInstance->FindNickOnly(destnick);
217         }
218         else
219                 dest = ServerInstance->FindNick(destnick);
220
221         if ((dest) && (dest->registered == REG_ALL))
222         {
223                 if (parameters[1].empty())
224                 {
225                         user->WriteNumeric(ERR_NOTEXTTOSEND, "No text to send");
226                         return CMD_FAILURE;
227                 }
228
229                 if ((dest->IsAway()) && (mt == MSG_PRIVMSG))
230                 {
231                         /* auto respond with aweh msg */
232                         user->WriteNumeric(RPL_AWAY, dest->nick, dest->awaymsg);
233                 }
234
235                 MessageTarget msgtarget(dest);
236                 MessageDetails msgdetails(mt, parameters[1]);
237
238                 ModResult MOD_RESULT;
239                 FIRST_MOD_RESULT(OnUserPreMessage, MOD_RESULT, (user, msgtarget, msgdetails));
240                 if (MOD_RESULT == MOD_RES_DENY)
241                 {
242                         FOREACH_MOD(OnUserMessageBlocked, (user, msgtarget, msgdetails));
243                         return CMD_FAILURE;
244                 }
245
246                 FOREACH_MOD(OnUserMessage, (user, msgtarget, msgdetails));
247
248                 if (IS_LOCAL(dest))
249                 {
250                         // direct write, same server
251                         dest->WriteFrom(user, "%s %s :%s", MessageTypeString[mt], dest->nick.c_str(), msgdetails.text.c_str());
252                 }
253
254                 FOREACH_MOD(OnUserPostMessage, (user, msgtarget, msgdetails));
255         }
256         else
257         {
258                 /* no such nick/channel */
259                 user->WriteNumeric(Numerics::NoSuchNick(parameters[0]));
260                 return CMD_FAILURE;
261         }
262         return CMD_SUCCESS;
263 }
264
265 template<MessageType MT>
266 class CommandMessage : public MessageCommandBase
267 {
268  public:
269         CommandMessage(Module* parent)
270                 : MessageCommandBase(parent, MT)
271         {
272         }
273
274         CmdResult Handle(User* user, const Params& parameters) CXX11_OVERRIDE
275         {
276                 return HandleMessage(user, parameters, MT);
277         }
278 };
279
280 class ModuleCoreMessage : public Module
281 {
282         CommandMessage<MSG_PRIVMSG> CommandPrivmsg;
283         CommandMessage<MSG_NOTICE> CommandNotice;
284
285  public:
286         ModuleCoreMessage()
287                 : CommandPrivmsg(this), CommandNotice(this)
288         {
289         }
290
291         Version GetVersion() CXX11_OVERRIDE
292         {
293                 return Version("PRIVMSG, NOTICE", VF_CORE|VF_VENDOR);
294         }
295 };
296
297 MODULE_INIT(ModuleCoreMessage)