/*
 * $Id: database.c,v 1.17 2002/10/17 20:02:29 ljb Exp $
 * originally Id: database.c,v 1.48 1998/07/30 20:48:29 labovit Exp 
 */

#include <stdio.h>
#include <string.h>
#include "mrt.h"
#include "trace.h"
#include <time.h>
#include <signal.h>
#include <sys/types.h>
#ifndef NT
#include <dirent.h>
#endif /* NT */
#include "config_file.h"
#include <fcntl.h>
#include "irrd.h"
#include <pipeline_defs.h>
#include <atomic_ops.h>

extern trace_t *default_trace;

#ifndef NT
/* !B/reload support functions */
static int reopen_DB        (irr_database_t *, char *);
static int reopen_JOURNAL   (irr_database_t *, char *);
static int replace_cache_db (irr_database_t *, uii_connection_t *, char *);

/* bootstrap load support functions */
static int ftp_db_fetch     (irr_database_t *);
static int fetch_remote_db  (irr_database_t *, char *);
#endif /* NT */

void munge_buffer (char *buffer, irr_database_t *irr_database);
int irr_check_serial_vs_journal (irr_database_t *database);

/* database_clear
 * delete all indicies associated with a database 
 */
void database_clear (irr_database_t *db) {
  int i;

  if (db == NULL) {
    trace (ERROR, default_trace, "*** NULL DATABASE!!! ****\n");
    return;
  }

  trace (NORM, default_trace, "Clearing out database %s\n", db->name);

  db->bytes = 0;
  for (i=0; i< IRR_MAX_KEYS; i++) 
    db->num_objects[i] = 0;
  
  radix_flush (db->radix);

  if (db->hash)
    HASH_Clear (db->hash);

  if (db->hash_spec)
    HASH_Clear (db->hash_spec);

  db->radix = New_Radix (128);

  if (db->db_fp != NULL)
    fclose (db->db_fp);

  db->db_fp = NULL;
  db->db_syntax = EMPTY_DB;
}


/* Control the process of reloading database (name).
 *
 * Can be invoked by irrdcacher via the !B command or from the
 * uii via the 'reload <db name>' command or as part of a atomic
 * DB rollback operation.
 *
 * The reload process is carried out atomically and is resistent
 * to disk crashes.
 *
 * If (tmp_dir) is non-null then routine atomically replaces the
 * database in (tmp_dir) with the corresponding cache DB.  This is
 * how the !B would invoke this command.  Otherwise the 'reload <db name>' 
 * is assumed which means to rebuild the indices.  If both (uii) and
 * (tmp_dir) are NULL then the function is called as part of a DB rollback
 * operation.
 *
 * The function assumes the name of the new DB is the same as the
 * corresponding DB cache name.
 *
 * Input:
 *  -name of the DB to be reloaded (name)
 *  -data struct for communicating with UII users (uii)
 *  -(optional) directory to find the replacement DB (tmp_dir) 
 *
 * Return:
 *  -1 if there were no errors
 *  -0 if an error occured.
 */
int irr_reload_database (char *name, uii_connection_t *uii, char *tmp_dir) {
  irr_database_t *database;

  /* do we know of this DB ? */
  database = find_database (name);
  if (database == NULL) {
    if (uii != NULL) 
      uii_send_data (uii, "%s database not found\r\n", name);
    return 0;
  }

  /* if we are called for rollback then we already have the lock */
  if (uii != NULL || tmp_dir != NULL)
    irr_update_lock (database);

  /* atomically move the new DB into our cache area */
  if (tmp_dir != NULL) {
    if (!replace_cache_db (database, uii, tmp_dir)) {
      irr_update_unlock (database);
      trace (ERROR, default_trace, "irr_reload_database (): reload (%s) aborted!\n",
	     database->name);
      if (uii != NULL) 
	uii_send_data (uii, "Operation aborted!\r\n");
      return 0;
    }
  }
    
  if (uii != NULL) 
    uii_send_data (uii, "Clearing out old data...\r\n");
  database_clear (database);

  if (uii != NULL) 
    uii_send_data (uii,"Loading the database and re-building the indicies ...\r\n");

  /* rebuild the indexes and reload the serial file */
  scan_irr_file (database, NULL, 0, NULL);
  scan_irr_serial (database);

  /* TODO - Check return code and do something besides log the trace */
  irr_check_serial_vs_journal (database);

  /* if we are called for rollback then we already have the lock */
  if (uii != NULL || tmp_dir != NULL)
    irr_update_unlock (database);

  if (uii != NULL) 
    uii_send_data (uii, "Successful operation\r\n");

  return 1;
}

