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