#include "first.h"

#include "plugins.h"
#include "plugin.h"
#include "base.h"
#include "array.h"
#include "log.h"

#include <string.h>
#include <stdlib.h>

#ifdef HAVE_VALGRIND_VALGRIND_H
# include <valgrind/valgrind.h>
#endif

#ifndef LIGHTTPD_STATIC
#ifdef HAVE_DLFCN_H
#include <dlfcn.h>
#endif
#endif
/*
 *
 * if you change this enum to add a new callback, be sure
 * - that PLUGIN_FUNC_SIZEOF is the last entry
 * - that you add:
 *   1. PLUGIN_CALL_... as callback-dispatcher
 *   2. count and assignment in plugins_call_init()
 *
 */

typedef enum {
	PLUGIN_FUNC_HANDLE_URI_CLEAN,
	PLUGIN_FUNC_HANDLE_DOCROOT,
	PLUGIN_FUNC_HANDLE_PHYSICAL,
	PLUGIN_FUNC_HANDLE_SUBREQUEST_START,
	/* PLUGIN_FUNC_HANDLE_SUBREQUEST, *//* max one handler_module per req */
	PLUGIN_FUNC_HANDLE_RESPONSE_START,
	PLUGIN_FUNC_HANDLE_REQUEST_DONE,
	PLUGIN_FUNC_HANDLE_REQUEST_RESET,
	PLUGIN_FUNC_HANDLE_REQUEST_ENV,
	PLUGIN_FUNC_HANDLE_CONNECTION_ACCEPT,
	PLUGIN_FUNC_HANDLE_CONNECTION_SHUT_WR,
	PLUGIN_FUNC_HANDLE_CONNECTION_CLOSE,
	PLUGIN_FUNC_HANDLE_TRIGGER,
	PLUGIN_FUNC_HANDLE_WAITPID,
	PLUGIN_FUNC_HANDLE_SIGHUP,
	/* PLUGIN_FUNC_INIT, *//* handled here in plugin.c */
	/* PLUGIN_FUNC_CLEANUP, *//* handled here in plugin.c */
	PLUGIN_FUNC_SET_DEFAULTS,
	PLUGIN_FUNC_WORKER_INIT,

	PLUGIN_FUNC_SIZEOF
} plugin_t;

#ifndef LIGHTTPD_STATIC
__attribute_cold__
static void plugin_unload(void *lib) {
    if (NULL == lib) return;
    #if defined(HAVE_VALGRIND_VALGRIND_H)
    /*if (!RUNNING_ON_VALGRIND) */
    #endif
      #ifdef _WIN32
        FreeLibrary(lib);
      #else
        dlclose(lib);
      #endif
}
#endif

static int plugin_data_init(server * const srv, const plugin * const p, void *lib, int i) {
  #ifndef LIGHTTPD_STATIC
    if (p->version != LIGHTTPD_VERSION_ID) {
        log_error(srv->errh, __FILE__, __LINE__,
          "plugin-version doesn't match lighttpd-version for mod_%s", p->name);
        return 0;
    }
  #endif

    plugin_data_base * const pd = (NULL != p->init)
      ? p->init()
      : ck_calloc(1, sizeof(plugin_data_base));
    if (NULL == pd) {
        log_error(srv->errh, __FILE__, __LINE__,
          "plugin-init failed for mod_%s", p->name);
        return 0;
    }

    pd->id = i + 1;
    pd->lib = lib;
    if (!pd->self) {/*third-party plugins not yet changed for lighttpd 1.4.83*/
        plugin * const cp = ck_calloc(1, sizeof(plugin));
        memcpy(cp, p, sizeof(plugin));
        cp->lib = lib;  /*(flag to indicate legacy plugin code)*/
        pd->self = cp;
    }

    /*(note: allocated w/ appropriate size at top of plugins_load())*/
    ((plugin_data_base **)srv->plugins.ptr)[srv->plugins.used++] = pd;
    return 1;
}

/**
 *
 *
 *
 */