/*JW  add a \n to the end of the file to seperate objects */
void append_blank_line (FILE *fp) {
  size_t num;
  long start_offset, tmp_pos;
  char buffer[4];
  
  if ( fp == NULL )
    return;

  start_offset = ftell (fp);
  if (fseek (fp, 0, SEEK_END) < 0) 
    trace (NORM, default_trace, 
	   "** ERROR ** fseek() failed in append_blank_line! [1]");
  
  tmp_pos = ftell (fp);

  if (tmp_pos > 1) {
    if (fseek (fp, (long) tmp_pos - 2, SEEK_SET) < 0) 
      trace (NORM, default_trace, 
	     "** ERROR ** fseek() failed in append_blank_line! [2]");
    if ((num = fread (buffer, 1, 2, fp)) != 2)
      return;

    buffer[2] = '\0';
    if (strcmp (buffer, "\n\n")) {
      buffer[0] = '\n';
      buffer[1] = '\n';
      if (fseek (fp, 0, SEEK_END) < 0) 
	trace (NORM, default_trace, 
	       "** ERROR ** fseek() failed in append_blank_line! [3]");
      fwrite (buffer, 1, 2, fp);
    }
    
    if (fseek (fp, start_offset, SEEK_SET) < 0) 
      trace (NORM, default_trace, 
	     "** ERROR ** fseek() failed in append_blank_line! [4]");
  }
}

/* Load and build the DB indexes and serial numbers on bootstrap.
 *
 * Function will attempt to remote ftp missing non-authoritative DB's if the
 * '-x' command line flag is not set.
 *
 * Function returns the number of empty DB's encountered or a 
 * 'no DB's configured' situation.
 *
 * Input:
 *  -command line option to turn on/off the auto-db fetch feature (db_fetch_flag)
 *   A value of 1 means to turn on the auto-fetch feature.
 *  -flag to indicate verbose output to stdout (verbose)
 *  -uses global DB linked list structure to loop through each DB (IRR.ll_database)
 *
 * Return:
 *  --1 if no DB's were configured in irrd.conf
 *  -0..n the number of empty DB's encounterd (ie, configured but DB has
 *   no objects)
 */
int irr_load_data (int db_fetch_flag, int verbose) {
  int empty_dbs = 0, n = 0, i;
  irr_database_t *db;

  LL_Iterate (IRR.ll_database, db) {

    /* keep a count.  want to know if the user has not configured any DB's */
    n++;

    irr_update_lock (db);

    /* load the DB and build the indicies.  if DB is not in our
     * cache then attempt a remote irrdcacher fetch */
    if (scan_irr_file (db, NULL, 0, NULL) != NULL) {
      i = empty_dbs++;	
	
#ifndef NT
	/* did the user override our missing DB remote fetch behavior? 
	 * if not then see if we can retrieve the DB from an ftp site */
	if (db_fetch_flag &&
	    !(db->flags & IRR_AUTHORITATIVE))
	  empty_dbs -= ftp_db_fetch (db);
#endif /* NT */

	/* DB is empty and we could not remote fetch */
	if (empty_dbs > i) {
	  trace (NORM, default_trace, "WARNING: Database %s is empty!\n", db->name);
	  if (verbose)
	    printf ("WARNING: Database %s is empty!\n", db->name);
	}
      }
      
    /* initialize the serial file */
    scan_irr_serial (db);
      
    /* TODO - Check return code and do something besides log the trace */
    irr_check_serial_vs_journal (db);
    append_blank_line (db->db_fp);

    irr_update_unlock (db);
  }

  /* signal to caller that the user has not config'd any DB's */
  if (n == 0)
    return -1;

  /* return the number of empty DB's. */
  return empty_dbs;
}

