/*
 * $Id: config.c,v 1.25 2002/10/17 20:02:29 ljb Exp $
 * originally Id: config.c,v 1.50 1998/07/20 01:22:03 labovit Exp 
 */

#include <stdio.h>
#include <string.h>
#include "mrt.h"
#include "trace.h"
#include <time.h>
#include <signal.h>
#include <fcntl.h>
#include <ctype.h>
#include "config_file.h"
#include "irrd.h"

extern trace_t *default_trace;

find_filter_t o_filter[] = {
  {"unused",	  XXX_F	         },	
  {"autnum",      AUTNUM_F       },
  {"asmacro",     ASMACRO_F      },
  {"community",   COMMUNITY_F    },
  {"domain",      DOMAIN_F       },
  {"inetnum",     INETNUM_F      },
  {"inet6num",    INET6NUM_F     },
  {"person",      PERSON_F       },
  {"dom-prefix",  DOMAIN_PREFIX_F},
  {"inet-rtr",    INET_RTR_F     },
  {"limerick",    LIMERICK_F     },
  {"mntner",      MNTNER_F       },
  {"route",       ROUTE_F        },
  {"role",        ROLE_F         },
  {"ipv6-site",   IPV6_SITE_F    },
  {"as-set",      AS_SET_F       },
  {"route-set",   ROUTE_SET_F    },
  {"filter-set",  FILTER_SET_F   },
  {"rtr-set",     RTR_SET_F      },
  {"peering-set", PEERING_SET_F  },
  {"dictionary",  DICTIONARY_F   },
  {"key-cert",    KEY_CERT_F     },
  {"repository",  REPOSITORY_F   },
};

u_int as_lookup_fn (u_short *as1, u_short *as2) {  
  return ((!((*as1) - (*as2))));
}

u_int as_hash_fn (u_short *as, u_int size)
{
    u_int value;

    value = *as % size;
    return (value);
}

#ifdef HJHJHH
void config_create_default () {
  char *tmp;
  CONFIG.ll_modules = LL_Create (0);

  tmp = strdup ("#####################################################################");
  config_add_module (0, "comment", get_comment_config, tmp);

  tmp = malloc (512);
  sprintf (tmp, "# IRRd -- version %s ", IRRD_VERSION);
  config_add_module (0, "comment", get_comment_config, tmp);
		
  tmp = strdup  ("#####################################################################");
  config_add_module (0, "comment", get_comment_config, tmp);
  config_add_module (0, "comment", get_comment_config, strdup ("!"));
  config_add_module (0, "comment", get_comment_config, strdup ("!"));  

  /*config_add_module (0, "irr_port", get_config_irr_port, NULL); 
  config_add_module (0, "irr_directory", get_config_irr_directory, NULL); 
  */
}
#endif 

void get_config_irr_max_connections () {
  config_add_output ("irr_max_connections %d\r\n", IRR.max_connections);
}

/* irr_max_con %d 
 * maximum number of silumtaneous open TCP connections to our whois port
 */
int config_irr_max_con (uii_connection_t *uii, int max) {

  if ((max <= 0) || (max >= 10000)) {
    config_notice (NORM, uii, "CONFIG Error -- usage: irr_max_connections <1-1000>\n", max);
    return (-1);
  }
  IRR.max_connections = max;
  config_add_module (0, "irr_max_connections", get_config_irr_max_connections, NULL); 
  return (1);
}

/* return the irr_port (whois) on which we are listening */
void get_config_irr_port () {
  if (IRR.irr_port_access == 0)
    config_add_output ("irr_port %d\r\n", IRR.irr_port);
  else
    config_add_output ("irr_port %d access %d\r\n", IRR.irr_port, IRR.irr_port_access);
}

/* irr_port %d
 * The port we listen on for RAWhoisd style machine queries
 */
void config_irr_port (uii_connection_t *uii, int port, int access_list) {
  if ((port <= 0) || (port > 60000)) {
    config_notice (NORM, uii, "CONFIG Error -- usage: irr_port <port_num> [access <acccess list>]\n");
    return;
  }
  IRR.irr_port = port;

  if ((access_list < 1) || (access_list > 101)) {
    config_notice (NORM, uii, "CONFIG Error -- usage: irr_port <port_num> [access <acccess list>]\n");
    return;
  }

  IRR.irr_port_access = access_list;
  
  config_add_module (0, "irr_port", get_config_irr_port, NULL); 
}

/* irr_port %d
 * The port we listen on for RAWhoisd style machine queries
 */
void config_irr_port_2 (uii_connection_t *uii, int port) {
  if ((port <= 0) || (port > 60000)) {
    config_notice (NORM, uii, "CONFIG Error -- usage: irr_port <port_num> [access <acccess list>]\n");
    return;
  }
  IRR.irr_port = port;

  /* reset to no access list filtering */
  IRR.irr_port_access = 0;

  config_add_module (0, "irr_port", get_config_irr_port, NULL); 
}

void get_config_irr_mirror_interval () {
  config_add_output ("irr_mirror_interval %d\r\n", IRR.mirror_interval);
}

void config_irr_mirror_interval (uii_connection_t *uii, int interval) {
  IRR.mirror_interval = interval;
  config_add_module (0, "irr_mirror_interval", get_config_irr_mirror_interval, NULL); 
}

void get_config_irr_directory () {
  config_add_output ("irr_directory %s\r\n", IRR.database_dir);
}

int config_irr_directory (uii_connection_t *uii, char *directory) {
  char status_filename[BUFSIZE];

  if (IRR.database_dir != NULL) 
    Delete (IRR.database_dir);

  IRR.database_dir = directory;
  config_add_module (0, "irr_directory", get_config_irr_directory, NULL); 

  sprintf(status_filename, "%s/IRRD_STATUS", directory);
  if (IRR.statusfile) {
    CloseStatusFile(IRR.statusfile);
    Delete(IRR.statusfile);
  }
  IRR.statusfile = InitStatusFile(status_filename);

  return (1);
}