#if defined(LIGHTTPD_STATIC)

/* pre-declare functions, as there is no header for them */
#define PLUGIN_INIT(x)\
	int x ## _plugin_init(plugin *p);

#include "plugin-static.h"

#undef PLUGIN_INIT

/* build NULL-terminated table of name + init-function */

typedef struct {
	const char* name;
	int (*plugin_init)(plugin *p);
} plugin_load_functions;

static const plugin_load_functions load_functions[] = {
#define PLUGIN_INIT(x) \
	{ #x, &x ## _plugin_init },

#include "plugin-static.h"

	{ NULL, NULL }
#undef PLUGIN_INIT
};

int plugins_load(server *srv) {
	ck_realloc_u32(&srv->plugins.ptr, 0,
	               srv->srvconf.modules->used, sizeof(plugin_data_base *));

	for (uint32_t i = 0; i < srv->srvconf.modules->used; ++i) {
		const data_string * const ds = (data_string *)srv->srvconf.modules->data[i];
		const char * const module = ds->value.ptr;

		uint32_t j;
		for (j = 0; load_functions[j].name; ++j) {
			if (0 == strcmp(load_functions[j].name, module)) {
				plugin p;
				memset(&p, '\0', sizeof(p));
				if ((*load_functions[j].plugin_init)(&p)
				    || !plugin_data_init(srv, &p, NULL, i)) {
					log_error(srv->errh, __FILE__, __LINE__, "%s plugin init failed", module);
					return -1;
				}
				break;
			}
		}
		if (!load_functions[j].name) {
			if (srv->srvconf.compat_module_load) {
				if (buffer_eq_slen(&ds->value, CONST_STR_LEN("mod_deflate")))
					continue;
			}
			if (buffer_eq_slen(&ds->value, CONST_STR_LEN("mod_h2"))) {
				srv->srvconf.h2proto = 0;
				continue;
			}
			log_error(srv->errh, __FILE__, __LINE__, "%s plugin not found", module);
			return -1;
		}
	}

	return 0;
}

#else /* defined(LIGHTTPD_STATIC) */

int plugins_load(server *srv) {
	ck_realloc_u32(&srv->plugins.ptr, 0,
	               srv->srvconf.modules->used, sizeof(plugin_data_base *));

	buffer * const tb = srv->tmp_buf;
  #ifdef _WIN32
	int (WINAPI *init)(plugin *pl);
  #else
	int (*init)(plugin *pl);
  #endif

	for (uint32_t i = 0; i < srv->srvconf.modules->used; ++i) {
		const buffer * const module = &((data_string *)srv->srvconf.modules->data[i])->value;
		void *lib = NULL;

		/* check if module is built-in to main executable */
		buffer_clear(tb);
		buffer_append_str2(tb, BUF_PTR_LEN(module),
		                       CONST_STR_LEN("_plugin_init"));
	  #ifdef _WIN32
		init = (int(WINAPI *)(plugin *))(intptr_t)
		  /* NOTE: The upstream at the next code line does
		   * GetProcAddress(GetModuleHandle(NULL), tb->ptr);
		   * but it does not work for our Lighttpd use case as a shared library,
		   * because its module is then different from the main module of the app
		   * using it. */
		  GetProcAddress(LoadPackagedLibrary(L"ReactNativeStaticServer\\lighttpd.dll", 0), tb->ptr);
	  #else
		init = (int (*)(plugin *))(intptr_t)dlsym(RTLD_DEFAULT, tb->ptr);
	  #endif

	  if (NULL == init) {
		buffer_copy_string(tb, srv->srvconf.modules_dir);
		buffer_append_path_len(tb, BUF_PTR_LEN(module));

	  #ifdef _WIN32
		buffer_append_string_len(tb, CONST_STR_LEN(".dll"));
		if (NULL == (lib = LoadLibrary(tb->ptr))) {
			if (srv->srvconf.compat_module_load) {
				if (buffer_eq_slen(module, CONST_STR_LEN("mod_deflate")))
					continue;
			}
			if (buffer_eq_slen(module, CONST_STR_LEN("mod_h2"))) {
				srv->srvconf.h2proto = 0;
				continue;
			}
			log_perror(srv->errh, __FILE__, __LINE__,
			  "LoadLibrary() %s", tb->ptr);
			return -1;
		}
		buffer_copy_buffer(tb, module);
		buffer_append_string_len(tb, CONST_STR_LEN("_plugin_init"));
		init = (int(WINAPI *)(plugin *))(intptr_t)GetProcAddress(lib, tb->ptr);
		if (init == NULL) {
			log_perror(srv->errh, __FILE__, __LINE__,
			  "GetProcAddress() %s", tb->ptr);
		        FreeLibrary(lib);
			return -1;
		}
	  #else
	   #if defined(__CYGWIN__)
		buffer_append_string_len(tb, CONST_STR_LEN(".dll"));
	   #else
		buffer_append_string_len(tb, CONST_STR_LEN(".so"));
	   #endif
		if (NULL == (lib = dlopen(tb->ptr, RTLD_NOW|RTLD_GLOBAL))) {
			if (srv->srvconf.compat_module_load) {
				if (buffer_eq_slen(module, CONST_STR_LEN("mod_deflate")))
					continue;
			}
			if (buffer_eq_slen(module, CONST_STR_LEN("mod_h2"))) {
				srv->srvconf.h2proto = 0;
				continue;
			}
			log_error(srv->errh, __FILE__, __LINE__,
			  "dlopen() failed for: %s %s", tb->ptr, dlerror());
			return -1;
		}
		buffer_clear(tb);
		buffer_append_str2(tb, BUF_PTR_LEN(module),
                                       CONST_STR_LEN("_plugin_init"));
		init = (int (*)(plugin *))(intptr_t)dlsym(lib, tb->ptr);
		if (NULL == init) {
			const char *error = dlerror();
			if (error != NULL) {
				log_error(srv->errh, __FILE__, __LINE__, "dlsym: %s", error);
			} else {
				log_error(srv->errh, __FILE__, __LINE__, "dlsym symbol not found: %s", tb->ptr);
			}
		        dlclose(lib);
			return -1;
		}
	  #endif
	  }

		plugin p;
		memset(&p, '\0', sizeof(p));
		if ((*init)(&p) || !plugin_data_init(srv, &p, lib, i)) {
			log_error(srv->errh, __FILE__, __LINE__, "%s plugin init failed", module->ptr);
			plugin_unload(lib);
			return -1;
		}
	}

	return 0;
}

#endif /* defined(LIGHTTPD_STATIC) */

typedef handler_t(*pl_cb_t)(void *, void *);

/*(alternative to multiple structs would be union for fn ptr type)*/

typedef struct {
  pl_cb_t fn;
  plugin_data_base *data;
} plugin_fn_data;

typedef struct {
  handler_t(*fn)(request_st *, void *);
  plugin_data_base *data;
} plugin_fn_req_data;

typedef struct {
  handler_t(*fn)(connection *, void *);
  plugin_data_base *data;
} plugin_fn_con_data;

typedef struct {
  handler_t(*fn)(server *, void *);
  plugin_data_base *data;
} plugin_fn_srv_data;

typedef struct {
  handler_t(*fn)(server *, void *, pid_t, int);
  plugin_data_base *data;
} plugin_fn_waitpid_data;

__attribute_hot__
static handler_t plugins_call_fn_req_data(request_st * const r, const int e) {
    const void * const plugin_slots = r->con->plugin_slots;
    const uint32_t offset = ((const uint16_t *)plugin_slots)[e];
    if (0 == offset) return HANDLER_GO_ON;
    const plugin_fn_req_data *plfd = (const plugin_fn_req_data *)
      (((uintptr_t)plugin_slots) + offset);
    handler_t rc = HANDLER_GO_ON;
    while (plfd->fn && (rc = plfd->fn(r, plfd->data)) == HANDLER_GO_ON)
        ++plfd;
    return rc;
}

__attribute_hot__
static handler_t plugins_call_fn_con_data(connection * const con, const int e) {
    const void * const plugin_slots = con->plugin_slots;
    const uint32_t offset = ((const uint16_t *)plugin_slots)[e];
    if (0 == offset) return HANDLER_GO_ON;
    const plugin_fn_con_data *plfd = (const plugin_fn_con_data *)
      (((uintptr_t)plugin_slots) + offset);
    handler_t rc = HANDLER_GO_ON;
    while (plfd->fn && (rc = plfd->fn(con, plfd->data)) == HANDLER_GO_ON)
        ++plfd;
    return rc;
}

static handler_t plugins_call_fn_srv_data(server * const srv, const int e) {
    const uint32_t offset = ((const uint16_t *)srv->plugin_slots)[e];
    if (0 == offset) return HANDLER_GO_ON;
    const plugin_fn_srv_data *plfd = (const plugin_fn_srv_data *)
      (((uintptr_t)srv->plugin_slots) + offset);
    handler_t rc = HANDLER_GO_ON;
    while (plfd->fn && (rc = plfd->fn(srv,plfd->data)) == HANDLER_GO_ON)
        ++plfd;
    return rc;
}

static void plugins_call_fn_srv_data_all(server * const srv, const int e) {
    const uint32_t offset = ((const uint16_t *)srv->plugin_slots)[e];
    if (0 == offset) return;
    const plugin_fn_srv_data *plfd = (const plugin_fn_srv_data *)
      (((uintptr_t)srv->plugin_slots) + offset);
    for (; plfd->fn; ++plfd)
        plfd->fn(srv, plfd->data);
}

/**
 * plugins that use
 *
 * - request_st *r
 * - void *p_d (plugin_data *)
 */

#define PLUGIN_CALL_FN_REQ_DATA(x, y) \
    handler_t plugins_call_##y(request_st * const r) {\
        return plugins_call_fn_req_data(r, x); \
    }