/* the database on disk contains "xx", or deleted objects. Our pointers
 * in memory point to new objects later. This routine creates a new databse
 * and reloads memory with offsets of the new database. The new database file
 * on disk is free of any "xx" objects.
 */
int irr_database_clean (irr_database_t *database) {
  char dbfilename[MAXPATHLEN], cleanfilename[MAXPATHLEN];
  char buffer[BUFSIZE];
  char newdb[256];
  FILE *clean_fp, *db_fp;
  irr_database_t *cleaned_db;
  int line = 0, blank_line = 1;
  int long_line = 0;
  int i, skip = 0, clean_flag = 0;

  /* database cleaning disabled */
  if (database->no_dbclean) {
    trace (TR_ERROR, default_trace, "Clean aborted -- configured for %s as disabled!\n",
	   database->name);
    return (0);
  }

  sprintf (dbfilename, "%s/%s.db", IRR.database_dir, database->name);
  if ((db_fp = fopen (dbfilename, "r")) == NULL) {
    trace (TR_ERROR, default_trace, "Could not open db file %s:%s\n", 
	   dbfilename, strerror (errno));
    return (-1);
  }

  irr_clean_lock(database);	/* clean_lock still allows queries of the db */

  sprintf (cleanfilename, "%s/.%s.clean.db", IRR.database_dir, database->name);
  if ((clean_fp = fopen (cleanfilename, "w+")) == NULL) {
    trace (TR_ERROR, default_trace, "Could not open temporary file %s:%s\n", 
	   cleanfilename, strerror (errno));
    irr_clean_unlock (database);
    return (-1);
  }

  trace (NORM, default_trace, "Starting clean of %s\n", database->name);

  while (fgets (buffer, BUFSIZE, db_fp) != NULL) {
    line++;

    i = strlen(buffer);

    if (!long_line) {
      if ((i < 2)) {
        blank_line++;
      } else {
        if (blank_line > 0) {
	  if (skip != 0)
            skip = 0;
          if ( !strncasecmp ("*xx", buffer, 3) )
            skip = 1;
          blank_line = 0;
        }
      }

      /* filter out comments if not at beginning of the database */
      /* should we really do this? */
      if ( (*buffer == '#') && (line > 42) ) {
        skip = 1;
        blank_line = 1; /* need to set in case object follows */
      }
    }
    long_line = (buffer[i-1] != '\n');

    if ((skip != 1)  && (blank_line < 2))
      fwrite (buffer, 1, i, clean_fp);
    else {
      clean_flag = 1;	/* mark that database has been modified */
      /* trace (TR_TRACE, default_trace, "Skipping %s", buffer);*/ 
    }
  }

  fclose (db_fp);

  if (!clean_flag) {  /* if no changes to db file, we are done */
    fclose(clean_fp);
    unlink(cleanfilename);	/* clean up after ourselves */
    irr_clean_unlock (database); /* remove the clean lock */
    trace (NORM, default_trace, "Finished clean of %s; no changes\n", database->name);
    return (1);
  }

  sprintf (newdb, ".%s.clean", database->name);
  cleaned_db = new_database(newdb);
  cleaned_db->db_fp = clean_fp;
  cleaned_db->journal_fd = database->journal_fd;
  cleaned_db->db_syntax = database->db_syntax;
  cleaned_db->obj_filter = database->obj_filter;

  scan_irr_file (cleaned_db, NULL, 0, NULL);

  irr_lock(database);

  radix_flush(database->radix);
  database->radix = cleaned_db->radix;
  if (database->hash)
    HASH_Destroy(database->hash);
  database->hash = cleaned_db->hash;
  if (database->hash_spec)
    HASH_Destroy(database->hash_spec);
  database->hash_spec = cleaned_db->hash_spec;

  /* clean up temporary database */
  Delete(cleaned_db->name);
  pthread_mutex_destroy (&cleaned_db->mutex_lock);
  pthread_mutex_destroy (&cleaned_db->mutex_clean_lock);
  Delete(cleaned_db);

  fclose (clean_fp);
  fclose (database->db_fp);	/* close old database file */

  /* move .<database>.clean.db to <database>.db */
  if (rename (cleanfilename, dbfilename) < 0) {
    trace (TR_ERROR, default_trace, "Could not rename file to %s:%s\n", 
	   dbfilename, strerror (errno));
    irr_update_unlock (database);
    return (-1);
  }

  if ((db_fp = fopen (dbfilename, "r+")) == NULL) {
    trace (TR_ERROR, default_trace, "Could not open db file %s:%s\n", 
	   dbfilename, strerror (errno));
    irr_update_unlock (database);
    return (-1);
  }

  database->db_fp = db_fp;
  irr_update_unlock (database);
  trace (NORM, default_trace, "Finished clean of %s\n", database->name);

  return (1);
}

