]> jfr.im git - irc/inspircd/inspircd.git/commitdiff
Allow modules to control the visible channel in a WHO request.
authorSadie Powell <redacted>
Fri, 7 Oct 2022 17:12:49 +0000 (18:12 +0100)
committerSadie Powell <redacted>
Wed, 12 Oct 2022 07:50:06 +0000 (08:50 +0100)
include/modules/who.h
src/coremods/core_who.cpp
src/modules/m_hidechans.cpp

index 17450dcf66b18fa72691d41b2149fad7fd011fe1..3321ef95df08753621fbe59e55a1c392bb8df9e1 100644 (file)
@@ -26,6 +26,7 @@ namespace Who
 {
        class EventListener;
        class MatchEventListener;
+       class VisibleEventListener;
        class Request;
 }
 
@@ -41,7 +42,7 @@ class Who::EventListener : public Events::ModuleEventListener
         * @param request Details about the WHO request which caused this response.
         * @param source The user who initiated this WHO request.
         * @param user The user that this line of the WHO request is about.
-        * @param memb The channel membership of the user or NULL if not targeted at a channel.
+        * @param memb The channel membership of the user or the first visible membership if not targeted at a channel.
         * @param numeric The numeric which will be sent in response to the request.
         * @return MOD_RES_ALLOW to explicitly allow the response, MOD_RES_DENY to explicitly deny the
         *         response, or MOD_RES_PASSTHRU to let another module handle the event.
@@ -68,6 +69,25 @@ class Who::MatchEventListener
        virtual ModResult OnWhoMatch(const Request& request, LocalUser* source, User* user) = 0;
 };
 
