/*
 * $Id: config.c,v 1.34 1999/10/18 18:41:21 labovit 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 "interface.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[] = {
  {"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   }
};


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);
}


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 ", MRT_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); 
  */
}

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 () {
  config_add_output ("irr_port %d\r\n", IRR.irr_port);
}


/* irr_port %d
 * The port we listen of for RAWhoisd style machine queries
 */
void config_irr_port (uii_connection_t *uii, int port) {
  if ((port <= 0) || (port > 20000)) {
    config_notice (NORM, uii, "CONFIG Error -- usage: irr_port <port_num>\n");
    return;
  }
  IRR.irr_port = port;
  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) {
  if (IRR.database_dir != NULL) 
    Delete (IRR.database_dir);

  IRR.database_dir = directory;
  config_add_module (0, "irr_directory", get_config_irr_directory, NULL); 
  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 (atts == 0) 
    config_add_output ("irr_database %s\r\n", database->name);
}


/* export-interval %d */
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);
    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 = strdup (filename);
  }

  return (1);
}



/* 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_sync_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_sync_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_lock (db);
  database_clear (db);
  irr_unlock (db);
  Delete (db); /* we need a real delete routine */
  Delete (name);
  return (1);
}

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

  database = find_database (name);

  if (database == NULL) {
    database = New (irr_database_t);
    database->radix = New_Radix (128);
    database->hash = 
      HASH_Create (1000,
		   HASH_KeyOffset, HASH_Offset (&hash_item, &hash_item.key),
		   HASH_DestroyFunction, irr_hash_destroy,
		   0);
    database->hash_spec = 
      HASH_Create (1000,
		   HASH_KeyOffset, HASH_Offset (&hash_item, &hash_item.key), 
                   HASH_DestroyFunction, irr_hash_destroy, 0);

    database->name = strdup (name);
    database->mirror_fd  = -1;
    database->journal_fd = -1;
    database->max_journal_bytes = IRR_MAX_JOURNAL_SIZE;
    database->fd         = NULL;
    database->db_syntax  = EMPTY_DB;
    database->obj_filter = 0; /* Any object bit-fields that are 1 will be filtered out
			       * of the DB (including mirroring, updates and reloads).
			       */
    pthread_mutex_init (&database->mutex_lock, NULL);
    /*rwl_init (&database->rwlock);*/
    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);
}

/* debug server file % */
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);
  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 % */
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);
  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 verbose */
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);

}



/* 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 tmp directory %s */
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);
  }

  return (1);
}




/* irr_database tmp directory %s */
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_sync_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_Set_Time (database->clean_timer, seconds);
    Timer_Turn_ON ((mtimer_t *) database->clean_timer);
  }

  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);
    return (-1);
  }

  if (object == NULL) {
    trace (TR_ERROR, default_trace,
	   "config_irr_database_filter (): NULL object input filter: ABORT! \n");
    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);
    }
  }

  free (s);
  return ret_val;
}