void get_config_irr_database (irr_database_t *database) {
  int atts = 0;

  if (database->flags & IRR_AUTHORITATIVE) {
    config_add_output ("irr_database %s authoritative\r\n", 
		       database->name);
    atts = 1;
  }

  if (database->mirror_prefix != NULL) {
    config_add_output ("irr_database %s mirror_host %s %d\r\n", 
		       database->name,
		       prefix_toa (database->mirror_prefix),
		       database->mirror_port);
    atts =1;
  }

  if (database->access_list != 0) {
    config_add_output ("irr_database %s access %d\r\n", 
		       database->name,
		       database->access_list);
    atts =1;
  }

  if (database->write_access_list != 0) {
    config_add_output ("irr_database %s write-access %d\r\n", 
		       database->name,
		       database->write_access_list);
    atts =1;
  }

  if (database->mirror_access_list != 0) {
    config_add_output ("irr_database %s mirror-access %d\r\n", 
		       database->name,
		       database->mirror_access_list);
    atts =1;
  }

  if (database->export_timer != NULL) {
    config_add_output ("irr_database %s export %d\r\n", 
		       database->name, 
		       database->export_timer->time_interval_base);
    atts =1;
  }

  if ((database->clean_timer != NULL) && (!database->no_dbclean)) {
    config_add_output ("irr_database %s clean %d\r\n", 
		       database->name, 
		       database->clean_timer->time_interval_base);
    atts =1;
  }

  if (database->no_dbclean) {
    config_add_output ("irr_database %s no-clean\r\n", database->name);
    atts =1;
  }

  if (database->flags & IRR_NODEFAULT) {
    config_add_output ("irr_database %s no-default\r\n", 
		       database->name);
    atts = 1;
  }
  
  if (atts == 0) 
    config_add_output ("irr_database %s\r\n", database->name);
}


int config_irr_database_nodefault (uii_connection_t *uii, char *name) {
  irr_database_t *database = NULL;

  if ((database = find_database (name)) == NULL) {
    config_notice (ERROR, uii, "Database %s not found!\r\n", name);
    Delete (name);
    return (-1);
  }

  Delete (name);
  database->flags |= IRR_NODEFAULT;

  return (1);
}

/* irr_database %s export %d [%s] */
int config_irr_database_export (uii_connection_t *uii, char *name, int interval, int num, char *filename) {
  irr_database_t *database = NULL;

  if ((database = find_database (name)) == NULL) {
    config_notice (ERROR, uii, "Database %s not found!\r\n", name);
    Delete (name);
    if (num == 1)
      Delete(filename);
    return (-1);
  }

  /* once a day */
  database->export_timer = (mtimer_t *) 
  New_Timer (irr_export_timer, interval, "IRR export", database);
  timer_set_jitter (database->export_timer, (int) (interval / 3.0));
  Timer_Turn_ON ((mtimer_t *) database->export_timer);
  
  if (num == 1) {
    database->export_filename = filename;
  }

  Delete(name); 
  return (1);
}

/* Parse the irrd.conf command 'irr_path %s' and save the specified 
 * path component (which can be used to find irrdcacher, wget, 
 * ripe2rpsl ...).  The specified path component is added to the
 * users PATH environment variable when external binaries are invoked.
 *
 * irrd.conf command:
 * irr_path %s
 *
 * eg, 'irr_path :/irr/bin:/usr/local/joe/mybin
 *
 * Input:
 *  -data struct for communicating with UII users (uii)
 *  -a path component (path)
 *
 * Return:
 *  -void
 *
 * set's 'IRR.path' to (path)
 */
int config_irr_path (uii_connection_t *uii, char *path) {
  char buf[BUFSIZE+2];

  if (path == NULL) {
    config_notice (ERROR, uii, "NULL irr path component!\r\n");
    return -1;
  }
  else if (strlen (path) > BUFSIZE) {
    config_notice (ERROR, uii, "path component too large! MAX(%d)\r\n", BUFSIZE);
    Delete (path);
    return -1;
  }

  Delete (IRR.path);

  /* make sure path component begin's with a ':' */
  if (*path != ':')
    sprintf (buf, ":%s", path);
  else
    strcpy (buf, path);

  IRR.path = strdup (buf);
  Delete (path);

  /* let the user know we have updated the path */
  config_notice (NORM, uii, "path component set to (%s)\r\n", IRR.path);

  return 1;
}


#ifndef NT
/* Reset the ftp URL for DB (name)
 *
 * URL to be used by irrdcacher for remote DB retrievel.  
 *
 * (name) must not be an authoritative DB.
 *
 * irrd.conf command:
 * irr_database %s remote_ftp_url %s
 *
 * eg, 'irr_database ripe remote_ftp_url ftp://ftp.ripe.net/ripe/dbase
 * 
 * Input:
 *  -data struct for communicating with UII users (uii)
 *  -DB name to associate the ftp URL with (name)
 *  -ftp URL to use for remote DB refreshes (dir)
 *
 * Return:
 *  -1 if there were no errors in the command
 *  --1 otherwise
 *
 *  set's the 'remote_ftp_url' irr_database_t member with (dir)
 */
