/* NeoStats - IRC Statistical Services
** Copyright (c) 1999-2004 Adam Rutter, Justin Hammond
** http://www.neostats.net/
**
**  Portions Copyright (c) 2000 - 2001 ^Enigma^
**
**  This program is free software; 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 of the License, 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307
**  USA
**
** NeoStats CVS Identification
** $Id: dl.c 1907 2004-07-17 06:10:52Z Fish $
*/

/** @file dl.c 
 *  @brief module functions
 */ 

#include <dlfcn.h>
#include <stdio.h>
#include <stdlib.h>
#include "stats.h"
#include "dl.h"
#include "hash.h"
#include "config.h"
#include "log.h"
#include "dns.h"
#ifdef SQLSRV
#include "sqlsrv/rta.h"
#endif

#define MAX_CMD_LINE_LENGTH		350

/** @brief Module list
 * 
 */
Module *ModList[NUM_MODULES];

char segv_location[SEGV_LOCATION_BUFSIZE];
char segv_inmodule[SEGV_INMODULE_BUFSIZE];

int del_all_bot_cmds(ModUser* bot_ptr);

/* @brief Module hash list */
static hash_t *mh;
/* @brief Module Socket List hash */
hash_t *sockh;
/* @brief Module Timer hash list */
static hash_t *th;
/* @brief Module Bot hash list */
static hash_t *bh;
/* @brief Module Chan Bot hash list */
static hash_t *bch;

#ifdef SQLSRV

char sqlbuf[BUFSIZE];


void *display_module_name (void *tbl, char *col, char *sql, void *row) {
	Module *mod_ptr = row;
	
	strlcpy(sqlbuf, mod_ptr->info->module_name, MAX_MOD_NAME);
	return sqlbuf;	
}

void *display_module_desc (void *tbl, char *col, char *sql, void *row) {
	Module *mod_ptr = row;
	
	strlcpy(sqlbuf, mod_ptr->info->module_description, BUFSIZE);
	return sqlbuf;
}

void *display_module_version (void *tbl, char *col, char *sql, void *row) {
	Module *mod_ptr = row;
	
	if (mod_ptr->isnewstyle == 1) {
		strlcpy(sqlbuf, mod_ptr->info->module_version, BUFSIZE);
		return sqlbuf;
	} else {
		bzero(sqlbuf, BUFSIZE);
		return sqlbuf;
	}
}

void *display_module_builddate (void *tbl, char *col, char *sql, void *row) {
	Module *mod_ptr = row;
	
	if (mod_ptr->isnewstyle == 1) {
		ircsnprintf(sqlbuf, BUFSIZE, "%s - %s", mod_ptr->info->module_build_date, mod_ptr->info->module_build_time);
		return sqlbuf;
	} else {
		bzero(sqlbuf, BUFSIZE);
		return sqlbuf;
	}
}

void *display_core_info (void *tbl, char *col, char *sql, void *row) {
	ircsnprintf(sqlbuf, BUFSIZE, "%s", me.versionfull);
	return sqlbuf;	
}

COLDEF neo_modulecols[] = {
	{
		"modules",
		"name",
		RTA_STR,
		MAX_MOD_NAME,
		offsetof(struct Module, info),
		RTA_READONLY,
		display_module_name,
		NULL,
		"The name of the Module"
	},
	{
		"modules",
		"description",
		RTA_STR,
		BUFSIZE,
		offsetof(struct Module, info),
		RTA_READONLY,
		display_module_desc, 
		NULL,
		"The Module Description"
	},
	{
		"modules",
		"version",
		RTA_STR,
		BUFSIZE,
		offsetof(struct Module, info),
		RTA_READONLY,
		display_module_version,
		NULL,
		"The module version"
	},
	{
		"modules",
		"builddate",
		RTA_STR,
		BUFSIZE,
		offsetof(struct Module, info),
		RTA_READONLY,
		display_module_builddate,
		NULL,
		"The module build date"
	},
	{
		"modules",
		"coreinfo",
		RTA_STR,
		BUFSIZE,
		offsetof(struct Module, info),
		RTA_READONLY,
		display_core_info,
		NULL,
		"The NeoStats core Version"
	},
};

TBLDEF neo_modules = {
	"modules",
	NULL, 	/* for now */
	sizeof(struct Module),
	0,
	TBL_HASH,
	neo_modulecols,
	sizeof(neo_modulecols) / sizeof(COLDEF),
	"",
	"The list of Modules loaded by NeoStats"
};
#endif /* SQLSRV */


void *ns_dlsym (void *handle, const char *name)
{
#ifdef NEED_UNDERSCORE_PREFIX
	char sym[128];
	void* ret;
	ret = dlsym ((int *) handle, name);
	if (ret == NULL) {
		ircsnprintf(sym, 128, "_%s", name);
		return (dlsym ((int *) handle, sym));
	}
	return ret;
#else
	return (dlsym ((int *) handle, name));
#endif
}

void *ns_dlopen (const char *file, int mode)
{
	return (dlopen (file, mode));
}

int ns_dlclose (void *handle)
{
	return (dlclose (handle));
}

char *ns_dlerror (void)
{
	return ((char *)dlerror ());
}

/** @brief Initialise module list hashes
 *
 * For core use only, initialises module list hashes
 *
 * @param none
 * 
 * @return none
*/
int 
InitModuleHash ()
{
	SET_SEGV_LOCATION();
	mh = hash_create (NUM_MODULES, 0, 0);
	if(!mh)
		return NS_FAILURE;
	bh = hash_create (B_TABLE_SIZE, 0, 0);
	if(!bh)
		return NS_FAILURE;
	th = hash_create (T_TABLE_SIZE, 0, 0);
	if(!th)
		return NS_FAILURE;
	bch = hash_create (C_TABLE_SIZE, 0, 0);
	if(!bch)
		return NS_FAILURE;
	sockh = hash_create (me.maxsocks, 0, 0);
	if(!sockh)
		return NS_FAILURE;

#ifdef SQLSRV
        /* add the module hash to the sql library */
	neo_modules.address = mh;
	rta_add_table(&neo_modules);
#endif
	                        

	return NS_SUCCESS;
}

void finiModuleHash() {
	hash_destroy(mh);
	hash_destroy(bh);
	hash_destroy(th);
	hash_destroy(bch);
	hash_destroy(sockh);
}
#ifdef DEBUG
void verify_hashes(void)
{
	if (hash_verify (sockh) == 0) {
		nlog (LOG_CRITICAL, LOG_CORE, "Eeeek, Corruption of the socket hash");
	}
	if (hash_verify (mh) == 0) {
		nlog (LOG_CRITICAL, LOG_CORE, "Eeeek, Corruption of the Module hash");
	}
	if (hash_verify (bh) == 0) {
		nlog (LOG_CRITICAL, LOG_CORE, "Eeeek, Corruption of the Bot hash");
	}
	if (hash_verify (th) == 0) {
		nlog (LOG_CRITICAL, LOG_CORE, "Eeeek, Corruption of the Timer hash");
	}
}
#endif /* DEBUG */

