mirror of https://github.com/pissnet/pissircd.git
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(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);
|
|
int can_send_to_user(Client *client, Client *target, const char **msgtext, const char **errmsg, SendType sendtype);
|
|
|
|
/* 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);
|
|
CommandAdd(modinfo->handle, "NOTICE", cmd_notice, 2, CMD_USER|CMD_SERVER);
|
|
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)
|
|
{
|
|
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, 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);
|
|
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(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))
|
|
{
|
|
/* 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, 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))
|
|
{
|
|
/* 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(client, recv_mtags, parc, parv, SEND_TYPE_PRIVMSG);
|
|
}
|
|
|
|
/*
|
|
** cmd_notice
|
|
** parv[1] = receiver list
|
|
** parv[2] = notice text
|
|
*/
|
|
CMD_FUNC(cmd_notice)
|
|
{
|
|
cmd_message(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(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)
|
|
{
|
|
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);
|
|
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;
|
|
}
|