/*
 * =================================================================
 * Filename:         operjoin.c
 * Description:      /join & /sajoin operoverride enhancement
 * Author:           AngryWolf <angrywolf@flashmail.com>
 * Documentation:    operjoin.txt (comes with the package)
 * =================================================================
 */

#include "config.h"
#include "struct.h"
#include "common.h"
#include "sys.h"
#include "numeric.h"
#include "msg.h"
#include "channel.h"
#include <time.h>
#include <sys/stat.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#ifdef _WIN32
#include <io.h>
#endif
#include <fcntl.h>
#include "h.h"
#ifdef STRIPBADWORDS
#include "badwords.h"
#endif
#ifdef _WIN32
#include "version.h"
#endif

/* ================================================================= */

#ifdef ABORT_COMPILATION
 #undef ABORT_COMPILATION
#endif
#ifdef NO_OPEROVERRIDE
 #error "*** NO_OPEROVERRIDE detected ***"
 #define ABORT_COMPILATION
#endif
#ifdef OPEROVERRIDE_VERIFY
 #error "*** OPEROVERRIDE_VERIFY detected ***"
 #define ABORT_COMPILATION
#endif
#ifdef ABORT_COMPILATION
 #error "This module is useless for you; compilation aborted"
#endif

extern void			sendto_one(aClient *to, char *pattern, ...);
extern void			sendto_realops(char *pattern, ...);
extern void			sendto_serv_butone_token(aClient *one, char *prefix, char *command, char *token, char *pattern, ...);
extern aChannel			*get_channel(aClient *, char *, int);
#ifndef HOOKTYPE_REHASH_COMPLETE
extern char			modebuf[MAXMODEPARAMS*2+1], parabuf[504];
#endif

#define IsSkoAdmin(sptr)	(IsAdmin(sptr) || IsNetAdmin(sptr) || IsSAdmin(sptr))
#define DelOverride(cmd, ovr)	if (ovr && CommandExists(cmd)) CmdoverrideDel(ovr); ovr = NULL
#define DelHook(x)		if (x) HookDel(x); x = NULL
#define Sender			(ssptr ? ssptr : sptr) /* sender of SJOIN or JOIN */
#define SenderName		(ssptr ? ssptr->name : parv[0]) /* name of sender */

static Cmdoverride		*AddOverride(char *msg, iFP cb);
static int			override_sajoin(Cmdoverride *, aClient *, aClient *, int, char *[]);
static int			override_join(Cmdoverride *, aClient *, aClient *, int, char *[]);
static int			override_cycle(Cmdoverride *, aClient *, aClient *, int, char *[]);
static int			m_myjoin(aClient *ssptr, aClient *cptr, aClient *sptr, int parc, char *parv[]);
#ifdef HOOKTYPE_REHASH_COMPLETE
static int			cb_rehash_complete();
#endif

static char			*PartFmt2 = ":%s PART %s :%s";
Cmdoverride			*OvrJoin, *OvrCycle, *OvrSajoin;
#ifdef HOOKTYPE_REHASH_COMPLETE
 #ifndef _WIN32
Hook				*HookRehashComplete;
 #endif
aCommand			*CmdPart;
#endif

#ifndef STATIC_LINKING
static ModuleInfo	*MyModInfo;
 #define MyMod		MyModInfo->handle
 #define SAVE_MODINFO	MyModInfo = modinfo;
#else
 #define MyMod		NULL
 #define SAVE_MODINFO
#endif

/* ================================================================= */

ModuleHeader MOD_HEADER(operjoin)
  = {
	"operjoin",
	"$Id: operjoin.c,v 5.1 2004/03/23 19:56:37 angrywolf Exp $",
	"Join & sajoin override",
	"3.2-b8-1",
	NULL 
    };

DLLFUNC int MOD_INIT(operjoin)(ModuleInfo *modinfo)
{
	SAVE_MODINFO

#if defined(HOOKTYPE_REHASH_COMPLETE) && !defined(_WIN32)
	HookRehashComplete = HookAddEx(modinfo->handle, HOOKTYPE_REHASH_COMPLETE, cb_rehash_complete);
#endif

        return MOD_SUCCESS;
}