int config_irr_remote_ftp_url (uii_connection_t *uii, char *name, char *dir) {
  int ret_code = 1;
  irr_database_t *db;
  regex_t url_re;
  char *ftp_url = "^ftp://[^ \t/]+/[[:graph:]]+$";

  /* Do we know about this DB? */
  if ((db = find_database (name)) == NULL) {
    config_notice (ERROR, uii, "Database %s not found!\r\n", name);
    ret_code = -1;
    goto INPUT_ERROR;
  }

  /* make sure this DB is not authoritative */
  if (db->flags & IRR_AUTHORITATIVE) {
    config_notice (ERROR, uii, "*ERROR* database %s is authoritative !\r\n",
		   name);
    ret_code = -1;
    goto INPUT_ERROR;
  }

  /* make sure the remote url is in valid URL format */

  /* compile our reg ex */
  regcomp (&url_re,  ftp_url, REG_EXTENDED|REG_NOSUB);

  /* check the URL format */
  if (regexec (&url_re, dir, 0, NULL, 0)) {
    config_notice (ERROR, uii, "Invalid URL format (%s)! (ftp://<remote server>/<remote directory>)\r\n", dir);
    ret_code = -1;
    }
  else {
    /* make purify happy */
    Delete (db->remote_ftp_url);
    db->remote_ftp_url = strdup (dir);
    config_notice (NORM, uii, "Remote ftp URL (%s) set for (%s)\r\n", dir, name);
  }
  regfree (&url_re);

INPUT_ERROR:
  Delete (dir);
  Delete (name);

  return ret_code;
}


/* Set the repository signature hex ID for DB (name) to (hexid).
 *
 * The hexid can be in "[[:xdigit:]]{8}" format or "0x[[:xdigit:]]{8}".
 * Internally the hexid will be stored as "0x[[:xdigit:]]{8}".
 *
 * Input:
 *  -data struct for communicating with UII users (uii)
 *  -DB to set the repository hex ID (name)
 *  -hex ID of the repository signature (hexid)
 * 
 * Return:
 *  -1 if the operation was successful
 *  --1 otherwise
 */
int config_irr_repos_hexid (uii_connection_t *uii, char *name, char *hexid) {
  char hex_out[16];
  int ret_code = -1;
  irr_database_t *db;
  regex_t re;
  char *hex_num  = "^(0x)?[[:xdigit:]]{8}$";
  
  /* Do we know about this DB? */
  if ((db = find_database (name)) == NULL) {
    config_notice (ERROR, uii, "Database %s not found!\r\n", name);
    goto INPUT_ERROR;
  }

  /* compile the regex */
  regcomp (&re, hex_num, REG_EXTENDED|REG_NOSUB);

  /* see if the (hexid) is well-formed */
  if (regexec (&re, hexid, 0, NULL, 0) == 0) {
    if (*hexid == '0')
      strcpy (hex_out, hexid);           /* perfect! */
    else
      sprintf (hex_out, "0x%s", hexid);  /* need to add a leading '0x' */

    /* replace the old repos hexid with the new */
    Delete (db->repos_hexid);
    db->repos_hexid = strdup (hex_out);
    ret_code = 1;
    config_notice (NORM, uii, 
		   "Repository hex ID (%s) set for (%s)\r\n", hexid, name);

  }
  else
    config_notice (NORM, uii, 
		   "Malformed hex ID (%s) expecting \"[[:xdigit:]]{8}\"\r\n", hexid);

  regfree (&re);

INPUT_ERROR:
  Delete (hexid);
  Delete (name);

  return ret_code;
}


/* Set the PGP password for DB (name)'s repository object.
 *
 * (passwd) can have spaces and may be enclosed in "'s.  The
 * enclosing "'s (if any) are not considered part of the password.
 *
 * Input:
 *  -data struct for communicating with UII users (uii)
 *  -DB to set pgp password (name)
 *  -password for the repository object (passwd)
 * 
 * Return:
 *  -1 if the operation was successful
 *  --1 otherwise
 */
int config_irr_pgppass (uii_connection_t *uii, char *name, char *passwd) {
  char pgppass[BUFSIZE+1];
  int ret_code = -1, n, offset;
  irr_database_t *db;
  regmatch_t rm[5];
  regex_t re;
  char *pass  = "^((\"(.*)\")|([^\" \t\n]*))[ \t]*\n?$";

  /* Do we know about this DB? */
  if ((db = find_database (name)) == NULL) {
    config_notice (ERROR, uii, "Database %s not found!\r\n", name);
    goto INPUT_ERROR;
  }

  /* compile the regex */
  regcomp (&re, pass, REG_EXTENDED);

  /* see if the (password) is well-formed */
  if (regexec (&re, passwd, 5, rm, 0) == 0) {
    if (rm[3].rm_so != -1)
      offset = 3;
    else
      offset = 4;
    if ((n = (rm[offset].rm_eo - rm[offset].rm_so)) <= BUFSIZE) {
      strncpy (pgppass, (char *) (passwd + rm[offset].rm_so), n);
      pgppass[n] = '\0';
      Delete (db->pgppass);
      db->pgppass = strdup (pgppass);
      ret_code = 1;
      config_notice (NORM, uii, "PGP password (%s) set for (%s)\r\n", pgppass, name);
    }
    else
      config_notice (ERROR, uii, "PGP password is too long!  MAX(%d)\r\n", BUFSIZE);
  }
  else
    config_notice (ERROR, uii, "Malformed PGP password\r\n");

  regfree (&re);

INPUT_ERROR:
  Delete (passwd);
  Delete (name);

  return ret_code;
}

#endif /* NT */

/* Add (dbname) to the list of rpsdist managed DB's.
 *
 * sample irrd.conf entry:
 * rpsdist_database bell

 * rpsdist does not trust DB's defined this way.  DB's 
 * will normally be added this way dynamically from the 
 * !C command.
 * 
 * A corresponding entry is made to irrd so that it will know
 * about the DB and will accept !ms...!me commands from rpsdist.
 * 
 * Input:
 *  -name of the DB to add to the rpsdist list of DB's
 *
 * Return:
 *  -1 if no errors
 *  --1 otherwise
 */
