/*************************************************
*     Exim - an Internet mail transport agent    *
*************************************************/

/* Copyright (c) The Exim Maintainers 2020 - 2025 */
/* Copyright (c) University of Cambridge 1995 - 2018 */
/* See the file NOTICE for conditions of use and distribution. */
/* SPDX-License-Identifier: GPL-2.0-or-later */


#include "exim.h"

#include <string.h>

/* This module contains tables that define the lookup methods and drivers
that are actually included in the binary. Its contents are controlled by
various macros in config.h that ultimately come from Local/Makefile. They are
all described in src/EDITME. */


tree_node * lookups_tree = NULL;
unsigned lookup_list_count = 0;

/* Lists of information about which drivers are included in the exim binary. */

auth_info * auths_available= NULL;
router_info * routers_available = NULL;
transport_info * transports_available = NULL;



#ifndef MACRO_PREDEF

gstring *
auth_show_supported(gstring * g)
{
uschar * b = US""               /* static-build authenticatornames */
#if defined(AUTH_CRAM_MD5) && AUTH_CRAM_MD5!=2
  " cram_md5"
#endif
#if defined(AUTH_CYRUS_SASL) && AUTH_CYRUS_SASL!=2
  " cyrus_sasl"
#endif
#if defined(AUTH_DOVECOT) && AUTH_DOVECOT!=2
  " dovecot"
#endif
#if defined(AUTH_EXTERNAL) && AUTH_EXTERNAL!=2
  " external"
#endif
#if defined(AUTH_GSASL) && AUTH_GSASL!=2
  " gsasl"
#endif
#if defined(AUTH_HEIMDAL_GSSAPI) && AUTH_HEIMDAL_GSSAPI!=2
  " heimdal_gssapi"
#endif
#if defined(AUTH_PLAINTEXT) && AUTH_PLAINTEXT!=2
  " plaintext"
#endif
#if defined(AUTH_SPA) && AUTH_SPA!=2
  " spa"
#endif
#if defined(AUTH_TLS) && AUTH_TLS!=2
  " tls"
#endif
  ;

uschar * d = US""		/* dynamic-module authenticator names */
#if defined(AUTH_CRAM_MD5) && AUTH_CRAM_MD5==2
  " cram_md5"
#endif
#if defined(AUTH_CYRUS_SASL) && AUTH_CYRUS_SASL==2
  " cyrus_sasl"
#endif
#if defined(AUTH_DOVECOT) && AUTH_DOVECOT==2
  " dovecot"
#endif
#if defined(AUTH_EXTERNAL) && AUTH_EXTERNAL==2
  " external"
#endif
#if defined(AUTH_GSASL) && AUTH_GSASL==2
  " gsasl"
#endif
#if defined(AUTH_HEIMDAL_GSSAPI) && AUTH_HEIMDAL_GSSAPI==2
  " heimdal_gssapi"
#endif
#if defined(AUTH_PLAINTEXT) && AUTH_PLAINTEXT==2
  " plaintext"
#endif
#if defined(AUTH_SPA) && AUTH_SPA==2
  " spa"
#endif
#if defined(AUTH_TLS) && AUTH_TLS==2
  " tls"
#endif
  ;

if (*b) g = string_fmt_append(g, "Authenticators (built-in):%s\n", b);
if (*d) g = string_fmt_append(g, "Authenticators (dynamic): %s\n", d);
return g;
}

