
/*
 * Routines within this files are Copyright Colten Edwards 1996
 * Aka panasync on irc.
 * Thanks to Shiek and Flier for helpful hints and suggestions. As well 
 * as code in some cases.
 */

#include "irc.h"
#include "ircaux.h"
#include "list.h"
#include "output.h"
#include "misc.h"
#include "module.h"
#include "struct.h"
#include "vars.h"

#ifdef WANT_DLL

#ifdef NO_DLFCN_H
#   include "../include/dlfcn.h"
#else
#if defined(__osf1__)
#include <loader.h>
#elif defined(HPUX)
#include <dl.h>
#else
#   include <dlfcn.h>
#endif
#endif

#ifndef RTLD_NOW
#   define RTLD_NOW 1
#endif

#ifndef RTLD_GLOBAL
#   define RTLD_GLOBAL 0
#endif


Packages *install_pack = NULL;
IrcVariableDll *dll_variable = NULL;



char *get_dllstring_var(char *typestr)
{
IrcVariableDll *dll = NULL;
	if (typestr)
		dll = (IrcVariableDll *) find_in_list((List **)&dll_variable, typestr, 0);
	return (dll?dll->string:NULL);
}


int get_dllint_var(char *typestr)
{
IrcVariableDll *dll = NULL;
	if (typestr)
		dll = (IrcVariableDll *) find_in_list((List **)&dll_variable, typestr, 0);
	return (dll?dll->integer:-1);
}

void set_dllstring_var(char *typestr, char *string)
{
	if (typestr)
	{
		IrcVariableDll *dll = NULL;
		if (typestr)
			dll = (IrcVariableDll *) find_in_list((List **)&dll_variable, typestr, 0);
		if (!dll)
			return;
		if (string)
			malloc_strcpy(&dll->string, string);
		else
			new_free(&dll->string);
	}
}

void set_dllint_var(char *typestr, unsigned int value)
{
	if (typestr)
	{
		IrcVariableDll *dll = NULL;
		if (typestr)
			dll = (IrcVariableDll *) find_in_list((List **)&dll_variable, typestr, 0);
		if (!dll)
			return;
		dll->integer = value;
	}
}