int config_rpsdist_database (uii_connection_t *uii, char *dbname) {
  int ret_code = -1;
  irr_database_t *db;

  /* look up the DB */
  db = find_database (dbname);

  /* mis-configuration checking */
  if (db != NULL &&
      db->mirror_prefix != NULL) {
    config_notice (NORM, uii, "CONFIG Error -- conflicting "
		   "\"irr_database/rpsdist_database\" configurations "
		   "for DB %s\r\n", dbname);
    goto CLEAN_UP;
  }

  if ((ret_code = config_irr_database_authoritative (uii, strdup (dbname))) < 0)
    goto CLEAN_UP;

  /* look up the DB */
  if (db == NULL)
    db = find_database (dbname);
  db->rpsdist_flag = 1;

CLEAN_UP:

  /* clean-up */
  Delete (dbname);

  return ret_code;
}

/* Accept rpsdist connections from (host).
 *
 * sample irrd.conf entry:
 * rpsdist accept 198.108.60.1
 *
 * Allows mirror-only hosts to connect to us and get
 * transaction updates from us. (eg, a router.  any host
 * that does not have an DB but wishes to have a local
 * copy of the IRR)
 * 
 * Input:
 *  -host to accept connections from which we will send 
 *   transactions but not accept updates.
 *
 * Return:
 *  -1 if no errors
 *  --1 otherwise
 */
int config_rpsdist_accept (uii_connection_t *uii, char *host) {
  int ret_code = 1;

  /* host */
  if (string_toprefix (host, default_trace) == NULL) {
    config_notice (NORM, uii, "CONFIG Error -- could not resolve %s\r\n", host);
    ret_code = -1;
  }
  
  Delete (host);
  
  return ret_code;
}

/* Accept rpsdist connections from (host) and also
 * accept poll requests and unicasted transactions.
 *
 * sample irrd.conf entry:
 * rpsdist ripe accept 198.108.60.1
 *
 * Allows a remote host to send us rpsdist updates
 * from more than 1 host or from a remote host that
 * we don't trust.
 * 
 * Input:
 *  -host to accept connections from which we will send 
 *   transactions and accept updates.
 *  -name of the DB to add to the rpsdist list of DB's
 *
 * Return:
 *  -1 if no errors
 *  --1 otherwise
 */
int config_rpsdist_accept_database (uii_connection_t *uii, char *dbname,
				    char *host) {
  int ret_code = -1;
  irr_database_t *db;
  
  /* look up the DB */
  db = find_database (dbname);

  /* mis-configuration checking */
  if (db != NULL &&
      db->mirror_prefix != NULL) {
    config_notice (NORM, uii, "CONFIG Error -- conflicting "
		   "\"irr_database/rpsdist_database\" configurations "
		   "for DB %s\r\n", dbname);
    goto CLEAN_UP;
  }

  /* host and port */
  if (string_toprefix (host, default_trace) == NULL) {
    config_notice (NORM, uii, "CONFIG Error -- could not resolve %s\r\n", host);
    goto CLEAN_UP;
  }
  
  if ((ret_code = config_irr_database_authoritative (uii, strdup (dbname))) < 0)
    goto CLEAN_UP;

  /* look up the DB */
  if (db == NULL)
    db = find_database (dbname);

  /* if we get here all is well */
  db->rpsdist_flag = 1;
  db->rpsdist_accept_host = strdup (host);
  
CLEAN_UP:

  /* clean-up */
  Delete (dbname);
  Delete (host);
  
  return ret_code;
}

/* Parse the conf command line for "rpsdist_database %s trusted %s %d".
 *
 * eg, rpsdist_database ripe trusted whois.ripe.net 43
 *
 * The DB becomes trusted for ripe which allows rpsdist to accept
 * signed updates to be accepted without auth checking.  The host
 * and port tell rpsdist where to connect for floods/polls.
 *
 * Input:
 *  -data struct for communicating with UII users (uii)
 *  -DB to associate the information to (dbname)
 *  -host and port to connect to for floods/polls (host, port)
 *
 * Return:
 *  -1 if there were no errors
 *  --1 otherwise
 */
int config_rpsdist_database_trusted (uii_connection_t *uii, char *dbname, 
				     char *host, int port) {
  int ret_code = -1;
  irr_database_t *db;
  
  /* look up the DB */
  db = find_database (dbname);

  /* mis-configuration checking */
  if (db != NULL          &&
      (db->rpsdist_trusted ||
       db->rpsdist_auth    ||
       db->mirror_prefix != NULL)) {
    config_notice (NORM, uii, "CONFIG Error -- conflicting "
		   "\"irr_database/rpsdist_database\" configurations "
		   "for DB %s\r\n", dbname);
    goto CLEAN_UP;
  }    

  /* host */
  if (string_toprefix (host, default_trace) == NULL) {
    config_notice (NORM, uii, "CONFIG Error -- could not resolve %s\r\n", host);
    goto CLEAN_UP;
  }
  
  /* if we get here there were no errors */
  config_notice (NORM, uii, 
		 "rpsdist DB (%s) set values: (host, port)="
		 "(%s, %d)\r\n", dbname, host, port);

  /* let irrd know about this DB */
  if ((ret_code = config_irr_database_authoritative (uii, strdup (dbname))) < 0)
    goto CLEAN_UP;

  /* look up the DB */
  if (db == NULL)
    db = find_database (dbname);
  
  /* set the values */
  db->rpsdist_flag    = 1;
  db->rpsdist_trusted = 1;
  db->rpsdist_host    = strdup (host);
  db->rpsdist_port    = port;
  
CLEAN_UP:

  /* clean-up */
  Delete (dbname);
  Delete (host);

  return ret_code;
}