gstring *
route_show_supported(gstring * g)
{
uschar * b = US""		/* static-build router names */
#if defined(ROUTER_ACCEPT) && ROUTER_ACCEPT!=2
  " accept"
#endif
#if defined(ROUTER_DNSLOOKUP) && ROUTER_DNSLOOKUP!=2
  " dnslookup"
#endif
# if defined(ROUTER_IPLITERAL) && ROUTER_IPLITERAL!=2
  " ipliteral"
#endif
#if defined(ROUTER_IPLOOKUP) && ROUTER_IPLOOKUP!=2
  " iplookup"
#endif
#if defined(ROUTER_MANUALROUTE) && ROUTER_MANUALROUTE!=2
  " manualroute"
#endif
#if defined(ROUTER_REDIRECT) && ROUTER_REDIRECT!=2
  " redirect"
#endif
#if defined(ROUTER_QUERYPROGRAM) && ROUTER_QUERYPROGRAM!=2
  " queryprogram"
#endif
  ;

uschar * d = US""		/* dynamic-module router names */
#if defined(ROUTER_ACCEPT) && ROUTER_ACCEPT==2
  " accept"
#endif
#if defined(ROUTER_DNSLOOKUP) && ROUTER_DNSLOOKUP==2
  " dnslookup"
#endif
# if defined(ROUTER_IPLITERAL) && ROUTER_IPLITERAL==2
  " ipliteral"
#endif
#if defined(ROUTER_IPLOOKUP) && ROUTER_IPLOOKUP==2
  " iplookup"
#endif
#if defined(ROUTER_MANUALROUTE) && ROUTER_MANUALROUTE==2
  " manualroute"
#endif
#if defined(ROUTER_REDIRECT) && ROUTER_REDIRECT==2
  " redirect"
#endif
#if defined(ROUTER_QUERYPROGRAM) && ROUTER_QUERYPROGRAM==2
  " queryprogram"
#endif
  ;

if (*b) g = string_fmt_append(g, "Routers (built-in):%s\n", b);
if (*d) g = string_fmt_append(g, "Routers (dynamic): %s\n", d);
return g;
}

gstring *
transport_show_supported(gstring * g)
{
uschar * b = US""		/* static-build transportnames */
#if defined(TRANSPORT_APPENDFILE) && TRANSPORT_APPENDFILE!=2
  " appendfile"
# ifdef SUPPORT_MAILDIR
    "/maildir"
# endif
# ifdef SUPPORT_MAILSTORE
    "/mailstore"
# endif
# ifdef SUPPORT_MBX
    "/mbx"
# endif
#endif
#if defined(TRANSPORT_AUTOREPLY) && TRANSPORT_AUTOREPLY!=2
  " autoreply"
#endif
#if defined(TRANSPORT_LMTP) && TRANSPORT_LMTP!=2
  " lmtp"
#endif
#if defined(TRANSPORT_PIPE) && TRANSPORT_PIPE!=2
  " pipe"
#endif
#if defined(EXPERIMENTAL_QUEUEFILE) && EXPERIMENTAL_QUEUEFILE!=2
  " queuefile"
#endif
#if defined(TRANSPORT_SMTP) && TRANSPORT_SMTP!=2
  " smtp"
#endif
  ;

uschar * d = US""		/* dynamic-module transportnames */
#if defined(TRANSPORT_APPENDFILE) && TRANSPORT_APPENDFILE==2
  " appendfile"
# ifdef SUPPORT_MAILDIR
    "/maildir"
# endif
# ifdef SUPPORT_MAILSTORE
    "/mailstore"
# endif
# ifdef SUPPORT_MBX
    "/mbx"
# endif
#endif
#if defined(TRANSPORT_AUTOREPLY) && TRANSPORT_AUTOREPLY==2
  " autoreply"
#endif
#if defined(TRANSPORT_LMTP) && TRANSPORT_LMTP==2
  " lmtp"
#endif
#if defined(TRANSPORT_PIPE) && TRANSPORT_PIPE==2
  " pipe"
#endif
#if defined(EXPERIMENTAL_QUEUEFILE) && EXPERIMENTAL_QUEUEFILE==2
  " queuefile"
#endif
#if defined(TRANSPORT_SMTP) && TRANSPORT_SMTP==2
  " smtp"
#endif
  ;

if (*b) g = string_fmt_append(g, "Transports (built-in):%s\n", b);
if (*d) g = string_fmt_append(g, "Transports (dynamic): %s\n", d);
return g;
}



static void
add_lookup_to_tree(lookup_info * li)
{
tree_node * new = store_get_perm(sizeof(tree_node) + Ustrlen(li->name),
							GET_UNTAINTED);
new->data.ptr = (void *)li;
Ustrcpy(new->name, li->name);
if (tree_insertnode(&lookups_tree, new))
  li->acq_num = lookup_list_count++;
else
  log_write(0, LOG_MAIN|LOG_PANIC, "Duplicate lookup name '%s'", li->name);
}