BUILT_IN_COMMAND(dll_load)
{
#if defined(__osf__)  /*	OSF /1 1.0/1.1/1.2 and related systems */
ldr_module_t handle;
#elif defined(HPUX)  /* 	HP machines */
	shl_t handle;
#else		     /*		linux SunOS AIX etc */
void *handle = NULL;
#endif
    
char *filename = NULL;
Irc_PackageInitProc *proc1Ptr;
Irc_PackageVersionProc *proc2Ptr;
char *f, *p, *procname = NULL;
int code = 0;

	if (command)
	{
		if (install_pack)
		{
			Packages *pkg = install_pack;
			List *pk;
			for ( ; pkg; pkg = pkg->next)
			{
				put_it("DLL [%s%s%s] installed", pkg->name, pkg->version?" ":"", pkg->version?pkg->version:"");

			}
			for (pk = (List *)dll_commands; pk; pk = pk->next)
				put_it("\t%10s\t%s", "Command", pk->name);
			for (pk = (List *)dll_functions; pk; pk = pk->next)
				put_it("\t%10s\t%s", "Alias", pk->name);
			for (pk = (List *)numeric_dll; pk; pk = pk->next)
				put_it("\t%10s\t%s", "Hook", pk->name);
			for (pk = (List *)dll_ctcp; pk; pk = pk->next)
				put_it("\t%10s\t%s", "Ctcp", pk->name);
			for (pk = (List *)dll_variable; pk; pk = pk->next)
				put_it("\t%10s\t%s", "Variable", pk->name);		
		}
		else
			bitchsay("No modules loaded");
		return;
	}
	if (!args || !*args)
	{
		userage("LoadDll", helparg);
		return;
	}

	filename = next_arg(args, &args);
	f = expand_twiddle(filename);
	
	if ((p = strrchr(filename, '/')))	
		p++;
	else
	{
		new_free(&f);
		if (!(p = path_search(filename, get_string_var(LOAD_PATH_VAR))))
			p = filename;
		f = expand_twiddle(p);
		p = filename;
	}

	procname  = m_strdup(p);
	if ((p = strchr(procname, '.')))
		*p = 0;

	p = procname;
	*p = toupper(*p);
	p++;
	while (p && *p)
	{
		*p = tolower(*p);
		p++;
	}

	if (!procname || find_in_list((List **)&install_pack, procname, 0))
	{
		if (procname)
			bitchsay("Module [%s] Already installed", procname);
		new_free(&f);
		new_free(&procname);
		return;
	}
	malloc_strcat(&procname, "_Init");

#if defined(__osf__)
	if ((handle = (Tcl_PackageInitProc *) load(f, LDR_NOFLAGS)) == LDR_NULL_MODULE)
	{
		new_free(&procname);
		new_free(&f);
		return;
	}       
#elif defined(HPUX)
	if ((handle = shl_load(f, BIND_IMMEDIATE, 0L)) == NULL)
	{
		new_free(&procname);
		new_free(&f);
		return;
	}	
#else
	handle = dlopen(f, RTLD_NOW | RTLD_GLOBAL);
	if (handle == NULL)
	{
		new_free(&procname);
		new_free(&f);
		bitchsay("couldn't load file: %s %s", filename, dlerror());
		return;
	}
#endif

#if defined(__osf__)
	/* OSF1 */
	*proc1Ptr = ldr_lookup_package(pkg, procname);
	code = (proc1Ptr)(&dll_commands);
#elif defined(HPUX)
	/* HPUX */
	if (shl_findsym(&handle, procname, (short) TYPE_PROCEDURE, (void *) proc1Ptr))
		code = (proc1Ptr)(&dll_commands);

#else
	if (!(proc1Ptr = (Irc_PackageInitProc *) dlsym(handle, (char *) procname)))
		bitchsay("UnSuccessful module load");
	else
		code = (proc1Ptr)(&dll_commands);
#endif
	if (!code && proc1Ptr)
	{
		Packages *new;
		new = (Packages *) new_malloc(sizeof(Packages));
		new->name = m_strdup(procname);
		new->handle = handle;

		if ((p = strrchr(new->name, '_')))
			*p = 0;
		p = m_sprintf("%s_Version", new->name);
		if ((proc2Ptr = (Irc_PackageVersionProc *) dlsym(handle, p)))
			new->version = m_strdup(((proc2Ptr)(&dll_commands)));

		if ((p = strrchr(new->name, '_')))
			*p = 0;
		p = m_sprintf("%s_Cleanup", new->name);
		if ((proc1Ptr = (Irc_PackageInitProc *) dlsym(handle, p)))
			new->cleanup = proc1Ptr;

		new_free(&p);
		add_to_list((List **)&install_pack, (List *)new);
	}
	else if (code)
		bitchsay("Error initiliziing module [%s]", procname);
	new_free(&procname);
	new_free(&f);
}

Packages *find_module(char *name)
{
Packages *new = NULL;
	if (name) 
		new = (Packages *)find_in_list((List **)&install_pack, name, 0);
	return new;
}

int add_module_proc(int mod_type, char *modname, char *procname, char *desc, int id, int flag, void *func1, void *func2)
{
	switch(mod_type)
	{
		case COMMAND_PROC:
		{
			IrcCommandDll *new;
			new = (IrcCommandDll *) new_malloc(sizeof(IrcCommandDll));
			new->name = m_strdup(procname);
			new->server_func = m_strdup(desc);
			new->func = func1;
			new->module = m_strdup(modname);
			add_to_list((List **)&dll_commands, (List *)new);
			break;
		}
		case ALIAS_PROC:
		{
			BuiltInDllFunctions *new = NULL;
			new = (BuiltInDllFunctions *) new_malloc(sizeof(BuiltInDllFunctions));
			new->name = m_strdup(procname);
			new->func = func1;
			new->module = m_strdup(modname);
			add_to_list((List **)&dll_functions, (List *)new);
			break;
		}
		case CTCP_PROC:
		{
			CtcpEntryDll *new = NULL;
			new = (CtcpEntryDll *) new_malloc(sizeof(CtcpEntryDll));
			new->name = m_strdup(procname);
			new->desc = m_strdup(desc);
			new->id = id;
			new->flag = flag;
			new->func = func1;
			new->repl = func2;
			new->module = m_strdup(modname);
			add_to_list((List **)&dll_ctcp, (List *)new);
			break;
		}
		case VAR_PROC:
		{
			IrcVariableDll *new = NULL;
			new = (IrcVariableDll *) new_malloc(sizeof(IrcVariableDll));
			new->type = id;
			new->integer = flag;
			new->name = m_strdup(procname);
			new->string = m_strdup(desc);
			new->module = m_strdup(modname);
			new->func = func1;
			add_to_list((List **)&dll_variable, (List *)new);
			break;
		}
		case RAW_PROC:
		{
			break;
		}
		case HOOK_PROC:
		{
			NumericFunction *new = NULL;
			new = (NumericFunction *) new_malloc(sizeof(NumericFunction));
			new->number = id;
			new->name = m_sprintf("%3.3u", id);
			new->func = func1;
			new->module = m_strdup(modname);
			add_to_list((List **)&numeric_dll, (List *)new);
			break;
		}
	}
	return 0;
}