/* Parse the conf command line for "rpsdist_database %s authoritative %s".
 *
 * eg, rpsdist_database radb authoritative "Iftdpa!"
 *
 * radb becomes authoritative which allows rpsdist to accept
 * updates.  The password is for the signing key which
 * is needed by rpsdist when flooding.
 *
 * Input:
 *  -data struct for communicating with UII users (uii)
 *  -DB to associate the information to (dbname)
 *  -pgp password for the signing key (passwd)
 *
 * Return:
 *  -1 if there were no errors
 *  --1 otherwise
 */
int config_rpsdist_database_authoritative (uii_connection_t *uii, char *dbname, 
					   char *passwd) {
  int ret_code = -1, n, offset;
  irr_database_t *db;
  char pgppass[BUFSIZE+1];
  regmatch_t rm[5];
  regex_t re_pass;
  char *pass = "^((\"(.*)\")|([^\" \t\n]*))[ \t]*\n?$";
  
  /* look up the DB */
  db = find_database (dbname);

  /* mis-configuration checking */
  if (db != NULL          &&
      (db->rpsdist_trusted ||
       db->mirror_prefix != NULL)) {
    config_notice (NORM, uii, "CONFIG Error -- conflicting "
		   "\"irr_database/rpsdist_database\" configurations "
		   "for DB %s\r\n", dbname);
    goto CLEAN_UP;
  }

  /* PGP password */
  regcomp (&re_pass, pass, REG_EXTENDED);

  /* see if the (password) is well-formed */
  if (regexec (&re_pass, passwd, 5, rm, 0) == 0) {
    if (rm[3].rm_so != -1)
      offset = 3;
    else
      offset = 4;

    if ((n = (rm[offset].rm_eo - rm[offset].rm_so)) <= BUFSIZE) {
      strncpy (pgppass, (char *) (passwd + rm[offset].rm_so), n);
      pgppass[n] = '\0';
      ret_code = 1;
    }
    else
      config_notice (ERROR, uii, "PGP password is too long!  MAX(%d)\r\n", 
		     BUFSIZE);
  }
  else
    config_notice (ERROR, uii, "Malformed PGP password\r\n");

  regfree (&re_pass);
  if (ret_code < 0)
    goto CLEAN_UP;

  /* if we get here there were no errors */
  config_notice (NORM, uii, 
		 "rpsdist DB (%s) set values: (PGPPASS)="
		 "(%s)\r\n", dbname, pgppass);

  /* let irrd know about this DB */
  if ((ret_code = config_irr_database_authoritative (NULL, strdup (dbname))) < 0)
    goto CLEAN_UP;

  /* look up the DB */
  if (db == NULL)
    db = find_database (dbname);
  
  /* set the values */
  db->rpsdist_flag = 1;
  db->rpsdist_auth = 1;
  db->pgppass      = strdup (pgppass);

CLEAN_UP:
  /* clean-up */
  Delete (dbname);
  Delete (passwd);

  return ret_code;
}

/* config no irr_database %s authoritative */
int no_config_irr_database_authoritative (uii_connection_t *uii, char *name) {
  irr_database_t *database = NULL;

  if ((database = find_database (name)) == NULL) {
    config_notice (ERROR, uii, "Database %s not found!\r\n", name);
    Delete (name);
    return (-1);
  }

  trace (NORM, default_trace, "CONFIG %s no authoritative\n", name);

  database->flags = 0;
  Delete (name);
  return (1);
}

/* config irr database %s authoritative */
int config_irr_database_authoritative (uii_connection_t *uii, char *name) {
  irr_database_t *database = NULL;

  if ((database = find_database (name)) == NULL)
    config_irr_database (uii, strdup (name));

  if ((database = find_database (name)) == NULL) {
    Delete (name);
    return (-1);
  }

  /* can't be authoritative and mirror at the same time! */
  if (database->mirror_prefix != NULL) {
    config_notice (ERROR, uii, "*ERROR* database %s is configured for mirroring!\r\n",
		   name);
    Delete (name);
    return (-1);
  }

  /* since we're authoritative, turn on cleaning by default */
  if ((database->clean_timer == NULL) && (!database->no_dbclean)) {
    database->clean_timer = (mtimer_t *) 
    New_Timer (irr_clean_timer, 60*60*24*2, "IRR dbclean", database);
    timer_set_jitter (database->clean_timer, 60*60*1);
    Timer_Turn_ON ((mtimer_t *) database->clean_timer);
  }

  trace (NORM, default_trace, "CONFIG %s authoritative\n", name);
  database->flags |= IRR_AUTHORITATIVE;
  config_add_module (0, "irr_database", get_config_irr_database, database); 
  Delete (name);
  return (1);
}

/* config irr_database %s mirror %s<hostname> %d<port> */
int config_irr_database_mirror (uii_connection_t *uii, char *name,
				 char *host, int port) {
  irr_database_t *database = NULL;

  if ((database = find_database (name)) == NULL)
    config_irr_database (uii, strdup (name));

  if ((database = find_database (name)) == NULL) {
    Delete (name);
    Delete (host);
    return (-1);
  }

  if (database->flags &= IRR_AUTHORITATIVE) {
    config_notice (ERROR, uii, "*ERROR* database %s is authoritative!\r\n", name);
    Delete (name);
    Delete (host);
    return (-1);
  }

  trace (NORM, default_trace, "CONFIG %s mirror\n", name);

  if ((database->mirror_prefix = string_toprefix (host, default_trace)) == NULL) {
    config_notice (NORM, uii, "CONFIG Error -- could not resolve %s\r\n", host);
    Delete (name);
    Delete (host);
    return (-1);
  }
  database->mirror_port = port;

  database->mirror_timer = (mtimer_t *)
    New_Timer (irr_mirror_timer, IRR.mirror_interval, "IRR Mirror", database);
  timer_set_jitter (database->mirror_timer, 40);
  Timer_Turn_ON ((mtimer_t *) database->mirror_timer);

  /* since we're mirroring, turn on cleaning by default */
  if ((database->clean_timer == NULL) && (!database->no_dbclean)) {
    database->clean_timer = (mtimer_t *) 
    New_Timer (irr_clean_timer, 60*60*24*2, "IRR dbclean", database);
    timer_set_jitter (database->clean_timer, 60*60*1);
    Timer_Turn_ON ((mtimer_t *) database->clean_timer);
  }

  Delete (name);
  Delete (host);
  return (1);
}