void irr_export_timer (mtimer_t *timer, irr_database_t *db) {
  irr_database_export (db);
}

void irr_clean_timer (mtimer_t *timer, irr_database_t *db) {
  irr_database_clean (db);
}

/* Given a DB as input determine it's syntax.
 * This function is designed to support reload's,
 * mirror's and !us...!ue updates.  It does not
 * syntax check the file but finds the first
 * attribute of the first object and determines
 * syntax.
 *
 * Return: (the DB syntax type)
 *   RPSL
 *   RIPE181
 *   EMPTY_DB
 *   UNRECOGNIZED
 */
enum DB_SYNTAX get_database_syntax (FILE *fp) {
  char buffer[BUFSIZE], *cp;
  long fpos;
  u_long offset = 0, position = 0;
  enum STATES state = BLANK_LINE, save_state;
  int db_syntax = EMPTY_DB;

  fpos = ftell (fp); /* save the current file pos */

  /* rewind */
  if (fseek (fp, 0L, SEEK_SET) < 0) 
    trace (NORM, default_trace, "** ERROR ** fseek failed in get_database_syntax");

  while (state != DB_EOF) {
    if ((cp = fgets (buffer, sizeof (buffer) - 1, fp)) != NULL) {
      position = offset;
      offset += strlen (buffer);
    }

    state = get_state (buffer, cp, state, &save_state);
    /* dump comment lines and lines that exceed the buffer size */
    if (state == OVRFLW     || 
	state == OVRFLW_END ||
	state == COMMENT    ||
	state == BLANK_LINE)
      continue;
    
    if (state == START_F) {
      if (buffer[0] == '%'            ||
	  !strncmp (buffer, "ADD", 3) ||
	  !strncmp (buffer, "DEL", 3))
	continue;

      /* we have a ripe181 DB or a deleted
       * ripe181 or rpsl object
       */
      if (buffer[0] == '*') {
	if (strlen (buffer) > 3) {
	  if (buffer[3] == ':')
	    db_syntax = RIPE181;
	  else
	    db_syntax = RPSL;
	}
	else
	  db_syntax = UNRECOGNIZED;
      }
      else
	db_syntax = RPSL; /* later check for all legal rpsl fields */
      
      break;
    }
  }

  /* restore file pos before exit */
  if (fseek (fp, fpos, SEEK_SET) < 0) 
    trace (NORM, default_trace, "** ERROR ** fseek failed in get_database_syntax");

  return (db_syntax);
}