DLLFUNC int MOD_LOAD(operjoin)(int module_load)
{
#ifdef HOOKTYPE_REHASH_COMPLETE
	int ret;
#endif

	OvrJoin		= AddOverride("join", override_join);
	OvrCycle	= AddOverride("cycle", override_cycle);
	OvrSajoin	= AddOverride("sajoin", override_sajoin);

#ifdef HOOKTYPE_REHASH_COMPLETE
	ret = cb_rehash_complete();
#endif

#ifdef HOOKTYPE_REHASH_COMPLETE
	if (!OvrJoin || !OvrCycle || !OvrSajoin || !ret)
#else
	if (!OvrJoin || !OvrCycle || !OvrSajoin)
#endif
		return MOD_FAILED;

	return MOD_SUCCESS;
}

DLLFUNC int MOD_UNLOAD(operjoin)(int module_unload)
{
#if defined(HOOKTYPE_REHASH_COMPLETE) && !defined(_WIN32)
	DelHook(HookRehashComplete);
#endif

	DelOverride("join", OvrJoin);
	DelOverride("cycle", OvrCycle);
	DelOverride("sajoin", OvrSajoin);

	return MOD_SUCCESS;
}

#ifdef HOOKTYPE_REHASH_COMPLETE
static int cb_rehash_complete()
{
	if (!(CmdPart = find_Command_simple("part")))
	{
		config_error("[\2operjoin\2] command PART not found");
		return 0;
	}

	return 1;
}
#endif

/* ================================================================= */

static Cmdoverride *AddOverride(char *msg, iFP cb)
{
	Cmdoverride *ovr = CmdoverrideAdd(MyMod, msg, cb);

#ifndef STATIC_LINKING
        if (ModuleGetError(MyMod) != MODERR_NOERROR || !ovr)
#else
        if (!ovr)
#endif
	{
#ifndef STATIC_LINKING
		config_error("Error replacing command %s when loading module %s: %s",
			msg, MOD_HEADER(operjoin).name, ModuleGetErrorStr(MyMod));
#else
		config_error("Error replacing command %s when loading module %s",
			msg, MOD_HEADER(operjoin).name);
#endif
		return NULL;
	}

	return ovr;
}

/* ================================================================= */

static u_int is_invited(aClient *sptr, aChannel *chptr)
{
        Link	*lp;

	for (lp = sptr->user->invited; lp; lp = lp->next)
		if (lp->value.chptr == chptr)
			return 1;

	return 0;
}

static void send_override_notice(aClient *ssptr, aClient *sptr, aChannel *chptr, char mode)
{
	if (ssptr)
	{
		/*
        	 * Complication between sendto_realops and sendto_snomask, when an IRCop
		 * uses SAJOIN that overrides a specific channel mode. Actually
		 * OperOverride messages go to EYES snomask, but SJOIN notices go to all IRCops.
		 * Someone not having snomask +e may get confused in this situation.
		 */

            	sendto_serv_butone_token(&me, me.name, MSG_GLOBOPS,
                	TOK_GLOBOPS, ":*** OperOverride -- %s (%s@%s) used SAJOIN to make %s join %s (overriding +%c).",
            		ssptr->name, ssptr->user->username, ssptr->user->realhost,
			sptr->name, chptr->chname, mode);
    		sendto_realops("*** OperOverride -- %s (%s@%s) used SAJOIN to make %s join %s (overriding +%c).",
            		ssptr->name, ssptr->user->username, ssptr->user->realhost,
			sptr->name, chptr->chname, mode);
#ifdef LOG_OVERRIDE
		ircd_log(LOG_OVERRIDE, "OVERRIDE: %s (%s@%s) used SAJOIN to make %s join %s (Overriding +%c)",
			ssptr->name, ssptr->user->username, ssptr->user->realhost,
			sptr->name, chptr->chname, mode);
#endif
	}
	else
	{
    		sendto_snomask(SNO_EYES,
            		"*** OperOverride -- %s (%s@%s) joined %s (overriding +%c).",
            		sptr->name, sptr->user->username, sptr->user->realhost,
			chptr->chname, mode);
#ifdef LOG_OVERRIDE
		ircd_log(LOG_OVERRIDE, "OVERRIDE: %s (%s@%s) joined %s (Overriding +%c)",
			sptr->name, sptr->user->username, sptr->user->realhost,
			chptr->chname, mode);
#endif
	}
}

