]> git.netwichtig.de Git - user/henk/code/inspircd.git/blob - src/modules/m_watch.cpp
bdd4b2b15414797720e20ac17fb94f24e73e787b
[user/henk/code/inspircd.git] / src / modules / m_watch.cpp
1 /*
2  * InspIRCd -- Internet Relay Chat Daemon
3  *
4  *   Copyright (C) 2016 Attila Molnar <attilamolnar@hush.com>
5  *
6  * This file is part of InspIRCd.  InspIRCd is free software: you can
7  * redistribute it and/or modify it under the terms of the GNU General Public
8  * License as published by the Free Software Foundation, version 2.
9  *
10  * This program is distributed in the hope that it will be useful, but WITHOUT
11  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
12  * FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
13  * details.
14  *
15  * You should have received a copy of the GNU General Public License
16  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
17  */
18
19
20 #include "inspircd.h"
21
22 #define INSPIRCD_MONITOR_MANAGER_ONLY
23 #include "m_monitor.cpp"
24
25 enum
26 {
27         RPL_GONEAWAY = 598,
28         RPL_NOTAWAY = 599,
29         RPL_LOGON = 600,
30         RPL_LOGOFF = 601,
31         RPL_WATCHOFF = 602,
32         RPL_WATCHSTAT = 603,
33         RPL_NOWON = 604,
34         RPL_NOWOFF = 605,
35         RPL_WATCHLIST = 606,
36         RPL_ENDOFWATCHLIST = 607,
37         // RPL_CLEARWATCH = 608, // unused
38         RPL_NOWISAWAY = 609,
39         ERR_TOOMANYWATCH = 512
40 };
41
42 class CommandWatch : public SplitCommand
43 {
44         // Additional penalty for /WATCH commands that request a list from the server
45         static const unsigned int ListPenalty = 4000;
46
47         IRCv3::Monitor::Manager& manager;
48
49         static void SendOnlineOffline(LocalUser* user, const std::string& nick, bool show_offline = true)
50         {
51                 User* target = IRCv3::Monitor::Manager::FindNick(nick);
52                 if (target)
53                 {
54                         // 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
55                         if (target->IsAway())
56                                 user->WriteNumeric(RPL_NOWISAWAY, "%s %s %s %lu :is away", target->nick.c_str(), target->ident.c_str(), target->dhost.c_str(), (unsigned long)target->awaytime);
57                         else
58                                 user->WriteNumeric(RPL_NOWON, "%s %s %s %lu :is online", target->nick.c_str(), target->ident.c_str(), target->dhost.c_str(), (unsigned long)target->age);
59                 }
60                 else if (show_offline)
61                         user->WriteNumeric(RPL_NOWOFF, "%s * * 0 :is offline", nick.c_str());
62         }
63
64         void HandlePlus(LocalUser* user, const std::string& nick)
65         {
66                 IRCv3::Monitor::Manager::WatchResult result = manager.Watch(user, nick, maxwatch);
67                 if (result == IRCv3::Monitor::Manager::WR_TOOMANY)
68                 {
69                         // List is full, send error numeric
70                         user->WriteNumeric(ERR_TOOMANYWATCH, "%s :Too many WATCH entries", nick.c_str());
71                         return;
72                 }
73                 else if (result == IRCv3::Monitor::Manager::WR_INVALIDNICK)
74                 {
75                         user->WriteNumeric(942, "%s :Invalid nickname", nick.c_str());
76                         return;
77                 }
78                 else if (result != IRCv3::Monitor::Manager::WR_OK)
79                         return;
80
81                 SendOnlineOffline(user, nick);
82         }
83
84         void HandleMinus(LocalUser* user, const std::string& nick)
85         {
86                 if (!manager.Unwatch(user, nick))
87                         return;
88
89                 User* target = IRCv3::Monitor::Manager::FindNick(nick);
90                 if (target)
91                         user->WriteNumeric(RPL_WATCHOFF, "%s %s %s %lu :stopped watching", target->nick.c_str(), target->ident.c_str(), target->dhost.c_str(), (unsigned long)target->age);
92                 else
93                         user->WriteNumeric(RPL_WATCHOFF, "%s * * 0 :stopped watching", nick.c_str());
94         }
95
96         void HandleList(LocalUser* user, bool show_offline)
97         {
98                 user->CommandFloodPenalty += ListPenalty;
99                 const IRCv3::Monitor::WatchedList& list = manager.GetWatched(user);
100                 for (IRCv3::Monitor::WatchedList::const_iterator i = list.begin(); i != list.end(); ++i)
101                 {
102                         const IRCv3::Monitor::Entry* entry = *i;
103                         SendOnlineOffline(user, entry->GetNick(), show_offline);
104                 }
105                 user->WriteNumeric(RPL_ENDOFWATCHLIST, ":End of WATCH list");
106         }
107
108         void HandleStats(LocalUser* user)
109         {
110                 user->CommandFloodPenalty += ListPenalty;
111
112                 // Do not show how many clients are watching this nick, it's pointless
113                 const IRCv3::Monitor::WatchedList& list = manager.GetWatched(user);
114                 user->WriteNumeric(RPL_WATCHSTAT, ":You have %lu and are on 0 WATCH entries", (unsigned long)list.size());
115
116                 Numeric::Builder<' '> out(user, RPL_WATCHLIST);
117                 for (IRCv3::Monitor::WatchedList::const_iterator i = list.begin(); i != list.end(); ++i)
118                 {
119                         const IRCv3::Monitor::Entry* entry = *i;
120                         out.Add(entry->GetNick());
121                 }
122                 out.Flush();
123                 user->WriteNumeric(RPL_ENDOFWATCHLIST, ":End of WATCH S");
124         }
125
126  public:
127         unsigned int maxwatch;
128
129         CommandWatch(Module* mod, IRCv3::Monitor::Manager& managerref)
130                 : SplitCommand(mod, "WATCH")
131                 , manager(managerref)
132         {
133                 allow_empty_last_param = false;
134                 syntax = "[<C|L|S|l|+<nick1>|-<nick>>]";
135         }
136
137         CmdResult HandleLocal(const std::vector<std::string>& parameters, LocalUser* user)
138         {
139                 if (parameters.empty())
140                 {
141                         HandleList(user, false);
142                         return CMD_SUCCESS;
143                 }
144
145                 bool watch_l_done = false;
146                 bool watch_s_done = false;
147
148                 for (std::vector<std::string>::const_iterator i = parameters.begin(); i != parameters.end(); ++i)
149                 {
150                         const std::string& token = *i;
151                         char subcmd = toupper(token[0]);
152                         if (subcmd == '+')
153                         {
154                                 HandlePlus(user, token.substr(1));
155                         }
156                         else if (subcmd == '-')
157                         {
158                                 HandleMinus(user, token.substr(1));
159                         }
160                         else if (subcmd == 'C')
161                         {
162                                 manager.UnwatchAll(user);
163                         }
164                         else if ((subcmd == 'L') && (!watch_l_done))
165                         {
166                                 watch_l_done = true;
167                                 // WATCH L requests a full list with online and offline nicks
168                                 // WATCH l requests a list with only online nicks
169                                 HandleList(user, (token[0] == 'L'));
170                         }
171                         else if ((subcmd == 'S') && (!watch_s_done))
172                         {
173                                 watch_s_done = true;
174                                 HandleStats(user);
175                         }
176                 }
177                 return CMD_SUCCESS;
178         }
179 };
180
181 class ModuleWatch : public Module
182 {
183         IRCv3::Monitor::Manager manager;
184         CommandWatch cmd;
185
186         void SendAlert(User* user, const std::string& nick, unsigned int numeric, const char* numerictext, time_t shownts)
187         {
188                 const IRCv3::Monitor::WatcherList* list = manager.GetWatcherList(nick);
189                 if (!list)
190                         return;
191
192                 std::string text = InspIRCd::Format("%s %s %s %lu :%s", nick.c_str(), user->ident.c_str(), user->dhost.c_str(), (unsigned long) shownts, numerictext);
193                 for (IRCv3::Monitor::WatcherList::const_iterator i = list->begin(); i != list->end(); ++i)
194                 {
195                         LocalUser* curr = *i;
196                         curr->WriteNumeric(numeric, text);
197                 }
198         }
199
200         void Online(User* user)
201         {
202                 SendAlert(user, user->nick, RPL_LOGON, "arrived online", user->age);
203         }
204
205         void Offline(User* user, const std::string& nick)
206         {
207                 SendAlert(user, nick, RPL_LOGOFF, "went offline", user->age);
208         }
209
210  public:
211         ModuleWatch()
212                 : manager(this, "watch")
213                 , cmd(this, manager)
214         {
215         }
216
217         void ReadConfig(ConfigStatus& status) CXX11_OVERRIDE
218         {
219                 ConfigTag* tag = ServerInstance->Config->ConfValue("watch");
220                 cmd.maxwatch = tag->getInt("maxwatch", 30, 1);
221         }
222
223         void OnPostConnect(User* user) CXX11_OVERRIDE
224         {
225                 Online(user);
226         }
227
228         void OnUserPostNick(User* user, const std::string& oldnick) CXX11_OVERRIDE
229         {
230                 // Detect and ignore nickname case change
231                 if (ServerInstance->FindNickOnly(oldnick) == user)
232                         return;
233
234                 Offline(user, oldnick);
235                 Online(user);
236         }
237
238         void OnUserQuit(User* user, const std::string& message, const std::string& oper_message) CXX11_OVERRIDE
239         {
240                 LocalUser* localuser = IS_LOCAL(user);
241                 if (localuser)
242                         manager.UnwatchAll(localuser);
243                 Offline(user, user->nick);
244         }
245
246         ModResult OnSetAway(User* user, const std::string& awaymsg) CXX11_OVERRIDE
247         {
248                 if (awaymsg.empty())
249                         SendAlert(user, user->nick, RPL_NOTAWAY, "is no longer away", ServerInstance->Time());
250                 else
251                         SendAlert(user, user->nick, RPL_GONEAWAY, awaymsg.c_str(), user->awaytime);
252
253                 return MOD_RES_PASSTHRU;
254         }
255
256         void On005Numeric(std::map<std::string, std::string>& tokens) CXX11_OVERRIDE
257         {
258                 tokens["WATCH"] = ConvToStr(cmd.maxwatch);
259         }
260
261         Version GetVersion() CXX11_OVERRIDE
262         {
263                 return Version("Provides WATCH support", VF_VENDOR);
264         }
265 };
266
267 MODULE_INIT(ModuleWatch)