/** @brief create new timer
 *
 * For core use only, creates a timer
 *
 * @param timer_name the name of new timer
 * 
 * @return pointer to new timer on success, NULL on error
*/
static ModTimer *
new_timer (char *timer_name)
{
	ModTimer *mod_tmr;
	hnode_t *tn;

	SET_SEGV_LOCATION();
	nlog (LOG_DEBUG2, LOG_CORE, "new_timer: %s", timer_name);
	mod_tmr = malloc (sizeof (ModTimer));
	strlcpy (mod_tmr->timername, timer_name, MAX_MOD_NAME);
	tn = hnode_create (mod_tmr);
	if (hash_isfull (th)) {
		nlog (LOG_WARNING, LOG_CORE, "new_timer: couldn't add new timer, hash is full!");
		return NULL;
	}
	hash_insert (th, tn, timer_name);
	return mod_tmr;
}

/** @brief find timer
 *
 * For core use only, finds a timer in the current list of timers
 *
 * @param timer_name the name of timer to find
 * 
 * @return pointer to timer if found, NULL if not found
*/
ModTimer *
findtimer (char *timer_name)
{
	hnode_t *tn;

	tn = hash_lookup (th, timer_name);
	if (tn)
		return (ModTimer *) hnode_get (tn);
	return NULL;
}

/** @brief add a timer to the timer list
 *
 * For module use. Adds a timer with the given function to the timer list
 *
 * @param func_name the name of function to register with this timer
 * @param timer_name the name of timer to register
 * @param mod_name the name of module registering the timer
 * @param interval the interval at which the timer triggers in seconds
 * 
 * @return NS_SUCCESS if added, NS_FAILURE if not 
*/
int
add_mod_timer (char *func_name, char *timer_name, char *mod_name, int interval)
{
	ModTimer *mod_tmr;

	SET_SEGV_LOCATION();
	if (ns_dlsym ((int *) get_dl_handle (mod_name), func_name) == NULL) {
		nlog (LOG_WARNING, LOG_CORE, "%s: Timer %s Function %s doesn't exist", mod_name, timer_name, func_name);
		return NS_FAILURE;
	}
	mod_tmr = new_timer (timer_name);
	if (mod_tmr) {
		mod_tmr->interval = interval;
		mod_tmr->lastrun = me.now;
		strlcpy (mod_tmr->modname, mod_name, MAX_MOD_NAME);
		mod_tmr->function = ns_dlsym ((int *) get_dl_handle (mod_name), func_name);
		nlog (LOG_DEBUG2, LOG_CORE, "add_mod_timer: Registered Module %s with timer for Function %s", mod_name, func_name);
		return NS_SUCCESS;
	}
	return NS_FAILURE;
}

/** @brief delete a timer from the timer list
 *
 * For module use. Deletes a timer with the given name from the timer list
 *
 * @param timer_name the name of timer to delete
 * 
 * @return NS_SUCCESS if deleted, NS_FAILURE if not found
*/
int
del_mod_timer (char *timer_name)
{
	ModTimer *mod_tmr;
	hnode_t *tn;

	SET_SEGV_LOCATION();
	tn = hash_lookup (th, timer_name);
	if (tn) {
		mod_tmr = hnode_get (tn);
		nlog (LOG_DEBUG2, LOG_CORE, "del_mod_timer: Unregistered Timer function %s from Module %s", timer_name, mod_tmr->modname);
		hash_delete (th, tn);
		hnode_destroy (tn);
		free (mod_tmr);
		return NS_SUCCESS;
	}
	return NS_FAILURE;
}

/** @brief delete all timers from the timer list for given module
 *
 * For core use. 
 *
 * @param 
 * 
 * @return NS_SUCCESS if deleted, NS_FAILURE if not found
*/
int
del_mod_timers (char *module_name)
{
	ModTimer *mod_tmr;
	hnode_t *modnode;
	hscan_t hscan;

	hash_scan_begin (&hscan, th);
	while ((modnode = hash_scan_next (&hscan)) != NULL) {
		mod_tmr = hnode_get (modnode);
		if (!ircstrcasecmp (mod_tmr->modname, module_name)) {
			nlog (LOG_DEBUG1, LOG_CORE, "del_mod_timers: Module %s has timer %s Registered. Deleting..", module_name, mod_tmr->timername);
			del_mod_timer (mod_tmr->timername);
		}
	}
	return NS_SUCCESS;
}

/** @brief delete a timer from the timer list
 *
 * For module use. Deletes a timer with the given name from the timer list
 *
 * @param timer_name the name of timer to delete
 * 
 * @return NS_SUCCESS if deleted, NS_FAILURE if not found
*/
int
change_mod_timer_interval (char *timer_name, int interval)
{
	ModTimer *mod_tmr;
	hnode_t *tn;

	SET_SEGV_LOCATION();
	tn = hash_lookup (th, timer_name);
	if (tn) {
		mod_tmr = hnode_get (tn);
		mod_tmr->interval = interval;
		nlog (LOG_DEBUG2, LOG_CORE, "change_mod_timer_interval: Changing timer interval for %s from Module %s", timer_name, mod_tmr->modname);
		return NS_SUCCESS;
	}
	return NS_FAILURE;
}

/** @brief list timers in use
 *
 * NeoStats command to list the current timers from IRC
 *
 * @param u pointer to user structure of the user issuing the request
 * 
 * @return none
*/
int
list_timers (User * u, char **av, int ac)
{
	ModTimer *mod_tmr = NULL;
	hscan_t ts;
	hnode_t *tn;

	SET_SEGV_LOCATION();
	prefmsg (u->nick, s_Services, "Module timer List:");
	hash_scan_begin (&ts, th);
	while ((tn = hash_scan_next (&ts)) != NULL) {
		mod_tmr = hnode_get (tn);
		prefmsg (u->nick, s_Services, "%s:--------------------------------", mod_tmr->modname);
		prefmsg (u->nick, s_Services, "Module Timer Name: %s", mod_tmr->timername);
		prefmsg (u->nick, s_Services, "Module Interval: %d", mod_tmr->interval);
		prefmsg (u->nick, s_Services, "Time till next Run: %ld", (long)(mod_tmr->interval - (me.now - mod_tmr->lastrun)));
	}
	prefmsg (u->nick, s_Services, "End of Module timer List");
	return 0;
}