static void operoverride_message(aClient *ssptr, aClient *cptr, aClient *sptr, aChannel *chptr, char *key)
{
	u_int over = 1;

	if (!chptr) /* SAJOIN <nick> 0 */
	{
            	sendto_serv_butone_token(&me, me.name, MSG_GLOBOPS,
            		TOK_GLOBOPS, ":%s used SAJOIN to make %s part all channels",
			ssptr->name, sptr->name);
    		sendto_realops("%s used SAJOIN to make %s part all channels",
			ssptr->name, sptr->name);
#ifdef LOG_SACMDS
		ircd_log(LOG_SACMDS, "SAJOIN: %s used SAJOIN to make %s part all channels",
			ssptr->name, sptr->name);
#endif
		return;
	}

	if (ssptr)
                sendto_one(sptr, ":%s %s %s :*** You were forced to join %s",
			me.name, IsWebTV(sptr) ? "PRIVMSG" : "NOTICE", sptr->name,
			chptr->chname);

	if (is_invited(sptr, chptr))
		over = 0;
	else
	{
#ifdef EXBTYPE_BAN
		if (is_banned(sptr, chptr, BANCHK_JOIN))
#else
		if (is_banned(cptr, sptr, chptr))
#endif
			send_override_notice(ssptr, sptr, chptr, 'b');
        	else if (*chptr->mode.key && (BadPtr(key) || strcmp(chptr->mode.key, key)))
			send_override_notice(ssptr, sptr, chptr, 'k');
        	else if (chptr->mode.mode & MODE_INVITEONLY)
			send_override_notice(ssptr, sptr, chptr, 'i');
		else if ((chptr->mode.mode & MODE_ONLYSECURE) && !(sptr->umodes & UMODE_SECURE))
			send_override_notice(ssptr, sptr, chptr, 'z');
        	else if (chptr->mode.limit && chptr->users >= chptr->mode.limit)
			send_override_notice(ssptr, sptr, chptr, 'l');
        	else if ((chptr->mode.mode & MODE_RGSTRONLY) && !IsARegNick(sptr))
			send_override_notice(ssptr, sptr, chptr, 'R');
		else
			over = 0;
	}

	if (ssptr && !over)
	{
            	sendto_serv_butone_token(&me, me.name, MSG_GLOBOPS,
            		TOK_GLOBOPS, ":%s used SAJOIN to make %s join %s",
			ssptr->name, sptr->name, chptr->chname);
    		sendto_realops("%s used SAJOIN to make %s join %s",
			ssptr->name, sptr->name, chptr->chname);
#ifdef LOG_SACMDS
		ircd_log(LOG_SACMDS, "SAJOIN: %s used SAJOIN to make %s join %s",
			ssptr->name, sptr->name, chptr->chname);
#endif
	}
}

static int oper_can_join(aClient *cptr, aClient *sptr, aChannel *chptr)
{
	Ban	*banned;

        if ((chptr->mode.mode & MODE_ADMONLY) && !IsSkoAdmin(sptr))
                return (ERR_ADMONLY);

#ifdef EXBTYPE_BAN
	banned = is_banned(sptr, chptr, BANCHK_JOIN);
#else
	banned = is_banned(cptr, sptr, chptr);
#endif

	/* Admin, Coadmin, Netadmin, and SAdmin can still walk +b in +O */
        if (!IsAdmin(sptr) && !IsCoAdmin(sptr) && !IsNetAdmin(sptr)
	    && !IsSAdmin(sptr) && banned
            && (chptr->mode.mode & MODE_OPERONLY))
                return (ERR_BANNEDFROMCHAN);

	/* Only NetAdmin/SAdmin can walk +b in +A */
	if (!IsNetAdmin(sptr) && !IsSAdmin(sptr)
	    && banned && (chptr->mode.mode & MODE_ADMONLY))
	    	return (ERR_BANNEDFROMCHAN);

        return 0;
}

/* ================================================================= */

/*
 * override_join
 *
 * Allows IRCops to join a channel by overriding channel rules if needed.
 */

static int override_join(Cmdoverride *ovr, aClient *cptr, aClient *sptr, int parc, char *parv[])
{
	/* sender must be at least a global operator with operflag +v */
	if (!MyConnect(sptr) || !IsPerson(sptr) || !IsOper(sptr) || !OPCanOverride(sptr))
		return CallCmdoverride(ovr, cptr, sptr, parc, parv);
	else
		return m_myjoin(NULL, cptr, sptr, parc, parv);
}

/*
 * override_cycle
 *
 * Uses override_join instead of m_join
 */

