{
class EventListener;
class MatchEventListener;
+ class VisibleEventListener;
class Request;
}
* @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.
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:
private:
ChanModeReference secretmode;
ChanModeReference privatemode;
- UserModeReference hidechansmode;
UserModeReference invisiblemode;
Events::ModuleEventProvider whoevprov;
Events::ModuleEventProvider whomatchevprov;
+ Events::ModuleEventProvider whovisibleevprov;
void BuildOpLevels()
{
}
/** 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;
: 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]";
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);
#include "inspircd.h"
+#include "modules/who.h"
#include "modules/whois.h"
/** Handles user mode +I
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)
{
}
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());
}
};