/* Add all the lookup types provided by the module */
static void
addlookupmodule(const struct lookup_module_info * lmi)
{
for (int j = 0; j < lmi->lookupcount; j++)
  add_lookup_to_tree(lmi->lookups[j]);
}



/* Hunt for the lookup with the given acquisition number */

static unsigned hunt_acq;

static void
acq_cb(uschar * name, uschar * ptr, void * ctx)
{
lookup_info * li = (lookup_info *)ptr;
if (li->acq_num == hunt_acq) *(lookup_info **)ctx = li;
}

const lookup_info *
lookup_with_acq_num(unsigned k)
{
const lookup_info * li = NULL;
hunt_acq = k;
tree_walk(lookups_tree, acq_cb, &li);
return li;
}



/* These need to be at file level for old versions of gcc (2.95.2 reported),
which give parse errors on an extern in function scope.  Each entry needs
to also be invoked in init_lookup_list() below  */

#if defined(LOOKUP_CDB) && LOOKUP_CDB!=2
extern lookup_module_info cdb_lookup_module_info;
#endif
#if defined(LOOKUP_DBM) && LOOKUP_DBM!=2
extern lookup_module_info dbmdb_lookup_module_info;
#endif
#if defined(LOOKUP_DNSDB) && LOOKUP_DNSDB!=2
extern lookup_module_info dnsdb_lookup_module_info;
#endif
#if defined(LOOKUP_DSEARCH) && LOOKUP_DSEARCH!=2
extern lookup_module_info dsearch_lookup_module_info;
#endif
#if defined(LOOKUP_IBASE) && LOOKUP_IBASE!=2
extern lookup_module_info ibase_lookup_module_info;
#endif
#if defined(LOOKUP_JSON) && LOOKUP_JSON!=2
extern lookup_module_info json_lookup_module_info;
#endif
#if defined(LOOKUP_LDAP) && LOOKUP_LDAP!=2
extern lookup_module_info ldap_lookup_module_info;
#endif
#if defined(LOOKUP_LSEARCH) && LOOKUP_LSEARCH!=2
extern lookup_module_info lsearch_lookup_module_info;
#endif
#if defined(LOOKUP_MYSQL) && LOOKUP_MYSQL!=2
extern lookup_module_info mysql_lookup_module_info;
#endif
#if defined(LOOKUP_NIS) && LOOKUP_NIS!=2
extern lookup_module_info nis_lookup_module_info;
#endif
#if defined(LOOKUP_NISPLUS) && LOOKUP_NISPLUS!=2
extern lookup_module_info nisplus_lookup_module_info;
#endif
#if defined(EXPERIMENTAL_NMH) && EXPERIMENTAL_NMH!=2
extern lookup_module_info nmh_lookup_module_info;
#endif
#if defined(LOOKUP_ORACLE) && LOOKUP_ORACLE!=2
extern lookup_module_info oracle_lookup_module_info;
#endif
#if defined(LOOKUP_PASSWD) && LOOKUP_PASSWD!=2
extern lookup_module_info passwd_lookup_module_info;
#endif
#if defined(LOOKUP_PGSQL) && LOOKUP_PGSQL!=2
extern lookup_module_info pgsql_lookup_module_info;
#endif
#if defined(LOOKUP_REDIS) && LOOKUP_REDIS!=2
extern lookup_module_info redis_lookup_module_info;
#endif
#if defined(LOOKUP_LMDB) && LOOKUP_LMDB!=2
extern lookup_module_info lmdb_lookup_module_info;
#endif
#if defined(EXIM_HAVE_SPF)
extern lookup_module_info spf_lookup_module_info;	/* see below */
#endif
#if defined(LOOKUP_SQLITE) && LOOKUP_SQLITE!=2
extern lookup_module_info sqlite_lookup_module_info;
#endif
#if defined(LOOKUP_TESTDB) && LOOKUP_TESTDB!=2
extern lookup_module_info testdb_lookup_module_info;
#endif
#if defined(LOOKUP_WHOSON) && LOOKUP_WHOSON!=2
extern lookup_module_info whoson_lookup_module_info;
#endif

extern lookup_module_info readsock_lookup_module_info;


