X-Git-Url: https://git.netwichtig.de/gitweb/?a=blobdiff_plain;f=src%2Fmodules%2Fm_watch.cpp;h=803bd2c40f2f540470d771c130a5a2e9f9652734;hb=5a19ff00aca5d979bf9ca45a2b0d6e85acdc9fc3;hp=c15609a4d07e11fafffa02ce764884aa33060970;hpb=0757a4a495daabf661ac3b7ab79f0a5ee423abe8;p=user%2Fhenk%2Fcode%2Finspircd.git diff --git a/src/modules/m_watch.cpp b/src/modules/m_watch.cpp index c15609a4d..803bd2c40 100644 --- a/src/modules/m_watch.cpp +++ b/src/modules/m_watch.cpp @@ -1,314 +1,275 @@ -/* +------------------------------------+ - * | Inspire Internet Relay Chat Daemon | - * +------------------------------------+ +/* + * InspIRCd -- Internet Relay Chat Daemon * - * InspIRCd is copyright (C) 2002-2006 ChatSpike-Dev. - * E-mail: - * - * - * - * Written by Craig Edwards, Craig McLure, and others. - * This program is free but copyrighted software; see - * the file COPYING for details. + * Copyright (C) 2019 Robby + * Copyright (C) 2017-2018 Sadie Powell + * Copyright (C) 2016 Attila Molnar * - * --------------------------------------------------- + * This file is part of InspIRCd. InspIRCd is free software: you can + * redistribute it and/or modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation, version 2. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . */ -using namespace std; - -#include -#include -#include -#include "users.h" -#include "channels.h" -#include "modules.h" -#include "helperfuncs.h" -#include "hashcomp.h" -/* $ModDesc: Provides support for the /watch command */ +#include "inspircd.h" +#include "modules/away.h" -static Server *Srv; +#define INSPIRCD_MONITOR_MANAGER_ONLY +#include "m_monitor.cpp" -class watchentry : public classbase +enum { - public: - userrec* watcher; - std::string target; + RPL_GONEAWAY = 598, + RPL_NOTAWAY = 599, + RPL_LOGON = 600, + RPL_LOGOFF = 601, + RPL_WATCHOFF = 602, + RPL_WATCHSTAT = 603, + RPL_NOWON = 604, + RPL_NOWOFF = 605, + RPL_WATCHLIST = 606, + RPL_ENDOFWATCHLIST = 607, + // RPL_CLEARWATCH = 608, // unused + RPL_NOWISAWAY = 609, + ERR_TOOMANYWATCH = 512, + ERR_INVALIDWATCHNICK = 942 }; -typedef std::vector watchlist; -watchlist watches; - -class cmd_watch : public command_t +class CommandWatch : public SplitCommand { - public: - cmd_watch() : command_t("WATCH",0,0) + // Additional penalty for /WATCH commands that request a list from the server + static const unsigned int ListPenalty = 4000; + + IRCv3::Monitor::Manager& manager; + + static void SendOnlineOffline(LocalUser* user, const std::string& nick, bool show_offline = true) { - this->source = "m_watch.so"; + User* target = IRCv3::Monitor::Manager::FindNick(nick); + if (target) + { + // 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 + if (target->IsAway()) + user->WriteNumeric(RPL_NOWISAWAY, target->nick, target->ident, target->GetDisplayedHost(), (unsigned long)target->awaytime, "is away"); + else + user->WriteNumeric(RPL_NOWON, target->nick, target->ident, target->GetDisplayedHost(), (unsigned long)target->age, "is online"); + } + else if (show_offline) + user->WriteNumeric(RPL_NOWOFF, nick, "*", "*", "0", "is offline"); } - void Handle (const char** parameters, int pcnt, userrec *user) + void HandlePlus(LocalUser* user, const std::string& nick) { - if (!pcnt) + IRCv3::Monitor::Manager::WatchResult result = manager.Watch(user, nick, maxwatch); + if (result == IRCv3::Monitor::Manager::WR_TOOMANY) { - for (watchlist::iterator q = watches.begin(); q != watches.end(); q++) - { - if (q->watcher == user) - { - userrec* targ = Srv->FindNick(q->target); - if (targ) - { - WriteServ(user->fd,"604 %s %s %s %s %lu :is online",user->nick,targ->nick,targ->ident,targ->dhost,targ->age); - } - } - } - WriteServ(user->fd,"607 %s :End of WATCH list",user->nick); + // List is full, send error numeric + user->WriteNumeric(ERR_TOOMANYWATCH, nick, "Too many WATCH entries"); + return; } - else if (pcnt > 0) + else if (result == IRCv3::Monitor::Manager::WR_INVALIDNICK) { - for (int x = 0; x < pcnt; x++) - { - const char *nick = parameters[x]; - if (!strcasecmp(nick,"C")) - { - // watch clear - bool done = false; - while (!done) - { - done = true; - for (watchlist::iterator q = watches.begin(); q != watches.end(); q++) - { - if (q->watcher == user) - { - done = false; - watches.erase(q); - break; - } - } - } - } - else if (!strcasecmp(nick,"L")) - { - for (watchlist::iterator q = watches.begin(); q != watches.end(); q++) - { - if (q->watcher == user) - { - userrec* targ = Srv->FindNick(q->target); - if (targ) - { - WriteServ(user->fd,"604 %s %s %s %s %lu :is online",user->nick,targ->nick,targ->ident,targ->dhost,targ->age); - } - } - } - WriteServ(user->fd,"607 %s :End of WATCH list",user->nick); - } - else if (!strcasecmp(nick,"S")) - { - std::string list = ""; - for (watchlist::iterator q = watches.begin(); q != watches.end(); q++) - { - if (q->watcher == user) - { - list = list + " " + q->target; - } - } - char* l = (char*)list.c_str(); - if (*l == ' ') - l++; - WriteServ(user->fd,"606 %s :%s",user->nick,l); - WriteServ(user->fd,"607 %s :End of WATCH S",user->nick); - } - else if (nick[0] == '-') - { - // removing an item from the list - nick++; - irc::string n1 = nick; - for (watchlist::iterator q = watches.begin(); q != watches.end(); q++) - { - if (q->watcher == user) - { - irc::string n2 = q->target.c_str(); - userrec* a = Srv->FindNick(q->target); - if (a) - { - WriteServ(user->fd,"602 %s %s %s %s %lu :stopped watching",user->nick,a->nick,a->ident,a->dhost,a->age); - } - else - { - WriteServ(user->fd,"602 %s %s * * 0 :stopped watching",user->nick,q->target.c_str()); - } - if (n1 == n2) - { - watches.erase(q); - break; - } - } - } - } - else if (nick[0] == '+') - { - nick++; - irc::string n1 = nick; - bool exists = false; - for (watchlist::iterator q = watches.begin(); q != watches.end(); q++) - { - if (q->watcher == user) - { - irc::string n2 = q->target.c_str(); - if (n1 == n2) - { - // already on watch list - exists = true; - } - } - } - if (!exists) - { - watchentry w; - w.watcher = user; - w.target = nick; - watches.push_back(w); - log(DEBUG,"*** Added %s to watchlist of %s",nick,user->nick); - } - userrec* a = Srv->FindNick(nick); - if (a) - { - WriteServ(user->fd,"604 %s %s %s %s %lu :is online",user->nick,a->nick,a->ident,a->dhost,a->age); - } - else - { - WriteServ(user->fd,"605 %s %s * * 0 :is offline",user->nick,nick); - } - } - } + user->WriteNumeric(ERR_INVALIDWATCHNICK, nick, "Invalid nickname"); + return; } - return; + else if (result != IRCv3::Monitor::Manager::WR_OK) + return; + + SendOnlineOffline(user, nick); } -}; -class Modulewatch : public Module -{ - cmd_watch* mycommand; - public: + void HandleMinus(LocalUser* user, const std::string& nick) + { + if (!manager.Unwatch(user, nick)) + return; - Modulewatch(Server* Me) - : Module::Module(Me) + User* target = IRCv3::Monitor::Manager::FindNick(nick); + if (target) + user->WriteNumeric(RPL_WATCHOFF, target->nick, target->ident, target->GetDisplayedHost(), (unsigned long)target->age, "stopped watching"); + else + user->WriteNumeric(RPL_WATCHOFF, nick, "*", "*", "0", "stopped watching"); + } + + void HandleList(LocalUser* user, bool show_offline) { - Srv = Me; - mycommand = new cmd_watch(); - Srv->AddCommand(mycommand); + user->CommandFloodPenalty += ListPenalty; + const IRCv3::Monitor::WatchedList& list = manager.GetWatched(user); + for (IRCv3::Monitor::WatchedList::const_iterator i = list.begin(); i != list.end(); ++i) + { + const IRCv3::Monitor::Entry* entry = *i; + SendOnlineOffline(user, entry->GetNick(), show_offline); + } + user->WriteNumeric(RPL_ENDOFWATCHLIST, "End of WATCH list"); } - void Implements(char* List) + void HandleStats(LocalUser* user) { - List[I_OnUserQuit] = List[I_OnGlobalConnect] = List[I_OnUserPostNick] = List[I_On005Numeric] = 1; + user->CommandFloodPenalty += ListPenalty; + + // Do not show how many clients are watching this nick, it's pointless + const IRCv3::Monitor::WatchedList& list = manager.GetWatched(user); + user->WriteNumeric(RPL_WATCHSTAT, InspIRCd::Format("You have %lu and are on 0 WATCH entries", (unsigned long)list.size())); + + Numeric::Builder<' '> out(user, RPL_WATCHLIST); + for (IRCv3::Monitor::WatchedList::const_iterator i = list.begin(); i != list.end(); ++i) + { + const IRCv3::Monitor::Entry* entry = *i; + out.Add(entry->GetNick()); + } + out.Flush(); + user->WriteNumeric(RPL_ENDOFWATCHLIST, "End of WATCH S"); } - virtual void OnUserQuit(userrec* user, const std::string &reason) + public: + unsigned int maxwatch; + + CommandWatch(Module* mod, IRCv3::Monitor::Manager& managerref) + : SplitCommand(mod, "WATCH") + , manager(managerref) { - log(DEBUG,"*** WATCH: On global quit: user %s",user->nick); - irc::string n2 = user->nick; - for (watchlist::iterator q = watches.begin(); q != watches.end(); q++) + allow_empty_last_param = false; + syntax = "C|L|l|S|(+|-) [(+|-)]+"; + } + + CmdResult HandleLocal(LocalUser* user, const Params& parameters) CXX11_OVERRIDE + { + if (parameters.empty()) { - irc::string n1 = q->target.c_str(); - if (n1 == n2) - { - log(DEBUG,"*** WATCH: On global quit: user %s is in notify of %s",user->nick,q->watcher->nick); - WriteServ(q->watcher->fd,"601 %s %s %s %s %lu :went offline",q->watcher->nick,user->nick,user->ident,user->dhost,time(NULL)); - } + HandleList(user, false); + return CMD_SUCCESS; } - bool done = false; - while (!done) + + bool watch_l_done = false; + bool watch_s_done = false; + + for (std::vector::const_iterator i = parameters.begin(); i != parameters.end(); ++i) { - done = true; - for (watchlist::iterator q = watches.begin(); q != watches.end(); q++) + const std::string& token = *i; + char subcmd = toupper(token[0]); + if (subcmd == '+') + { + HandlePlus(user, token.substr(1)); + } + else if (subcmd == '-') + { + HandleMinus(user, token.substr(1)); + } + else if (subcmd == 'C') + { + manager.UnwatchAll(user); + } + else if ((subcmd == 'L') && (!watch_l_done)) + { + watch_l_done = true; + // WATCH L requests a full list with online and offline nicks + // WATCH l requests a list with only online nicks + HandleList(user, (token[0] == 'L')); + } + else if ((subcmd == 'S') && (!watch_s_done)) { - if (q->watcher == user) - { - done = false; - watches.erase(q); - break; - } + watch_s_done = true; + HandleStats(user); } } + return CMD_SUCCESS; } +}; + +class ModuleWatch + : public Module + , public Away::EventListener +{ + IRCv3::Monitor::Manager manager; + CommandWatch cmd; - virtual void OnGlobalConnect(userrec* user) + void SendAlert(User* user, const std::string& nick, unsigned int numeric, const char* numerictext, time_t shownts) { - irc::string n2 = user->nick; - log(DEBUG,"*** WATCH: On global connect: user %s",user->nick); - for (watchlist::iterator q = watches.begin(); q != watches.end(); q++) + const IRCv3::Monitor::WatcherList* list = manager.GetWatcherList(nick); + if (!list) + return; + + Numeric::Numeric num(numeric); + num.push(nick).push(user->ident).push(user->GetDisplayedHost()).push(ConvToStr(shownts)).push(numerictext); + for (IRCv3::Monitor::WatcherList::const_iterator i = list->begin(); i != list->end(); ++i) { - irc::string n1 = q->target.c_str(); - if (n1 == n2) - { - log(DEBUG,"*** WATCH: On global connect: user %s is in notify of %s",user->nick,q->watcher->nick); - WriteServ(q->watcher->fd,"600 %s %s %s %s %lu :arrived online",q->watcher->nick,user->nick,user->ident,user->dhost,user->age); - } + LocalUser* curr = *i; + curr->WriteNumeric(num); } } - virtual void OnUserPostNick(userrec* user, const std::string &oldnick) + void Online(User* user) { - irc::string n2 = oldnick.c_str(); - irc::string n3 = user->nick; - log(DEBUG,"*** WATCH: On global nickchange: old nick: %s new nick: %s",oldnick.c_str(),user->nick); - for (watchlist::iterator q = watches.begin(); q != watches.end(); q++) - { - irc::string n1 = q->target.c_str(); - // changed from a nick on the watchlist to one that isnt - if (n1 == n2) - { - log(DEBUG,"*** WATCH: On global nickchange: old nick %s was on notify list of %s",oldnick.c_str(),q->watcher->nick); - WriteServ(q->watcher->fd,"601 %s %s %s %s %lu :went offline",q->watcher->nick,oldnick.c_str(),user->ident,user->dhost,time(NULL)); - } - else if (n1 == n3) - { - // changed from a nick not on notify to one that is - log(DEBUG,"*** WATCH: On global nickchange: new nick %s is on notify list of %s",user->nick,q->watcher->nick); - WriteServ(q->watcher->fd,"600 %s %s %s %s %lu :arrived online",q->watcher->nick,user->nick,user->ident,user->dhost,user->age); - } - } - } + SendAlert(user, user->nick, RPL_LOGON, "arrived online", user->age); + } - virtual void On005Numeric(std::string &output) + void Offline(User* user, const std::string& nick) { - // we don't really have a limit... - output = output + " WATCH=999"; + SendAlert(user, nick, RPL_LOGOFF, "went offline", user->age); } - - virtual ~Modulewatch() + + public: + ModuleWatch() + : Away::EventListener(this) + , manager(this, "watch") + , cmd(this, manager) { } - - virtual Version GetVersion() + + void ReadConfig(ConfigStatus& status) CXX11_OVERRIDE { - return Version(1,0,0,1,VF_VENDOR); + ConfigTag* tag = ServerInstance->Config->ConfValue("watch"); + cmd.maxwatch = tag->getUInt("maxwatch", 30, 1); } -}; + void OnPostConnect(User* user) CXX11_OVERRIDE + { + Online(user); + } -class ModulewatchFactory : public ModuleFactory -{ - public: - ModulewatchFactory() + void OnUserPostNick(User* user, const std::string& oldnick) CXX11_OVERRIDE { + // Detect and ignore nickname case change + if (ServerInstance->FindNickOnly(oldnick) == user) + return; + + Offline(user, oldnick); + Online(user); } - - ~ModulewatchFactory() + + void OnUserQuit(User* user, const std::string& message, const std::string& oper_message) CXX11_OVERRIDE { + LocalUser* localuser = IS_LOCAL(user); + if (localuser) + manager.UnwatchAll(localuser); + Offline(user, user->nick); } - - virtual Module * CreateModule(Server* Me) + + void OnUserAway(User* user) CXX11_OVERRIDE { - return new Modulewatch(Me); + SendAlert(user, user->nick, RPL_GONEAWAY, user->awaymsg.c_str(), user->awaytime); } - -}; + void OnUserBack(User* user) CXX11_OVERRIDE + { + SendAlert(user, user->nick, RPL_NOTAWAY, "is no longer away", ServerInstance->Time()); + } -extern "C" void * init_module( void ) -{ - return new ModulewatchFactory; -} + void On005Numeric(std::map& tokens) CXX11_OVERRIDE + { + tokens["WATCH"] = ConvToStr(cmd.maxwatch); + } + + Version GetVersion() CXX11_OVERRIDE + { + return Version("Adds the /WATCH command which allows users to find out when their friends are connected to the server.", VF_VENDOR); + } +}; +MODULE_INIT(ModuleWatch)