Add umode +I to allow users to hide their idle time (#220)

This commit is contained in:
David Schultz 2022-08-30 15:49:43 -05:00 committed by GitHub
parent 1e7fd14296
commit 01fb744c40
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
9 changed files with 161 additions and 13 deletions

View file

@ -78,6 +78,7 @@
* Global nick-change notices -- sno_globalnickchange * Global nick-change notices -- sno_globalnickchange
* Oper-override (modehacking only) -- override * Oper-override (modehacking only) -- override
* Stop services kills -- no_kill_services * Stop services kills -- no_kill_services
* Allows you to hide your idle time (umode +I) -- umode_hide_idle_time
*/ */
#loadmodule "extensions/chm_adminonly"; #loadmodule "extensions/chm_adminonly";
#loadmodule "extensions/chm_nonotice"; #loadmodule "extensions/chm_nonotice";
@ -110,6 +111,7 @@
#loadmodule "extensions/sno_globaloper"; #loadmodule "extensions/sno_globaloper";
#loadmodule "extensions/override"; #loadmodule "extensions/override";
#loadmodule "extensions/no_kill_services"; #loadmodule "extensions/no_kill_services";
#loadmodule "extensions/umode_hide_idle_time";
/* serverinfo {}: Contains information about the server. (OLD M:) */ /* serverinfo {}: Contains information about the server. (OLD M:) */
serverinfo { serverinfo {
@ -474,6 +476,8 @@ privset "local_op" {
* oper:receive_immunity: * oper:receive_immunity:
* confers the benefits of chmode +M (operpeace) (from extensions/chm_operpeace) * confers the benefits of chmode +M (operpeace) (from extensions/chm_operpeace)
* usermode:helpops allows setting +h (from extensions/helpops) * usermode:helpops allows setting +h (from extensions/helpops)
* auspex:usertimes:
* allows viewing user idle/connect times even when +I is set (from extensions/umode_hide_idle_time)
*/ */
privs = oper:general, oper:privs, oper:testline, oper:kill, oper:operwall, oper:message, privs = oper:general, oper:privs, oper:testline, oper:kill, oper:operwall, oper:message,
usermode:servnotice, auspex:oper, auspex:hostname, auspex:umodes, auspex:cmodes; usermode:servnotice, auspex:oper, auspex:hostname, auspex:umodes, auspex:cmodes;

View file

@ -66,6 +66,7 @@ extension_LTLIBRARIES = \
identify_msg.la \ identify_msg.la \
cap_realhost.la \ cap_realhost.la \
invex_regonly.la \ invex_regonly.la \
umode_hide_idle_time.la \
cap_oper.la \ cap_oper.la \
example_module.la example_module.la

View file

@ -0,0 +1,80 @@
/*
* Copyright (C) 2021 David Schultz <me@zpld.me>
*
* This program 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; either version 2 of the License, or
* (at your option) any later version.
*
* 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, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
* USA
*/
#include "stdinc.h"
#include "modules.h"
#include "client.h"
#include "hook.h"
#include "ircd.h"
#include "logger.h"
#include "send.h"
#include "s_conf.h"
#include "s_user.h"
#include "s_newconf.h"
static const char hide_desc[] = "Provides user mode +I to hide a user's idle time";
static void h_huc_doing_idle_time_hook(void *);
mapi_hfn_list_av1 huc_hfnlist[] = {
{ "doing_whois_show_idle", h_huc_doing_idle_time_hook },
{ "doing_trace_show_idle", h_huc_doing_idle_time_hook },
{ "doing_stats_show_idle", h_huc_doing_idle_time_hook },
{ "doing_who_show_idle", h_huc_doing_idle_time_hook },
{ NULL, NULL }
};
static void
h_huc_doing_idle_time_hook(void *data_)
{
hook_data_client_approval *data = data_;
if (data->approved == 0)
return;
if (data->target->umodes & user_modes['I'])
{
if (HasPrivilege(data->client, "auspex:usertimes"))
data->approved = WHOIS_IDLE_AUSPEX;
else if (data->client != data->target)
data->approved = WHOIS_IDLE_HIDE;
}
}
static int
_modinit(void)
{
user_modes['I'] = find_umode_slot();
construct_umodebuf();
if (!user_modes['I'])
{
ierror("umode_hide_idle_time: unable to allocate usermode slot for +I, unloading extension");
return -1;
}
return 0;
}
static void
_moddeinit(void)
{
user_modes['I'] = 0;
construct_umodebuf();
}
DECLARE_MODULE_AV2(hide_idle_time, _modinit, _moddeinit, NULL, NULL, huc_hfnlist, NULL, NULL, hide_desc);

View file

@ -19,6 +19,7 @@ User modes: (? designates that the umode is provided by an extension
+G - Deny users not on your /ACCEPT list and not in a channel +G - Deny users not on your /ACCEPT list and not in a channel
with you from messaging you and inviting you to channels. with you from messaging you and inviting you to channels.
This is a softer form of +g. This is a softer form of +g.
? +I - Hides your idle time.
+Q - Prevents you from being affected by channel forwarding. +Q - Prevents you from being affected by channel forwarding.
+R - Prevents unidentified users that you have not accepted from +R - Prevents unidentified users that you have not accepted from
messaging you. messaging you.

View file

@ -21,6 +21,14 @@ enum hook_priority
HOOK_MONITOR = 100 HOOK_MONITOR = 100
}; };
/* for idle time privacy features */
enum whois_idle_approval
{
WHOIS_IDLE_HIDE = 0,
WHOIS_IDLE_SHOW = 1,
WHOIS_IDLE_AUSPEX = 2
};
typedef void (*hookfn) (void *data); typedef void (*hookfn) (void *data);
extern int h_iosend_id; extern int h_iosend_id;

View file

@ -60,11 +60,13 @@ struct Message stats_msgtab = {
int doing_stats_hook; int doing_stats_hook;
int doing_stats_p_hook; int doing_stats_p_hook;
int doing_stats_show_idle_hook;
mapi_clist_av1 stats_clist[] = { &stats_msgtab, NULL }; mapi_clist_av1 stats_clist[] = { &stats_msgtab, NULL };
mapi_hlist_av1 stats_hlist[] = { mapi_hlist_av1 stats_hlist[] = {
{ "doing_stats", &doing_stats_hook }, { "doing_stats", &doing_stats_hook },
{ "doing_stats_p", &doing_stats_p_hook }, { "doing_stats_p", &doing_stats_p_hook },
{ "doing_stats_show_idle", &doing_stats_show_idle_hook },
{ NULL, NULL } { NULL, NULL }
}; };
@ -1621,19 +1623,27 @@ stats_l_client(struct Client *source_p, struct Client *target_p,
else else
{ {
/* fire the doing_stats_show_idle hook to allow modules to tell us whether to show the idle time */
hook_data_client_approval hdata_showidle;
hdata_showidle.client = source_p;
hdata_showidle.target = target_p;
hdata_showidle.approved = WHOIS_IDLE_SHOW;
call_hook(doing_stats_show_idle_hook, &hdata_showidle);
sendto_one_numeric(source_p, RPL_STATSLINKINFO, Lformat, sendto_one_numeric(source_p, RPL_STATSLINKINFO, Lformat,
show_ip(source_p, target_p) ? show_ip(source_p, target_p) ?
(IsUpper(statchar) ? (IsUpper(statchar) ?
get_client_name(target_p, SHOW_IP) : get_client_name(target_p, SHOW_IP) :
get_client_name(target_p, HIDE_IP)) : get_client_name(target_p, HIDE_IP)) :
get_client_name(target_p, MASK_IP), get_client_name(target_p, MASK_IP),
(int) rb_linebuf_len(&target_p->localClient->buf_sendq), hdata_showidle.approved ? (int) rb_linebuf_len(&target_p->localClient->buf_sendq) : 0,
(int) target_p->localClient->sendM, hdata_showidle.approved ? (int) target_p->localClient->sendM : 0,
(int) target_p->localClient->sendK, hdata_showidle.approved ? (int) target_p->localClient->sendK : 0,
(int) target_p->localClient->receiveM, hdata_showidle.approved ? (int) target_p->localClient->receiveM : 0,
(int) target_p->localClient->receiveK, hdata_showidle.approved ? (int) target_p->localClient->receiveK : 0,
rb_current_time() - target_p->localClient->firsttime, rb_current_time() - target_p->localClient->firsttime,
(rb_current_time() > target_p->localClient->lasttime) ? (rb_current_time() > target_p->localClient->lasttime) && hdata_showidle.approved ?
(rb_current_time() - target_p->localClient->lasttime) : 0, (rb_current_time() - target_p->localClient->lasttime) : 0,
"-"); "-");
} }

View file

@ -52,10 +52,12 @@ struct Message trace_msgtab = {
}; };
int doing_trace_hook; int doing_trace_hook;
int doing_trace_show_idle_hook;
mapi_clist_av1 trace_clist[] = { &trace_msgtab, NULL }; mapi_clist_av1 trace_clist[] = { &trace_msgtab, NULL };
mapi_hlist_av1 trace_hlist[] = { mapi_hlist_av1 trace_hlist[] = {
{ "doing_trace", &doing_trace_hook }, { "doing_trace", &doing_trace_hook },
{ "doing_trace_show_idle", &doing_trace_show_idle_hook },
{ NULL, NULL } { NULL, NULL }
}; };
DECLARE_MODULE_AV2(trace, NULL, NULL, trace_clist, trace_hlist, NULL, NULL, NULL, trace_desc); DECLARE_MODULE_AV2(trace, NULL, NULL, trace_clist, trace_hlist, NULL, NULL, NULL, trace_desc);
@ -381,13 +383,22 @@ report_this_status(struct Client *source_p, struct Client *target_p)
case STAT_CLIENT: case STAT_CLIENT:
{ {
/* fire the doing_trace_show_idle hook to allow modules to tell us whether to show the idle time */
hook_data_client_approval hdata_showidle;
hdata_showidle.client = source_p;
hdata_showidle.target = target_p;
hdata_showidle.approved = WHOIS_IDLE_SHOW;
call_hook(doing_trace_show_idle_hook, &hdata_showidle);
sendto_one_numeric(source_p, sendto_one_numeric(source_p,
SeesOper(target_p, source_p) ? RPL_TRACEOPERATOR : RPL_TRACEUSER, SeesOper(target_p, source_p) ? RPL_TRACEOPERATOR : RPL_TRACEUSER,
SeesOper(target_p, source_p) ? form_str(RPL_TRACEOPERATOR) : form_str(RPL_TRACEUSER), SeesOper(target_p, source_p) ? form_str(RPL_TRACEOPERATOR) : form_str(RPL_TRACEUSER),
class_name, name, class_name, name,
show_ip(source_p, target_p) ? ip : empty_sockhost, show_ip(source_p, target_p) ? ip : empty_sockhost,
(unsigned long)(rb_current_time() - target_p->localClient->lasttime), hdata_showidle.approved ? (unsigned long)(rb_current_time() - target_p->localClient->lasttime) : 0,
(unsigned long)(rb_current_time() - target_p->localClient->last)); hdata_showidle.approved ? (unsigned long)(rb_current_time() - target_p->localClient->last) : 0);
cnt++; cnt++;
} }

View file

@ -91,8 +91,14 @@ _moddeinit(void)
delete_isupport("WHOX"); delete_isupport("WHOX");
} }
int doing_who_show_idle_hook;
mapi_clist_av1 who_clist[] = { &who_msgtab, NULL }; mapi_clist_av1 who_clist[] = { &who_msgtab, NULL };
DECLARE_MODULE_AV2(who, _modinit, _moddeinit, who_clist, NULL, NULL, NULL, NULL, who_desc); mapi_hlist_av1 who_hlist[] = {
{ "doing_who_show_idle", &doing_who_show_idle_hook },
{ NULL, NULL }
};
DECLARE_MODULE_AV2(who, _modinit, _moddeinit, who_clist, who_hlist, NULL, NULL, NULL, who_desc);
/* /*
** m_who ** m_who
@ -531,7 +537,19 @@ do_who(struct Client *source_p, struct Client *target_p, struct membership *mspt
if (fmt->fields & FIELD_HOP) if (fmt->fields & FIELD_HOP)
append_format(str, sizeof str, &pos, " %d", ConfigServerHide.flatten_links && !IsOperGeneral(source_p) && !IsExemptShide(source_p) ? 0 : target_p->hopcount); append_format(str, sizeof str, &pos, " %d", ConfigServerHide.flatten_links && !IsOperGeneral(source_p) && !IsExemptShide(source_p) ? 0 : target_p->hopcount);
if (fmt->fields & FIELD_IDLE) if (fmt->fields & FIELD_IDLE)
append_format(str, sizeof str, &pos, " %d", (int)(MyClient(target_p) ? rb_current_time() - target_p->localClient->last : 0)); {
/* fire the doing_who_show_idle hook to allow modules to tell us whether to show the idle time */
hook_data_client_approval hdata_showidle;
hdata_showidle.client = source_p;
hdata_showidle.target = target_p;
hdata_showidle.approved = WHOIS_IDLE_SHOW;
call_hook(doing_who_show_idle_hook, &hdata_showidle);
append_format(str, sizeof str, &pos, " %d",
hdata_showidle.approved ? (int)(MyClient(target_p) ? rb_current_time() - target_p->localClient->last : 0) : 0);
}
if (fmt->fields & FIELD_ACCOUNT) if (fmt->fields & FIELD_ACCOUNT)
{ {
/* display as in whois */ /* display as in whois */

View file

@ -60,12 +60,14 @@ struct Message whois_msgtab = {
int doing_whois_hook; int doing_whois_hook;
int doing_whois_global_hook; int doing_whois_global_hook;
int doing_whois_channel_visibility_hook; int doing_whois_channel_visibility_hook;
int doing_whois_show_idle_hook;
mapi_clist_av1 whois_clist[] = { &whois_msgtab, NULL }; mapi_clist_av1 whois_clist[] = { &whois_msgtab, NULL };
mapi_hlist_av1 whois_hlist[] = { mapi_hlist_av1 whois_hlist[] = {
{ "doing_whois", &doing_whois_hook }, { "doing_whois", &doing_whois_hook },
{ "doing_whois_global", &doing_whois_global_hook }, { "doing_whois_global", &doing_whois_global_hook },
{ "doing_whois_channel_visibility", &doing_whois_channel_visibility_hook }, { "doing_whois_channel_visibility", &doing_whois_channel_visibility_hook },
{ "doing_whois_show_idle", &doing_whois_show_idle_hook },
{ NULL, NULL } { NULL, NULL }
}; };
@ -373,10 +375,23 @@ single_whois(struct Client *source_p, struct Client *target_p, int operspy)
target_p->name, buf); target_p->name, buf);
} }
/* fire the doing_whois_show_idle hook to allow modules to tell us whether to show the idle time */
hook_data_client_approval hdata_showidle;
hdata_showidle.client = source_p;
hdata_showidle.target = target_p;
hdata_showidle.approved = WHOIS_IDLE_SHOW;
call_hook(doing_whois_show_idle_hook, &hdata_showidle);
sendto_one_numeric(source_p, RPL_WHOISIDLE, form_str(RPL_WHOISIDLE), sendto_one_numeric(source_p, RPL_WHOISIDLE, form_str(RPL_WHOISIDLE),
target_p->name, target_p->name,
(long)(rb_current_time() - target_p->localClient->last), hdata_showidle.approved ? (long)(rb_current_time() - target_p->localClient->last) : 0,
(unsigned long)target_p->localClient->firsttime); (unsigned long)target_p->localClient->firsttime);
if (hdata_showidle.approved == WHOIS_IDLE_AUSPEX || hdata_showidle.approved == WHOIS_IDLE_HIDE)
/* if the target has hidden their idle time, notify the source */
sendto_one_numeric(source_p, RPL_WHOISTEXT, form_str(RPL_WHOISTEXT), target_p->name, "is using a private idle time");
} }
else else
{ {