#if 0
int remove_module_proc(int mod_type, char *modname, char *procname, char *desc)
{
	switch(mod_type)
	{
		case COMMAND_PROC:
		{
			IrcCommandDll *ptr, *last;
			for (ptr = last = dll_commands; ptr;)
			{
				ptr = last->next;
				if (!strcmp(last->module, modname))
				{
					new_free(&last->name);
					new_free(&last->module);
					new_free(&last->server_func);
					new_free(&last->result);
					new_free((char **)&last);
				}
				last = ptr;
			}
			break;
		}
		case ALIAS_PROC:
		{
			BuiltInDllFunctions *ptr, *last;
			for (ptr = last = dll_functions; ptr;)
			{
				ptr = last->next;
				if (!strcmp(last->module, modname))
				{
					new_free(&last->name);
					new_free(&last->module);
					new_free((char **)&last);
				}
				last = ptr;
			}
			break;
		}
		case CTCP_PROC:
		{
			CtcpEntryDll *ptr, *last;
			for (ptr = last = dll_ctcp; ptr;)
			{
				ptr = last->next;
				if (!strcmp(last->module, modname))
				{
					new_free(&last->name);
					new_free(&last->module);
					new_free(&last->desc);
					new_free((char **)&last);
				}
				last = ptr;
			}
			break;
		}
		case VAR_PROC:
		{
			IrcVariableDll *ptr, *last;
			for (ptr = last = dll_variable; ptr;)
			{
				ptr = last->next;
				if (!strcmp(last->module, modname))
				{
					new_free(&last->name);
					new_free(&last->module);
					new_free(&last->string);
					new_free((char **)&last);
				}
				last = ptr;
			}
			break;
		}
		case RAW_PROC:
		{
			break;
		}
		case HOOK_PROC:
		{
			NumericFunction *ptr, *last;
			for (ptr = last = numeric_dll; ptr;)
			{
				ptr = last->next;
				if (!strcmp(last->module, modname))
				{
					new_free(&last->name);
					new_free(&last->module);
					new_free((char **)&last);
				}
				last = ptr;
			}
			break;
		}
	}
	return 0;
}

int remove_package(char *name)
{
Packages *new = NULL;
	if ((new = (Packages *) remove_from_list((List **)&install_pack, name)))
	{
		dlclose(new->handle);
		new_free(&new->name);
		new_free(&new->version);
		new_free((char **)&new);
		return 1;
	}
	return  0;
}
#endif

BUILT_IN_COMMAND(unload_dll)
{
#if 0
Packages *new = NULL;
int success = 0;
char *name;
	name = next_arg(args, &args);
	if (name && (new = find_module(name)))
	{
		if (new->cleanup)
			success = (new->cleanup)(&dll_commands);
		else
		{
			remove_module_proc(COMMAND_PROC, name, NULL, NULL);
			remove_module_proc(ALIAS_PROC, name, NULL, NULL);
			remove_module_proc(CTCP_PROC, name, NULL, NULL);
			remove_module_proc(VAR_PROC, name, NULL, NULL);
			remove_module_proc(HOOK_PROC, name, NULL, NULL);
			remove_module_proc(RAW_PROC, name, NULL, NULL);
		}
		remove_package(name);
	} else
		put_it("No such module loaded");
#endif
}

#endif