int irr_database_export (irr_database_t *database) {
  char file1[BUFSIZE], file2[BUFSIZE];
  char new[BUFSIZE], command[BUFSIZE];
  u_long serial;

  if (IRR.ftp_dir == NULL) {
    trace (TR_ERROR, default_trace, "Aborting export -- ftp directory not configured!\n");
    return (0);
  }

  /* new database maybe? */
  if (database->db_fp == NULL) {
    trace (NORM, default_trace, "Export aborted -- NULL file pointer for %s\n", 
	   database->name);
    return (-1);
  }

  sprintf (file1, "%s/%s.db", IRR.database_dir, database->name);

  if (IRR.tmp_dir != NULL) 
    sprintf (file2, "%s/.%s.db.export", IRR.tmp_dir, database->name);
  else
    sprintf (file2, "%s/.%s.db.export", IRR.ftp_dir, database->name);

  /* copy to export area (or maybe tmp directory) _atomically_ */
  irr_clean_lock (database); /* clean lock still allows reads of database */
  serial = database->serial_number;
  if (irr_copy_file (file1, file2, 1) != 1) {
    irr_clean_unlock (database);
    trace (TR_ERROR, default_trace, "Export failed! Aborting.\n");
    return (-1);
  }
  irr_clean_unlock (database);
  /* -- done with atomic copy */

  /* TODO - gzipping is optional - this shouldn't be fatal .
     Additionally, the flags may be different and should come from configure. */
  /* compress -- we should really find the path in configure.in */
  sprintf (command, "%s -n -q -f %s", GZIP_CMD, file2); 
  trace (NORM, default_trace, "Running gzip -n -q -f %s\n", file2);
  if (system (command) < 0) {
    trace (NORM, default_trace, "Error occured during gzip!\n");
    return (-1);
  }

  /* if necessary, copy from the tmp directory */
  if (IRR.tmp_dir != NULL) {
    sprintf (file1, "%s/.%s.db.export.gz", IRR.tmp_dir, database->name);
    sprintf (file2, "%s/.%s.db.export.gz", IRR.ftp_dir, database->name);
    if (irr_copy_file (file1, file2, 0) != 1) {
      trace (TR_ERROR, default_trace, "Export failed! Aborting.\n");
      return (-1); 
    }
  }
  
  /* move .<database>.db.export.gz to <database>.db.gz */
  sprintf (file2, "%s/.%s.db.export.gz", IRR.ftp_dir, database->name);
  if (database->export_filename != NULL) 
    sprintf (new, "%s/%s.db.gz", IRR.ftp_dir, database->export_filename);
  else
    sprintf (new, "%s/%s.db.gz", IRR.ftp_dir, database->name);
  trace (NORM, default_trace, "Atomic move %s -> %s\n", 
	 file2, new);
  if (rename (file2, new) < 0) {
    trace (TR_ERROR, default_trace, "Could not rename file to %s:%s\n", 
	   new, strerror (errno));
    return (-1);
  }

  write_irr_serial_export (serial, database);  
  
  trace (NORM, default_trace, "Database %s copied to export directory\n",
	 database->name);

  return (1);
}

/*
 * irr_copy_file
 * Copies two files.  If add_eof_flag != 0, add # EOF to end of file
 */

int irr_copy_file (char *infile, char *outfile, int add_eof_flag) {
  char buf[MIRROR_BUFFER];
  FILE *fp1, *fp2;
  int n1 = 0, n2 = 0;

  trace (NORM, default_trace, "Starting copy of %s to %s\n", infile, outfile);

  if ((fp1 = fopen (infile, "r")) == NULL) {
    trace (TR_ERROR, default_trace, "*ERROR* Could not open %s for reading: "
	   " %s\n", infile, strerror (errno));
    return (-1);
  }

  if ((fp2 = fopen (outfile, "w")) == NULL) {
    trace (TR_ERROR, default_trace, "*ERROR* Could not open %s for writing: "
	   " %s\n", outfile, strerror (errno));
    fclose (fp1);
    return (-1);
  }

  while ((n1 = fread (buf, 1, MIRROR_BUFFER, fp1))) {
    if (n1 < 0) {
      trace (TR_ERROR, default_trace, 
	     "Encountered error copying (read failed) %s->%s (%d, %d): %s\n", 
	     infile, outfile, n1, n2, strerror (errno));
      fclose (fp1);
      fclose (fp2);
      return (-1);
    }

    if ((n2 = fwrite (buf, 1, n1, fp2)) != n1) {
      trace (TR_ERROR, default_trace, 
	     "Encountered error copying %s->%s (%d, %d): %s\n", 
	     infile, outfile, n1, n2, strerror (errno));
      fclose (fp1);
      fclose (fp2);
      return (-1);
    }
  }

  /* check feof and make sure no errors */
  if (ferror (fp1)) {
    trace (TR_ERROR, default_trace, "Encountered error copying %s: %s\n", 
	   infile, strerror (errno));
    fclose (fp1);
    fclose (fp2);
    return (-1);
  }

  /* write #EOF tag for nice export */
  if (add_eof_flag) {
    char buffer[100];
    sprintf (buffer, "\n# EOF\n\n"); 
    fwrite (buffer, 1, strlen (buffer), fp2); 
  }

  fclose (fp1);
  fclose (fp2);
  return (1);
}
  