#ifdef LOOKUP_MODULE_DIR
static void *
mod_open(const uschar * name, const uschar * class, uschar ** errstr)
{
const uschar * path = string_sprintf(
  LOOKUP_MODULE_DIR "/%s_%s." DYNLIB_FN_EXT, name, class);
void * dl;
if (!(dl = dlopen(CS path, RTLD_NOW)))
  {
  if (errstr)
    *errstr = string_sprintf("Error loading %s: %s", name, dlerror());
  else
    (void) dlerror();		/* clear out error state */
  return NULL;
  }

/* FreeBSD nsdispatch() can trigger dlerror() errors about
_nss_cache_cycle_prevention_function; we need to clear the dlerror()
state before calling dlsym(), so that any error afterwards only comes
from dlsym().  */

(void) dlerror();
return dl;
}


/* Try to load a lookup module with the given name.

Arguments:
    name		name of the lookup
    errstr		if not NULL, place "open fail" error message here

Return: boolean success
*/

static BOOL
lookup_mod_load(const uschar * name, uschar ** errstr)
{
void * dl;
struct lookup_module_info * info;
const char * errormsg;

if (!(dl = mod_open(name, US"lookup", errstr)))
  return FALSE;

info = (struct lookup_module_info *) dlsym(dl, "_lookup_module_info");
if ((errormsg = dlerror()))
  {
  EARLY_DEBUG(D_any, "%s does not appear to be a lookup module (%s)\n", name, errormsg);
  log_write(0, LOG_MAIN|LOG_PANIC, "%s does not appear to be a lookup module (%s)", name, errormsg);
  dlclose(dl);
  return FALSE;
  }
if (info->magic != LOOKUP_MODULE_INFO_MAGIC)
  {
  EARLY_DEBUG(D_any, "Lookup module %s is not compatible with this version of Exim\n", name);
  log_write(0, LOG_MAIN|LOG_PANIC, "Lookup module %s is not compatible with this version of Exim", name);
  dlclose(dl);
  return FALSE;
  }

addlookupmodule(info);
EARLY_DEBUG(D_lookup, "Loaded %q (%d lookup type%s)\n",
				    name, info->lookupcount,
				    info->lookupcount > 1 ? "s" : "");
return TRUE;
}


/* Try to load a lookup module, assuming the module name is the same
as the lookup type name.  This will only work for single-method modules.
Other have to be always-load (see the RE in init_lookup_list() below).
*/

BOOL
lookup_one_mod_load(const uschar * name, uschar ** errstr)
{
if (!lookup_mod_load(name, errstr)) return FALSE;
/*XXX notify daemon? */
return TRUE;
}

#endif	/*LOOKUP_MODULE_DIR*/



misc_module_info * misc_module_list = NULL;

static void
misc_mod_add(misc_module_info * mi)
{
mi->next = misc_module_list;
misc_module_list = mi;

if (mi->init)
  {
  EARLY_DEBUG(D_any, "Module init: %q\n", mi->name);
  expand_level++;
  if (!mi->init(mi))
    EARLY_DEBUG(D_any, "module init call failed for %q\n", mi->name);
  expand_level--;
  }

if (mi->lib_vers_report)
  DEBUG(D_any) debug_printf_indent("%Y", mi->lib_vers_report(NULL));

/* EARLY_DEBUG(D_any, "added %q\n", mi->name); */
}


#ifdef LOOKUP_MODULE_DIR

/* Load a "misc" module, and add to list */

static misc_module_info *
misc_mod_load(const uschar * name, uschar ** errstr)
{
void * dl;
struct misc_module_info * mi;
const char * errormsg;

EARLY_DEBUG(D_any, "loading module %q\n", name);
if (!(dl = mod_open(name, US"miscmod", errstr)))
  {
  if (errstr) EARLY_DEBUG(D_any, " mod_open: %s\n", *errstr);
  return NULL;
  }

mi = (struct misc_module_info *) dlsym(dl,
				    CS string_sprintf("%s_module_info", name));
if ((errormsg = dlerror()))
  {
  EARLY_DEBUG(D_any, "%s does not appear to be a '%s' module (%s)\n",
	  name, name, errormsg);
  log_write(0, LOG_MAIN|LOG_PANIC,
    "%s does not contain the expected module info symbol (%s)", name, errormsg);
  dlclose(dl);
  return NULL;
  }
if (mi->dyn_magic != MISC_MODULE_MAGIC)
  {
  EARLY_DEBUG(D_any, "Module %s is not compatible with this version of Exim\n", name);
  log_write(0, LOG_MAIN|LOG_PANIC, "Module %s is not compatible with this version of Exim", name);
  dlclose(dl);
  return FALSE;
  }

EARLY_DEBUG(D_lookup, "loaded %q\n", name);
misc_mod_add(mi);
return mi;
}