/** @brief run pending module timer functions
 *
 * NeoStats command to run pending timer functions
 *
 * @param none
 * 
 * @return none
*/
void
run_mod_timers (void)
{
	ModTimer *mod_tmr = NULL;
	hscan_t ts;
	hnode_t *tn;

/* First, lets see if any modules have a function that is due to run..... */
	hash_scan_begin (&ts, th);
	while ((tn = hash_scan_next (&ts)) != NULL) {
		SET_SEGV_LOCATION();
		mod_tmr = hnode_get (tn);
		if (me.now - mod_tmr->lastrun > mod_tmr->interval) {
			strlcpy (segv_location, mod_tmr->modname, SEGV_LOCATION_BUFSIZE);
			if (setjmp (sigvbuf) == 0) {
				SET_SEGV_INMODULE(mod_tmr->modname);
				nlog(LOG_DEBUG3, LOG_CORE, "run_mod_timers: Running timer %s for module %s", mod_tmr->timername, mod_tmr->modname);
				if (mod_tmr->function () < 0) {
					nlog(LOG_DEBUG2, LOG_CORE, "run_mod_timers: Deleting Timer %s for Module %s as requested", mod_tmr->timername, mod_tmr->modname);
					hash_scan_delete(th, tn);
					hnode_destroy(tn);
					free(mod_tmr);
				} else {
					mod_tmr->lastrun = (int) me.now;
				}
				CLEAR_SEGV_INMODULE();
			} else {
				nlog (LOG_CRITICAL, LOG_CORE, "run_mod_timers: setjmp() failed, can't call module %s\n", mod_tmr->modname);
			}
		}
	}
}

/** @brief create a new socket
 *
 * For core use only, creates a new socket for a module
 *
 * @param sock_name the name of the socket to create
 * 
 * @return pointer to created socket on success, NULL on error
*/
static ModSock *
new_sock (char *sock_name)
{
	ModSock *mod_sock;
	hnode_t *sn;

	SET_SEGV_LOCATION();
	nlog (LOG_DEBUG2, LOG_CORE, "new_sock: %s", sock_name);
	mod_sock = smalloc (sizeof (ModSock));
	strlcpy (mod_sock->sockname, sock_name, MAX_MOD_NAME);
	sn = hnode_create (mod_sock);
	if (hash_isfull (sockh)) {
		nlog (LOG_CRITICAL, LOG_CORE, "new_sock: socket hash is full, can't add a new socket");
		return NULL;
	}
	hash_insert (sockh, sn, mod_sock->sockname);
	return mod_sock;
}

/** \fn @brief find socket
 *
 * For core use only, finds a socket in the current list of socket
 *
 * @param sock_name the name of socket to find
 * 
 * @return pointer to socket if found, NULL if not found
 */
ModSock *
findsock (char *sock_name)
{
	hnode_t *sn;

	sn = hash_lookup (sockh, sock_name);
	if (sn)
		return hnode_get (sn);
	return NULL;
}

/** @brief add a socket to the socket list
 *
 * For core use. Adds a socket with the given functions to the socket list
 *
 * @param readfunc the name of read function to register with this socket
 * @param writefunc the name of write function to register with this socket
 * @param errfunc the name of error function to register with this socket
 * @param sock_name the name of socket to register
 * @param socknum the socket number to register with this socket
 * @param mod_name the name of module registering the socket
 * 
 * @return pointer to socket if found, NULL if not found
*/
int
add_socket (char *readfunc, char *writefunc, char *errfunc, char *sock_name, int socknum, char *mod_name)
{
	ModSock *mod_sock;

	SET_SEGV_LOCATION();
	if (readfunc) {
		if (ns_dlsym ((int *) get_dl_handle (mod_name), readfunc) == NULL) {
			nlog (LOG_WARNING, LOG_CORE, "add_socket: read socket function doesn't exist = %s (%s)", readfunc, mod_name);
			return NS_FAILURE;
		}
	}
	if (writefunc) {
		if (ns_dlsym ((int *) get_dl_handle (mod_name), writefunc) == NULL) {
			nlog (LOG_WARNING, LOG_CORE, "add_socket: write socket function doesn't exist = %s (%s)", writefunc, mod_name);
			return NS_FAILURE;
		}
	}
	if (errfunc) {
		if (ns_dlsym ((int *) get_dl_handle (mod_name), errfunc) == NULL) {
			nlog (LOG_WARNING, LOG_CORE, "add_socket: error socket function doesn't exist = %s (%s)", errfunc, mod_name);
			return NS_FAILURE;
		}
	}
	mod_sock = new_sock (sock_name);
	mod_sock->sock_no = socknum;
	strlcpy (mod_sock->modname, mod_name, MAX_MOD_NAME);
	mod_sock->readfnc = ns_dlsym ((int *) get_dl_handle (mod_name), readfunc);
	mod_sock->writefnc = ns_dlsym ((int *) get_dl_handle (mod_name), writefunc);
	mod_sock->errfnc = ns_dlsym ((int *) get_dl_handle (mod_name), errfunc);
	mod_sock->socktype = SOCK_STANDARD;
	
	nlog (LOG_DEBUG2, LOG_CORE, "add_socket: Registered Module %s with Standard Socket functions %s", mod_name, mod_sock->sockname);
	return NS_SUCCESS;
}

/** @brief add a poll interface to the socket list
 *
 * For core use. Adds a socket with the given functions to the socket list
 *
 * @param beforepoll the name of function to call before we select
 * @param afterpoll the name of the function to call after we select
 * @param sock_name the name of socket to register
 * @param mod_name the name of module registering the socket
 * 
 * @return pointer to socket if found, NULL if not found
*/
int
add_sockpoll (char *beforepoll, char *afterpoll, char *sock_name, char *mod_name, void *data)
{
	ModSock *mod_sock;

	SET_SEGV_LOCATION();
	if (beforepoll) {
		if (ns_dlsym ((int *) get_dl_handle (mod_name), beforepoll) == NULL) {
			nlog (LOG_WARNING, LOG_CORE, "add_sockpoll: read socket function doesn't exist = %s (%s)", beforepoll, mod_name);
			return NS_FAILURE;
		}
	}
	if (afterpoll) {
		if (ns_dlsym ((int *) get_dl_handle (mod_name), afterpoll) == NULL) {
			nlog (LOG_WARNING, LOG_CORE, "add_sockpoll: write socket function doesn't exist = %s (%s)", afterpoll, mod_name);
			return NS_FAILURE;
		}
	}
	mod_sock = new_sock (sock_name);
	strlcpy (mod_sock->modname, mod_name, MAX_MOD_NAME);
	mod_sock->socktype = SOCK_POLL;
	mod_sock->beforepoll = ns_dlsym ((int *) get_dl_handle (mod_name), beforepoll);
	mod_sock->afterpoll = ns_dlsym ((int *) get_dl_handle (mod_name), afterpoll);
	mod_sock->data = data;
	nlog (LOG_DEBUG2, LOG_CORE, "add_sockpoll: Registered Module %s with Poll Socket functions %s", mod_name, mod_sock->sockname);
	return NS_SUCCESS;
}