+class Who::VisibleEventListener
+       : public Events::ModuleEventListener
+{
+ public:
+       VisibleEventListener(Module* mod)
+               : ModuleEventListener(mod, "event/who-visible")
+       {
+       }
+
+       /** Called when a WHO request needs to check if a channel is visible.
+        * @param request Details about the WHO request which caused this match attempt.
+        * @param source The user who initiated this WHO request.
+        * @param memb The channel membership of the user to check the visibility of.
+        * @return MOD_RES_ALLOW to explicitly allow the match, MOD_RES_DENY to explicitly deny the
+        *         match, or MOD_RES_PASSTHRU to let another module handle the event.
+        */
+       virtual ModResult OnWhoVisible(const Request& request, LocalUser* source, Membership* memb) = 0;
+};
+
 class Who::Request
 {
  public:
index f43473d08f6939ba892e337b7210b236375b75c4..160590814c3812e09990d04b78e0511e38d7fabc 100644 (file)
@@ -116,10 +116,10 @@ class CommandWho : public SplitCommand
  private:
        ChanModeReference secretmode;
        ChanModeReference privatemode;
-       UserModeReference hidechansmode;
        UserModeReference invisiblemode;
        Events::ModuleEventProvider whoevprov;
        Events::ModuleEventProvider whomatchevprov;
+       Events::ModuleEventProvider whovisibleevprov;
 
        void BuildOpLevels()
        {
@@ -165,15 +165,23 @@ class CommandWho : public SplitCommand
        }
 
        /** Gets the first channel which is visible between the source and the target users. */
-       Membership* GetFirstVisibleChannel(LocalUser* source, User* user)
+       Membership* GetFirstVisibleChannel(const WhoData& data, LocalUser* source, User* user)
        {
                for (User::ChanList::iterator iter = user->chans.begin(); iter != user->chans.end(); ++iter)
                {
                        Membership* memb = *iter;
 
-                       // TODO: move the +I check into m_hidechans.
-                       bool has_modes = memb->chan->IsModeSet(secretmode) || memb->chan->IsModeSet(privatemode) || user->IsModeSet(hidechansmode);
-                       if (source == user || !has_modes || memb->chan->HasUser(source))
+                       // Let a module handle this first if it wants to.
+                       ModResult res;
+                       FIRST_MOD_RESULT_CUSTOM(whovisibleevprov, Who::VisibleEventListener, OnWhoVisible, res, (data, source, memb));
+                       if (res == MOD_RES_ALLOW)
+                               return memb; // Module explicitly picked this chan.
+
+                       if (res == MOD_RES_DENY)
+                               continue; // Module explicitly rejected this chan.
+
+                       // A module didn't specify either way so use the default behaviour.
+                       if (source == user || !memb->chan->IsModeSet(secretmode) || !memb->chan->IsModeSet(privatemode) || memb->chan->HasUser(source))
                                return memb;
                }
                return NULL;
@@ -203,10 +211,10 @@ class CommandWho : public SplitCommand
                : SplitCommand(parent, "WHO", 1, 3)
                , secretmode(parent, "secret")
                , privatemode(parent, "private")
-               , hidechansmode(parent, "hidechans")
                , invisiblemode(parent, "invisible")
                , whoevprov(parent, "event/who")
                , whomatchevprov(parent, "event/who-match")
+               , whovisibleevprov(parent, "event/who-visible")
        {
                allow_empty_last_param = false;
                syntax = "<server>|<nick>|<channel>|<realname>|<host>|0 [[Aafhilmnoprstux][%acdfhilnorstu] <server>|<nick>|<channel>|<realname>|<host>|0]";
@@ -453,7 +461,7 @@ void CommandWho::WhoUsers(LocalUser* source, const std::vector<std::string>& par
 void CommandWho::SendWhoLine(LocalUser* source, const std::vector<std::string>& parameters, Membership* memb, User* user, WhoData& data)
 {
        if (!memb)
-               memb = GetFirstVisibleChannel(source, user);
+               memb = GetFirstVisibleChannel(data, source, user);
 
        bool source_can_see_target = source == user || source->HasPrivPermission("users/auspex");
        Numeric::Numeric wholine(data.whox ? RPL_WHOSPCRPL : RPL_WHOREPLY);
index 9a9d7050777370a86f14412d4bdc57d7d7335ec9..ff8c4152a30f912f679d3e3c122ef5de1cb68e6d 100644 (file)
@@ -24,6 +24,7 @@
 
 
 #include "inspircd.h"
+#include "modules/who.h"
 #include "modules/whois.h"
 
 /** Handles user mode +I
@@ -34,13 +35,33 @@ class HideChans : public SimpleUserModeHandler
        HideChans(Module* Creator) : SimpleUserModeHandler(Creator, "hidechans", 'I') { }
 };
 
-class ModuleHideChans : public Module, public Whois::LineEventListener
+class ModuleHideChans CXX11_FINAL
+       : public Module
+       , public Who::VisibleEventListener
+       , public Whois::LineEventListener
 {
+ private:
        bool AffectsOpers;
        HideChans hm;
+
+       ModResult ShouldHideChans(LocalUser* source, User* target)
+       {
+               if (source == target)
+                       return MOD_RES_PASSTHRU; // User is targeting themself.
+
+               if (!target->IsModeSet(hm))
+                       return MOD_RES_PASSTHRU; // Mode not set on the target.
+
+               if (!AffectsOpers && source->HasPrivPermission("users/auspex"))
+                       return MOD_RES_PASSTHRU; // Opers aren't exempt or the oper doesn't have the right priv.
+
+               return MOD_RES_DENY;
+       }
+
  public:
        ModuleHideChans()
-               : Whois::LineEventListener(this)
+               : Who::VisibleEventListener(this)
+               , Whois::LineEventListener(this)
                , hm(this)
        {
        }
@@ -55,30 +76,17 @@ class ModuleHideChans : public Module, public Whois::LineEventListener
                AffectsOpers = ServerInstance->Config->ConfValue("hidechans")->getBool("affectsopers");
        }
 
-       ModResult OnWhoisLine(Whois::Context& whois, Numeric::Numeric& numeric) CXX11_OVERRIDE
+       ModResult OnWhoVisible(const Who::Request& request, LocalUser* source, Membership* memb) CXX11_OVERRIDE
        {
-               /* always show to self */
-               if (whois.IsSelfWhois())
-                       return MOD_RES_PASSTHRU;
+               return ShouldHideChans(source, memb->user);
+       }
 
-               /* don't touch anything except 319 */
+       ModResult OnWhoisLine(Whois::Context& whois, Numeric::Numeric& numeric) CXX11_OVERRIDE
+       {
                if (numeric.GetNumeric() != RPL_WHOISCHANNELS)
                        return MOD_RES_PASSTHRU;
 
-               /* don't touch if -I */
-               if (!whois.GetTarget()->IsModeSet(hm))
-                       return MOD_RES_PASSTHRU;
-
-               /* if it affects opers, we don't care if they are opered */
-               if (AffectsOpers)
-                       return MOD_RES_DENY;
-
-               /* doesn't affect opers, sender is opered */
-               if (whois.GetSource()->HasPrivPermission("users/auspex"))
-                       return MOD_RES_PASSTHRU;
-
-               /* user must be opered, boned. */
-               return MOD_RES_DENY;
+               return ShouldHideChans(whois.GetSource(), whois.GetTarget());
        }
 };