/* config irr_database %s access %d
 *  Control access to database
 */
int config_irr_database_access (uii_connection_t *uii, char *name, int num) {
  irr_database_t *database = NULL;

  if ((database = find_database (name)) == NULL) 
    config_irr_database (uii, strdup (name));

  if ((database = find_database (name)) == NULL) {
    Delete (name);
    return (-1);
  }

  trace (NORM, default_trace, "CONFIG %s access-list %d\n", name, num);
  config_add_module (0, "access", get_config_irr_database, database); 

  database->access_list = num;
  Delete (name);
  return (1);
}

/* config irr_database %s mirror-access %d */
int config_irr_database_mirror_access (uii_connection_t *uii, char *name, int num) {
  irr_database_t *database = NULL;

  if ((database = find_database (name)) == NULL) {
    config_notice (ERROR, uii, "Database %s not found!\r\n", name);
    Delete (name);
    return (-1);
  }

  trace (NORM, default_trace, "CONFIG %s access-list write %d\n", name, num);
  config_add_module (0, "sychronization", get_config_irr_database, database); 

  database->mirror_access_list = num;
  Delete (name);
  return (1);
}

/* config irr_database %s write-access %d */
int config_irr_database_access_write (uii_connection_t *uii, char *name, int num) {
  irr_database_t *database = NULL;

  if ((database = find_database (name)) == NULL) {
    config_notice (ERROR, uii, "Database %s not found!\r\n", name);
    Delete (name);
    return (-1);
  }

  trace (NORM, default_trace, "CONFIG %s access-list write %d\n", name, num);
  config_add_module (0, "database access", get_config_irr_database, database); 

  database->write_access_list = num;
  Delete (name);
  return (1);
}

/* no config irr_database %s */
int no_config_irr_database (uii_connection_t *uii, char *name) {
  irr_database_t *db;

  if ((db = find_database (name)) == NULL) {
    Delete (name);
    config_notice (NORM, uii, "CONFIG no database %s -- database not found!\r\n", db->name);
    return (-1);
  }

  config_notice (NORM, uii, "CONFIG database %s deleted\r\n", db->name);
  LL_Remove (IRR.ll_database, db);
  irr_update_lock (db);
  radix_flush(db->radix);
  HASH_Destroy(db->hash);
  HASH_Destroy(db->hash_spec);
  Delete(db->name);
  irr_update_unlock (db);
  pthread_mutex_destroy(&db->mutex_lock);
  pthread_mutex_destroy(&db->mutex_clean_lock);
  Delete (db);
  Delete (name);
  return (1);
}

/* config irr_database xxxx */
int config_irr_database (uii_connection_t *uii, char *name) {
  irr_database_t *database;

  database = find_database (name);

  if (database == NULL) {
    database = new_database (name);
    LL_Add (IRR.ll_database, database);
  }
  config_add_module (0, "irr_database", get_config_irr_database, database); 
  Delete (name);
  return (1);
}

void get_config_server_debug () {
  config_add_output ("debug server file-name %s\r\n", 
		     default_trace->logfile->logfile_name);

  if ((strcmp ("stdout", default_trace->logfile->logfile_name) != 0) &&
      (default_trace->logfile->max_filesize > 0))
    config_add_output ("debug server file-max-size %d\r\n", 
		       default_trace->logfile->max_filesize);

  if (default_trace->syslog_flag)
    config_add_output ("debug server syslog\r\n");
}

/* no debug server */
int no_config_debug_server (uii_connection_t *uii) {
  /* need to do something here !!! */
  return (1);
}

/* debug server verbose */
int config_debug_server_verbose (uii_connection_t *uii) {
  set_trace (default_trace, TRACE_FLAGS, TR_ALL, NULL);
  config_add_module (0, "debug", get_config_server_debug, NULL);
  return (1);
}

/* no debug server verbose */
int config_no_debug_server_verbose (uii_connection_t *uii) {
  set_trace (default_trace, TRACE_FLAGS, NORM, NULL);
  config_add_module (0, "debug", get_config_server_debug, NULL);
  return (1);
}

/* debug server file-name %s */
int config_debug_server_file (uii_connection_t *uii, char *filename) {
  set_trace (default_trace, TRACE_LOGFILE, filename, NULL);
  config_add_module (0, "debug", get_config_server_debug, NULL);
  Delete (filename);
  return (1);
}

/* debug server syslog */
int config_debug_server_syslog (uii_connection_t *uii) {
  set_trace (default_trace, TRACE_USE_SYSLOG, 1, 0);
  config_add_module (0, "syslog trace",
		     get_config_server_debug, IRR.submit_trace);
  return (1);
}

/* debug server size %d */
int config_debug_server_size (uii_connection_t *uii, int bytes) {
  set_trace (default_trace, TRACE_MAX_FILESIZE, bytes, NULL);
  config_add_module (0, "debug", get_config_server_debug, IRR.submit_trace);
  return (1);
}

void get_config_submission_debug () {
  config_add_output ("debug submission file-name %s\r\n", 
		     IRR.submit_trace->logfile->logfile_name);
  /* also add verbose and syslog here */

}

/* no debug submission */
int no_config_debug_submission (uii_connection_t *uii) {

  /* do something !!! */
  return (1);
}

