* Global nick-change notices -- sno_globalnickchange
* Oper-override (modehacking only) -- override
* 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_nonotice";
#loadmodule "extensions/sno_globaloper";
#loadmodule "extensions/override";
#loadmodule "extensions/no_kill_services";
+#loadmodule "extensions/umode_hide_idle_time";
/* serverinfo {}: Contains information about the server. (OLD M:) */
serverinfo {
* oper:receive_immunity:
* confers the benefits of chmode +M (operpeace) (from extensions/chm_operpeace)
* 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,
usermode:servnotice, auspex:oper, auspex:hostname, auspex:umodes, auspex:cmodes;
identify_msg.la \
cap_realhost.la \
invex_regonly.la \
+ umode_hide_idle_time.la \
cap_oper.la \
example_module.la
--- /dev/null
+/*
+ * 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);
+G - Deny users not on your /ACCEPT list and not in a channel
with you from messaging you and inviting you to channels.
This is a softer form of +g.
+ ? +I - Hides your idle time.
+Q - Prevents you from being affected by channel forwarding.
+R - Prevents unidentified users that you have not accepted from
messaging you.
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);
extern int h_iosend_id;
int doing_stats_hook;
int doing_stats_p_hook;
+int doing_stats_show_idle_hook;
mapi_clist_av1 stats_clist[] = { &stats_msgtab, NULL };
mapi_hlist_av1 stats_hlist[] = {
{ "doing_stats", &doing_stats_hook },
{ "doing_stats_p", &doing_stats_p_hook },
+ { "doing_stats_show_idle", &doing_stats_show_idle_hook },
{ NULL, NULL }
};
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,
show_ip(source_p, target_p) ?
(IsUpper(statchar) ?
get_client_name(target_p, SHOW_IP) :
get_client_name(target_p, HIDE_IP)) :
get_client_name(target_p, MASK_IP),
- (int) rb_linebuf_len(&target_p->localClient->buf_sendq),
- (int) target_p->localClient->sendM,
- (int) target_p->localClient->sendK,
- (int) target_p->localClient->receiveM,
- (int) target_p->localClient->receiveK,
+ hdata_showidle.approved ? (int) rb_linebuf_len(&target_p->localClient->buf_sendq) : 0,
+ hdata_showidle.approved ? (int) target_p->localClient->sendM : 0,
+ hdata_showidle.approved ? (int) target_p->localClient->sendK : 0,
+ hdata_showidle.approved ? (int) target_p->localClient->receiveM : 0,
+ hdata_showidle.approved ? (int) target_p->localClient->receiveK : 0,
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,
"-");
}
};
int doing_trace_hook;
+int doing_trace_show_idle_hook;
mapi_clist_av1 trace_clist[] = { &trace_msgtab, NULL };
mapi_hlist_av1 trace_hlist[] = {
{ "doing_trace", &doing_trace_hook },
+ { "doing_trace_show_idle", &doing_trace_show_idle_hook },
{ NULL, NULL }
};
DECLARE_MODULE_AV2(trace, NULL, NULL, trace_clist, trace_hlist, NULL, NULL, NULL, trace_desc);
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,
SeesOper(target_p, source_p) ? RPL_TRACEOPERATOR : RPL_TRACEUSER,
SeesOper(target_p, source_p) ? form_str(RPL_TRACEOPERATOR) : form_str(RPL_TRACEUSER),
class_name, name,
show_ip(source_p, target_p) ? ip : empty_sockhost,
- (unsigned long)(rb_current_time() - target_p->localClient->lasttime),
- (unsigned long)(rb_current_time() - target_p->localClient->last));
+ hdata_showidle.approved ? (unsigned long)(rb_current_time() - target_p->localClient->lasttime) : 0,
+ hdata_showidle.approved ? (unsigned long)(rb_current_time() - target_p->localClient->last) : 0);
cnt++;
}
delete_isupport("WHOX");
}
+int doing_who_show_idle_hook;
+
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
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);
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)
{
/* display as in whois */
int doing_whois_hook;
int doing_whois_global_hook;
int doing_whois_channel_visibility_hook;
+int doing_whois_show_idle_hook;
mapi_clist_av1 whois_clist[] = { &whois_msgtab, NULL };
mapi_hlist_av1 whois_hlist[] = {
{ "doing_whois", &doing_whois_hook },
{ "doing_whois_global", &doing_whois_global_hook },
{ "doing_whois_channel_visibility", &doing_whois_channel_visibility_hook },
+ { "doing_whois_show_idle", &doing_whois_show_idle_hook },
{ NULL, NULL }
};
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),
- target_p->name,
- (long)(rb_current_time() - target_p->localClient->last),
- (unsigned long)target_p->localClient->firsttime);
+ target_p->name,
+ hdata_showidle.approved ? (long)(rb_current_time() - target_p->localClient->last) : 0,
+ (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
{