/** @brief delete a socket from the socket list
 *
 * For module use. Deletes a socket with the given name from the socket list
 *
 * @param socket_name the name of socket to delete
 * 
 * @return NS_SUCCESS if deleted, NS_FAILURE if not found
*/
int
del_socket (char *sock_name)
{
	ModSock *mod_sock;
	hnode_t *sn;

	SET_SEGV_LOCATION();
	sn = hash_lookup (sockh, sock_name);
	if (sn) {
		mod_sock = hnode_get (sn);
		nlog (LOG_DEBUG2, LOG_CORE, "del_socket: Unregistered Socket function %s from Module %s", sock_name, mod_sock->modname);
		hash_scan_delete (sockh, sn);
		hnode_destroy (sn);
		free (mod_sock);
		return NS_SUCCESS;
	}
	return NS_FAILURE;
}

/** @brief delete a socket from the socket list
 *
 * For module use. Deletes a socket with the given name from the socket list
 *
 * @param socket_name the name of socket to delete
 * 
 * @return NS_SUCCESS if deleted, NS_FAILURE if not found
*/
int
del_sockets (char *module_name)
{
	ModSock *mod_sock;
	hnode_t *modnode;
	hscan_t hscan;

	hash_scan_begin (&hscan, sockh);
	while ((modnode = hash_scan_next (&hscan)) != NULL) {
		mod_sock = hnode_get (modnode);
		if (!ircstrcasecmp (mod_sock->modname, module_name)) {
			nlog (LOG_DEBUG1, LOG_CORE, "del_sockets: Module %s had Socket %s Registered. Deleting..", module_name, mod_sock->sockname);
			del_socket (mod_sock->sockname);
		}
	}
	return NS_SUCCESS;
}

/** @brief list sockets in use
 *
 * NeoStats command to list the current sockets from IRC
 *
 * @param u pointer to user structure of the user issuing the request
 * 
 * @return none
*/
int
list_sockets (User * u, char **av, int ac)
{
	ModSock *mod_sock = NULL;
	hscan_t ss;
	hnode_t *sn;

	SET_SEGV_LOCATION();
	prefmsg (u->nick, s_Services, "Sockets List: (%d)", (int)hash_count (sockh));
	hash_scan_begin (&ss, sockh);
	while ((sn = hash_scan_next (&ss)) != NULL) {
		mod_sock = hnode_get (sn);
		prefmsg (u->nick, s_Services, "%s:--------------------------------", mod_sock->modname);
		prefmsg (u->nick, s_Services, "Socket Name: %s", mod_sock->sockname);
		if (mod_sock->socktype == SOCK_STANDARD) {
			prefmsg (u->nick, s_Services, "Socket Number: %d", mod_sock->sock_no);
		} else {
			prefmsg (u->nick, s_Services, "Poll Interface");
		}
	}
	prefmsg (u->nick, s_Services, "End of Socket List");
	return 0;
}

/** @brief Add a bot to a channel
 *
 * @param bot string containing bot name
 * @param chan string containing channel name
 * 
 * @return none
 */
void
add_bot_to_chan (char *bot, char *chan)
{
	hnode_t *cbn;
	ModChanBot *mod_chan_bot;
	lnode_t *bmn;
	char *botname;

	cbn = hash_lookup (bch, chan);
	if (!cbn) {
		mod_chan_bot = malloc (sizeof (ModChanBot));
		strlcpy (mod_chan_bot->chan, chan, CHANLEN);
		mod_chan_bot->bots = list_create (B_TABLE_SIZE);
		cbn = hnode_create (mod_chan_bot);
		if (hash_isfull (bch)) {
			nlog (LOG_CRITICAL, LOG_CORE, "eek, bot channel hash is full");
			return;
		}
		hash_insert (bch, cbn, mod_chan_bot->chan);
	} else {
		mod_chan_bot = hnode_get (cbn);
	}
	if (list_isfull (mod_chan_bot->bots)) {
		nlog (LOG_CRITICAL, LOG_CORE, "add_bot_to_chan: bot channel list is full adding channel %s", chan);
		return;
	}
	botname = sstrdup (bot);
	bmn = lnode_create (botname);
	list_append (mod_chan_bot->bots, bmn);
	return;
}

/** @brief delete a bot from a channel
 *
 * @param bot string containing bot name
 * @param chan string containing channel name
 * 
 * @return none
 */
void
del_bot_from_chan (char *bot, char *chan)
{
	hnode_t *cbn;
	ModChanBot *mod_chan_bot;
	lnode_t *bmn;
	char *botname;

	cbn = hash_lookup (bch, chan);
	if (!cbn) {
		nlog (LOG_WARNING, LOG_CORE, "del_bot_from_chan: can't find channel %s for botchanhash", chan);
		return;
	}
	mod_chan_bot = hnode_get (cbn);
	bmn = list_find (mod_chan_bot->bots, bot, comparef);
	if (!bmn) {
		nlog (LOG_WARNING, LOG_CORE, "del_bot_from_chan: can't find bot %s in %s in botchanhash", bot, chan);
		return;
	}
	list_delete (mod_chan_bot->bots, bmn);
	botname = lnode_get(bmn);
	free (botname);
	lnode_destroy (bmn);
	if (list_isempty (mod_chan_bot->bots)) {
		/* delete the hash and list because its all over */
		hash_delete (bch, cbn);
		list_destroy (mod_chan_bot->bots);
		free (mod_chan_bot->chan);
		hnode_destroy (cbn);
	}
}

/** @brief send a message to a channel bot
 *
 * @param origin 
 * @param av (note chan string in av[0])
 * @param ac
 * 
 * @return none
 */
void
bot_chan_message (char *origin, char **av, int ac)
{
	hnode_t *cbn;
	ModChanBot *mod_chan_bot;
	lnode_t *bmn;
	ModUser *mod_usr;

	cbn = hash_lookup (bch, av[0]);
	if (!cbn) {
		/* this isn't bad, just means our bot parted the channel? */
		nlog (LOG_DEBUG1, LOG_CORE, "bot_chan_message: can't find channel %s", av[0]);
		return;
	}
	mod_chan_bot = hnode_get (cbn);
	bmn = list_first (mod_chan_bot->bots);
	while (bmn) {
		mod_usr = findbot (lnode_get (bmn));
		if (mod_usr->chanfunc) {
			nlog (LOG_DEBUG2, LOG_CORE, "bot_chan_message: running module for chanmessage %s", av[0]);
			SET_SEGV_INMODULE(mod_usr->modname);
			mod_usr->chanfunc (origin, av, ac);
			CLEAR_SEGV_INMODULE();
		}
		bmn = list_next (mod_chan_bot->bots, bmn);
	}
}

/** @brief send a message to a channel bot
 *
 * @param origin 
 * @param av (note chan string in av[0])
 * @param ac
 * 
 * @return none
 */
