]> git.netwichtig.de Git - user/henk/code/inspircd.git/blob - src/coremods/core_privmsg.cpp
2daeef3ad6636ac6d852b1b1df4596eb111c7c42
[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 class MessageCommandBase : public Command
25 {
26         ChanModeReference moderatedmode;
27         ChanModeReference noextmsgmode;
28
29         /** Send a PRIVMSG or NOTICE message to all local users from the given user
30          * @param user User sending the message
31          * @param msg The message to send
32          * @param mt Type of the message (MSG_PRIVMSG or MSG_NOTICE)
33          * @param tags Message tags to include in the outgoing protocol message
34          */
35         static void SendAll(User* user, const std::string& msg, MessageType mt, const ClientProtocol::TagMap& tags);
36
37  public:
38         MessageCommandBase(Module* parent, MessageType mt)
39                 : Command(parent, ClientProtocol::Messages::Privmsg::CommandStrFromMsgType(mt), 2, 2)
40                 , moderatedmode(parent, "moderated")
41                 , noextmsgmode(parent, "noextmsg")
42         {
43                 syntax = "<target>{,<target>} <message>";
44         }
45
46         /** Handle command.
47          * @param parameters The parameters to the command
48          * @param user The user issuing the command
49          * @return A value from CmdResult to indicate command success or failure.
50          */
51         CmdResult HandleMessage(User* user, const Params& parameters, MessageType mt);
52
53         RouteDescriptor GetRouting(User* user, const Params& parameters) CXX11_OVERRIDE
54         {
55                 if (IS_LOCAL(user))
56                         // This is handled by the OnUserPostMessage hook to split the LoopCall pieces
57                         return ROUTE_LOCALONLY;
58                 else
59                         return ROUTE_MESSAGE(parameters[0]);
60         }
61 };
62
63 void MessageCommandBase::SendAll(User* user, const std::string& msg, MessageType mt, const ClientProtocol::TagMap& tags)
64 {
65         ClientProtocol::Messages::Privmsg message(ClientProtocol::Messages::Privmsg::nocopy, user, "$*", msg, mt);
66         message.AddTags(tags);
67         message.SetSideEffect(true);
68         ClientProtocol::Event messageevent(ServerInstance->GetRFCEvents().privmsg, message);
69
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)->Send(messageevent);
75         }
76 }
77
78 CmdResult MessageCommandBase::HandleMessage(User* user, const 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], parameters.GetTags());
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, msgdetails.tags_out);
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], parameters.GetTags());
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                         ClientProtocol::Messages::Privmsg privmsg(ClientProtocol::Messages::Privmsg::nocopy, user, chan, msgdetails.text, msgdetails.type, msgtarget.status);
177                         privmsg.AddTags(msgdetails.tags_out);
178                         privmsg.SetSideEffect(true);
179                         chan->Write(ServerInstance->GetRFCEvents().privmsg, privmsg, msgtarget.status, msgdetails.exemptions);
180
181                         FOREACH_MOD(OnUserPostMessage, (user, msgtarget, msgdetails));
182                 }
183                 else
184                 {
185                         /* channel does not exist */
186                         user->WriteNumeric(Numerics::NoSuchChannel(parameters[0]));
187                         return CMD_FAILURE;
188                 }
189                 return CMD_SUCCESS;
190         }
191
192         const char* destnick = parameters[0].c_str();
193
194         if (localuser)
195         {
196                 const char* targetserver = strchr(destnick, '@');
197
198                 if (targetserver)
199                 {
200                         std::string nickonly;
201
202                         nickonly.assign(destnick, 0, targetserver - destnick);
203                         dest = ServerInstance->FindNickOnly(nickonly);
204                         if (dest && strcasecmp(dest->server->GetName().c_str(), targetserver + 1))
205                         {
206                                 /* Incorrect server for user */
207                                 user->WriteNumeric(Numerics::NoSuchNick(parameters[0]));
208                                 return CMD_FAILURE;
209                         }
210                 }
211                 else
212                         dest = ServerInstance->FindNickOnly(destnick);
213         }
214         else
215                 dest = ServerInstance->FindNick(destnick);
216
217         if ((dest) && (dest->registered == REG_ALL))
218         {
219                 if (parameters[1].empty())
220                 {
221                         user->WriteNumeric(ERR_NOTEXTTOSEND, "No text to send");
222                         return CMD_FAILURE;
223                 }
224
225                 if ((dest->IsAway()) && (mt == MSG_PRIVMSG))
226                 {
227                         /* auto respond with aweh msg */
228                         user->WriteNumeric(RPL_AWAY, dest->nick, dest->awaymsg);
229                 }
230
231                 MessageTarget msgtarget(dest);
232                 MessageDetails msgdetails(mt, parameters[1], parameters.GetTags());
233
234
235                 ModResult MOD_RESULT;
236                 FIRST_MOD_RESULT(OnUserPreMessage, MOD_RESULT, (user, msgtarget, msgdetails));
237                 if (MOD_RESULT == MOD_RES_DENY)
238                 {
239                         FOREACH_MOD(OnUserMessageBlocked, (user, msgtarget, msgdetails));
240                         return CMD_FAILURE;
241                 }
242
243                 FOREACH_MOD(OnUserMessage, (user, msgtarget, msgdetails));
244
245                 LocalUser* const localtarget = IS_LOCAL(dest);
246                 if (localtarget)
247                 {
248                         // direct write, same server
249                         ClientProtocol::Messages::Privmsg privmsg(ClientProtocol::Messages::Privmsg::nocopy, user, localtarget->nick, msgdetails.text, mt);
250                         privmsg.AddTags(msgdetails.tags_out);
251                         privmsg.SetSideEffect(true);
252                         localtarget->Send(ServerInstance->GetRFCEvents().privmsg, privmsg);
253                 }
254
255                 FOREACH_MOD(OnUserPostMessage, (user, msgtarget, msgdetails));
256         }
257         else
258         {
259                 /* no such nick/channel */
260                 user->WriteNumeric(Numerics::NoSuchNick(parameters[0]));
261                 return CMD_FAILURE;
262         }
263         return CMD_SUCCESS;
264 }
265
266 template<MessageType MT>
267 class CommandMessage : public MessageCommandBase
268 {
269  public:
270         CommandMessage(Module* parent)
271                 : MessageCommandBase(parent, MT)
272         {
273         }
274
275         CmdResult Handle(User* user, const Params& parameters) CXX11_OVERRIDE
276         {
277                 return HandleMessage(user, parameters, MT);
278         }
279 };
280
281 class ModuleCoreMessage : public Module
282 {
283         CommandMessage<MSG_PRIVMSG> CommandPrivmsg;
284         CommandMessage<MSG_NOTICE> CommandNotice;
285
286  public:
287         ModuleCoreMessage()
288                 : CommandPrivmsg(this), CommandNotice(this)
289         {
290         }
291
292         Version GetVersion() CXX11_OVERRIDE
293         {
294                 return Version("PRIVMSG, NOTICE", VF_CORE|VF_VENDOR);
295         }
296 };
297
298 MODULE_INIT(ModuleCoreMessage)