static int override_cycle(Cmdoverride *ovr, aClient *cptr, aClient *sptr, int parc, char *parv[])
{
	char channels[1024];

#ifdef HOOKTYPE_REHASH_COMPLETE
	if (!CmdPart)
		return CallCmdoverride(ovr, cptr, sptr, parc, parv);
#endif
	
        if (IsServer(sptr))
                return 0;

        if (parc < 2)
                return 0;
        parv[2] = "cycling";
	strncpyzt(channels, parv[1], 1020);
#ifdef HOOKTYPE_REHASH_COMPLETE
	CmdPart->func(cptr, sptr, 3, parv);
#else
        (void) m_part(cptr, sptr, 3, parv);
#endif
	parv[1] = channels;
        parv[2] = NULL;
	return override_join(ovr, cptr, sptr, 2, parv);
}

/*
 * override_sajoin
 *
 * Same as m_sajoin, but forces a user to join the given channels, even if
 * not allowed (overrides channel settings).
 */

static int override_sajoin(Cmdoverride *ovr, aClient *cptr, aClient *sptr, int parc, char *parv[])
{
        aClient *acptr;

	if (!IsPerson(sptr) || !IsOper(sptr) || (MyClient(sptr) && !OPCanOverride(sptr)))
		return CallCmdoverride(ovr, cptr, sptr, parc, parv);

        if (!IsSAdmin(sptr) && !IsULine(sptr))
        {
    		sendto_one(sptr, err_str(ERR_NOPRIVILEGES),
			me.name, parv[0]);
    		return 0;
        }

        if (parc < 3 || BadPtr(parv[2]))
        {
		sendto_one(sptr, err_str(ERR_NEEDMOREPARAMS),
			me.name, parv[0], MSG_SAJOIN);
		return 0;
        }

        if (!(acptr = find_person(parv[1], NULL)))
        {
                sendto_one(sptr, err_str(ERR_NOSUCHNICK),
			me.name, parv[0], parv[1]);
                return 0;
        }

	/*
	 * Notice messages will be sent later (merged into operoverride_message).
	 * With this we can be sure we don't send weird SJOIN notices to opers and
	 * unexpected error messages to the user being forced to join invalid
	 * channels.
	 */

        if (MyClient(acptr))
        {
                parv[0] = acptr->name; /* Why parv[1] ? */
                parv[1] = parv[2];

                m_myjoin(sptr, acptr, acptr, 2, parv);
        }
        else
                sendto_one(acptr, ":%s SAJOIN %s %s",
			parv[0], parv[1], parv[2]);

	return 0;
}


/*
 * m_myjoin
 *
 * Same as do_join, but with heavy modifications. Comments have been kept.
 *
 *     ssptr: the sender of the sajoin command (local client, can be NULL)
 *     sptr: local client joining channel(s)
 */

