m_globops.la \
m_hash.la \
m_help.la \
+ m_highlight.la \
m_info.la \
m_invite.la \
m_ison.la \
m_globops_la_LDFLAGS = $(MODULE_FLAGS)
m_hash_la_LDFLAGS = $(MODULE_FLAGS)
m_help_la_LDFLAGS = $(MODULE_FLAGS)
+m_highlight_la_LDFLAGS = $(MODULE_FLAGS)
m_info_la_LDFLAGS = $(MODULE_FLAGS)
m_invite_la_LDFLAGS = $(MODULE_FLAGS)
m_ison_la_LDFLAGS = $(MODULE_FLAGS)
m_globops_la_SOURCES = m_globops.c
m_hash_la_SOURCES = m_hash.c
m_help_la_SOURCES = m_help.c
+m_highlight_la_SOURCES = m_highlight.c
m_info_la_SOURCES = m_info.c
m_invite_la_SOURCES = m_invite.c
m_ison_la_SOURCES = m_ison.c
--- /dev/null
+/*
+ * ircd-hybrid: an advanced, lightweight Internet Relay Chat Daemon (ircd)
+ *
+ * Copyright (c) 2017 plexus development team
+ *
+ * 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 "client.h"
+#include "irc_string.h"
+#include "numeric.h"
+#include "modules.h"
+#include "memory.h"
+#include "hook.h"
+#include "channel_mode.h"
+#include "hash.h"
+#include "s_misc.h"
+#include "send.h"
+#include "conf.h"
+
+/* Per token, longest chain we will search */
+#define MAX_SEARCH_LEN 75
+
+static int
+is_masshl(struct Channel *chptr, const char *message)
+{
+ char msg[IRCD_BUFSIZE];
+ char *start = msg;
+ int hits = 0;
+
+ strlcpy(msg, message, sizeof(msg));
+
+ while (start < msg + sizeof(msg))
+ {
+ // Skip non nick
+ while (*start && !IsNickChar(*start))
+ ++start;
+
+ // Go to end of nick
+ char *end = start;
+ while (*end && IsNickChar(*end))
+ ++end;
+
+ const char *token = start;
+
+ *end = 0; // null end of token
+ start = end + 1; // proceed to next
+
+ struct Client *target = hash_find_client(token);
+
+ if (target == NULL || !IsClient(target))
+ continue;
+
+ // find_channel_link is O(n) over either target->channel or chptr->members - whichever is shorter
+ // don't bother with this client if it is >= MAX_SEARCH_LEN
+ int len = IRCD_MIN(dlink_list_length(&target->channel), dlink_list_length(&chptr->members));
+ if (len >= MAX_SEARCH_LEN)
+ continue;
+
+ struct Membership *ms = find_channel_link(target, chptr);
+ if (ms == NULL)
+ continue;
+
+ ++hits;
+
+ if (hits >= ConfigFileEntry.mass_highlight_nicks)
+ break;
+ }
+
+ return hits;
+}
+
+static void
+highlight_can_send(struct can_send_data *data)
+{
+ struct Client *client = data->client;
+ struct Channel *chptr = data->chptr;
+ struct Membership *ms = data->membership;
+ const char *message = data->message;
+
+ if (!MyClient(client) || ConfigFileEntry.mass_highlight_nicks == 0)
+ return;
+
+ if (HasUMode(client, UMODE_OPER) || IsExemptLimits(client))
+ return;
+
+ // allow through non member messages because otherwise highlight blocker would be an
+ // information leak on who is on the channel
+ if (!ms || (ms->flags & (CHFL_OWNER | CHFL_PROTECTED | CHFL_CHANOP | CHFL_HALFOP | CHFL_VOICE)))
+ return;
+
+ int hits = is_masshl(chptr, message);
+ if (hits < ConfigFileEntry.mass_highlight_nicks)
+ return;
+
+ sendto_snomask(SNO_BOTS, L_ALL | SNO_ROUTE, "Blocked mass highlight from %s to channel %s",
+ get_client_name(client, HIDE_IP), chptr->chname);
+
+ data->ret = CAN_SEND_NO;
+}
+
+static struct Event can_send_event =
+{
+ .event = &can_send_hook,
+ .handler = highlight_can_send
+};
+
+static void
+module_init(void)
+{
+ hook_add(&can_send_event);
+}
+
+static void
+module_exit(void)
+{
+ hook_del(&can_send_event);
+}
+
+struct module module_entry =
+{
+ .version = "$Revision$",
+ .modinit = module_init,
+ .modexit = module_exit,
+};