]> jfr.im git - solanum.git/commitdiff
Add umode +I to allow users to hide their idle time (#220)
authorDavid Schultz <redacted>
Tue, 30 Aug 2022 20:49:43 +0000 (15:49 -0500)
committerGitHub <redacted>
Tue, 30 Aug 2022 20:49:43 +0000 (16:49 -0400)
doc/reference.conf
extensions/Makefile.am
extensions/umode_hide_idle_time.c [new file with mode: 0644]
help/users/umode
include/hook.h
modules/m_stats.c
modules/m_trace.c
modules/m_who.c
modules/m_whois.c

index 35afb85aadcd6160b3aa77d802f0e2dc37c394ba..4cd8c03720625f236456b895200b1c5a5a3c2e75 100644 (file)
@@ -78,6 +78,7 @@
  * 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 {
@@ -474,6 +476,8 @@ privset "local_op" {
         * 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;
index 5fe872dd235ff375430776dc3ad1ea3e4370116b..e404a9eeae52ce07c4de94644ec68f3e7fd45197 100644 (file)
@@ -66,6 +66,7 @@ extension_LTLIBRARIES =               \
   identify_msg.la              \
   cap_realhost.la              \
   invex_regonly.la             \
+  umode_hide_idle_time.la      \
   cap_oper.la                  \
   example_module.la
 
diff --git a/extensions/umode_hide_idle_time.c b/extensions/umode_hide_idle_time.c
new file mode 100644 (file)
index 0000000..1307763
--- /dev/null
@@ -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);
index 124896d8d2b44670c3d1bfb56d98639f177b6e0a..942568126496ee5bb42f4a825ca54c649ec8e139 100644 (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
                   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.
index c1971614c5cfea5a03e2f07086adffd77588491b..fc56b2888768b1da9405a347efced945c2705a88 100644 (file)
@@ -21,6 +21,14 @@ enum hook_priority
        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;
index 31177b71d73dc9bf4d2635b34b565460df69d764..b67d8c307aae42acace007828cd51fd424e3f7b2 100644 (file)
@@ -60,11 +60,13 @@ struct Message stats_msgtab = {
 
 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 }
 };
 
@@ -1621,19 +1623,27 @@ stats_l_client(struct Client *source_p, struct Client *target_p,
 
        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,
                                    "-");
        }
index a634635be32f84e02a1fb37b227a635f8640c96d..ae9a0057829e96d71b7574c6680164873a60f63a 100644 (file)
@@ -52,10 +52,12 @@ struct Message trace_msgtab = {
 };
 
 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);
@@ -381,13 +383,22 @@ report_this_status(struct Client *source_p, struct Client *target_p)
 
        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++;
                }
index 0c07732860dbc2eed2e74f700f47e97fcd46efaf..d47d44bb43e2dbe639dd2a181ce21443a2b9e162 100644 (file)
@@ -91,8 +91,14 @@ _moddeinit(void)
        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
@@ -531,7 +537,19 @@ do_who(struct Client *source_p, struct Client *target_p, struct membership *mspt
                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 */
index ecd8656937e337af284874f54c13f647fe59a1f5..68bb17c9bdf5ef7f7fc09646196cc97a9537b9e4 100644 (file)
@@ -60,12 +60,14 @@ struct Message whois_msgtab = {
 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 }
 };
 
@@ -373,10 +375,23 @@ single_whois(struct Client *source_p, struct Client *target_p, int operspy)
                                        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
        {