int
bot_message (char *origin, char **av, int ac)
{
	int ret = NS_SUCCESS;
	User *u;
	User *bot_user;
	ModUser *mod_usr;
	char** argv;
	int argc = 0;
	int i;

	if (ac < 2) {
		nlog(LOG_WARNING, LOG_CORE, "Invalid String passed to Bot_message");
		return NS_FAILURE;
	}

	/* Check command length */
	if (strnlen (av[1], MAX_CMD_LINE_LENGTH) >= MAX_CMD_LINE_LENGTH) {
		prefmsg (origin, s_Services, "command line too long!");
		notice (s_Services, "%s tried to send a very LARGE command, we told them to shove it!", origin);
		return NS_SUCCESS;
	}

	u = finduser (origin);

	if(!u) {
		return NS_SUCCESS;
	}

	if (flood (u)) {
		return NS_SUCCESS;
	}

	bot_user = finduser(av[0]);
	if (!bot_user) {
		nlog (LOG_DEBUG1, LOG_CORE, "bot_message: bot %s not found", av[0]);
		return NS_SUCCESS;
	}
	mod_usr = findbot (bot_user->nick);
	/* Check to see if any of the Modules have this nick Registered */
	if (!mod_usr) {
		nlog (LOG_DEBUG1, LOG_CORE, "bot_message: mod_usr %s not found", bot_user->nick);
		return NS_SUCCESS;
	}
	nlog (LOG_DEBUG1, LOG_CORE, "bot_message: bot %s", mod_usr->nick);

	SET_SEGV_LOCATION();
	if (setjmp (sigvbuf) == 0) {
		SET_SEGV_INMODULE(mod_usr->modname);
		if(mod_usr->function) {
			AddStringToList (&argv, bot_user->nick, &argc);
			for(i = 1; i < ac; i++) {
				AddStringToList (&argv, av[i], &argc);
			}
			mod_usr->function (u->nick, argv, argc);
			free(argv);
		} else {
#if 0
			/* Trap CTCP commands and silently drop them to avoid unknown command errors 
			 * Why bother? Well we might be able to use some of them in the future
			 * so this is mainly a test and we may want to pass some of this onto
			 * SecureServ for a quick trojan check so log attempts to give an indication 
			 * of usage.
			 */
			if (av[1][0] == '\1') {
				char* buf;
				buf = joinbuf(av, ac, 1);
				nlog (LOG_NORMAL, LOG_MOD, "%s requested CTCP %s", u->nick, buf);
				free(buf);
				return NS_SUCCESS;
			}
#endif
			if (!u) {
				nlog (LOG_WARNING, LOG_CORE, "Unable to finduser %s (%s)", u->nick, mod_usr->nick);
			} else {
				AddStringToList (&argv, bot_user->nick, &argc);
				for(i = 1; i < ac; i++) {
					AddStringToList (&argv, av[i], &argc);
				}
				if(mod_usr->botcmds) {
					ret = run_bot_cmd(mod_usr, u, argv, argc);
				} else {
					ret = NS_FAILURE;
				}
				free(argv);
			}
		}
		CLEAR_SEGV_INMODULE();
	}
	return ret;
}

/** @brief dump list of module bots and channels
 *
 * @param u
 * 
 * @return none
 */
int
list_bot_chans (User * u, char **av, int ac)
{
	hscan_t hs;
	hnode_t *hn;
	lnode_t *ln;
	ModChanBot *mod_chan_bot;

	prefmsg (u->nick, s_Services, "BotChanDump:");
	hash_scan_begin (&hs, bch);
	while ((hn = hash_scan_next (&hs)) != NULL) {
		mod_chan_bot = hnode_get (hn);
		prefmsg (u->nick, s_Services, "%s:--------------------------------", mod_chan_bot->chan);
		ln = list_first (mod_chan_bot->bots);
		while (ln) {
			prefmsg (u->nick, s_Services, "Bot Name: %s", (char *)lnode_get (ln));
			ln = list_next (mod_chan_bot->bots, ln);
		}
	}
	return 0;
}

/** @brief create a new bot
 *
 * @param bot_name string containing bot name
 * 
 * @return none
 */
static ModUser *
new_bot (char *bot_name)
{
	ModUser *mod_usr;
	hnode_t *bn;

	SET_SEGV_LOCATION();
	nlog (LOG_DEBUG2, LOG_CORE, "new_bot: %s", bot_name);
	mod_usr = malloc (sizeof (ModUser));
	strlcpy (mod_usr->nick, bot_name, MAXNICK);
	bn = hnode_create (mod_usr);
	if (hash_isfull (bh)) {
		chanalert (s_Services, "Warning ModuleBotlist is full");
		return NULL;
	}
	hash_insert (bh, bn, mod_usr->nick);
	return mod_usr;
}

/** @brief 
 *
 * @param 
 * 
 * @return
 */
ModUser *
add_mod_user (char *nick, char *mod_name)
{
	ModUser *mod_usr;
	Module *mod_ptr;
	hnode_t *mn;

	SET_SEGV_LOCATION();
	mn = hash_lookup (mh, mod_name);
	if (mn) {
		mod_ptr = hnode_get (mn);
		if(mod_ptr) {
			/* add a brand new user */
			mod_usr = new_bot (nick);
			if(mod_usr) {
				strlcpy (mod_usr->modname, mod_name, MAX_MOD_NAME);
				mod_usr->function = ns_dlsym (mod_ptr->dl_handle, "__BotMessage");
				mod_usr->chanfunc = ns_dlsym (mod_ptr->dl_handle, "__ChanMessage");
				mod_usr->botcmds = NULL;
				return mod_usr;
			}
		}
	}
	nlog (LOG_WARNING, LOG_CORE, "add_mod_user: Couldn't Add ModuleBot to List");
	return NULL;
}

/** @brief 
 *
 * @param 
 * 
 * @return
 */
ModUser *
add_neostats_mod_user (char *nick)
{
	ModUser *mod_usr;

	SET_SEGV_LOCATION();
	/* add a brand new user */
	mod_usr = new_bot (nick);
	if(mod_usr) {
#if 0
		strlcpy (mod_usr->modname, "NeoStats", MAX_MOD_NAME);
#else
		mod_usr->modname[0] = 0;
#endif
		mod_usr->function = NULL;
		mod_usr->chanfunc = NULL;
		mod_usr->botcmds = hash_create(-1, 0, 0);
		return mod_usr;
	}
	nlog (LOG_WARNING, LOG_CORE, "add_neostats_mod_user: Couldn't Add ModuleBot to List");
	return NULL;
}

/** @brief 
 *
 * @param 
 * 
 * @return
 */
ModUser *
findbot (char *bot_name)
{
	hnode_t *bn;

	SET_SEGV_LOCATION();

	bn = hash_lookup (bh, bot_name);
	if (bn) {
		return (ModUser *) hnode_get (bn);
	}
	return NULL;
}

/** @brief 
 *
 * @param 
 * 
 * @return
 */
int
del_mod_user (char *bot_name)
{
	ModUser *mod_usr;
	hnode_t *bn;

	SET_SEGV_LOCATION();
	bn = hash_lookup (bh, bot_name);
	if (bn) {
		hash_delete (bh, bn);
		mod_usr = hnode_get (bn);
		del_all_bot_cmds(mod_usr);
		hnode_destroy (bn);
		free (mod_usr);
		return NS_SUCCESS;
	}
	return NS_FAILURE;
}

