/*
 * =================================================================
 * Filename:          courtroom_lite.c
 * Description:       Courtroom channel mode
 * Author:            AngryWolf <angrywolf@flashmail.com>
 * Documentation:     courtroom_lite.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

#ifndef EXTCMODE
 #error "This module requires extended channel modes to be enabled."
 #error "See the EXTCMODE macro in include/config.h for more information."
 #error "Compilation aborted."
#endif

typedef struct _chanlist ChanList;

struct _chanlist
{
	ChanList		*prev, *next;
	aClient			*cptr;
	char			*channel;
};

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, ...);

#define FLAG_COURTROOM		'U'
#define IsCourtRoom(x)		((x)->mode.extmode & MODE_COURTROOM)
#define SetCourtRoom(x)		(x)->mode.extmode |= MODE_COURTROOM
#define IsSkoAdmin(sptr)	(IsAdmin(sptr) || IsNetAdmin(sptr) || IsSAdmin(sptr) || IsCoAdmin(sptr))
#define IsParam(x)		(parc > (x) && !BadPtr(parv[(x)]))
#define IsNotParam(x)		(parc <= (x) || BadPtr(parv[(x)]))
#define DelCmode(x)		if (x) CmodeDel(x); x = NULL
#define DelHook(x)		if (x) HookDel(x); x = NULL
#define DelOverride(cmd, ovr)	if (ovr && CommandExists(cmd)) CmdoverrideDel(ovr); ovr = NULL

DLLFUNC int			MOD_UNLOAD(courtroom_lite)(int module_unload);
static Cmode			*AddCmode(Module *module, CmodeInfo *req, Cmode_t *mode);
static Cmdoverride		*AddOverride(char *msg, iFP cb);
static int			ModeCR_is_ok(aClient *, aChannel *, char *, int, int);
static int			ovr_part(Cmdoverride *, aClient *, aClient *, int, char *[]);
static int			cb_rehash();
static int			cb_rehash_complete();
static int			cb_part(aClient *, aClient *, aChannel *, char *);
static inline ChanList		*add_rejoin(aClient *sptr, char *channel);
static inline void		del_rejoin(ChanList *r);

static unsigned			rejoin_ok = 0;
static Cmode_t			MODE_COURTROOM = 0L;
static Cmode			*ModeCourtRoom = NULL;
static Cmdoverride		*OvrPart = NULL;
static Hook			*HookRehashDone = NULL;
static Hook			*HookPart = NULL;
static Hook			*HookConfRehash = NULL;
static ChanList			*Rejoins = NULL;
static u_short			module_loaded = 0;

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

// =================================================================

ModuleHeader MOD_HEADER(courtroom_lite)
  = {
	"courtroom_lite",
	"$Id: courtroom_lite.c,v 1.4 2004/07/02 20:58:28 angrywolf Exp $",
	"Channel mode +U (courtrooms)",
	"3.2-b8-1",
	NULL 
    };

DLLFUNC int MOD_TEST(courtroom_lite)(ModuleInfo *modinfo)
{
	CmodeInfo ModeCR;

	memset(&ModeCR, 0, sizeof ModeCR);

	ModeCR.paracount	= 0;
	ModeCR.is_ok		= ModeCR_is_ok;
	ModeCR.flag		= FLAG_COURTROOM;
	ModeCourtRoom		= AddCmode(modinfo->handle, &ModeCR,
					&MODE_COURTROOM);

	if (!ModeCourtRoom)
	{
		MOD_UNLOAD(courtroom_lite)(0);
		return MOD_FAILED;
	}

	return MOD_SUCCESS;
}

DLLFUNC int MOD_INIT(courtroom_lite)(ModuleInfo *modinfo)
{
	SAVE_MODINFO
#ifndef STATIC_LINKING
	ModuleSetOptions(modinfo->handle, MOD_OPT_PERM);
#endif
	rejoin_ok	= 0;
	Rejoins		= NULL;

	HookConfRehash	= HookAddEx(modinfo->handle, HOOKTYPE_REHASH,
				cb_rehash);
	HookRehashDone	= HookAddEx(modinfo->handle, HOOKTYPE_REHASH_COMPLETE,
				cb_rehash_complete);
	HookPart	= HookAddEx(modinfo->handle, HOOKTYPE_LOCAL_PART,
				cb_part);

	return MOD_SUCCESS;
}

DLLFUNC int MOD_LOAD(courtroom_lite)(int module_load)
{
	cb_rehash_complete();

	return MOD_SUCCESS;
}

DLLFUNC int MOD_UNLOAD(courtroom_lite)(int module_unload)
{
	ChanList		*r;
	ListStruct		*next;

	DelCmode(ModeCourtRoom);
	DelHook(HookPart);
	DelHook(HookRehashDone);
	DelHook(HookConfRehash);

	cb_rehash();

	for (r = Rejoins; r; r = (ChanList *) next)
	{
		next = (ListStruct *) r->next;
		del_rejoin(r);
	}

	return MOD_SUCCESS;
}

static int cb_rehash()
{
	module_loaded = 0;
	DelOverride("part", OvrPart);

	return 0;
}

static int cb_rehash_complete()
{
	if (!module_loaded)
	{
		OvrPart = AddOverride("part", ovr_part);
		module_loaded = 1;
	}

	return 0;
}

// =================================================================

static Cmode *AddCmode(Module *module, CmodeInfo *req, Cmode_t *mode)
{
	Cmode *cmode;

	*mode = 0;
	cmode = CmodeAdd(module, *req, mode);

#ifndef STATIC_LINKING
        if (ModuleGetError(module) != MODERR_NOERROR || !cmode)
#else
        if (!cmode)
#endif
	{
#ifndef STATIC_LINKING
		config_error("Error adding channel mode +%c when loading module %s: %s",
			req->flag, MOD_HEADER(courtroom_lite).name, ModuleGetErrorStr(module));
#else
		config_error("Error adding channel mode +%c when loading module %s",
			req->flag, MOD_HEADER(courtroom_lite).name);
#endif
		return NULL;
	}

	return cmode;
}

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(courtroom_lite).name, ModuleGetErrorStr(MyMod));
#else
		config_error("Error replacing command %s when loading module %s",
			msg, MOD_HEADER(courtroom_lite).name);
#endif
		return NULL;
	}

	return ovr;
}

// =================================================================

static inline ChanList *add_rejoin(aClient *sptr, char *channel)
{
	ChanList *r;

	r = (ChanList *) MyMallocEx(sizeof(ChanList));
	r->cptr = sptr;
	r->channel = strdup(channel);
	AddListItem(r, Rejoins);

	return r;
}

static inline void del_rejoin(ChanList *r)
{
	DelListItem(r, Rejoins);
	MyFree(r->channel);
	MyFree(r);
}

// =================================================================

static int ModeCR_is_ok(aClient *sptr, aChannel *chptr, char *para, int type,
                        int what)
{
	if ((type == EXCHK_ACCESS) || (type == EXCHK_ACCESS_ERR))
	{
		if (IsPerson(sptr) && !IsULine(sptr))
		{
			if (!IsSkoAdmin(sptr))
			{
				if (type == EXCHK_ACCESS_ERR)
					sendnotice(sptr,
						"*** Courtroom mode (+%c) can "
						"only be set by Administrators.",
						FLAG_COURTROOM);
				return EX_ALWAYS_DENY;
			}
		}

		return EX_ALLOW;
	}

	return 0;
}

static void do_rejoins()
{
	ChanList	*r;
	ListStruct	*next;
	int		flags;
	aChannel	*chptr;

	for (r = Rejoins; r; r = (ChanList *) next)
	{
		next = (ListStruct *) r->next;
		flags = (ChannelExists(r->channel)) ? CHFL_DEOPPED : CHFL_CHANOP;
		chptr = get_channel(r->cptr, r->channel, CREATE);

		sendnotice(r->cptr, "*** You were forced to join %s "
			"(you are not allowed to leave courtrooms)",
			chptr->chname);
		join_channel(chptr, r->cptr, r->cptr, flags);
		if (!IsCourtRoom(chptr))
		{
			SetCourtRoom(chptr);
			sendto_serv_butone(&me, ":%s MODE %s +%c 0",
				me.name, chptr->chname, FLAG_COURTROOM);
			sendto_channel_butserv(chptr, &me,
				":%s MODE %s +%c", me.name, chptr->chname,
				FLAG_COURTROOM);
		}
		del_rejoin(r);
	}
}

// =================================================================

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

	rejoin_ok = (IsPerson(sptr) && !IsULine(sptr) && !IsAnOper(sptr));
	ret = CallCmdoverride(ovr, cptr, sptr, parc, parv);

	if (rejoin_ok)
		do_rejoins();

	rejoin_ok = 0;
	return ret;
}

static int cb_part(aClient *cptr, aClient *sptr, aChannel *chptr, char *comment)
{
	if (rejoin_ok && IsCourtRoom(chptr))
		add_rejoin(sptr, chptr->chname);

	return 0;
}