#if 0 /*(handled differently in http_response_prepare())*/
PLUGIN_CALL_FN_REQ_DATA(PLUGIN_FUNC_HANDLE_URI_CLEAN, handle_uri_clean)
PLUGIN_CALL_FN_REQ_DATA(PLUGIN_FUNC_HANDLE_DOCROOT, handle_docroot)
PLUGIN_CALL_FN_REQ_DATA(PLUGIN_FUNC_HANDLE_PHYSICAL, handle_physical)
PLUGIN_CALL_FN_REQ_DATA(PLUGIN_FUNC_HANDLE_SUBREQUEST_START, handle_subrequest_start)
#endif
PLUGIN_CALL_FN_REQ_DATA(PLUGIN_FUNC_HANDLE_RESPONSE_START, handle_response_start)
PLUGIN_CALL_FN_REQ_DATA(PLUGIN_FUNC_HANDLE_REQUEST_DONE, handle_request_done)
PLUGIN_CALL_FN_REQ_DATA(PLUGIN_FUNC_HANDLE_REQUEST_RESET, handle_request_reset)
PLUGIN_CALL_FN_REQ_DATA(PLUGIN_FUNC_HANDLE_REQUEST_ENV, handle_request_env)

/**
 * plugins that use
 *
 * - connection *con
 * - void *p_d (plugin_data *)
 */