#endif	/*LOOKUP_MODULE_DIR*/


/* Find a "misc" module by name, if loaded.
For now use a linear search down a linked list.  If the number of
modules gets large, we might consider a tree.
*/

misc_module_info *
misc_mod_findonly(const uschar * name)
{
for (misc_module_info * mi = misc_module_list; mi; mi = mi->next)
  if (Ustrcmp(name, mi->name) == 0)
    return mi;
return NULL;
}

/* Find a "misc" module, possibly already loaded, by name. */

misc_module_info *
misc_mod_find(const uschar * name, uschar ** errstr)
{
misc_module_info * mi;
if ((mi = misc_mod_findonly(name))) return mi;
#ifdef LOOKUP_MODULE_DIR
return misc_mod_load(name, errstr);
#else
*errstr = string_sprintf("module '%s' not found", name);
return NULL;
#endif	/*LOOKUP_MODULE_DIR*/
}


/* For any "misc" module having a connection-init routine, call it. */

int
misc_mod_conn_init(const uschar * sender_helo_name,
  const uschar * sender_host_address, const uschar * * errstr)
{
for (const misc_module_info * mi = misc_module_list; mi; mi = mi->next)
  if (mi->conn_init)
    if ((mi->conn_init) (sender_helo_name, sender_host_address, errstr) != OK)
      return FAIL;
return OK;
}

/* Ditto, smtp-reset */

void
misc_mod_smtp_reset(void)
{
for (const misc_module_info * mi = misc_module_list; mi; mi = mi->next)
  if (mi->smtp_reset)
    (mi->smtp_reset)();
}

/* Ditto, msg-init */

int
misc_mod_msg_init(void)
{
for (const misc_module_info * mi = misc_module_list; mi; mi = mi->next)
  if (mi->msg_init)
    if ((mi->msg_init)() != OK)
      return FAIL;
return OK;
}

/* Ditto, authres.  Having to sort the responses (mainly for the testsuite)
is pretty painful - maybe we should sort the modules on insertion to
the list? */

gstring *
misc_mod_authres(gstring * g)
{
typedef struct {
  const uschar * name;
  gstring *	 res;
} pref;
pref prefs[] = {
  {US"spf", NULL}, {US"dkim", NULL}, {US"dmarc", NULL}, {US"arc", NULL}
};
gstring * others = NULL;

for (const misc_module_info * mi = misc_module_list; mi; mi = mi->next)
  if (mi->authres)
    {
    pref * p;
    for (p = prefs; p < prefs + nelem(prefs); p++)
      if (Ustrcmp(p->name, mi->name) == 0) break;

    if (p) p->res = (mi->authres)(NULL);
    else   others = (mi->authres)(others);
    }

for (pref * p = prefs; p < prefs + nelem(prefs); p++)
  g = gstring_append(g, p->res);
return gstring_append(g, others);
}