static int m_myjoin(aClient *ssptr, aClient *cptr, aClient *sptr, int parc, char *parv[])
{
	Membership	*lp;
	aChannel	*chptr;
	char		jbuf[BUFSIZE];
	char		*name, *key = NULL;
	char		*p = NULL, *p2 = NULL;
	int		i, flags = 0;
	Hook		*h;

	if (parc < 2 || *parv[1] == '\0')
	{
		sendto_one(Sender, err_str(ERR_NEEDMOREPARAMS),
			me.name, SenderName, "JOIN");
		return 0;
	}

	*jbuf = '\0';
	/*
	   ** Rebuild list of channels joined to be the actual result of the
	   ** JOIN.  Note that "JOIN 0" is the destructive problem.
	 */
	for (i = 0, name = strtoken(&p, parv[1], ","); name;
	    name = strtoken(&p, NULL, ","))
	{
		/* pathological case only on longest channel name.
		   ** If not dealt with here, causes desynced channel ops
		   ** since ChannelExists() doesn't see the same channel
		   ** as one being joined. cute bug. Oct 11 1997, Dianora/comstud
		   ** Copied from Dianora's "hybrid 5" ircd.
		 */

		if (strlen(name) > CHANNELLEN)	/* same thing is done in get_channel() */
			name[CHANNELLEN] = '\0';

		clean_channelname(name);
		if (check_channelmask(Sender, Sender, name) == -1)
			continue;
		if (*name == '0' && !atoi(name))
		{
			(void)strcpy(jbuf, "0");
			i = 1;
			continue;
		}
		else if (!IsChannelName(name))
		{
			sendto_one(Sender, err_str(ERR_NOSUCHCHANNEL),
				me.name, SenderName, name);
			continue;
		}
		if (*jbuf)
			(void)strlcat(jbuf, ",", sizeof jbuf);
		(void)strlncat(jbuf, name, sizeof jbuf, sizeof(jbuf) - i - 1);
		i += strlen(name) + 1;
	}
	/* This strcpy should be safe since jbuf contains the "filtered"
	 * result of parv[1] which should never be larger than the source.
	 */
	(void)strcpy(parv[1], jbuf);

	p = NULL;
	if (parv[2])
		key = strtoken(&p2, parv[2], ",");
	parv[2] = NULL;		/* for m_names call later, parv[parc] must == NULL */
	for (name = strtoken(&p, jbuf, ","); name;
	    key = (key) ? strtoken(&p2, NULL, ",") : NULL,
	    name = strtoken(&p, NULL, ","))
	{
		/*
		   ** JOIN 0 sends out a part for all channels a user
		   ** has joined.
		 */
		if (*name == '0' && !atoi(name))
		{
			while ((lp = sptr->user->channel))
			{
				chptr = lp->chptr;
				sendto_channel_butserv(chptr, sptr,
				    PartFmt2, parv[0], chptr->chname,
				    "Left all channels");
				RunHook4(HOOKTYPE_LOCAL_PART, cptr, sptr, chptr, "Left all channels");
				if (ssptr)
					operoverride_message(ssptr, cptr, sptr,
						NULL, NULL);
				remove_user_from_channel(sptr, chptr);
			}
			sendto_serv_butone_token(cptr, parv[0],
			    MSG_JOIN, TOK_JOIN, "0");
			continue;
		}

		flags = (ChannelExists(name)) ? CHFL_DEOPPED : CHFL_CHANOP;

		if (!(chptr = get_channel(sptr, name, CREATE)))
			continue;
		if ((lp = find_membership_link(sptr->user->channel, chptr)))
		{
			if (ssptr)
				sendto_one(ssptr, err_str(ERR_USERONCHANNEL), me.name, parv[0], 
					   sptr->name, chptr->chname);
			continue;
		}

		if (!ssptr)
		{
#ifdef HOOK_ALLOW
			i = HOOK_CONTINUE;
			for (h = Hooks[HOOKTYPE_PRE_LOCAL_JOIN]; h; h = h->next) 
			{
				i = (*(h->func.intfunc))(sptr,chptr,parv);
				if (i == HOOK_DENY || i == HOOK_ALLOW)
					break;
			}

			/* Denied, get out now! */
			if (i == HOOK_DENY)
			{
				/* Rejected... if we just created a new chan we should destroy it too. -- Syzop */
				if (!chptr->users)
				        sub1_from_channel(chptr);
				continue;
			}
			/* If they are allowed, don't check can_join */
			if (i != HOOK_ALLOW && 
			(i = oper_can_join(cptr, sptr, chptr)))
			{
				if (i != -1)
					sendto_one(sptr, err_str(i),
					    me.name, parv[0], name);
				continue;
			}
#else
			if ((i = oper_can_join(cptr, sptr, chptr)))
			{
				if (i != -1)
					sendto_one(sptr, err_str(i),
						me.name, parv[0], name);
				continue;
			}

			i = 0;
			for (h = Hooks[HOOKTYPE_PRE_LOCAL_JOIN]; h; h = h->next) {
				if((*(h->func.intfunc))(sptr,chptr,parv) > 0) {
					i = 1;
					break;
				}
			}

			if (i)
			{
				/* Rejected... if we just created a new chan we should destroy it too. -- Syzop */
				if (!chptr->users)
					sub1_from_channel(chptr);
				continue;
			}
#endif
		}

		operoverride_message(ssptr, cptr, sptr, chptr, key);

#ifdef LOG_OVERRIDE
		join_channel(chptr, cptr, sptr, flags);
#else
		/*
		   **  Complete user entry to the new channel (if any)
		 */
		add_user_to_channel(chptr, sptr, flags);
		/*
		   ** notify all other users on the new channel
		 */
		if (chptr->mode.mode & MODE_AUDITORIUM)
		{
			sendto_one(sptr, ":%s!%s@%s JOIN :%s",
				sptr->name, sptr->user->username,
				GetHost(sptr), chptr->chname);
			sendto_chanops_butone(NULL, chptr, ":%s!%s@%s JOIN :%s",
			    sptr->name, sptr->user->username,
			    GetHost(sptr), chptr->chname);
		}
		else
			sendto_channel_butserv(chptr, sptr,
			    ":%s JOIN :%s", parv[0], chptr->chname);
	
		sendto_serv_butone_token_opt(cptr, OPT_NOT_SJ3, parv[0], MSG_JOIN,
			    TOK_JOIN, "%s", chptr->chname);

 #ifdef JOIN_INSTEAD_OF_SJOIN_ON_REMOTEJOIN
		if (!(flags & CHFL_CHANOP))
			sendto_serv_butone_token_opt(cptr, OPT_SJ3, parv[0], MSG_JOIN,
			    TOK_JOIN, "%s", chptr->chname);
		else
		{
 #endif
			/* I _know_ that the "@%s " look a bit wierd
			   with the space and all .. but its to get around
			   a SJOIN bug --stskeeps */
			sendto_serv_butone_token_opt(cptr, OPT_SJ3|OPT_SJB64,
				me.name, MSG_SJOIN, TOK_SJOIN,
				"%B %s :%s%s ", chptr->creationtime, 
				chptr->chname, flags & CHFL_CHANOP ? "@" : "", sptr->name);
			sendto_serv_butone_token_opt(cptr, OPT_SJ3|OPT_NOT_SJB64,
				me.name, MSG_SJOIN, TOK_SJOIN,
				"%li %s :%s%s ", chptr->creationtime, 
				chptr->chname, flags & CHFL_CHANOP ? "@" : "", sptr->name);
 #ifdef JOIN_INSTEAD_OF_SJOIN_ON_REMOTEJOIN
		}
 #endif		

		/*
		   ** Make a (temporal) creationtime, if someone joins
		   ** during a net.reconnect : between remote join and
		   ** the mode with TS. --Run
		 */
		if (chptr->creationtime == 0)
		{
			chptr->creationtime = TStime();
			sendto_serv_butone_token(cptr, me.name,
				MSG_MODE, TOK_MODE, "%s + %lu",
				chptr->chname, chptr->creationtime);
		}
		del_invite(sptr, chptr);
		if (flags & CHFL_CHANOP)
			sendto_serv_butone_token_opt(cptr, OPT_NOT_SJ3, 
				me.name,
				MSG_MODE, TOK_MODE, "%s +o %s %lu",
				chptr->chname, parv[0],
				chptr->creationtime);
		if (chptr->topic)
		{
			sendto_one(sptr, rpl_str(RPL_TOPIC),
			    me.name, parv[0], name, chptr->topic);
			sendto_one(sptr,
			    rpl_str(RPL_TOPICWHOTIME), me.name,
			    parv[0], name, chptr->topic_nick,
			    chptr->topic_time);
		}
		if (chptr->users == 1 && MODES_ON_JOIN)
		{
			chptr->mode.mode = MODES_ON_JOIN;
 #ifdef NEWCHFLOODPROT
			if (iConf.modes_on_join.floodprot.per)
			{
				chptr->mode.floodprot = MyMalloc(sizeof(ChanFloodProt));
				memcpy(chptr->mode.floodprot, &iConf.modes_on_join.floodprot, sizeof(ChanFloodProt));
			}
 #else
			chptr->mode.kmode = iConf.modes_on_join.kmode;
			chptr->mode.per = iConf.modes_on_join.per;
			chptr->mode.msgs = iConf.modes_on_join.msgs;
 #endif
			*modebuf = *parabuf = 0;
			channel_modes(sptr, modebuf, parabuf, chptr);
			/* This should probably be in the SJOIN stuff */
			sendto_serv_butone_token(&me, me.name, MSG_MODE, TOK_MODE, 
				"%s %s %s %lu", chptr->chname, modebuf, parabuf, 
				chptr->creationtime);
			sendto_one(sptr, ":%s MODE %s %s %s", me.name, chptr->chname, modebuf, parabuf);
		}
		parv[1] = chptr->chname;
		(void)m_names(cptr, sptr, 2, parv);
		RunHook4(HOOKTYPE_LOCAL_JOIN, cptr, sptr, chptr, parv);

 #ifdef NEWCHFLOODPROT
                /* I'll explain this only once:
                 * 1. if channel is +f
                 * 2. local client OR synced server
                 * 3. then, increase floodcounter
                 * 4. if we reached the limit AND only if source was a local client.. do the action (+i).
                 * Nr 4 is done because otherwise you would have a noticeflood with 'joinflood detected'
                 * from all servers.
                 */
                if (chptr->mode.floodprot && (MyClient(sptr) || sptr->srvptr->serv->flags.synced) &&
                    do_chanflood(chptr->mode.floodprot, FLD_JOIN) && MyClient(sptr))
                {
                        do_chanflood_action(chptr, FLD_JOIN, "join");
                }
 #endif
#endif
	}

	return 0;
}