/*
 * irr_check_serial_vs_journal:
 * For mirrored or authoritative databases will return 0 if the last
 * journal entry is out of sync with the CURRENTSERIAL.  A trace
 * message is logged.
 * Otherwise, 1 is returned.
 */

int irr_check_serial_vs_journal (irr_database_t *database) {
  u_long last_journal_sn = 0;

  /* If we are authoritative, or mirror, then check the last number
     of the journal files and verify that our CURRENTSERIAL is <= to it. */
  if (((database->flags & IRR_AUTHORITATIVE) == IRR_AUTHORITATIVE) ||
      (database->mirror_prefix != NULL)) {
    if (find_last_serial (database->name, JOURNAL_NEW, &last_journal_sn) == 0)
      last_journal_sn = 0;
  }

  if ((last_journal_sn > 0) &&
      (last_journal_sn > database->serial_number)) {
    trace (NORM, default_trace, 
           "WARNING: DB %s journal SN (%lu) > CURRENTSERIAL (%lu)!\n",
           database->name, last_journal_sn, database->serial_number);
    return (0);
  }

return (1);
}

int reopen_DB (irr_database_t *db, char *dir) {
  char tname[BUFSIZE+1];

  /* reopen the DB */
  sprintf (tname, "%s/%s.db", dir, db->name);
  return ((db->db_fp = fopen (tname, "r+")) != NULL);
}

int reopen_JOURNAL (irr_database_t *db, char *name) {

  return ((db->journal_fd = open (name, O_RDWR|O_APPEND, 0664)) >= 0);
}


/* Control the process of atomically replacing the *.DB files from
 * (tmp_dir) with the corresponding DB cache area files.  This function
 * assumes the files in (tmp_dir) are named identically with the
 * DB cache area objects they are intended to replace.
 *
 * Return:
 *  -1 if the operation took place without error
 *  -0 otherwise
 */
int replace_cache_db (irr_database_t *db, uii_connection_t *uii, char *tmp_dir) {
  int db_open, journal_open;
  char fname[BUFSIZE+1], uc[BUFSIZE];
  atom_finfo_t afinfo = {0};

  /* let the user know what's going on */
  if (uii != NULL) 
    uii_send_data (uii, "DB fetched.  Performing atomic replace...\r\n");

  /* we are going to rename the DB so it must be closed */
  db_open = (db->db_fp != NULL);
  fclose (db->db_fp);
  db->db_fp = NULL;

  /* this is the name of the DB we are importing */
  sprintf (fname, "%s.db", db->name);
  afinfo.tmp_dir = tmp_dir;
  
#ifndef NT
  /* atomically move the DB from the (tmp_dir) to the cache area */
  if (!atomic_move (IRR.database_dir, fname, uii, &afinfo)) {
    /* the atomic move failed, reopen the DB to restore */
    if (db_open &&
	!reopen_DB (db, IRR.database_dir)) {
      trace (ERROR, default_trace, 
	     "replace_cache_db (): could reopen (%s). exit (0)\n", db->name);
      exit (0);
    }

    return 0;
  }

  /* if the DB is mirrored then we need to move the *.CURRENTSERIAL file 
   * to the cache area */
  if (db->mirror_prefix != NULL) {
    strcpy (uc, db->name);
    convert_toupper (uc);
    sprintf (fname, "%s.CURRENTSERIAL", uc);
    if (!atomic_move (IRR.database_dir, fname, uii, &afinfo)) {
      if (db_open &&
	  !reopen_DB (db, IRR.database_dir)) {
	trace (ERROR, default_trace, 
	       "replace_cache_db (): could reopen (%s). exit (0)\n", db->name);
	exit (0);
      }
      return 0;
    }
  }
#endif /* NT */
  
  /* JOURNAL file processing.  the old JOURNAL files need to be
   * removed since we are importing a new DB */

  /* close the JOURNAL file */
  journal_open = 0;
  if (db->journal_fd >= 0) {
    close (db->journal_fd);
    journal_open = 1;
  }

  db->journal_fd = -1;

  /* atomically remove the old JOURNAL files */
  sprintf (fname, "%s.%s", db->name, SJOURNAL_NEW);
  sprintf (uc,    "%s.%s", db->name, SJOURNAL_OLD);
#ifndef NT
  if (!atomic_del (IRR.database_dir, fname, uii, &afinfo) ||
      !atomic_del (IRR.database_dir, uc,    uii, &afinfo)) {
    if ((db_open && !reopen_DB      (db, IRR.database_dir)) ||
	(journal_open && !reopen_JOURNAL (db, uc))) {
      trace (ERROR, default_trace, 
	     "replace_cache_db (): could reopen (%s). exit (0)\n", db->name);
      exit (0);
    }
    return 0;
  }

  /* clean up the *.bak files */
  atomic_cleanup (&afinfo);
#endif /* NT */

  return 1;
}