/** @brief 
 *
 * @param 
 * 
 * @return
 */
int
bot_nick_change (char *oldnick, char *newnick)
{
	User *u;
	ModUser *mod_usr_new, *mod_usr;

	SET_SEGV_LOCATION();
	/* First, try to find out if the newnick is unique! */
	u = finduser (oldnick);
	if (!u) {
		nlog (LOG_WARNING, LOG_CORE, "A non-registered bot(%s) attempted to change its nick to %s", oldnick, newnick);
		return NS_FAILURE;
	}
	u = finduser (newnick);
	if (!u) {
		if ((mod_usr = findbot (oldnick)) != NULL) {
			nlog (LOG_DEBUG3, LOG_CORE, "Bot %s Changed its nick to %s", oldnick, newnick);
			mod_usr_new = new_bot (newnick);

			/* add a brand new user */ 
			strlcpy (mod_usr_new->nick, newnick, MAXNICK);
			strlcpy (mod_usr_new->modname, mod_usr->modname, MAX_MOD_NAME);
			mod_usr_new->function = mod_usr->function;

			/* Now Delete the Old bot nick */   
			del_mod_user (oldnick);
			snick_cmd (oldnick, newnick);
			return NS_SUCCESS;
		}
	}
	nlog (LOG_NOTICE, LOG_CORE, "Couldn't find Bot Nick %s in Bot list", oldnick);
	return NS_FAILURE;
}

/** @brief 
 *
 * @param 
 * 
 * @return
 */
int
list_bots (User * u, char **av, int ac)
{
	ModUser *mod_usr;
	hnode_t *bn;
	hscan_t bs;

	SET_SEGV_LOCATION();
	prefmsg (u->nick, s_Services, "Module Bot List:");
	hash_scan_begin (&bs, bh);
	while ((bn = hash_scan_next (&bs)) != NULL) {
		mod_usr = hnode_get (bn);
		if(mod_usr->modname[0] == 0) {
			prefmsg (u->nick, s_Services, "NeoStats");
			prefmsg (u->nick, s_Services, "Bots: %s", mod_usr->nick);
		} else {
			prefmsg (u->nick, s_Services, "Module: %s", mod_usr->modname);
			prefmsg (u->nick, s_Services, "Module Bots: %s", mod_usr->nick);
		}
	}
	prefmsg (u->nick, s_Services, "End of Module Bot List");
	return 0;
}

/** @brief del_bots
 *
 * 
 *
 * @return none
 */
int	del_bots (char* module_name)
{
	ModUser *mod_usr;
	hnode_t *modnode;
	hscan_t hscan;

	hash_scan_begin (&hscan, bh);
	while ((modnode = hash_scan_next (&hscan)) != NULL) {
		mod_usr = hnode_get (modnode);
		if (!ircstrcasecmp (mod_usr->modname, module_name)) {
			nlog (LOG_DEBUG1, LOG_CORE, "Module %s had bot %s Registered. Deleting..", module_name, mod_usr->nick);
			del_bot (mod_usr->nick, "Module Unloaded");
		}
	}
	return NS_SUCCESS;
}
/** @brief ModuleEvent
 *
 * 
 *
 * @return none
 */
void
ModuleEvent (char *event, char **av, int ac)
{
	Module *module_ptr;
	EventFnList *ev_list;
	hscan_t ms;
	hnode_t *mn;

	SET_SEGV_LOCATION();
	hash_scan_begin (&ms, mh);
	while ((mn = hash_scan_next (&ms)) != NULL) {
		module_ptr = hnode_get (mn);
		ev_list = module_ptr->event_list;
		if (ev_list) {
			while (ev_list->cmd_name != NULL) {
				/* This goes through each Command */
				if (!ircstrcasecmp (ev_list->cmd_name, event)) {
					nlog (LOG_DEBUG1, LOG_CORE, "Running Module %s for Command %s -> %s", module_ptr->info->module_name, event, ev_list->cmd_name);
					SET_SEGV_LOCATION();
					if (setjmp (sigvbuf) == 0) {
						SET_SEGV_INMODULE(module_ptr->info->module_name);
						ev_list->function (av, ac);
						CLEAR_SEGV_INMODULE();
					} else {
						nlog (LOG_CRITICAL, LOG_CORE, "setjmp() Failed, Can't call Module %s\n", module_ptr->info->module_name);
					}
					SET_SEGV_LOCATION();
#ifndef VALGRIND
					break;
#endif
				}
				ev_list++;
			}
		}
	}
}

/** @brief 
 *
 * 
 *
 * @return none
 */
void
ModuleFunction (int cmdptr, char *cmd, char* origin, char **av, int ac)
{
	Module *module_ptr;
	Functions *fn_list;
	hscan_t ms;
	hnode_t *mn;

	SET_SEGV_LOCATION();
	hash_scan_begin (&ms, mh);
	while ((mn = hash_scan_next (&ms)) != NULL) {
		module_ptr = hnode_get (mn);
		fn_list = module_ptr->function_list;
		if(fn_list) {
			while (fn_list->cmd_name != NULL) {
				/* This goes through each Command */
				if (fn_list->srvmsg == cmdptr) {
					if (!strcmp (fn_list->cmd_name, cmd)) {
						nlog (LOG_DEBUG1, LOG_CORE, "Running Module %s for Function %s", module_ptr->info->module_name, fn_list->cmd_name);
						SET_SEGV_LOCATION();
						if (setjmp (sigvbuf) == 0) {
							SET_SEGV_INMODULE(module_ptr->info->module_name);
							fn_list->function (origin, av, ac);
							CLEAR_SEGV_INMODULE();
						}
						SET_SEGV_LOCATION();
						break;
					}
				}
				fn_list++;
			}
		}
	}
}

/** @brief 
 *
 * 
 *
 * @return none
 */
void
ModulesVersion (const char* nick, const char *remoteserver)
{
	Module *module_ptr;
	hscan_t ms;
	hnode_t *mn;

	SET_SEGV_LOCATION();
	hash_scan_begin (&ms, mh);
	while ((mn = hash_scan_next (&ms)) != NULL) {
		module_ptr = hnode_get (mn);
		if(module_ptr->isnewstyle && module_ptr->function_list == NULL) {
			numeric(RPL_VERSION, nick,
				"Module %s version: %s %s %s",
				module_ptr->info->module_name, module_ptr->info->module_version, 
				module_ptr->info->module_build_date, module_ptr->info->module_build_time);
		}
	}
}

/** @brief 
 *
 * @param 
 * 
 * @return
 */
