#include "httpd.h"        /* Apache server interface */
#include "http_config.h"
#include "http_conf_globals.h"

#include <dlfcn.h> /* dlopen() stuff itself */

module dlopen_module;

static int mod_dlopen_loadfinished = 0;
static int mod_dlopen_goterrors = 0;
static struct {module *modp; char *modname;} dynamic_modules [DYNAMIC_MODULE_LIMIT];
static int dynamic_modules_dim = 0 ;

const char *mod_dlopen_load_module(cmd_parms *cmd,
			     void *dummy,
			     char *modname,
			     char *filename)
{
    module *loadedmod;
    void *dlhandle;
    char *realfilename;

    if(mod_dlopen_loadfinished) {
        /* We already loaded the modules */
        int i = -1 ;
	while (++ i < dynamic_modules_dim) {
	  if (strcmp(dynamic_modules [i].modname, modname) == 0) {
	    /* Only add modules that are not already enabled.  */
	    if (dynamic_modules [i].modp->next == NULL) {
	      /* re-add modules the regular way */
	      add_module(dynamic_modules [i].modp);
	    }
	    return NULL;
	  }
	}
	return NULL;
    }

    /* Memory leak here - how are we supposed to free it up? */
    realfilename = server_root_relative(cmd->pool, filename);

    dlhandle = dlopen(realfilename, RTLD_LAZY | RTLD_GLOBAL);
    if(dlhandle == NULL) {
	mod_dlopen_goterrors = 1;
	return pstrcat(cmd->pool, "dlopen() error in mod_dlopen: ",
		       dlerror(), " loading ", modname, NULL);
    }

    loadedmod = (module *)dlsym(dlhandle, modname);
    if(loadedmod == NULL) {
	mod_dlopen_goterrors = 1;
	return pstrcat(cmd->pool, "dlsym() error in mod_dlopen: ",
		       dlerror(), NULL);
    }

    /* Jordans paranoia (we never free that memory, anyway ...) */
    {   char *p = malloc (strlen (modname) + 1) ;
        if (p == 0) {
	  char buf [12] ;
	  sprintf (buf, "%u", sizeof (module)) ;
	  return pstrcat(cmd->pool, "dlopen() error in mod_dlopen: ",
			 "cannot dup module name (",  buf,  " bytes)", NULL);
	}
	modname = strcpy (p, modname) ;
    }

    add_module(loadedmod);
    if(loadedmod->create_server_config)
	set_module_config(cmd->server->module_config,
			  loadedmod,
			  (*loadedmod->create_server_config)(cmd->pool,
							     cmd->server));
    else
	set_module_config(cmd->server->module_config,
			  loadedmod,
			  NULL);

    if(loadedmod->create_dir_config)
	set_module_config(cmd->server->lookup_defaults,
			  loadedmod,
			  (*loadedmod->create_dir_config)(cmd->pool,
							  NULL));
    else
	set_module_config(cmd->server->lookup_defaults,
			  loadedmod,
			  NULL);

    if (dynamic_modules_dim > DYNAMIC_MODULE_LIMIT) {
        char buf [12] ;
	sprintf (buf, "%u", DYNAMIC_MODULE_LIMIT) ;
	mod_dlopen_goterrors = 1;
	return pstrcat(cmd->pool, "dlopen() error in mod_dlopen: ",
		       "more than ",  buf,  " modules loaded", NULL);
    }
    dynamic_modules [dynamic_modules_dim].modname = modname ;
    dynamic_modules [dynamic_modules_dim].modp    = loadedmod ;
    dynamic_modules_dim ++ ;
    return NULL;
}

/* These are here because dynamically loaded objects don't seem to get atoi()
   from libc :( */
int atoi(const char *numstring)
{
    return (int) strtol (numstring, (char **) NULL, 10);
}

long int atol(const char *numstring)
{
    return strtol (numstring, (char **) NULL, 10);
}

void mod_dlopen_check_dynamods(server_rec *dummy, pool *p)
{
    int i = atoi("1"); int j = atol("1");
    if(mod_dlopen_loadfinished == 1)
	return; /* We already did init in a previous incarnation */

    if(mod_dlopen_goterrors != 0) {
	fprintf(stderr, "mod_dlopen got errors loading modules");
	exit(1);
    }
    mod_dlopen_loadfinished = 1;
}

command_rec dlopen_commands[] = {
    { "LoadModule", mod_dlopen_load_module, NULL, RSRC_CONF, TAKE2,
      "a module name, and the name of a file to load it from" },
    { NULL }
};

module dlopen_module = {
    STANDARD_MODULE_STUFF,
    mod_dlopen_check_dynamods, /* Initializer */
    NULL, /* create per-dir config */
    NULL, /* merge per-dir config */
    NULL, /* server config */
    NULL, /* merge server config */
    dlopen_commands, /* command table */
    NULL, /* handlers */
    NULL, /* filename translation */
    NULL, /* check_user_id */
    NULL, /* check auth */
    NULL, /* check access */
    NULL, /* type checker */
    NULL  /* logger */
};