#define PLUGIN_CALL_FN_CON_DATA(x, y) \
    handler_t plugins_call_##y(connection *con) {\
        return plugins_call_fn_con_data(con, x); \
    }

PLUGIN_CALL_FN_CON_DATA(PLUGIN_FUNC_HANDLE_CONNECTION_ACCEPT, handle_connection_accept)
PLUGIN_CALL_FN_CON_DATA(PLUGIN_FUNC_HANDLE_CONNECTION_SHUT_WR, handle_connection_shut_wr)
PLUGIN_CALL_FN_CON_DATA(PLUGIN_FUNC_HANDLE_CONNECTION_CLOSE, handle_connection_close)

#undef PLUGIN_CALL_FN_SRV_CON_DATA

/**
 * plugins that use
 *
 * - server *srv
 * - void *p_d (plugin_data *)
 */

handler_t plugins_call_set_defaults(server *srv) {
    return plugins_call_fn_srv_data(srv, PLUGIN_FUNC_SET_DEFAULTS);
}

handler_t plugins_call_worker_init(server *srv) {
    return plugins_call_fn_srv_data(srv, PLUGIN_FUNC_WORKER_INIT);
}

void plugins_call_handle_trigger(server *srv) {
    plugins_call_fn_srv_data_all(srv, PLUGIN_FUNC_HANDLE_TRIGGER);
}