int
load_module (char *modfilename, User * u)
{
#ifndef HAVE_LIBDL
	const char *dl_error;
#else /* HAVE_LIBDL */
	char *dl_error;
#endif /* HAVE_LIBDL */
	void *dl_handle;
	int do_msg;
	char path[255];
	char loadmodname[255];
	char **av;
	int ac = 0;
	int i = 0;
	int isnewstyle = 1;
#ifdef OLD_MODULE_EXPORT_SUPPORT
	ModuleInfo *(*mod_get_info) () = NULL;
	Functions *(*mod_get_funcs) () = NULL;
	EventFnList *(*mod_get_events) () = NULL;
#endif /* OLD_MODULE_EXPORT_SUPPORT */
	ModuleInfo *mod_info_ptr = NULL;
	Functions *mod_funcs_ptr = NULL;
	EventFnList *event_fn_ptr = NULL;
	Module *mod_ptr = NULL;
	hnode_t *mn;
	int (*ModInit) (int modnum, int apiver);

	SET_SEGV_LOCATION();
	if (u == NULL) {
		do_msg = 0;
	} else {
		do_msg = 1;
	}
	strlcpy (loadmodname, modfilename, 255);
	strlwr (loadmodname);
	ircsnprintf (path, 255, "%s/%s.so", MOD_PATH, loadmodname);
	dl_handle = ns_dlopen (path, RTLD_NOW || RTLD_GLOBAL);
	if (!dl_handle) {
		dl_error = ns_dlerror ();
		if (do_msg) {
			prefmsg (u->nick, s_Services, "Couldn't Load Module: %s %s", dl_error, path);
		}
		nlog (LOG_WARNING, LOG_CORE, "Couldn't Load Module: %s %s", dl_error, path);
		return NS_FAILURE;
	}

	/* new system */
	mod_info_ptr = ns_dlsym (dl_handle, "__module_info");
#ifdef OLD_MODULE_EXPORT_SUPPORT
	if(mod_info_ptr == NULL) {
		/* old system */
		isnewstyle = 0;
		mod_get_info = ns_dlsym (dl_handle, "__module_get_info");
#ifndef HAVE_LIBDL
		if (mod_get_info == NULL) {
			dl_error = ns_dlerror ();
#else /* HAVE_LIBDL */
		if ((dl_error = ns_dlerror ()) != NULL) {
#endif /* HAVE_LIBDL */
			if (do_msg) {
				prefmsg (u->nick, s_Services, "Couldn't Load Module: %s %s", dl_error, path);
			}
			nlog (LOG_WARNING, LOG_CORE, "Couldn't Load Module: %s %s", dl_error, path);
			ns_dlclose (dl_handle);
			return NS_FAILURE;
		}
		mod_info_ptr = (*mod_get_info) ();
		if (mod_info_ptr == NULL) {
			ns_dlclose (dl_handle);
			nlog (LOG_WARNING, LOG_CORE, "Module has no info structure: %s", path);
			return NS_FAILURE;
		}
	}
#else /* OLD_MODULE_EXPORT_SUPPORT */
#ifndef HAVE_LIBDL
	if(mod_info_ptr == NULL) {
		dl_error = ns_dlerror ();
#else /* HAVE_LIBDL */
	if ((dl_error = ns_dlerror ()) != NULL) {
#endif /* HAVE_LIBDL */
		if (do_msg) {
			prefmsg (u->nick, s_Services, "Couldn't Load Module: %s %s", dl_error, path);
		}
		nlog (LOG_WARNING, LOG_CORE, "Couldn't Load Module: %s %s", dl_error, path);
		ns_dlclose (dl_handle);
		return NS_FAILURE;
	}
#endif /* OLD_MODULE_EXPORT_SUPPORT */

	/* new system */
	mod_funcs_ptr = ns_dlsym (dl_handle, "__module_functions");
#ifdef OLD_MODULE_EXPORT_SUPPORT
	if(mod_funcs_ptr == NULL) {
		/* old system */
		mod_get_funcs = ns_dlsym (dl_handle, "__module_get_functions");
		/* no error check here - this one isn't essential to the functioning of the module */

		if (mod_get_funcs) {
			mod_funcs_ptr = (*mod_get_funcs) ();
		}
	}
#endif /* OLD_MODULE_EXPORT_SUPPORT */

	/* new system */
	event_fn_ptr = ns_dlsym (dl_handle, "__module_events");
#ifdef OLD_MODULE_EXPORT_SUPPORT
	if(event_fn_ptr == NULL)
	{
		/* old system */
		mod_get_events = ns_dlsym (dl_handle, "__module_get_events");
		/* no error check here - this one isn't essential to the functioning of the module */

		if (mod_get_events)
			event_fn_ptr = (*mod_get_events) ();
	}
#endif

	/* Check that the Module hasn't already been loaded */
	if (hash_lookup (mh, mod_info_ptr->module_name)) {
		ns_dlclose (dl_handle);
		if (do_msg)
			prefmsg (u->nick, s_Services, "Module %s already Loaded, Can't Load 2 Copies", mod_info_ptr->module_name);
		return NS_FAILURE;
	}

	mod_ptr = (Module *) smalloc (sizeof (Module));

	mn = hnode_create (mod_ptr);
	if (hash_isfull (mh)) {
		if (do_msg) {
			chanalert (s_Services, "Module List is Full. Can't Load any more modules");
			prefmsg (u->nick, s_Services, "Module List is Full, Can't Load any more Modules");
		}
		ns_dlclose (dl_handle);
		free (mod_ptr);
		return NS_FAILURE;
	} else {
		hash_insert (mh, mn, mod_info_ptr->module_name);
	}
	nlog (LOG_DEBUG1, LOG_CORE, "Module Internal name: %s", mod_info_ptr->module_name);
	nlog (LOG_DEBUG1, LOG_CORE, "Module description: %s", mod_info_ptr->module_description);

	mod_ptr->info = mod_info_ptr;
	mod_ptr->function_list = mod_funcs_ptr;
	mod_ptr->dl_handle = dl_handle;
	mod_ptr->event_list = event_fn_ptr;
	mod_ptr->isnewstyle = isnewstyle;

	/* assign a module number to this module */
	i = 0;
	while (ModList[i] != NULL)
		i++;
	/* no need to check for overflow of NUM_MODULES, as its done above */
	ModList[i] = mod_ptr;
	nlog (LOG_DEBUG1, LOG_CORE, "Assigned %d to Module %s for ModuleNum", i, mod_ptr->info->module_name);

	/* Module side user authentication for SecureServ helpers */
	mod_ptr->mod_auth_cb = ns_dlsym ((int *) dl_handle, "__ModuleAuth");

	/* call __ModInit (replacement for library __init() call */
	ModInit = ns_dlsym ((int *) dl_handle, "__ModInit");
	if (ModInit) {
		int err;
		SET_SEGV_LOCATION();
		SET_SEGV_INMODULE(mod_ptr->info->module_name);
		err = (*ModInit) (i, API_VER);
		if (err == NS_ERR_VERSION ) {
			nlog (LOG_NORMAL, LOG_CORE, "Module %s was built with an old version of NeoStats. Please recompile %s.", mod_ptr->info->module_name, mod_ptr->info->module_name);
			unload_module(mod_ptr->info->module_name, NULL);
			return NS_FAILURE;
		}
		else if (err < 1) {
			nlog (LOG_NORMAL, LOG_CORE, "Unable to load module %s. See %s.log for further information.", mod_ptr->info->module_name, mod_ptr->info->module_name);
			unload_module(mod_ptr->info->module_name, NULL);
			return NS_FAILURE;
		}
		CLEAR_SEGV_INMODULE();
		SET_SEGV_LOCATION();
	}

	/* Let this module know we are online if we are! */
	if (me.onchan == 1) {
		while (event_fn_ptr->cmd_name != NULL) {
			if (!ircstrcasecmp (event_fn_ptr->cmd_name, EVENT_ONLINE)) {
				AddStringToList (&av, me.s->name, &ac);
				SET_SEGV_LOCATION();
				SET_SEGV_INMODULE(mod_ptr->info->module_name);
				event_fn_ptr->function (av, ac);
				CLEAR_SEGV_INMODULE();
				SET_SEGV_LOCATION();
				free (av);
				break;
			}
			event_fn_ptr++;
		}
	}
	if (do_msg) {
		prefmsg (u->nick, s_Services, "Module %s Loaded, Description: %s", mod_info_ptr->module_name, mod_info_ptr->module_description);
		globops (me.name, "%s Module Loaded", mod_info_ptr->module_name);
	}
	return NS_SUCCESS;
}

/** @brief 
 *
 * @param 
 * 
 * @return
 */
int
get_dl_handle (char *mod_name)
{
	Module *mod_ptr;
	hnode_t *mn;

	mn = hash_lookup (mh, mod_name);
	if (mn) {
		mod_ptr = hnode_get (mn);
		return (int) mod_ptr->dl_handle;
	}
	return 0;
}

/** @brief 
 *
 * @param 
 * 
 * @return
 */
int
get_mod_num (char *mod_name)
{
	int i;

	for (i = 0; i < NUM_MODULES; i++) {
		if (ModList[i] != NULL) {
			if (!ircstrcasecmp (ModList[i]->info->module_name, mod_name)) {
				return i;
			}
		}
	}
	/* if we get here, it wasn't found */
	nlog (LOG_DEBUG1, LOG_CORE, "get_mod_num: can't find %s in module number list", mod_name);
	return NS_FAILURE;
}

/** @brief 
 *
 * @param 
 * 
 * @return
 */
Module *
get_mod_ptr (char *mod_name)
{
	int i;

	for (i = 0; i < NUM_MODULES; i++) {
		if (ModList[i] != NULL) {
			if (!ircstrcasecmp (ModList[i]->info->module_name, mod_name)) {
				return ModList[i];
			}
		}
	}
	/* if we get here, it wasn't found */
	nlog (LOG_DEBUG1, LOG_CORE, "get_mod_ptr: can't find %s in module number list", mod_name);
	return NULL;
}

/** @brief 
 *
 * @param 
 * 
 * @return
 */
int
list_modules (User * u, char **av, int ac)
{
	Module *mod_ptr = NULL;
	hnode_t *mn;
	hscan_t hs;

	SET_SEGV_LOCATION();
	hash_scan_begin (&hs, mh);
	while ((mn = hash_scan_next (&hs)) != NULL) {
		mod_ptr = hnode_get (mn);
		prefmsg (u->nick, s_Services, "Module: %s (%s)", mod_ptr->info->module_name, mod_ptr->info->module_version);
		prefmsg (u->nick, s_Services, "Module Description: %s", mod_ptr->info->module_description);
		prefmsg (u->nick, s_Services, "Module Number: %d", get_mod_num (mod_ptr->info->module_name));
	}
	prefmsg (u->nick, s_Services, "End of Module List");
	return 0;
}

/** @brief 
 *
 * @param 
 * 
 * @return
 */
int
unload_module (char *module_name, User * u)
{
	Module *mod_ptr;
	hnode_t *modnode;
	int i;
	void (*ModFini) ();

	SET_SEGV_LOCATION();
	/* Check to see if this Module is loaded....  */
	modnode = hash_lookup (mh, module_name);
	if (modnode) {
		chanalert (s_Services, "Unloading Module %s", module_name);
	} else {
		if (u) {
			prefmsg (u->nick, s_Services, "Couldn't Find Module %s Loaded, Try /msg %s modlist", module_name, s_Services);
			chanalert (s_Services, "%s tried to Unload %s but its not loaded", u->nick, module_name);
		}
		return NS_FAILURE;
	}

	/* canx any DNS queries running */
	canx_dns(module_name);
	/* Check to see if this Module has any timers registered....  */
	del_mod_timers (module_name);

	/* check if the module had a socket registered... */
	del_sockets (module_name);

	/* Remove module....  */
	modnode = hash_lookup (mh, module_name);
	if (modnode) {
		nlog (LOG_DEBUG1, LOG_CORE, "Deleting Module %s from ModHash", module_name);
		globops (me.name, "%s Module Unloaded", module_name);

		i = get_mod_num (module_name);

		mod_ptr = hnode_get (modnode);

		/* Remove from the module hash so we dont call events for this module during hte signoff*/
		hash_delete (mh, modnode);

		
		/* call __ModFini (replacement for library __fini() call */
		ModFini = ns_dlsym ((int *) mod_ptr->dl_handle, "__ModFini");
		if (ModFini) {
			SET_SEGV_INMODULE(module_name);
			(*ModFini) ();
			CLEAR_SEGV_INMODULE();
		}

		/* now, see if this Module has any bots with it 
		 * we delete the modules *after* we call ModFini, so the bot 
		 * can still send messages generated from ModFini calls */
		del_bots(module_name);

		hnode_destroy (modnode);
		/* Close module */
		SET_SEGV_INMODULE(module_name);
#ifndef VALGRIND
		ns_dlclose (mod_ptr->dl_handle);
#endif
		CLEAR_SEGV_INMODULE();

		if (i >= 0) {
			nlog (LOG_DEBUG1, LOG_CORE, "Freeing %d from Module Numbers", i);
			/* free the module number */
			ModList[i] = NULL;
		}
		free (mod_ptr);
		return NS_SUCCESS;
	}
	return NS_FAILURE;
}

/** @brief unload all loaded modules
 *
 * For core use only, unloads all loaded modules
 *
 * @param user pointer to user unloading modules or NULL if system unload
 * 
 * @return none
*/
void unload_modules(void)
{
	Module *mod_ptr;
	hscan_t ms;
	hnode_t *mn;
	/* Unload the Modules */
	hash_scan_begin (&ms, mh);
	while ((mn = hash_scan_next (&ms)) != NULL) {
		mod_ptr = hnode_get (mn);
		unload_module (mod_ptr->info->module_name, NULL);
	}
}