void
init_lookup_list(void)
{
#ifdef LOOKUP_MODULE_DIR
DIR * dd;
int countmodules = 0;
#endif
static BOOL lookup_list_init_done = FALSE;

if (lookup_list_init_done)
  return;
lookup_list_init_done = TRUE;

#if defined(LOOKUP_CDB) && LOOKUP_CDB!=2
addlookupmodule(&cdb_lookup_module_info);
#endif

#if defined(LOOKUP_DBM) && LOOKUP_DBM!=2
addlookupmodule(&dbmdb_lookup_module_info);
#endif

#if defined(LOOKUP_DNSDB) && LOOKUP_DNSDB!=2
addlookupmodule(&dnsdb_lookup_module_info);
#endif

#if defined(LOOKUP_DSEARCH) && LOOKUP_DSEARCH!=2
addlookupmodule(&dsearch_lookup_module_info);
#endif

#if defined(LOOKUP_IBASE) && LOOKUP_IBASE!=2
addlookupmodule(&ibase_lookup_module_info);
#endif

#if defined(LOOKUP_LDAP) && LOOKUP_LDAP!=2
addlookupmodule(&ldap_lookup_module_info);
#endif

#if defined(LOOKUP_JSON) && LOOKUP_JSON!=2
addlookupmodule(&json_lookup_module_info);
#endif

#if defined(LOOKUP_LSEARCH) && LOOKUP_LSEARCH!=2
addlookupmodule(&lsearch_lookup_module_info);
#endif

#if defined(LOOKUP_MYSQL) && LOOKUP_MYSQL!=2
addlookupmodule(&mysql_lookup_module_info);
#endif

#if defined(LOOKUP_NIS) && LOOKUP_NIS!=2
addlookupmodule(&nis_lookup_module_info);
#endif

#if defined(LOOKUP_NISPLUS) && LOOKUP_NISPLUS!=2
addlookupmodule(&nisplus_lookup_module_info);
#endif

#if defined(EXPERIMENTAL_NMH) && EXPERIMENTAL_NMH!=2
addlookupmodule(&nmh_lookup_module_info);
#endif

#if defined(LOOKUP_ORACLE) && LOOKUP_ORACLE!=2
addlookupmodule(&oracle_lookup_module_info);
#endif

#if defined(LOOKUP_PASSWD) && LOOKUP_PASSWD!=2
addlookupmodule(&passwd_lookup_module_info);
#endif

#if defined(LOOKUP_PGSQL) && LOOKUP_PGSQL!=2
addlookupmodule(&pgsql_lookup_module_info);
#endif

#if defined(LOOKUP_REDIS) && LOOKUP_REDIS!=2
addlookupmodule(&redis_lookup_module_info);
#endif

#if defined(LOOKUP_LMDB) && LOOKUP_LMDB!=2
addlookupmodule(&lmdb_lookup_module_info);
#endif

#if defined(LOOKUP_SQLITE) && LOOKUP_SQLITE!=2
addlookupmodule(&sqlite_lookup_module_info);
#endif

#if defined(LOOKUP_TESTDB) && LOOKUP_TESTDB!=2
addlookupmodule(&testdb_lookup_module_info);
#endif

#if defined(LOOKUP_WHOSON) && LOOKUP_WHOSON!=2
addlookupmodule(&whoson_lookup_module_info);
#endif

/* This is provided by the spf "misc" module, and the lookup aspect is always
linked statically whether or not the "misc" module (and hence libspf2) is
dynamic-load. */

#ifdef EXIM_HAVE_SPF
addlookupmodule(&spf_lookup_module_info);
#endif

/* This is a custom expansion, and not available as either
a list-syntax lookup or a lookup expansion. However, it is
implemented by a lookup module. */

addlookupmodule(&readsock_lookup_module_info);

DEBUG(D_lookup) debug_printf_indent("Total %d built-in lookups\n", lookup_list_count);


#ifdef LOOKUP_MODULE_DIR
if (!(dd = exim_opendir(CUS LOOKUP_MODULE_DIR)))
  {
  EARLY_DEBUG(D_lookup, "Couldn't open %s: not loading lookup modules\n", LOOKUP_MODULE_DIR);
  log_write(0, LOG_MAIN|LOG_PANIC,
	  "Couldn't open %s: not loading lookup modules\n", LOOKUP_MODULE_DIR);
  }
else
  {
  /* Look specifically for modules we know offer several lookup types and
  load them now, since we cannot load-on-first-use. */

  struct dirent * ent;
  const pcre2_code * regex_islookupmod = regex_must_compile(
    US"(lsearch|ldap|nis)_lookup\\." DYNLIB_FN_EXT "$", MCS_NOFLAGS, TRUE);

  EARLY_DEBUG(D_lookup, "Loading lookup modules from %s\n", LOOKUP_MODULE_DIR);
  while ((ent = readdir(dd)))
    {
    char * name = ent->d_name;
    int len = (int)strlen(name);
    if (regex_match_and_setup(regex_islookupmod, US name, 0, 0))
      {
      uschar * errstr;
      if (lookup_mod_load(expand_nstring[1], &errstr))
	countmodules++;
      else
	{
	EARLY_DEBUG(D_any, "%s\n", errstr);
	log_write(0, LOG_MAIN|LOG_PANIC, "%s", errstr);
	}
      }
    }
  closedir(dd);
  }

EARLY_DEBUG(D_lookup, "Loaded %d lookup modules\n", countmodules);
#endif
}


