mirror of
https://github.com/unrealircd/unrealircd.git
synced 2026-06-30 23:26:39 +02:00
3142b57f77
In CommandAdd() the flag CMD_TEXTANALYSIS now means that the last parameter of the command will run through the text analysis system. This flag is set in PRIVMSG NOTICE PART QUIT AWAY SETNAME TOPIC
707 lines
19 KiB
C
707 lines
19 KiB
C
/*
|
|
* Unreal Internet Relay Chat Daemon, src/modules/message.c
|
|
* (C) 2000-2001 Carsten V. Munk and the UnrealIRCd Team
|
|
* Moved to modules by Fish (Justin Hammond)
|
|
*
|
|
* 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 1, 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., 675 Mass Ave, Cambridge, MA 02139, USA.
|
|
*/
|
|
|
|
#include "unrealircd.h"
|
|
|
|
/* Forward declarations */
|
|
const char *_StripColors(const char *text);
|
|
int ban_version(Client *client, const char *text);
|
|
CMD_FUNC(cmd_private);
|
|
CMD_FUNC(cmd_notice);
|
|
CMD_FUNC(cmd_tagmsg);
|
|
void cmd_message(ClientContext *clictx, Client *client, MessageTag *recv_mtags, int parc, const char *parv[], SendType sendtype);
|
|
int _can_send_to_channel(Client *client, Channel *channel, const char **msgtext, const char **errmsg, SendType sendtype, ClientContext *clictx);
|
|
int can_send_to_user(Client *client, Client *target, const char **msgtext, const char **errmsg, SendType sendtype, ClientContext *clictx);
|
|
|
|
/* Variables */
|
|
long CAP_MESSAGE_TAGS = 0; /**< Looked up at MOD_LOAD, may stay 0 if message-tags support is absent */
|
|
|
|
ModuleHeader MOD_HEADER
|
|
= {
|
|
"message", /* Name of module */
|
|
"6.0.2", /* Version */
|
|
"private message and notice", /* Short description of module */
|
|
"UnrealIRCd Team",
|
|
"unrealircd-6",
|
|
};
|
|
|
|
MOD_TEST()
|
|
{
|
|
MARK_AS_OFFICIAL_MODULE(modinfo);
|
|
EfunctionAddConstString(modinfo->handle, EFUNC_STRIPCOLORS, _StripColors);
|
|
EfunctionAdd(modinfo->handle, EFUNC_CAN_SEND_TO_CHANNEL, _can_send_to_channel);
|
|
return MOD_SUCCESS;
|
|
}
|
|
|
|
/* This is called on module init, before Server Ready */
|
|
MOD_INIT()
|
|
{
|
|
CommandAdd(modinfo->handle, "PRIVMSG", cmd_private, 2, CMD_USER|CMD_SERVER|CMD_RESETIDLE|CMD_VIRUS|CMD_TEXTANALYSIS);
|
|
CommandAdd(modinfo->handle, "NOTICE", cmd_notice, 2, CMD_USER|CMD_SERVER|CMD_TEXTANALYSIS);
|
|
CommandAdd(modinfo->handle, "TAGMSG", cmd_tagmsg, 1, CMD_USER|CMD_SERVER);
|
|
MARK_AS_OFFICIAL_MODULE(modinfo);
|
|
return MOD_SUCCESS;
|
|
}
|
|
|
|
/* Is first run when server is 100% ready */
|
|
MOD_LOAD()
|
|
{
|
|
CAP_MESSAGE_TAGS = ClientCapabilityBit("message-tags");
|
|
|
|
return MOD_SUCCESS;
|
|
}
|
|
|
|
/* Called when module is unloaded */
|
|
MOD_UNLOAD()
|
|
{
|
|
return MOD_SUCCESS;
|
|
}
|
|
|
|
#define CANPRIVMSG_CONTINUE 100
|
|
#define CANPRIVMSG_SEND 101
|
|
/** Check if PRIVMSG's are permitted from a person to another person.
|
|
* client: source client
|
|
* target: target client
|
|
* sendtype: One of SEND_TYPE_*
|
|
* text: Pointer to a pointer to a text [in, out]
|
|
* cmd: Pointer to a pointer which contains the command to use [in, out]
|
|
*/
|
|
int can_send_to_user(Client *client, Client *target, const char **msgtext, const char **errmsg, SendType sendtype, ClientContext *clictx)
|
|
{
|
|
int ret;
|
|
Hook *h;
|
|
int n;
|
|
static char errbuf[256];
|
|
|
|
*errmsg = NULL;
|
|
|
|
if (IsVirus(client))
|
|
{
|
|
ircsnprintf(errbuf, sizeof(errbuf), "You are only allowed to talk in '%s'", SPAMFILTER_VIRUSCHAN);
|
|
*errmsg = errbuf;
|
|
return 0;
|
|
}
|
|
|
|
if (MyUser(client) && target_limit_exceeded(client, target, target->name))
|
|
{
|
|
/* target_limit_exceeded() is an exception, in the sense that
|
|
* it will send a different numeric. So we don't set errmsg.
|
|
*/
|
|
return 0;
|
|
}
|
|
|
|
if (is_silenced(client, target))
|
|
{
|
|
RunHook(HOOKTYPE_SILENCED, client, target, sendtype);
|
|
/* Silently discarded, no error message */
|
|
return 0;
|
|
}
|
|
|
|
// Possible FIXME: make match_spamfilter also use errmsg, or via a wrapper? or use same numeric?
|
|
if (MyUser(client) && (sendtype != SEND_TYPE_TAGMSG))
|
|
{
|
|
int spamtype = (sendtype == SEND_TYPE_NOTICE ? SPAMF_USERNOTICE : SPAMF_USERMSG);
|
|
const char *cmd = sendtype_to_cmd(sendtype);
|
|
|
|
if (match_spamfilter(client, *msgtext, spamtype, cmd, target->name, 0, clictx, NULL))
|
|
return 0;
|
|
}
|
|
|
|
n = HOOK_CONTINUE;
|
|
for (h = Hooks[HOOKTYPE_CAN_SEND_TO_USER]; h; h = h->next)
|
|
{
|
|
n = (*(h->func.intfunc))(client, target, msgtext, errmsg, sendtype, clictx);
|
|
if (n == HOOK_DENY)
|
|
{
|
|
if (!*errmsg)
|
|
{
|
|
unreal_log(ULOG_ERROR, "main", "BUG_CAN_SEND_TO_USER_NO_ERRMSG", client,
|
|
"[BUG] Module $module did not set errmsg!!!",
|
|
log_data_string("module", h->owner->header->name));
|
|
abort();
|
|
}
|
|
return 0;
|
|
}
|
|
if (!*msgtext || !**msgtext)
|
|
{
|
|
if (sendtype != SEND_TYPE_TAGMSG)
|
|
return 0;
|
|
else
|
|
*msgtext = "";
|
|
}
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
|
|
/** Check if user is allowed to send to a prefix (eg: @#channel).
|
|
* @param client The client (sender)
|
|
* @param channel The target channel
|
|
* @param mode The member mode to send to (eg: 'o')
|
|
*/
|
|
int can_send_to_member_mode(Client *client, Channel *channel, char mode)
|
|
{
|
|
Membership *lp;
|
|
|
|
if (op_can_override("channel:override:message:prefix",client,channel,NULL))
|
|
return 1;
|
|
|
|
lp = find_membership_link(client->user->channel, channel);
|
|
|
|
/* Check if user is allowed to send. RULES:
|
|
* Need at least voice (+) in order to send to +,% or @
|
|
* Need at least ops (@) in order to send to & or ~
|
|
*/
|
|
if (!lp || !check_channel_access_membership(lp, "vhoaq"))
|
|
{
|
|
sendnumeric(client, ERR_CHANOPRIVSNEEDED, channel->name);
|
|
return 0;
|
|
}
|
|
|
|
#if 0
|
|
if (!(prefix & PREFIX_OP) && ((prefix & PREFIX_OWNER) || (prefix & PREFIX_ADMIN)) &&
|
|
!check_channel_access_membership(lp, "oaq"))
|
|
{
|
|
sendnumeric(client, ERR_CHANOPRIVSNEEDED, channel->name);
|
|
return 0;
|
|
}
|
|
#endif
|
|
|
|
return 1;
|
|
}
|
|
|
|
int has_client_mtags(MessageTag *mtags)
|
|
{
|
|
MessageTag *m;
|
|
|
|
for (m = mtags; m; m = m->next)
|
|
if (*m->name == '+')
|
|
return 1;
|
|
return 0;
|
|
}
|
|
|
|
/* General message handler to users and channels. Used by PRIVMSG, NOTICE, etc.
|
|
*/
|
|
void cmd_message(ClientContext *clictx, Client *client, MessageTag *recv_mtags, int parc, const char *parv[], SendType sendtype)
|
|
{
|
|
Client *target;
|
|
Channel *channel;
|
|
char targets[BUFSIZE];
|
|
char *targetstr, *p, *p2, *pc;
|
|
const char *text, *errmsg;
|
|
int ret;
|
|
int ntargets = 0;
|
|
const char *cmd = sendtype_to_cmd(sendtype);
|
|
int maxtargets = max_targets_for_command(cmd);
|
|
Hook *h;
|
|
MessageTag *mtags;
|
|
int sendflags;
|
|
|
|
/* Force a labeled-response, even if we don't send anything
|
|
* and the request was sent to other servers (which won't
|
|
* reply either :D).
|
|
*/
|
|
labeled_response_force = 1;
|
|
|
|
if (parc < 2 || *parv[1] == '\0')
|
|
{
|
|
sendnumeric(client, ERR_NORECIPIENT, cmd);
|
|
return;
|
|
}
|
|
|
|
if ((sendtype != SEND_TYPE_TAGMSG) && (parc < 3 || *parv[2] == '\0'))
|
|
{
|
|
sendnumeric(client, ERR_NOTEXTTOSEND);
|
|
return;
|
|
}
|
|
|
|
if (MyConnect(client))
|
|
parv[1] = (char *)canonize(parv[1]);
|
|
|
|
strlcpy(targets, parv[1], sizeof(targets));
|
|
for (p = NULL, targetstr = strtoken(&p, targets, ","); targetstr; targetstr = strtoken(&p, NULL, ","))
|
|
{
|
|
if (MyUser(client) && (++ntargets > maxtargets))
|
|
{
|
|
sendnumeric(client, ERR_TOOMANYTARGETS, targetstr, maxtargets, cmd);
|
|
break;
|
|
}
|
|
|
|
/* The nicks "ircd" and "irc" are special (and reserved) */
|
|
if (!strcasecmp(targetstr, "ircd") && MyUser(client))
|
|
return;
|
|
|
|
if (!strcasecmp(targetstr, "irc") && MyUser(client))
|
|
{
|
|
/* When ban version { } is enabled the IRCd sends a CTCP VERSION request
|
|
* from the "IRC" nick. So we need to handle CTCP VERSION replies to "IRC".
|
|
*/
|
|
if (!strncmp(parv[2], "\1VERSION ", 9))
|
|
ban_version(client, parv[2] + 9);
|
|
else if (!strncmp(parv[2], "\1SCRIPT ", 8))
|
|
ban_version(client, parv[2] + 8);
|
|
return;
|
|
}
|
|
|
|
p2 = strchr(targetstr, '#');
|
|
|
|
/* Message to channel */
|
|
if (p2 && (channel = find_channel(p2)))
|
|
{
|
|
char pfixchan[CHANNELLEN + 4];
|
|
int replaced = 0;
|
|
char member_modes_tmp[2];
|
|
char *member_modes = NULL;
|
|
if (p2 - targetstr > 0)
|
|
{
|
|
/* There is (posssibly) a prefix involved... */
|
|
char prefix_tmp[32];
|
|
char prefix;
|
|
strlncpy(prefix_tmp, targetstr, sizeof(prefix_tmp), p2 - targetstr);
|
|
prefix = lowest_ranking_prefix(prefix_tmp);
|
|
if (prefix)
|
|
{
|
|
/* Rewrite the target. Eg: @&~#chan becomes @#chan */
|
|
snprintf(pfixchan, sizeof(pfixchan), "%c%s", prefix, channel->name);
|
|
targetstr = pfixchan;
|
|
replaced = 1;
|
|
/* And set 'member_modes' */
|
|
member_modes_tmp[0] = prefix_to_mode(prefix);
|
|
member_modes_tmp[1] = '\0';
|
|
member_modes = member_modes_tmp;
|
|
/* Oh, and some access check */
|
|
if (MyUser(client) && !can_send_to_member_mode(client, channel, *member_modes))
|
|
continue;
|
|
}
|
|
}
|
|
if (!replaced)
|
|
{
|
|
/* Replace target so the privmsg always goes to the "official" channel name */
|
|
strlcpy(pfixchan, channel->name, sizeof(pfixchan));
|
|
targetstr = pfixchan;
|
|
}
|
|
|
|
if (IsVirus(client) && strcasecmp(channel->name, SPAMFILTER_VIRUSCHAN))
|
|
{
|
|
sendnotice(client, "You are only allowed to talk in '%s'", SPAMFILTER_VIRUSCHAN);
|
|
continue;
|
|
}
|
|
|
|
text = parv[2];
|
|
errmsg = NULL;
|
|
if (MyUser(client) && !IsULine(client))
|
|
{
|
|
if (!can_send_to_channel(client, channel, &text, &errmsg, sendtype, clictx))
|
|
{
|
|
/* Send the error message, but only if:
|
|
* 1) The user has not been killed
|
|
* 2) It is not a NOTICE
|
|
*/
|
|
if (IsDead(client))
|
|
return;
|
|
if (!IsDead(client) && (sendtype != SEND_TYPE_NOTICE) && !BadPtr(errmsg))
|
|
sendnumeric(client, ERR_CANNOTSENDTOCHAN, channel->name, errmsg, p2);
|
|
continue; /* skip delivery to this target */
|
|
}
|
|
}
|
|
mtags = NULL;
|
|
sendflags = SEND_ALL;
|
|
|
|
if (!strchr(CHANCMDPFX,parv[2][0]))
|
|
sendflags |= SKIP_DEAF;
|
|
|
|
if ((*parv[2] == '\001') && strncmp(&parv[2][1], "ACTION ", 7))
|
|
sendflags |= SKIP_CTCP;
|
|
|
|
if (MyUser(client) && (sendtype != SEND_TYPE_TAGMSG))
|
|
{
|
|
int spamtype = (sendtype == SEND_TYPE_NOTICE ? SPAMF_CHANNOTICE : SPAMF_CHANMSG);
|
|
|
|
if (match_spamfilter(client, text, spamtype, cmd, channel->name, 0, clictx, NULL))
|
|
return;
|
|
}
|
|
|
|
new_message(client, recv_mtags, &mtags);
|
|
|
|
RunHook(HOOKTYPE_PRE_CHANMSG, client, channel, &mtags, text, sendtype);
|
|
|
|
if (!text)
|
|
{
|
|
free_message_tags(mtags);
|
|
continue;
|
|
}
|
|
|
|
if (sendtype != SEND_TYPE_TAGMSG)
|
|
{
|
|
/* PRIVMSG or NOTICE */
|
|
sendto_channel(channel, client, client->direction,
|
|
member_modes, 0, sendflags, mtags,
|
|
":%s %s %s :%s",
|
|
client->name, cmd, targetstr, text);
|
|
} else {
|
|
/* TAGMSG:
|
|
* Only send if the message includes any user message tags
|
|
* and if the 'message-tags' module is loaded.
|
|
* Do not allow empty and useless TAGMSG.
|
|
*/
|
|
if (!CAP_MESSAGE_TAGS || !has_client_mtags(mtags))
|
|
{
|
|
free_message_tags(mtags);
|
|
continue;
|
|
}
|
|
sendto_channel(channel, client, client->direction,
|
|
member_modes, CAP_MESSAGE_TAGS, sendflags, mtags,
|
|
":%s TAGMSG %s",
|
|
client->name, targetstr);
|
|
}
|
|
|
|
RunHook(HOOKTYPE_CHANMSG, client, channel, sendflags, member_modes, targetstr, mtags, text, sendtype);
|
|
|
|
free_message_tags(mtags);
|
|
|
|
continue;
|
|
}
|
|
else if (p2)
|
|
{
|
|
sendnumeric(client, ERR_NOSUCHNICK, p2);
|
|
continue;
|
|
}
|
|
|
|
|
|
/* Message to $servermask */
|
|
if (*targetstr == '$')
|
|
{
|
|
MessageTag *mtags = NULL;
|
|
|
|
if (!ValidatePermissionsForPath("chat:notice:global", client, NULL, NULL, NULL))
|
|
{
|
|
/* Apparently no other IRCd does this, but I think it's confusing not to
|
|
* send an error message, especially with our new privilege system.
|
|
* Error message could be more descriptive perhaps.
|
|
*/
|
|
sendnumeric(client, ERR_NOPRIVILEGES);
|
|
continue;
|
|
}
|
|
new_message(client, recv_mtags, &mtags);
|
|
sendto_match_butone(IsServer(client->direction) ? client->direction : NULL,
|
|
client, targetstr + 1,
|
|
(*targetstr == '#') ? MATCH_HOST :
|
|
MATCH_SERVER,
|
|
mtags,
|
|
":%s %s %s :%s", client->name, cmd, targetstr, parv[2]);
|
|
free_message_tags(mtags);
|
|
continue;
|
|
}
|
|
|
|
/* nickname addressed? */
|
|
target = hash_find_nickatserver(targetstr, NULL);
|
|
if (target)
|
|
{
|
|
const char *errmsg = NULL;
|
|
text = parv[2];
|
|
if (!can_send_to_user(client, target, &text, &errmsg, sendtype, clictx))
|
|
{
|
|
/* Message is discarded */
|
|
if (IsDead(client))
|
|
return;
|
|
if ((sendtype != SEND_TYPE_NOTICE) && !BadPtr(errmsg))
|
|
sendnumeric(client, ERR_CANTSENDTOUSER, target->name, errmsg);
|
|
} else
|
|
{
|
|
/* We may send the message */
|
|
MessageTag *mtags = NULL;
|
|
|
|
/* Inform sender that recipient is away, if this is so */
|
|
if ((sendtype == SEND_TYPE_PRIVMSG) && MyConnect(client) && target->user && target->user->away)
|
|
sendnumeric(client, RPL_AWAY, target->name, target->user->away);
|
|
|
|
new_message(client, recv_mtags, &mtags);
|
|
if ((sendtype == SEND_TYPE_TAGMSG) && !has_client_mtags(mtags))
|
|
{
|
|
free_message_tags(mtags);
|
|
continue;
|
|
}
|
|
labeled_response_inhibit = 1;
|
|
if (MyUser(target))
|
|
{
|
|
/* Deliver to end-user */
|
|
if (sendtype == SEND_TYPE_TAGMSG)
|
|
{
|
|
if (HasCapability(target, "message-tags"))
|
|
{
|
|
sendto_prefix_one(target, client, mtags, ":%s %s %s",
|
|
client->name, cmd, target->name);
|
|
}
|
|
} else {
|
|
sendto_prefix_one(target, client, mtags, ":%s %s %s :%s",
|
|
client->name, cmd, target->name, text);
|
|
}
|
|
} else {
|
|
/* Send to another server */
|
|
if (sendtype == SEND_TYPE_TAGMSG)
|
|
{
|
|
sendto_prefix_one(target, client, mtags, ":%s %s %s",
|
|
client->id, cmd, target->id);
|
|
} else {
|
|
sendto_prefix_one(target, client, mtags, ":%s %s %s :%s",
|
|
client->id, cmd, target->id, text);
|
|
}
|
|
}
|
|
labeled_response_inhibit = 0;
|
|
RunHook(HOOKTYPE_USERMSG, client, target, mtags, text, sendtype);
|
|
free_message_tags(mtags);
|
|
continue;
|
|
}
|
|
continue; /* Message has been delivered or rejected, continue with next target */
|
|
}
|
|
|
|
/* If nick@server -and- the @server portion was set::services-server then send a special message */
|
|
if (!target && SERVICES_NAME)
|
|
{
|
|
char *server = strchr(targetstr, '@');
|
|
if (server && strncasecmp(server + 1, SERVICES_NAME, strlen(SERVICES_NAME)) == 0)
|
|
{
|
|
sendnumeric(client, ERR_SERVICESDOWN, targetstr);
|
|
continue;
|
|
}
|
|
}
|
|
|
|
/* nothing, nada, not anything found */
|
|
sendnumeric(client, ERR_NOSUCHNICK, targetstr);
|
|
continue;
|
|
}
|
|
}
|
|
|
|
/*
|
|
** cmd_private
|
|
** parv[1] = receiver list
|
|
** parv[2] = message text
|
|
*/
|
|
CMD_FUNC(cmd_private)
|
|
{
|
|
cmd_message(clictx, client, recv_mtags, parc, parv, SEND_TYPE_PRIVMSG);
|
|
}
|
|
|
|
/*
|
|
** cmd_notice
|
|
** parv[1] = receiver list
|
|
** parv[2] = notice text
|
|
*/
|
|
CMD_FUNC(cmd_notice)
|
|
{
|
|
cmd_message(clictx, client, recv_mtags, parc, parv, SEND_TYPE_NOTICE);
|
|
}
|
|
|
|
/*
|
|
** cmd_tagmsg
|
|
** parv[1] = receiver list
|
|
*/
|
|
CMD_FUNC(cmd_tagmsg)
|
|
{
|
|
/* compatibility hack */
|
|
parv[2] = "";
|
|
parv[3] = NULL;
|
|
cmd_message(clictx, client, recv_mtags, parc, parv, SEND_TYPE_TAGMSG);
|
|
}
|
|
|
|
/* Taken from xchat by Peter Zelezny
|
|
* changed very slightly by codemastr
|
|
* RGB color stripping support added -- codemastr
|
|
*
|
|
* NOTE: if you change/update/enhance StripColors() then consider changing
|
|
* the StripControlCodes() function as well (in misc.c) !!
|
|
*/
|
|
const char *_StripColors(const char *text)
|
|
{
|
|
int i = 0, len = strlen(text), save_len=0;
|
|
char nc = 0, col = 0, rgb = 0;
|
|
const char *save_text=NULL;
|
|
static char new_str[4096];
|
|
|
|
while (len > 0)
|
|
{
|
|
if ((col && isdigit(*text) && nc < 2) ||
|
|
((col == 1) && (*text == ',') && isdigit(text[1]) && (nc > 0) && (nc < 3)))
|
|
{
|
|
nc++;
|
|
if (*text == ',')
|
|
{
|
|
nc = 0;
|
|
col++;
|
|
}
|
|
}
|
|
/* Syntax for RGB is ^DHHHHHH where H is a hex digit.
|
|
* If < 6 hex digits are specified, the code is displayed
|
|
* as text
|
|
*/
|
|
else if ((rgb && isxdigit(*text) && nc < 6) || (rgb && *text == ',' && nc < 7))
|
|
{
|
|
nc++;
|
|
if (*text == ',')
|
|
nc = 0;
|
|
}
|
|
else
|
|
{
|
|
if (col)
|
|
col = 0;
|
|
if (rgb)
|
|
{
|
|
if (nc != 6)
|
|
{
|
|
text = save_text+1;
|
|
len = save_len-1;
|
|
rgb = 0;
|
|
continue;
|
|
}
|
|
rgb = 0;
|
|
}
|
|
if (*text == '\003')
|
|
{
|
|
col = 1;
|
|
nc = 0;
|
|
}
|
|
else if (*text == '\004')
|
|
{
|
|
save_text = text;
|
|
save_len = len;
|
|
rgb = 1;
|
|
nc = 0;
|
|
}
|
|
else if (*text != '\026') /* (strip reverse too) */
|
|
{
|
|
new_str[i] = *text;
|
|
i++;
|
|
}
|
|
}
|
|
text++;
|
|
len--;
|
|
}
|
|
new_str[i] = 0;
|
|
if (new_str[0] == '\0')
|
|
return NULL;
|
|
return new_str;
|
|
}
|
|
|
|
/** Check ban version { } blocks, returns 1 if banned and 0 if not. */
|
|
int ban_version(Client *client, const char *text)
|
|
{
|
|
int len;
|
|
ConfigItem_ban *ban;
|
|
char ctcp_reply[BUFSIZE];
|
|
|
|
strlcpy(ctcp_reply, text, sizeof(ctcp_reply));
|
|
len = strlen(ctcp_reply);
|
|
if (!len)
|
|
return 0;
|
|
|
|
if (ctcp_reply[len-1] == '\1')
|
|
ctcp_reply[len-1] = '\0'; /* remove CTCP REPLY terminator (ASCII 1) */
|
|
|
|
if ((ban = find_ban(NULL, ctcp_reply, CONF_BAN_VERSION)))
|
|
{
|
|
if (find_tkl_exception(TKL_BAN_VERSION, client))
|
|
return 0; /* we are exempt */
|
|
|
|
return take_action(client, ban->action, ban->reason, BAN_VERSION_TKL_TIME, 0, NULL);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/** Can user send a message to this channel?
|
|
* @param client The client
|
|
* @param channel The channel
|
|
* @param msgtext The message to send (MAY be changed, even if user is allowed to send)
|
|
* @param errmsg The error message (will be filled in)
|
|
* @param sendtype One of SEND_TYPE_*
|
|
* @returns Returns 1 if the user is allowed to send, otherwise 0.
|
|
* (note that this behavior was reversed in UnrealIRCd versions <5.x.
|
|
*/
|
|
int _can_send_to_channel(Client *client, Channel *channel, const char **msgtext, const char **errmsg, SendType sendtype, ClientContext *clictx)
|
|
{
|
|
Membership *lp;
|
|
int member, i = 0;
|
|
Hook *h;
|
|
|
|
if (!MyUser(client))
|
|
return 1;
|
|
|
|
*errmsg = NULL;
|
|
|
|
member = IsMember(client, channel);
|
|
|
|
lp = find_membership_link(client->user->channel, channel);
|
|
|
|
/* Modules can plug in as well */
|
|
for (h = Hooks[HOOKTYPE_CAN_SEND_TO_CHANNEL]; h; h = h->next)
|
|
{
|
|
i = (*(h->func.intfunc))(client, channel, lp, msgtext, errmsg, sendtype, clictx);
|
|
if (i != HOOK_CONTINUE)
|
|
{
|
|
if (!*errmsg)
|
|
{
|
|
unreal_log(ULOG_ERROR, "main", "BUG_CAN_SEND_TO_CHANNEL_NO_ERRMSG", client,
|
|
"[BUG] Module $module did not set errmsg!!!",
|
|
log_data_string("module", h->owner->header->name));
|
|
abort();
|
|
}
|
|
break;
|
|
}
|
|
if (!*msgtext || !**msgtext)
|
|
{
|
|
if (sendtype != SEND_TYPE_TAGMSG)
|
|
return 0;
|
|
else
|
|
*msgtext = "";
|
|
}
|
|
}
|
|
|
|
if (i != HOOK_CONTINUE)
|
|
{
|
|
if (!*errmsg)
|
|
*errmsg = "You are banned";
|
|
/* Don't send message if the user was previously a member
|
|
* and isn't anymore, so if the user is KICK'ed, eg by floodprot.
|
|
*/
|
|
if (member && !IsDead(client) && !find_membership_link(client->user->channel, channel))
|
|
*errmsg = NULL;
|
|
return 0;
|
|
}
|
|
|
|
/* Now we are going to check bans */
|
|
|
|
/* ..but first: exempt ircops */
|
|
if (op_can_override("channel:override:message:ban",client,channel,NULL))
|
|
return 1;
|
|
|
|
/* If local client is banned and not +vhoaq... */
|
|
if (MyUser(client) &&
|
|
!check_channel_access_membership(lp, "vhoaq") &&
|
|
is_banned(client, channel, BANCHK_MSG, msgtext, errmsg))
|
|
{
|
|
/* Modules can set 'errmsg', otherwise we default to this: */
|
|
if (!*errmsg)
|
|
*errmsg = "You are banned";
|
|
return 0;
|
|
}
|
|
|
|
return 1;
|
|
}
|