void plugins_call_handle_sighup(server *srv) {
    plugins_call_fn_srv_data_all(srv, PLUGIN_FUNC_HANDLE_SIGHUP);
}

handler_t plugins_call_handle_waitpid(server *srv, pid_t pid, int status) {
    const uint32_t offset =
      ((const uint16_t *)srv->plugin_slots)[PLUGIN_FUNC_HANDLE_WAITPID];
    if (0 == offset) return HANDLER_GO_ON;
    const plugin_fn_waitpid_data *plfd = (const plugin_fn_waitpid_data *)
      (((uintptr_t)srv->plugin_slots) + offset);
    handler_t rc = HANDLER_GO_ON;
    while (plfd->fn&&(rc=plfd->fn(srv,plfd->data,pid,status))==HANDLER_GO_ON)
        ++plfd;
    return rc;
}

static void plugins_call_cleanup(server * const srv) {
    plugin_data_base ** const ps = srv->plugins.ptr;
    for (uint32_t i = 0; i < srv->plugins.used; ++i) {
        plugin_data_base * const pd = ps[i];
        if (pd->self->cleanup)
            pd->self->cleanup(pd);
        free(pd->cvlist);
    }
}

__attribute_cold__
static void plugins_call_init_reverse(server *srv, const uint32_t offset) {
    if (0 == offset) return;
    plugin_fn_data *a = (plugin_fn_data *)
      (((uintptr_t)srv->plugin_slots) + offset);
    plugin_fn_data *b = a;
    while (b->fn) ++b;
    for (; a < --b; ++a) { /* swap to reverse list */
        plugin_fn_data tmp = *a;
        *a = *b;
        *b = tmp;
    }
}

__attribute_cold__
static void plugins_call_init_slot(server *srv, pl_cb_t fn, void *data, const uint32_t offset) {
    if (fn) {
        plugin_fn_data *plfd = (plugin_fn_data *)
          (((uintptr_t)srv->plugin_slots) + offset);
        while (plfd->fn) ++plfd;
        plfd->fn = fn;
        plfd->data = data;
    }
}

