]> git.netwichtig.de Git - user/henk/code/inspircd.git/blob - src/modules/m_watch.cpp
Fix the cloaking module on C++98 compilers.
[user/henk/code/inspircd.git] / src / modules / m_watch.cpp
1 /*
2  * InspIRCd -- Internet Relay Chat Daemon
3  *
4  *   Copyright (C) 2019 Robby <robby@chatbelgie.be>
5  *   Copyright (C) 2017-2018 Sadie Powell <sadie@witchery.services>
6  *   Copyright (C) 2016 Attila Molnar <attilamolnar@hush.com>
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 #include "modules/away.h"
24
25 #define INSPIRCD_MONITOR_MANAGER_ONLY
26 #include "m_monitor.cpp"
27
28 enum
29 {
30         RPL_GONEAWAY = 598,
31         RPL_NOTAWAY = 599,
32         RPL_LOGON = 600,
33         RPL_LOGOFF = 601,
34         RPL_WATCHOFF = 602,
35         RPL_WATCHSTAT = 603,
36         RPL_NOWON = 604,
37         RPL_NOWOFF = 605,
38         RPL_WATCHLIST = 606,
39         RPL_ENDOFWATCHLIST = 607,
40         // RPL_CLEARWATCH = 608, // unused
41         RPL_NOWISAWAY = 609,
42         ERR_TOOMANYWATCH = 512,
43         ERR_INVALIDWATCHNICK = 942
44 };
45
46 class CommandWatch : public SplitCommand
47 {
48         // Additional penalty for /WATCH commands that request a list from the server
49         static const unsigned int ListPenalty = 4000;
50
51         IRCv3::Monitor::Manager& manager;
52
53         static void SendOnlineOffline(LocalUser* user, const std::string& nick, bool show_offline = true)
54         {
55                 User* target = IRCv3::Monitor::Manager::FindNick(nick);
56                 if (target)
57                 {
58                         // The away state should only be sent if the client requests away notifications for a nick but 2.0 always sends them so we do that too
59                         if (target->IsAway())
60                                 user->WriteNumeric(RPL_NOWISAWAY, target->nick, target->ident, target->GetDisplayedHost(), (unsigned long)target->awaytime, "is away");
61                         else
62                                 user->WriteNumeric(RPL_NOWON, target->nick, target->ident, target->GetDisplayedHost(), (unsigned long)target->age, "is online");
63                 }
64                 else if (show_offline)
65                         user->WriteNumeric(RPL_NOWOFF, nick, "*", "*", "0", "is offline");
66         }
67
68         void HandlePlus(LocalUser* user, const std::string& nick)
69         {
70                 IRCv3::Monitor::Manager::WatchResult result = manager.Watch(user, nick, maxwatch);
71                 if (result == IRCv3::Monitor::Manager::WR_TOOMANY)
72                 {
73                         // List is full, send error numeric
74                         user->WriteNumeric(ERR_TOOMANYWATCH, nick, "Too many WATCH entries");
75                         return;
76                 }
77                 else if (result == IRCv3::Monitor::Manager::WR_INVALIDNICK)
78                 {
79                         user->WriteNumeric(ERR_INVALIDWATCHNICK, nick, "Invalid nickname");
80                         return;
81                 }
82                 else if (result != IRCv3::Monitor::Manager::WR_OK)
83                         return;
84
85                 SendOnlineOffline(user, nick);
86         }
87
88         void HandleMinus(LocalUser* user, const std::string& nick)
89         {
90                 if (!manager.Unwatch(user, nick))
91                         return;
92
93                 User* target = IRCv3::Monitor::Manager::FindNick(nick);
94                 if (target)
95                         user->WriteNumeric(RPL_WATCHOFF, target->nick, target->ident, target->GetDisplayedHost(), (unsigned long)target->age, "stopped watching");
96                 else
97                         user->WriteNumeric(RPL_WATCHOFF, nick, "*", "*", "0", "stopped watching");
98         }
99
100         void HandleList(LocalUser* user, bool show_offline)
101         {
102                 user->CommandFloodPenalty += ListPenalty;
103                 const IRCv3::Monitor::WatchedList& list = manager.GetWatched(user);
104                 for (IRCv3::Monitor::WatchedList::const_iterator i = list.begin(); i != list.end(); ++i)
105                 {
106                         const IRCv3::Monitor::Entry* entry = *i;
107                         SendOnlineOffline(user, entry->GetNick(), show_offline);
108                 }
109                 user->WriteNumeric(RPL_ENDOFWATCHLIST, "End of WATCH list");
110         }
111
112         void HandleStats(LocalUser* user)
113         {
114                 user->CommandFloodPenalty += ListPenalty;
115
116                 // Do not show how many clients are watching this nick, it's pointless
117                 const IRCv3::Monitor::WatchedList& list = manager.GetWatched(user);
118                 user->WriteNumeric(RPL_WATCHSTAT, InspIRCd::Format("You have %lu and are on 0 WATCH entries", (unsigned long)list.size()));
119
120                 Numeric::Builder<' '> out(user, RPL_WATCHLIST);
121                 for (IRCv3::Monitor::WatchedList::const_iterator i = list.begin(); i != list.end(); ++i)
122                 {
123                         const IRCv3::Monitor::Entry* entry = *i;
124                         out.Add(entry->GetNick());
125                 }
126                 out.Flush();
127                 user->WriteNumeric(RPL_ENDOFWATCHLIST, "End of WATCH S");
128         }
129
130  public:
131         unsigned int maxwatch;
132
133         CommandWatch(Module* mod, IRCv3::Monitor::Manager& managerref)
134                 : SplitCommand(mod, "WATCH")
135                 , manager(managerref)
136         {
137                 allow_empty_last_param = false;
138                 syntax = "C|L|l|S|(+|-)<nick> [(+|-)<nick>]+";
139         }
140
141         CmdResult HandleLocal(LocalUser* user, const Params& parameters) CXX11_OVERRIDE
142         {
143                 if (parameters.empty())
144                 {
145                         HandleList(user, false);
146                         return CMD_SUCCESS;
147                 }
148
149                 bool watch_l_done = false;
150                 bool watch_s_done = false;
151
152                 for (std::vector<std::string>::const_iterator i = parameters.begin(); i != parameters.end(); ++i)
153                 {
154                         const std::string& token = *i;
155                         char subcmd = toupper(token[0]);
156                         if (subcmd == '+')
157                         {
158                                 HandlePlus(user, token.substr(1));
159                         }
160                         else if (subcmd == '-')
161                         {
162                                 HandleMinus(user, token.substr(1));
163                         }
164                         else if (subcmd == 'C')
165                         {
166                                 manager.UnwatchAll(user);
167                         }
168                         else if ((subcmd == 'L') && (!watch_l_done))
169                         {
170                                 watch_l_done = true;
171                                 // WATCH L requests a full list with online and offline nicks
172                                 // WATCH l requests a list with only online nicks
173                                 HandleList(user, (token[0] == 'L'));
174                         }
175                         else if ((subcmd == 'S') && (!watch_s_done))
176                         {
177                                 watch_s_done = true;
178                                 HandleStats(user);
179                         }
180                 }
181                 return CMD_SUCCESS;
182         }
183 };
184
185 class ModuleWatch
186         : public Module
187         , public Away::EventListener
188 {
189         IRCv3::Monitor::Manager manager;
190         CommandWatch cmd;
191
192         void SendAlert(User* user, const std::string& nick, unsigned int numeric, const char* numerictext, time_t shownts)
193         {
194                 const IRCv3::Monitor::WatcherList* list = manager.GetWatcherList(nick);
195                 if (!list)
196                         return;
197
198                 Numeric::Numeric num(numeric);
199                 num.push(nick).push(user->ident).push(user->GetDisplayedHost()).push(ConvToStr(shownts)).push(numerictext);
200                 for (IRCv3::Monitor::WatcherList::const_iterator i = list->begin(); i != list->end(); ++i)
201                 {
202                         LocalUser* curr = *i;
203                         curr->WriteNumeric(num);
204                 }
205         }
206
207         void Online(User* user)
208         {
209                 SendAlert(user, user->nick, RPL_LOGON, "arrived online", user->age);
210         }
211
212         void Offline(User* user, const std::string& nick)
213         {
214                 SendAlert(user, nick, RPL_LOGOFF, "went offline", user->age);
215         }
216
217  public:
218         ModuleWatch()
219                 : Away::EventListener(this)
220                 , manager(this, "watch")
221                 , cmd(this, manager)
222         {
223         }
224
225         void ReadConfig(ConfigStatus& status) CXX11_OVERRIDE
226         {
227                 ConfigTag* tag = ServerInstance->Config->ConfValue("watch");
228                 cmd.maxwatch = tag->getUInt("maxwatch", 30, 1);
229         }
230
231         void OnPostConnect(User* user) CXX11_OVERRIDE
232         {
233                 Online(user);
234         }
235
236         void OnUserPostNick(User* user, const std::string& oldnick) CXX11_OVERRIDE
237         {
238                 // Detect and ignore nickname case change
239                 if (ServerInstance->FindNickOnly(oldnick) == user)
240                         return;
241
242                 Offline(user, oldnick);
243                 Online(user);
244         }
245
246         void OnUserQuit(User* user, const std::string& message, const std::string& oper_message) CXX11_OVERRIDE
247         {
248                 LocalUser* localuser = IS_LOCAL(user);
249                 if (localuser)
250                         manager.UnwatchAll(localuser);
251                 Offline(user, user->nick);
252         }
253
254         void OnUserAway(User* user) CXX11_OVERRIDE
255         {
256                 SendAlert(user, user->nick, RPL_GONEAWAY, user->awaymsg.c_str(), user->awaytime);
257         }
258
259         void OnUserBack(User* user) CXX11_OVERRIDE
260         {
261                 SendAlert(user, user->nick, RPL_NOTAWAY, "is no longer away", ServerInstance->Time());
262         }
263
264         void On005Numeric(std::map<std::string, std::string>& tokens) CXX11_OVERRIDE
265         {
266                 tokens["WATCH"] = ConvToStr(cmd.maxwatch);
267         }
268
269         Version GetVersion() CXX11_OVERRIDE
270         {
271                 return Version("Adds the /WATCH command which allows users to find out when their friends are connected to the server.", VF_VENDOR);
272         }
273 };
274
275 MODULE_INIT(ModuleWatch)