extern dlink_list global_channel_list;
extern int msg_is_ctcp(const char *msg);
+extern void channel_flags_to_prefix(unsigned int flags, char *c);
extern int ban_matches(struct Client *who, struct Ban *bp);
extern int find_bmask(struct Client *, const dlink_list *const, struct Extban *);
extern int is_bwsave(const struct Channel *chptr, struct Client *source_p);
struct Client *servptr; /**< Points to server this Client is on */
struct Client *from; /**< == self, if Local Client, *NEVER* NULL! */
+ time_t signon; /**< Signon time. For remote clients is oldest nick TS */
time_t tsinfo; /**< TS on the nick, SVINFO on server */
unsigned int flags; /**< client flags */
char *cgisockhost; /**< if this client is a webirc client, this is the ip of where they are from */
char *cgihost; /**< if this client is a webirc client, this is the host of where they are from */
+ int seen_privmsgs; /**< number of privmsgs seen from the client to channels */
};
int enable_forwarding;
const EVP_MD *message_digest_algorithm;
int mass_highlight_nicks;
+ int delay_secs;
+ int delay_message_count;
+ int delay_signon_secs;
};
struct config_channel_entry
--- /dev/null
+/*
+ * ircd-hybrid: an advanced, lightweight Internet Relay Chat Daemon (ircd)
+ *
+ * Copyright (c) 2018 Adam <Adam@anope.org>
+ *
+ * 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
+ */
+
+struct Client;
+struct Channel;
+
+extern void delay_init(void);
+extern void delay_queue(struct Client *client, struct Channel *channel, int privmsg, unsigned int flags, const char *message);
MATCH_HOST = 2
};
+enum
+{
+ SEND_FLAG_CTCP = 0x1,
+ SEND_FLAG_LOCAL = 0x2,
+ SEND_FLAG_REMOTE = 0x4
+};
+
/*
* struct decls
*/
extern void sendto_one(struct Client *, const char *, ...) AFP(2,3);
extern void sendto_channel_butone(struct Client *, struct Client *,
struct Channel *, unsigned int,
- int is_ctcp,
+ unsigned int,
const char *, ...) AFP(6,7);
extern void sendto_common_channels_local(struct Client *, int, unsigned int, unsigned int,
const char *, ...) AFP(5,6);
#include "hash.h"
#include "packet.h"
#include "s_user.h"
+#include "delay.h"
enum
{
return 0;
}
-static void
-flags_to_prefix(unsigned int flags, char *c)
-{
- if (flags & CHFL_VOICE)
- {
- *c = '+';
- }
- else if (flags & CHFL_HALFOP)
- {
- *c = '%';
- }
- else if (flags & CHFL_CHANOP)
- {
- *c = '@';
- }
- else if (flags & CHFL_PROTECTED)
- {
- *c = CHANPROTECT_PREFIX;
- }
- else if (flags & CHFL_OWNER)
- {
- *c = CHANOWNER_PREFIX;
- }
-}
-
/* msg_channel()
*
* inputs - flag privmsg or notice
if (flood_attack_channel(p_or_n, source_p, chptr))
return;
+ source_p->seen_privmsgs++;
+
ctcp_reply = p_or_n == NOTICE && msg_is_ctcp(text);
if (MyClient(source_p) && !ctcp_reply && target_check_channel(source_p, chptr))
{
return;
if (e->flags)
- flags_to_prefix(e->flags, &c);
+ channel_flags_to_prefix(e->flags, &c);
+
+ int send_flags = SEND_FLAG_LOCAL | SEND_FLAG_REMOTE;
+
+ if (IsClient(source_p) && !IsIdentified(source_p)
+ && !HasFlag(source_p, FLAGS_SERVICE) && !HasUMode(source_p, UMODE_OPER)
+ && (source_p->seen_privmsgs < ConfigFileEntry.delay_message_count || source_p->signon + ConfigFileEntry.delay_signon_secs > CurrentTime))
+ {
+ delay_queue(source_p, chptr, p_or_n == PRIVMSG, e->flags, text);
+ send_flags = SEND_FLAG_REMOTE; // not forwarded to users
+ }
if (c)
- sendto_channel_butone(client_p, source_p, chptr, e->flags, msg_is_ctcp(text), "%s %c%s :%s",
+ sendto_channel_butone(client_p, source_p, chptr, e->flags,
+ send_flags | (msg_is_ctcp(text) ? SEND_FLAG_CTCP : 0), "%s %c%s :%s",
command, c, chptr->chname, text);
else
- sendto_channel_butone(client_p, source_p, chptr, 0, msg_is_ctcp(text), "%s %s :%s",
+ sendto_channel_butone(client_p, source_p, chptr, 0,
+ send_flags | (msg_is_ctcp(text) ? SEND_FLAG_CTCP : 0), "%s %s :%s",
command, chptr->chname, text);
return;
}
if (parc > 2)
source_p->hopcount = atoi(parv[2]);
if (newts)
+ {
source_p->tsinfo = newts;
+ if (newts < CurrentTime)
+ {
+ source_p->signon = newts;
+ }
+ }
else
newts = source_p->tsinfo = CurrentTime;
source_p->hopcount = atoi(parv[2]);
source_p->tsinfo = newts;
+ if (newts < CurrentTime)
+ {
+ // take a guess based on nick ts
+ source_p->signon = newts;
+ }
strlcpy(source_p->svid, svsid, sizeof(source_p->svid));
/* copy the nick in place */
conf_parser.y \
conf_lexer.l \
dbuf.c \
+ delay.c \
dnsbl.c \
event.c \
extban.c \
return 0;
}
+void
+channel_flags_to_prefix(unsigned int flags, char *c)
+{
+ if (flags & CHFL_VOICE)
+ {
+ *c = '+';
+ }
+ else if (flags & CHFL_HALFOP)
+ {
+ *c = '%';
+ }
+ else if (flags & CHFL_CHANOP)
+ {
+ *c = '@';
+ }
+ else if (flags & CHFL_PROTECTED)
+ {
+ *c = CHANPROTECT_PREFIX;
+ }
+ else if (flags & CHFL_OWNER)
+ {
+ *c = CHANOWNER_PREFIX;
+ }
+}
+
+
/*!
* \param chptr pointer to Channel struct
* \param source_p pointer to Client struct
SetUnknown(client_p);
strcpy(client_p->username, "unknown");
strcpy(client_p->svid, "0");
+ client_p->signon = CurrentTime;
return client_p;
}
ConfigFileEntry.enable_forwarding = 0;
ConfigFileEntry.message_digest_algorithm = EVP_sha1();
ConfigFileEntry.mass_highlight_nicks = 0;
+ ConfigFileEntry.delay_secs = 0;
+ ConfigFileEntry.delay_message_count = 0;
+ ConfigFileEntry.delay_signon_secs = 0;
}
static void
default_split_server_count { return DEFAULT_SPLIT_SERVER_COUNT; }
default_split_user_count { return DEFAULT_SPLIT_USER_COUNT; }
defer { return DEFER; }
+delay_secs { return DELAY_SECS; }
+delay_message_count { return DELAY_MESSAGE_COUNT; }
+delay_signon_secs { return DELAY_SIGNON_SECS; }
deny { return DENY; }
description { return DESCRIPTION; }
die { return DIE; }
%token DEFAULT_SPLIT_SERVER_COUNT
%token DEFAULT_SPLIT_USER_COUNT
%token DEFER
+%token DELAY_SECS
+%token DELAY_MESSAGE_COUNT
+%token DELAY_SIGNON_SECS
%token DENY
%token DESCRIPTION
%token DIE
general_cloak_key |
general_account_whois | general_disable_chmodes | general_use_target_change |
general_enable_extbans | general_enable_forwarding | general_ssl_message_digest_algorithm |
- general_mass_highlight_nicks |
+ general_mass_highlight_nicks | general_delay_secs | general_delay_message_count | general_delay_signon_secs |
error;
ConfigFileEntry.mass_highlight_nicks = $3;
};
+general_delay_secs: DELAY_SECS '=' timespec ';'
+{
+ ConfigFileEntry.delay_secs = $3;
+};
+
+general_delay_message_count: DELAY_MESSAGE_COUNT '=' NUMBER ';'
+{
+ ConfigFileEntry.delay_message_count = $3;
+};
+
+general_delay_signon_secs: DELAY_SIGNON_SECS '=' timespec ';'
+{
+ ConfigFileEntry.delay_signon_secs = $3;
+};
+
umode_oitems: umode_oitems ',' umode_oitem | umode_oitem;
umode_oitem: T_NOCTCP
{
--- /dev/null
+/*
+ * ircd-hybrid: an advanced, lightweight Internet Relay Chat Daemon (ircd)
+ *
+ * Copyright (c) 2018 Adam <Adam@anope.org>
+ *
+ * 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 "ircd_defs.h"
+#include "ircd.h"
+#include "list.h"
+#include "mempool.h"
+#include "irc_string.h"
+#include "client.h"
+#include "hash.h"
+#include "send.h"
+#include "event.h"
+#include "conf.h"
+
+struct delayed_message
+{
+ char uid[IDLEN + 1];
+ char chname[CHANNELLEN + 1];
+ time_t ts;
+ int privmsg;
+ unsigned int flags;
+ char message[IRCD_BUFSIZE];
+ time_t created;
+ dlink_node node;
+};
+
+static mp_pool_t *delay_pool;
+static dlink_list delay_list;
+
+static void delay_process(void *);
+
+void
+delay_init(void)
+{
+ delay_pool = mp_pool_new("delay message", sizeof(struct delayed_message), 128);
+ eventAddIsh("delay_process", delay_process, NULL, 1);
+}
+
+void
+delay_queue(struct Client *client, struct Channel *channel, int privmsg, unsigned int flags, const char *message)
+{
+ struct delayed_message *dmsg = mp_pool_get(delay_pool);
+ strlcpy(dmsg->uid, client->id, sizeof(dmsg->uid));
+ strlcpy(dmsg->chname, channel->chname, sizeof(dmsg->chname));
+ dmsg->ts = channel->channelts;
+ dmsg->privmsg = privmsg;
+ dmsg->flags = flags;
+ strlcpy(dmsg->message, message, sizeof(dmsg->message));
+ dmsg->created = CurrentTime;
+ dlinkAddTail(dmsg, &dmsg->node, &delay_list);
+}
+
+static void
+delay_process(void *unused)
+{
+ dlink_node *ptr, *ptr_next;
+
+ DLINK_FOREACH_SAFE(ptr, ptr_next, delay_list.head)
+ {
+ struct delayed_message *dmsg = ptr->data;
+
+ if (dmsg->created + ConfigFileEntry.delay_secs > CurrentTime)
+ {
+ break;
+ }
+
+ struct Client *client = hash_find_id(dmsg->uid);
+ if (client == NULL)
+ {
+ goto next;
+ }
+
+ struct Channel *channel = hash_find_channel(dmsg->chname);
+ if (channel == NULL || dmsg->ts != channel->channelts)
+ {
+ goto next;
+ }
+
+ const char *command = dmsg->privmsg ? "PRIVMSG" : "NOTICE";
+ const char *text = dmsg->message;
+ char c = 0;
+ channel_flags_to_prefix(dmsg->flags, &c);
+
+ if (can_send(channel, client, NULL, text, !dmsg->privmsg) >= 0)
+ {
+ goto next;
+ }
+
+ if (c)
+ sendto_channel_butone(client->from, client, channel, dmsg->flags,
+ SEND_FLAG_LOCAL | (msg_is_ctcp(text) ? SEND_FLAG_CTCP : 0), "%s %c%s :%s",
+ command, c, channel->chname, text);
+ else
+ sendto_channel_butone(client->from, client, channel, 0,
+ SEND_FLAG_LOCAL | (msg_is_ctcp(text) ? SEND_FLAG_CTCP : 0), "%s %s :%s",
+ command, channel->chname, text);
+
+ next:
+ dlinkDelete(ptr, &delay_list);
+ mp_pool_release(dmsg);
+ }
+}
#include "ssl.h"
#include "httpd.h"
#include "userhost.h"
+#include "delay.h"
#ifdef HAVE_LIBGEOIP
assemble_cmode_buffer();
extban_init();
cloak_reload();
+ delay_init();
write_pidfile(pidFileName);
numeric, ID_or_name(target_p, target_p), parv[2]);
}
else
- sendto_channel_butone(client_p, source_p, chptr, 0, 0, "%s %s %s",
+ sendto_channel_butone(client_p, source_p, chptr, 0, SEND_FLAG_LOCAL | SEND_FLAG_REMOTE, "%s %s %s",
numeric, chptr->chname, parv[2]);
}
userhost_add(source_p->realhost, 1);
SetUserHost(source_p);
+ /* Initialize number of privmsgs if bursting to not trigger delay */
+ if (!HasFlag(source_p->servptr, FLAGS_EOB))
+ source_p->seen_privmsgs = ConfigFileEntry.delay_message_count;
+
if (HasFlag(source_p->servptr, FLAGS_EOB))
sendto_snomask(SNO_CCONN, L_ALL | SNO_REMOTE,
"Client connecting from %s: %s (%s@%s) [%s] [%s]",
void
sendto_channel_butone(struct Client *one, struct Client *from,
struct Channel *chptr, unsigned int type,
- int is_ctcp,
+ unsigned int flags,
const char *pattern, ...)
{
va_list alocal, aremote, auid;
if (MyClient(target_p) && HasUMode(target_p, UMODE_DEAF))
continue;
- if (is_ctcp && is_noctcp(from, target_p))
+ if ((flags & SEND_FLAG_CTCP) && is_noctcp(from, target_p))
continue;
if (is_bwsave(chptr, target_p))
if (MyConnect(target_p))
{
- if (target_p->localClient->serial != current_serial)
+ if ((flags & SEND_FLAG_LOCAL) && target_p->localClient->serial != current_serial)
{
send_message(target_p, local_buf);
target_p->localClient->serial = current_serial;
/* Now check whether a message has been sent to this
* remote link already
*/
- if (target_p->from->localClient->serial != current_serial)
+ if ((flags & SEND_FLAG_REMOTE) && target_p->from->localClient->serial != current_serial)
{
if (IsCapable(target_p->from, CAP_TS6))
send_message_remote(target_p->from, from, uid_buf);