#ifndef NT
/* Control process of fetching (db->name) from a remote ftp site.
 *
 * If (ftp_url) is non-null then it will be used instead of 
 * (db->remote_ftp_url) for the remote ftp URL.  If (db->name)
 * is successfully fetched it is loaded into the DB cache and
 * new indexes are rebuilt.  irrdcacher and wget are the externals
 * that actually perform the fetch operation.
 *
 * Input:
 *  -pointer to a database info struct (db)
 *  -ftp URL to override the (db->remote_ftp_url) value (ftp_url)
 *
 * Return:
 *  -1 if the (db->name) was sucessfully fetched
 *  -0 otherwise
 */
int fetch_remote_db (irr_database_t *db, char *ftp_url) {
  int fetched = 0;
  char *p;

  /* save db->remote_ftp_url and override it's value
   * if (ftp_url) is non-NULL */
  p = db->remote_ftp_url;
  if (ftp_url != NULL)
    db->remote_ftp_url = ftp_url;

  /* try fetching (db->name) from (db->remote_ftp_url) */
  if (db->remote_ftp_url != NULL) {
    
    irr_update_unlock (db);
    if ((fetched = uii_irr_irrdcacher (NULL, strdup (db->name))))
      trace (NORM, default_trace, "Remote fetch successful (%s)\n",
	     db->name);
    else
      trace (NORM, default_trace, "Remote fetch unsuccessful (%s)\n",
	     db->name);
    irr_update_lock (db);
  }

  /* restore original value */
  db->remote_ftp_url = p;

  return fetched;
}

/* Manage the process of fetching a DB from a remote ftp site.
 *
 * Function decides what remote sites to attempt and in what order.
 * Function will not attempt to fetch authoritative DB's.
 *
 * Input:
 *  -pointer to a database info struct (db)
 *
 * Return:
 *  -1 if the (db->name) was sucessfully fetched
 *  -0 otherwise
 */
int ftp_db_fetch (irr_database_t *db) {
  int db_fetched = 0;
  
  /* sanity check */
  if (db->flags & IRR_AUTHORITATIVE)
    return 0;

  /* Choice #1: fetch from the user specified db->remote_ftp_url */
  if (db->remote_ftp_url != NULL) {
    trace (NORM, default_trace, "Trying remote DB fetch (%s)\n",
	   db->remote_ftp_url);
    db_fetched = fetch_remote_db (db, NULL);
  }
  
  /* JW: later try getting the info from repository objects */
  
  /* Choice #2: fetch from ftp.radb.net */
  if (!db_fetched) {
    trace (NORM, default_trace, "Trying remote DB fetch (%s)\n",
	   DEF_FTP_URL);
    db_fetched = fetch_remote_db (db, DEF_FTP_URL);
  }
  
  if (!db_fetched)
    trace (NORM, default_trace, "Abandon remote fetch effort for (%s)\n",
	   db->name);

  return db_fetched;
}

#endif /* NT */


syntax highlighted by Code2HTML, v. 0.9.1