handler_t plugins_call_init(server *srv) {
	/* note: p->init() is called during plugins_load() -> plugin_data_init()
	 * but the historical name of this function remains */
	plugin_data_base ** const ps = srv->plugins.ptr;
	uint16_t offsets[PLUGIN_FUNC_SIZEOF];
	memset(offsets, 0, sizeof(offsets));

	for (uint32_t i = 0; i < srv->plugins.used; ++i) {
		plugin_data_base * const pd = ps[i];
		const plugin * const p = pd->self;

		if (p->priv_defaults && HANDLER_ERROR == p->priv_defaults(srv, pd)) {
			return HANDLER_ERROR;
		}

		if (p->handle_uri_clean)
			++offsets[PLUGIN_FUNC_HANDLE_URI_CLEAN];
		if (p->handle_uri_raw && !p->handle_uri_clean)
			++offsets[PLUGIN_FUNC_HANDLE_URI_CLEAN]; /*(same as above)*/
		if (p->handle_request_env)
			++offsets[PLUGIN_FUNC_HANDLE_REQUEST_ENV];
		if (p->handle_request_done)
			++offsets[PLUGIN_FUNC_HANDLE_REQUEST_DONE];
		if (p->handle_connection_accept)
			++offsets[PLUGIN_FUNC_HANDLE_CONNECTION_ACCEPT];
		if (p->handle_connection_shut_wr)
			++offsets[PLUGIN_FUNC_HANDLE_CONNECTION_SHUT_WR];
		if (p->handle_connection_close)
			++offsets[PLUGIN_FUNC_HANDLE_CONNECTION_CLOSE];
		if (p->handle_trigger)
			++offsets[PLUGIN_FUNC_HANDLE_TRIGGER];
		if (p->handle_sighup)
			++offsets[PLUGIN_FUNC_HANDLE_SIGHUP];
		if (p->handle_waitpid)
			++offsets[PLUGIN_FUNC_HANDLE_WAITPID];
		if (p->handle_subrequest_start)
			++offsets[PLUGIN_FUNC_HANDLE_SUBREQUEST_START];
		if (p->handle_response_start)
			++offsets[PLUGIN_FUNC_HANDLE_RESPONSE_START];
		if (p->handle_docroot)
			++offsets[PLUGIN_FUNC_HANDLE_DOCROOT];
		if (p->handle_physical)
			++offsets[PLUGIN_FUNC_HANDLE_PHYSICAL];
		if (p->handle_request_reset)
			++offsets[PLUGIN_FUNC_HANDLE_REQUEST_RESET];
		if (p->set_defaults)
			++offsets[PLUGIN_FUNC_SET_DEFAULTS];
		if (p->worker_init)
			++offsets[PLUGIN_FUNC_WORKER_INIT];
	}

	/* allocate first space for response.c:http_response_config() */
	++offsets[PLUGIN_FUNC_HANDLE_URI_CLEAN];

	uint32_t nslots =
	  (sizeof(offsets)+sizeof(plugin_fn_data)-1) / sizeof(plugin_fn_data);
	for (uint32_t i = 0; i < PLUGIN_FUNC_SIZEOF; ++i) {
		/* note: allocate at least one slot for
		 *   PLUGIN_FUNC_HANDLE_URI_CLEAN
		 *   PLUGIN_FUNC_HANDLE_DOCROOT
		 *   PLUGIN_FUNC_HANDLE_PHYSICAL
		 *   PLUGIN_FUNC_HANDLE_SUBREQUEST_START
		 * in order to be able to overwrite NULL w/ fn ptr in response.c
		 */
		if (offsets[i] || i <= PLUGIN_FUNC_HANDLE_SUBREQUEST_START) {
			uint32_t offset = nslots;
			nslots += offsets[i]+1; /* +1 to mark end of each list */
			force_assert(offset * sizeof(plugin_fn_data) <= USHRT_MAX);
			offsets[i] = (uint16_t)(offset * sizeof(plugin_fn_data));
		}
	}

	/* allocate and fill slots of two dimensional array */
	srv->plugin_slots = ck_calloc(nslots, sizeof(plugin_fn_data));
	memcpy(srv->plugin_slots, offsets, sizeof(offsets));

	/* allocate first space for response.c:http_response_config() */
	plugins_call_init_slot(srv, (pl_cb_t)(uintptr_t)1, NULL,
				offsets[PLUGIN_FUNC_HANDLE_URI_CLEAN]);

	/* add handle_uri_raw before handle_uri_clean, but in same slot */
	for (uint32_t i = 0; i < srv->plugins.used; ++i) {
		plugin_data_base * const pd = ps[i];
		const plugin * const p = pd->self;
		plugins_call_init_slot(srv, (pl_cb_t)p->handle_uri_raw, pd,
					offsets[PLUGIN_FUNC_HANDLE_URI_CLEAN]);
	}

	for (uint32_t i = 0; i < srv->plugins.used; ++i) {
		plugin_data_base * const pd = ps[i];
		const plugin * const p = pd->self;

		if (!p->handle_uri_raw)
			plugins_call_init_slot(srv, (pl_cb_t)p->handle_uri_clean, pd,
						offsets[PLUGIN_FUNC_HANDLE_URI_CLEAN]);
		plugins_call_init_slot(srv, (pl_cb_t)p->handle_request_env, pd,
					offsets[PLUGIN_FUNC_HANDLE_REQUEST_ENV]);
		plugins_call_init_slot(srv, (pl_cb_t)p->handle_request_done, pd,
					offsets[PLUGIN_FUNC_HANDLE_REQUEST_DONE]);
		plugins_call_init_slot(srv, (pl_cb_t)p->handle_connection_accept, pd,
					offsets[PLUGIN_FUNC_HANDLE_CONNECTION_ACCEPT]);
		plugins_call_init_slot(srv, (pl_cb_t)p->handle_connection_shut_wr, pd,
					offsets[PLUGIN_FUNC_HANDLE_CONNECTION_SHUT_WR]);
		plugins_call_init_slot(srv, (pl_cb_t)p->handle_connection_close, pd,
					offsets[PLUGIN_FUNC_HANDLE_CONNECTION_CLOSE]);
		plugins_call_init_slot(srv, (pl_cb_t)p->handle_trigger, pd,
					offsets[PLUGIN_FUNC_HANDLE_TRIGGER]);
		plugins_call_init_slot(srv, (pl_cb_t)p->handle_sighup, pd,
					offsets[PLUGIN_FUNC_HANDLE_SIGHUP]);
		plugins_call_init_slot(srv, (pl_cb_t)(uintptr_t)p->handle_waitpid, pd,
					offsets[PLUGIN_FUNC_HANDLE_WAITPID]);
		plugins_call_init_slot(srv, (pl_cb_t)p->handle_subrequest_start, pd,
					offsets[PLUGIN_FUNC_HANDLE_SUBREQUEST_START]);
		plugins_call_init_slot(srv, (pl_cb_t)p->handle_response_start, pd,
					offsets[PLUGIN_FUNC_HANDLE_RESPONSE_START]);
		plugins_call_init_slot(srv, (pl_cb_t)p->handle_docroot, pd,
					offsets[PLUGIN_FUNC_HANDLE_DOCROOT]);
		plugins_call_init_slot(srv, (pl_cb_t)p->handle_physical, pd,
					offsets[PLUGIN_FUNC_HANDLE_PHYSICAL]);
		plugins_call_init_slot(srv, (pl_cb_t)p->handle_request_reset, pd,
					offsets[PLUGIN_FUNC_HANDLE_REQUEST_RESET]);
		plugins_call_init_slot(srv, (pl_cb_t)p->set_defaults, pd,
					offsets[PLUGIN_FUNC_SET_DEFAULTS]);
		plugins_call_init_slot(srv, (pl_cb_t)p->worker_init, pd,
					offsets[PLUGIN_FUNC_WORKER_INIT]);
	}

	/* reverse cleanup lists to balance ctor/dtor-like plugin behaviors */
	plugins_call_init_reverse(srv,offsets[PLUGIN_FUNC_HANDLE_REQUEST_RESET]);
	plugins_call_init_reverse(srv,offsets[PLUGIN_FUNC_HANDLE_CONNECTION_CLOSE]);

	return HANDLER_GO_ON;
}

void plugins_free(server *srv) {
	if (srv->plugin_slots) {
		plugins_call_cleanup(srv);
		free(srv->plugin_slots);
		srv->plugin_slots = NULL;
	}

	plugin_data_base ** const ps = srv->plugins.ptr;
	for (uint32_t i = 0; i < srv->plugins.used; ++i) {
		plugin_data_base * const pd = ps[i];

		/* third-party plugins not yet changed for lighttpd 1.4.83 */
		if (pd->self->lib) { /*(flag to indicate legacy plugin code)*/
			plugin *p;
			*(const plugin **)&p = pd->self;
			free(p);
		}

	  #ifndef LIGHTTPD_STATIC
		plugin_unload(pd->lib);
	  #endif
		free(pd);
	}
	free(srv->plugins.ptr);
	srv->plugins.ptr = NULL;
	srv->plugins.used = 0;

	array_free_data(&plugin_stats);
}