/* debug submission file-name %s */
int config_debug_submission_file (uii_connection_t *uii, char *filename) {
  set_trace (IRR.submit_trace, TRACE_LOGFILE, filename, NULL);
  config_add_module (0, "debug", get_config_submission_debug, 
		     IRR.submit_trace);
  Delete (filename);
  return (1);
}

/* debug submission verbose */
int config_debug_submission_verbose (uii_connection_t *uii) {
  set_trace (IRR.submit_trace, TRACE_FLAGS, TR_ALL, NULL);
  config_add_module (0, "debug", get_config_submission_debug, NULL);
  return (1);
}

/* debug submission file-max-size %d */
int config_debug_submission_maxsize (uii_connection_t *uii, int size) {
  set_trace (IRR.submit_trace, TRACE_MAX_FILESIZE, size, NULL);
  config_add_module (0, "debug", get_config_submission_debug, NULL);
  return (1);
}

/* debug submission syslog */
int config_debug_submission_syslog (uii_connection_t *uii) {
  set_trace (IRR.submit_trace, TRACE_USE_SYSLOG, 1, NULL);
  config_add_module (0, "debug", get_config_submission_debug, NULL);
  return (1);
}

/* db_admin % s */
void get_config_dbadmin () {
  config_add_output ("db_admin %s\r\n", IRR.db_admin);
}

int config_dbadmin (uii_connection_t *uii, char *email) {
  IRR.db_admin = email;
  config_add_module (0, "dbadmin", get_config_dbadmin, NULL);
  return (1);
}

void get_config_responsefooter () {
  char *st;

  LL_Iterate (IRR.ll_response_footer, st) {
    config_add_output ("response_footer %s\r\n", st);
  }
}

/* response_footer %s */
int config_response_footer (uii_connection_t *uii, char *st) {

  if (IRR.ll_response_footer == NULL)
    IRR.ll_response_footer = LL_Create (0);
  if (st == NULL) 
    st = " ";
  LL_Add (IRR.ll_response_footer, strdup (st));
  config_add_module (0, "responsefooter", get_config_responsefooter, NULL);
  return (1);
}

void get_config_responsenotifyheader () {
  char *st;

  LL_Iterate (IRR.ll_response_notify_header, st) {
    config_add_output ("response_notify_header %s\r\n", st);
  }
}

/* response_notify_header %s */
int config_response_notify_header (uii_connection_t *uii, char *st) {

  if (IRR.ll_response_notify_header == NULL)
    IRR.ll_response_notify_header = LL_Create (0);
  if (st == NULL) 
    st = " ";
  LL_Add (IRR.ll_response_notify_header, strdup (st));
  config_add_module (0, "responsenotifyheader", get_config_responsenotifyheader, NULL);
  return (1);
}

void get_config_responseforwardheader () {
  char *st;

  LL_Iterate (IRR.ll_response_forward_header, st) {
    config_add_output ("response_forward_header %s\r\n", st);
  }
}

/* response_foward_header %s */
int config_response_forward_header (uii_connection_t *uii, char *st) {

  if (IRR.ll_response_forward_header == NULL)
    IRR.ll_response_forward_header = LL_Create (0);
  if (st == NULL) 
    st = " ";
  LL_Add (IRR.ll_response_forward_header, strdup (st));
  config_add_module (0, "responseforwardheader", get_config_responseforwardheader, NULL);
  return (1);
}

/* pgp_dir %s */  
void get_config_pgpdir () { 
  config_add_output ("pgp_dir %s\r\n", IRR.pgp_dir);
}

int config_pgpdir (uii_connection_t *uii, char *pgpdir) {
  IRR.pgp_dir = pgpdir;
  config_add_module (0, "pipe_line configuration", 
		     get_config_pgpdir, NULL);
  return (1);
}

/* override_cryptpw %s */
void get_config_override () { 
  config_add_output ("override_cryptpw %s\r\n", IRR.override_password);
}

int config_override (uii_connection_t *uii, char *override) {
  IRR.override_password = override;
  config_add_module (0, "override onfiguration", 
		     get_config_override, NULL);
  return (1);
}

/* irr_server %s */
void get_config_irr_host () { 
  config_add_output ("irr_server %s \r\n", IRR.irr_host);
}

int config_irr_host (uii_connection_t *uii, char *host) {
  IRR.irr_host = host;
  config_add_module (0, "pipe_line configuration", 
		     get_config_irr_host, NULL);
  return (1);
}

void get_irr_ftp_dir () { 
  config_add_output ("ftp directory %s \r\n", IRR.ftp_dir);
}

/* ftp directory %s */
int config_export_directory (uii_connection_t *uii, char *dir) {

  IRR.ftp_dir = dir;
  config_add_module (0, "ftp directory",
		     get_irr_ftp_dir, NULL);
  return (1);
}

void get_tmp_directory () { 
  config_add_output ("tmp directory %s\r\n", IRR.tmp_dir);
}

/* tmp directory %s */
int config_tmp_directory (uii_connection_t *uii, char *dir) {

  IRR.tmp_dir = dir;
  config_add_module (0, "tmp directory",
		     get_tmp_directory, NULL);
  return (1);
}

/* irr_database %s no-clean */
int config_irr_database_no_clean (uii_connection_t *uii, char *name) {
  irr_database_t *database;

  if ((database = find_database (name)) == NULL) {
    config_notice (ERROR, uii, "Database %s not found!\r\n", name);
    Delete (name);
    return (-1);
  }

  database->no_dbclean = 1;

  if (database->clean_timer) {
    Timer_Turn_OFF (database->clean_timer);
  }

  Delete (name);
  return (1);
}