/* Add module info struct to the modules list for those that are
built as static */

#if !defined(DISABLE_DKIM) && (!defined(SUPPORT_DKIM) || SUPPORT_DKIM!=2)
extern misc_module_info dkim_module_info;
#endif
#if defined(SUPPORT_DMARC) && SUPPORT_DMARC!=2
extern misc_module_info dmarc_module_info;
#endif
#if defined(EXIM_HAVE_SPF) && EXIM_HAVE_SPF!=2
extern misc_module_info spf_module_info;
#endif
#if defined(EXPERIMENTAL_ARC) && (!defined(SUPPORT_ARC) || SUPPORT_ARC!=2)
extern misc_module_info arc_module_info;
#endif
#if defined(RADIUS_CONFIG_FILE) && (!defined(SUPPORT_RADIUS) || SUPPORT_RADIUS!=2)
extern misc_module_info radius_module_info;
#endif
#if defined(SUPPORT_PAM) && SUPPORT_PAM!=2
extern misc_module_info pam_module_info;
#endif
#if defined(EXIM_PERL) && (!defined(SUPPORT_PERL) || SUPPORT_PERL!=2)
extern misc_module_info perl_module_info;
#endif
#if !defined(DISABLE_EXIM_FILTER) && (!defined(SUPPORT_EXIM_FILTER) || SUPPORT_EXIM_FILTER!=2)
extern misc_module_info exim_filter_module_info;
#endif
#if !defined(DISABLE_SIEVE_FILTER) && (!defined(SUPPORT_SIEVE_FILTER) || SUPPORT_SIEVE_FILTER!=2)
extern misc_module_info sieve_filter_module_info;
#endif

void
init_misc_mod_list(void)
{
static BOOL onetime = FALSE;
if (onetime) return;
onetime = TRUE;

#if !defined(DISABLE_DKIM) && (!defined(SUPPORT_DKIM) || SUPPORT_DKIM!=2)
  misc_mod_add(&dkim_module_info);
#endif
#if defined(EXIM_HAVE_SPF) && EXIM_HAVE_SPF!=2
  misc_mod_add(&spf_module_info);
#endif
#if defined(SUPPORT_DMARC) && SUPPORT_DMARC!=2
/* dmarc depends on spf so this add must go after, for the both-static case */
  misc_mod_add(&dmarc_module_info);
#endif
#if defined(EXPERIMENTAL_ARC) && (!defined(SUPPORT_ARC) || SUPPORT_ARC!=2)
  misc_mod_add(&arc_module_info);
#endif
#if defined(RADIUS_CONFIG_FILE) && (!defined(SUPPORT_RADIUS) || SUPPORT_RADIUS!=2)
  misc_mod_add(&radius_module_info);
#endif
#if defined(SUPPORT_PAM) && SUPPORT_PAM!=2
  misc_mod_add(&pam_module_info);
#endif
#if defined(EXIM_PERL) && (!defined(SUPPORT_PERL) || SUPPORT_PERL!=2)
  misc_mod_add(&perl_module_info);
#endif
#if !defined(DISABLE_EXIM_FILTER) && (!defined(SUPPORT_EXIM_FILTER) || SUPPORT_EXIM_FILTER!=2)
  misc_mod_add(&exim_filter_module_info);
#endif
#if !defined(DISABLE_SIEVE_FILTER) && (!defined(SUPPORT_SIEVE_FILTER) || SUPPORT_SIEVE_FILTER!=2)
  misc_mod_add(&sieve_filter_module_info);
#endif
}


#endif	/*!MACRO_PREDEF*/
/* End of drtables.c */
