/************************************************************************
* IRC - Internet Relay Chat, modules/m_message.c
*
* Copyright (C) 2000-2003 TR-IRCD Development
*
* Copyright (C) 1990 Jarkko Oikarinen and
* University of Oulu, Co Center
*
* See file AUTHORS in IRC package for additional names of
* the programmers.
*
* This program is free softwmare; 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, 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 "struct.h"
#include "common.h"
#include "sys.h"
#include "h.h"
#include "msg.h"
#include "numeric.h"
#include "channel.h"
#include "s_conf.h"
#include "chanmode.h"
#include "hook.h"
static char *token1 = TOK1_PRIVMSG;
static char *token2 = TOK1_NOTICE;
static int hookid_call_m_private = 0;
static struct Message p_msgtab[] = {
{MSG_PRIVATE, 0, MAXPARA, M_SLOW | M_IDLE_RESET | M_FLOOD_END, 0L,
m_unregistered, m_private, m_private, m_private, m_ignore}
};
static struct Message n_msgtab[] = {
{MSG_NOTICE, 0, MAXPARA, M_SLOW | M_FLOOD_END, 0L,
m_ignore, m_notice, m_notice, m_notice, m_notice}
};
#ifndef STATIC_MODULES
char *_version = "$Revision: 1.8 $";
void _modinit(void)
{
mod_add_cmd(p_msgtab);
mod_add_cmd(n_msgtab);
tok1_msgtab[(u_char) *token1].msg = p_msgtab;
tok1_msgtab[(u_char) *token2].msg = n_msgtab;
hookid_call_m_private = hook_add_event("calling m_private");
}
void _moddeinit(void)
{
mod_del_cmd(p_msgtab);
mod_del_cmd(n_msgtab);
tok1_msgtab[(u_char) *token1].msg = NULL;
tok1_msgtab[(u_char) *token2].msg = NULL;
hook_del_event("calling m_private");
}
#else
void m_message_init(void)
{
mod_add_cmd(p_msgtab);
mod_add_cmd(n_msgtab);
tok1_msgtab[(u_char) *token1].msg = p_msgtab;
tok1_msgtab[(u_char) *token2].msg = n_msgtab;
hookid_call_m_private = hook_add_event("calling m_private");
}
#endif
static int private_more(aClient *cptr, aClient *sptr, int parc, char **parv)
{
struct hook_data thisdata;
thisdata.client_p = cptr;
thisdata.source_p = sptr;
thisdata.parc = parc;
thisdata.parv = parv;
if (hook_call_event(hookid_call_m_private, &thisdata))
return thisdata.check;
return 0;
}
/*
* flood_attack_channel
* inputs - flag 0 if PRIVMSG 1 if NOTICE. RFC
* says NOTICE must not auto reply
* - pointer to source Client
* - pointer to target channel
* output - 1 if target is under flood attack
* side effects - check for flood attack on target chptr
*/
static inline int flood_attack_channel(int p_or_n, aClient *source_p, aChannel *chptr)
{
int delta;
if (IsULine(source_p) || IsServer(source_p))
return 0;
if (!IsChanFlood(chptr))
return 0;
if ((chptr->firsttime_priv_recv + chptr->mode.intime) < (timeofday + chptr->mode.intime)) {
delta = timeofday - chptr->firsttime_priv_recv;
chptr->count_priv_recv -= delta;
chptr->firsttime_priv_recv = timeofday;
if (chptr->count_priv_recv <= 0) {
chptr->count_priv_recv = 0;
chptr->flood_noticed = 0;
}
}
if ((chptr->count_priv_recv >= chptr->mode.lines)
|| chptr->flood_noticed) {
if (chptr->flood_noticed == 0) {
sendto_lev(FLOOD_LEV, "Possible Flooder %^C target: %H", source_p, chptr);
chptr->flood_noticed = 1;
/* Add a bit of penalty */
chptr->count_priv_recv += 2;
}
if (MyClient(source_p) && !p_or_n)
send_me_notice(source_p, ":*** Message to %H throttled due to flooding", chptr);
return 1;
} else
chptr->count_priv_recv++;
return 0;
}
/*
* m_message (used in m_private() and m_notice()) the general
* function to deliver MSG's between users/channels
*
* parv[0] = sender prefix
* parv[1] = receiver list
* parv[2] = message text
*
* massive cleanup * rev argv 6/91
*
*/
/* We do not need link traversal here, because users
* do not exist in the root channel, and it is already
* set to +tnL, after cleaning. -TimeMr14C
*/
static inline int m_message(aClient *cptr, aClient *sptr, int parc, char *parv[], int notice)
{
aClient *acptr;
char *s;
int i, ret, ischan;
aChannel *chptr;
char *nick, *server, *p, *cmd, *tok, *dccmsg;
tok = notice ? TOK1_NOTICE : TOK1_PRIVMSG;
cmd = notice ? MSG_NOTICE : MSG_PRIVATE;
if (parc < 2 || *parv[1] == '\0') {
send_me_numeric(sptr, ERR_NORECIPIENT, cmd);
return -1;
}
if (parc < 3 || *parv[2] == '\0') {
send_me_numeric(sptr, ERR_NOTEXTTOSEND);
return -1;
}
if (MyConnect(sptr)) {
if (ServerOpts.anti_spambot) {
if (sptr->count_join_part >= ServerOpts.max_join_leave_count)
return 0;
}
if (ServerOpts.no_messages_on_away) {
if (IsPerson(sptr) && sptr->user->away) {
send_me_numeric(sptr, ERR_ALREADYAWAY);
return -1;
}
}
parv[1] = canonize(parv[1]);
}
for (p = NULL, nick = strtoken(&p, parv[1], ","), i = 0;
nick && i < 20; nick = strtoken(&p, NULL, ",")) {
/*
* If someone is spamming via "/msg nick1,nick2,nick3,nick4 SPAM"
* (or even to channels) then subject them to flood control!
* -Taner
*/
if (i++ > 10) {
if (!IsAnOper(sptr) && !IsULine(sptr)) {
sptr->since += 4;
}
}
/*
* channel msg?
*/
ischan = IsChannelName(nick);
if (ischan && (chptr = find_channel(nick))) {
if (notice && !IsPrivileged(sptr) && !IsChanUser(sptr, chptr, CHFL_CHANOP)) {
send_me_notice(sptr, ":ONLY ChanOps can send NOTICE to channels");
return 0;
}
if (!notice)
switch (check_for_ctcp(parv[2], NULL, 0)) {
case CTCP_DCCSEND:
case CTCP_DCC:
send_me_notice(sptr,
":You may not send a DCC command to a channel (%s)", nick);
continue;
case CTCP_YES:
/* The mysterious +C channelmode! */
if (IsChanNoCTCP(chptr)) {
send_me_numeric(sptr, ERR_NOCTCPINCHAN, nick);
continue;
}
/* falls into the default case */
case CTCP_NONE:
default:
break;
}
ret = can_send(sptr, chptr, parv[2]);
if (ret) {
if (!notice)
send_me_numeric(sptr, ret, nick);
} else {
if (!flood_attack_channel(notice, sptr, chptr))
sendto_channel_butone(cptr, 0, 0, sptr, chptr, tok, ":%s", parv[2]);
}
continue;
}
/*
* nickname addressed?
*/
if (!ischan && (acptr = find_person(nick))) {
if (IsNoNonReg(acptr) && !IsRegNick(sptr) && !IsPrivileged(sptr)) {
send_me_numeric(sptr, ERR_NONONREG, acptr->name);
return 0;
}
if (!notice && MyConnect(acptr)) {
if (check_for_ctcp(parv[2], &dccmsg, 1) == CTCP_YES) {
if ((acptr != sptr) && IsNoCTCP(acptr) && !IsPrivileged(sptr)) {
send_me_numeric(sptr, ERR_NOTEXTTOSEND);
continue;
}
}
if (check_for_ctcp(parv[2], &dccmsg, 0) == CTCP_DCCSEND) {
if (check_dccsend(sptr, acptr, dccmsg))
continue;
}
}
if ((acptr != sptr) && IsNoColor(acptr) && !IsPrivileged(sptr) &&
msg_has_colors(parv[2])) {
send_me_numeric(sptr, ERR_NOTEXTTOSEND);
continue;
}
if (!is_silenced(sptr, acptr) && check_accept(sptr, acptr)) {
if (!notice && MyClient(acptr) && acptr->user && acptr->user->away)
send_me_numeric(sptr, RPL_AWAY, acptr->name, acptr->user->away);
sendto_one_person(acptr, sptr, tok, "%~C :%s", acptr, parv[2]);
}
continue;
}
if (nick[1] == '#' && nick[0] != '#') {
if (nick[0] == '@') {
if ((chptr = find_channel(nick + 1))) {
if ((ret = can_send(sptr, chptr, parv[2]) == 0))
sendto_channel_butone(cptr, CHFL_CHANOP, 0, sptr, chptr,
tok, ":%s", parv[2]);
else if (!notice)
send_me_numeric(sptr, ret, nick + 1);
}
} else if (nick[0] == '%') {
if ((chptr = find_channel(nick + 1))) {
if ((ret = can_send(sptr, chptr, parv[2]) == 0))
sendto_channel_butone(cptr, CHFL_HALFOP, 0, sptr, chptr,
tok, ":%s", parv[2]);
else if (!notice)
send_me_numeric(sptr, ret, nick + 1);
}
} else if (nick[0] == '+') {
if ((chptr = find_channel(nick + 1))) {
if ((ret = can_send(sptr, chptr, parv[2]) == 0))
sendto_channel_butone(cptr, CHFL_VOICE, 0, sptr, chptr,
tok, ":%s", parv[2]);
else if (!notice)
send_me_numeric(sptr, ret, nick + 1);
}
} else
send_me_numeric(sptr, ERR_NOSUCHNICK, nick + 1);
continue;
}
if (nick[0] == '@' && nick[1] == '+' && nick[2] == '#') {
if ((chptr = find_channel(nick + 2))) {
if ((ret = can_send(sptr, chptr, parv[2]) == 0))
sendto_channel_butone(cptr, CHFL_CHANOP | CHFL_VOICE, 0,
sptr, chptr, tok, ":%s", parv[2]);
else if (!notice)
send_me_numeric(sptr, ret, nick + 2);
} else
send_me_numeric(sptr, ERR_NOSUCHNICK, nick + 2);
continue;
}
if (nick[0] == '@' && nick[1] == '%' && nick[2] == '#') {
if ((chptr = find_channel(nick + 2))) {
if ((ret = can_send(sptr, chptr, parv[2]) == 0))
sendto_channel_butone(cptr, CHFL_CHANOP | CHFL_HALFOP, 0,
sptr, chptr, tok, ":%s", parv[2]);
else if (!notice)
send_me_numeric(sptr, ret, nick + 2);
} else
send_me_numeric(sptr, ERR_NOSUCHNICK, nick + 2);
continue;
}
if (nick[0] == '%' && nick[1] == '+' && nick[2] == '#') {
if ((chptr = find_channel(nick + 2))) {
if ((ret = can_send(sptr, chptr, parv[2]) == 0))
sendto_channel_butone(cptr, CHFL_HALFOP | CHFL_VOICE, 0,
sptr, chptr, tok, ":%s", parv[2]);
else if (!notice)
send_me_numeric(sptr, ret, nick + 2);
} else
send_me_numeric(sptr, ERR_NOSUCHNICK, nick + 2);
continue;
}
if (nick[0] == '@' && nick[1] == '%' && nick[2] == '+' && nick[3] == '#') {
if ((chptr = find_channel(nick + 3))) {
if ((ret = can_send(sptr, chptr, parv[2]) == 0))
sendto_channel_butone(cptr,
CHFL_CHANOP | CHFL_HALFOP |
CHFL_VOICE, 0, sptr, chptr, tok, ":%s", parv[2]);
else if (!notice)
send_me_numeric(sptr, ret, nick + 3);
} else
send_me_numeric(sptr, ERR_NOSUCHNICK, nick + 3);
continue;
}
if (IsAnOper(sptr)) {
/*
* the following two cases allow masks in NOTICEs
* (for OPERs only)
*
* Armin, 8Jun90 (gruner@informatik.tu-muenchen.de)
*/
if ((*nick == '$' || *nick == '#')) {
if (!(s = (char *) strrchr(nick, '.'))) {
send_me_numeric(sptr, ERR_NOTOPLEVEL, nick);
continue;
}
while (*++s)
if (*s == '.' || *s == '*' || *s == '?')
break;
if (*s == '*' || *s == '?') {
send_me_numeric(sptr, ERR_WILDTOPLEVEL, nick);
continue;
}
sendto_match_butone(IsServer(cptr) ? cptr : NULL,
nick + 1,
(*nick == '#') ? MATCH_HOST : MATCH_SERVER, sptr,
"%s :%s", nick, parv[2]);
continue;
}
}
/*
* user@server addressed?
*/
if (!ischan && (server = (char *) strchr(nick, '@'))
&& (acptr = find_server(server + 1))) {
char *p = NULL;
if (!IsMe(acptr)) {
sendto_one_server(acptr, sptr, tok, "%s :%s", nick, parv[2]);
continue;
}
/*
* Look for users which match the destination host
* (no host == wildcard) and if one and one only is found
* connected to me, deliver message!
*/
acptr = find_person(strtoken(&p, nick, "@"));
if (acptr) {
sendto_one(acptr, ":%C %s %C :%s", sptr, cmd, acptr, parv[2]);
continue;
}
}
send_me_numeric(sptr, ERR_NOSUCHNICK, nick);
}
if ((i > 20) && sptr->user)
sendto_lev(FLOOD_LEV, "User %^C tried to msg %d users", sptr, i);
return 0;
}
/*
* * m_private * parv[0] = sender prefix * parv[1] =
* receiver list * parv[2] = message text
*/
int m_private(aClient *cptr, aClient *sptr, int parc, char *parv[])
{
int t;
if (IsZombie(sptr)) {
send_me_numeric(sptr, ERR_YOUAREZOMBIE, parv[1]);
return 0;
} else {
t = private_more(cptr, sptr, parc, parv);
if (!t)
return m_message(cptr, sptr, parc, parv, 0);
}
return 0;
}
/*
* * m_notice * parv[0] = sender prefix * parv[1] = receiver list *
* parv[2] = notice text
*/
int m_notice(aClient *cptr, aClient *sptr, int parc, char *parv[])
{
int t;
if (IsZombie(sptr)) {
send_me_numeric(sptr, ERR_YOUAREZOMBIE, parv[1]);
return 0;
} else {
t = private_more(cptr, sptr, parc, parv);
if (!t)
return m_message(cptr, sptr, parc, parv, 1);
}
return 0;
}
syntax highlighted by Code2HTML, v. 0.9.1