/************************************************************************ * 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; }