/* irr_database %s clean %d */
int config_irr_database_clean (uii_connection_t *uii, char *name, int seconds) {
  irr_database_t *database;

  if ((database = find_database (name)) == NULL) {
    config_notice (ERROR, uii, "Database %s not found!\r\n", name);
    Delete (name);
    return (-1);
  }

  database->no_dbclean = 0;

  if (database->clean_timer == NULL) {
    database->clean_timer = (mtimer_t *) 
      New_Timer (irr_clean_timer, seconds, "IRR dbclean", database);
    timer_set_jitter (database->clean_timer, (int) (seconds/ 3.0));
    Timer_Turn_ON ((mtimer_t *) database->clean_timer);
  }
  else {
    Timer_Turn_OFF ((mtimer_t *) database->clean_timer);
    Timer_Set_Time (database->clean_timer, seconds);
    timer_set_jitter (database->clean_timer, (int) (seconds/ 3.0));
    Timer_Turn_ON ((mtimer_t *) database->clean_timer);
  }

  Delete (name);
  return (1);
}

/* Look for input of the form ~(...) and remove the '~(' and ')'.
 *
 * Return:
 *  1 if the above form is found in the input string
 *  0 otherwise
 *
 * If ~(...) is found in the input then remove it from the input
 * string and reset the input string.
 */
int remove_tilda (char **t) {
  char *q, *r, *s = *t;
  
  /* Find the first printable character */
  for (; isspace ((int) *s); s++);
  
  /* Look for a string like this: ~(...). 
   * Remove the opening '~(' and closing ')'
   */
  if (*s == '~'                        && 
      (q = strchr (s, '('))    != NULL &&
      (r = strrchr (++q, ')')) != NULL &&
      r > q) {
    *r = '\0';
    s = strdup (q);
    free (*t);
    *t = s;
    return 1;
  }
  
  return 0;
}

/* Search all known object types and check for a match
 * with the input string, "name".
 *
 * Return:
 *   The corresponding 'enum OBJECT_FILTERS' value that
 *   matches the input string (see scan.h for 'enum OBJECT_FILTERS' types).
 *  XXX_F otherwise (ie, if no match is found).
 */
enum OBJECT_FILTERS find_object_type (char *name) {
  int i;

  for (i = 0; i < MAX_OBJECT_FILTERS; i++)
    if (!strcasecmp (o_filter[i].name, name))
      return o_filter[i].filter_f;

  return XXX_F;
}

/* Set the bit field data structure, "database->obj_filter", which
 * controls which objects are used/accepted/kept in the database.
 * This is useful when we are dealing with VERY large databases like RIPE
 * that have lots of objects that we do NOT want to index or return queries
 * for.  "database->obj_filter" affects all loading operations, ie, mirroring,
 * updates/submissions, bootstrap loads and reloads.
 *
 * The filter is defined by the user in irrd.conf.  Filters define objects to keep in 
 * the DB.  The filter should be constructed as so:
 *
 * irr_database radb filter route|autnum|mntner
 * (allow only routes, autnum's and maintainer objects)
 *
 * irr_database radb filter ~(route|autnum)
 * (allow all objects except routes and autnums)
 *
 * Also recognized are the keywords "routing-registry-objects" and "non-critical"
 * which filter on the routing registry objects only (see scan.h).
 *
 * irr_database radb filter non-critical
 * (use all routing registry objects)
 *
 * Return:
 *   1 if a valid filter is found
 *  -1 if a non-valid filter is found
 * 
 *  "database->obj_filter" is set to the new filter is a valid filter is found.
 *  Otherwise "database->obj_filter" is left unchanged.
 */
int config_irr_database_filter (uii_connection_t *uii, char *name, char *object) {
  irr_database_t *database;
  char *last, *filter, *s;
  enum OBJECT_FILTERS F;
  u_long filter_bak;
  int ret_val = 1, tilda;

  if ((database = find_database (name)) == NULL) {
    config_notice (ERROR, uii, "Database %s not found!\r\n", name);
    Delete (name);
    if (object != NULL)
      Delete (object);
    return (-1);
  }

  if (object == NULL) {
    trace (TR_ERROR, default_trace,
	   "config_irr_database_filter (): NULL object input filter: ABORT! \n");
    Delete (name);
    return -1;
  }

  s                    = strdup (object);
  filter_bak           = database->obj_filter;
  database->obj_filter = ~0;

  tilda = remove_tilda (&s);

  filter = strtok_r (s, "|", &last);
  while (filter != NULL) {
    /* put a '0' in proper bit position ==> use/keep/accept this object type */
    if ((F = find_object_type (filter)) != XXX_F)
      database->obj_filter &= ~F;
    else if (!strcasecmp (filter, "routing-registry-objects") ||
	     !strcasecmp (filter, "non-critical"))
      database->obj_filter &= ~ROUTING_REGISTRY_OBJECTS;
    else {
      database->obj_filter = filter_bak;
      trace (TR_ERROR, default_trace, "Unknown filter %s\n", filter);
      ret_val = -1;
      break;
    }

    filter = strtok_r (NULL, "|", &last);
  }

  if (ret_val != -1) {
    /* Check to make sure at least one filter was applied */
    if (!(~database->obj_filter)) {
      database->obj_filter = filter_bak;
      trace (TR_ERROR, default_trace, "DB object filter unchanged!\n");
      trace (TR_ERROR, default_trace, "Did not recognize any object types: (%s)\n", object);
      ret_val = -1;
    }
    else {
      /* If a ~(...) was given we need to reverse the filter logic */
      if (tilda)
	database->obj_filter = ~database->obj_filter;
      trace (NORM, default_trace, "Config filter %s (%s)\n", database->name, object);
    }
  }

  Delete (name);
  Delete (object);
  free (s);
  return ret_val;
}


syntax highlighted by Code2HTML, v. 0.9.1