/*
 * $id: uii_commands.c,v 1.1.1.1 1998/08/10 20:06:56 dogcow Exp $
 * originally Id: uii_commands.c,v 1.19 1998/06/15 16:20:36 gerald Exp 
 */

#include <stdio.h>
#include <string.h>
#include "mrt.h"
#include "trace.h"
/*#include "interface.h"*/
#include <time.h>
#include <signal.h>
#include "config_file.h"
#include <sys/types.h>
#include <ctype.h>
#include "irrd.h"

#include <fcntl.h>
#ifndef SETPGRP_VOID
#include <sys/termios.h>
#endif

#ifndef localtime_r /* Not present on SunOS, at least. */
#define localtime_r(a,b) localtime(a)
#endif

extern trace_t *default_trace;

/* Handler routines for IRR lookups
 * the show_xxx functions are human readable UII commands
 * the irr_xxx are used by the RAWhoisd machine telnet interface
 */
void uii_show_ip_less (uii_connection_t *uii, prefix_t *prefix, int flag);
void uii_show_ip_exact (uii_connection_t *uii, prefix_t *prefix);
void uii_show_ip_more (uii_connection_t *uii, prefix_t *prefix);

void show_database (uii_connection_t *uii) {
  irr_database_t *database;
  char buf[MAXLINE];
  struct tm my_tm;

  uii_add_bulk_output (uii, "Listening on port %d (fd=%d)", IRR.irr_port, IRR.sockfd);
  if (IRR.whois_port > 0) {
    uii_add_bulk_output (uii, ", and port %d (fd=%d)", IRR.whois_port, IRR.sockfd);
  }
  uii_add_bulk_output (uii, "\r\n");

  if (IRR.use_disk == 1)
    uii_add_bulk_output (uii, "Disk indexing\r\n");
  else
    uii_add_bulk_output (uii, "Memory-only indexing\r\n");

  if (IRR.database_syntax == RIPE181) {
    uii_add_bulk_output (uii, "RIPE181 Syntax\r\n\r\n");
  }
  else if (IRR.database_syntax == RPSL) {
    uii_add_bulk_output (uii, "RPSL Syntax\r\n\r\n");
  }
  else if (IRR.database_syntax == EMPTY_DB) {
    uii_add_bulk_output (uii, "Unknown Syntax, all DB's empty\r\n\r\n");
  }
  else if (IRR.database_syntax == MIXED) {
    uii_add_bulk_output (uii, "RIPE181 and RPSL Syntaxes\r\n\r\n");
  }
  else {
    uii_add_bulk_output (uii, "** Unknown Database Syntax\r\n\r\n");
  }

    

  uii_add_bulk_output (uii, "%6s    %-9s    %6s   %4s   %s", 
		       "Database", "Size (kb)", "Rt Obj",
		       "AutNum Obj", "Serial #\r\n");
  uii_add_bulk_output (uii, "%6s    %-9s    %6s   %4s   %s", 
		       "---------", "--------", "------",
		       "---------", "---------\r\n");

  LL_Iterate (IRR.ll_database, database) {
    uii_add_bulk_output (uii, " %-10s   %8.1f ", 
			 database->name,
			 ((float) database->bytes / 1000.0));

    uii_add_bulk_output (uii, "    %6d     %4d        %d\r\n", 
			 database->num_objects[ROUTE],
			 database->num_objects[AUT_NUM],
			 database->serial_number);
  }


  uii_add_bulk_output (uii, "\r\n");

  LL_Iterate (IRR.ll_database, database) {


    uii_add_bulk_output (uii, "%s ", database->name);
    if (database->flags & IRR_AUTHORITATIVE)
      uii_add_bulk_output (uii, " AUTHORITATIVE");
    if (database->obj_filter > 0) 
      uii_add_bulk_output (uii, " (filter 0x%x)", database->obj_filter);
    uii_add_bulk_output (uii, "\r\n");

    /* mirroring */
    if (database->mirror_prefix != NULL) {
      nice_time (time_left (database->mirror_timer), buf);
      uii_add_bulk_output (uii, "   Mirroring %s:%d (Next in %s)\r\n", 
			   prefix_toa (database->mirror_prefix),
			   database->mirror_port, 
			   buf);
      if (database->mirror_error_message[0] != '\0') {
	if (database->mirror_error_message[strlen(database->mirror_error_message) -1] == '\n')
	  database->mirror_error_message[strlen(database->mirror_error_message) -1] = '\0';
	uii_add_bulk_output (uii, "   %s \r\n", database->mirror_error_message);
      }
      if (database->mirror_fd != -1) {
	uii_add_bulk_output (uii, "   MIRRORING (%d bytes) (%d seconds)\r\n",
			     database->mirror_update_size,
			     time (NULL) - database->mirror_started);
      }
      else if (database->last_mirrored <= 0) {
	uii_add_bulk_output (uii, "   Never mirrored\r\n");
      }
      else {
	char tmpt[MAXLINE];
	strftime (tmpt, MAXLINE, "%T %D", 
		  localtime_r ((time_t *) &database->last_mirrored, &my_tm));
	uii_add_bulk_output (uii, "   Last mirrored %s\r\n", tmpt);
	uii_add_bulk_output (uii, "   %d bytes, %d change(s)\r\n", 
			     database->mirror_update_size, database->num_changes);
      }
    }
    /*
     * mirroring not turned on 
     * Either loaded, or email
     */
    else {
      char tmpt[MAXLINE];

      if (database->flags & IRR_AUTHORITATIVE) {
	if (database->last_update == 0)
	  uii_add_bulk_output (uii, "   Last email/tcp update Never\r\n");
	else {
	  strftime (tmpt, MAXLINE, "%T %D", 
		    localtime_r ((time_t *) &database->last_update, &my_tm));
	  uii_add_bulk_output (uii, "   Last email/tcp update %s\r\n", tmpt);
	}
      }

      if (database->time_loaded == 0)
	uii_add_bulk_output (uii, "   Last loaded Never\r\n");
      else {
	strftime (tmpt, MAXLINE, "%T %D", 
		  localtime_r ((time_t *) &database->time_loaded, &my_tm));
	uii_add_bulk_output (uii, "   Last loaded %s\r\n", tmpt);
      }
    } 

    if (database->export_timer != NULL) {
      nice_time (time_left (database->export_timer), buf);
      uii_add_bulk_output (uii, "   Next export in %s\r\n", buf);
    }


    if (database->no_dbclean) {
      uii_add_bulk_output (uii, "   Cleaning disabled\r\n");
    }
    else if (database->clean_timer != NULL) {
      nice_time (time_left (database->clean_timer), buf);
      uii_add_bulk_output (uii, "   Next dbclean in %s\r\n", buf);
    }

  }
  uii_send_bulk_data (uii);
}


/* uii_irr_reload 
 * Reload the database 
 */
void uii_irr_reload (uii_connection_t *uii, char *name) {
  if (name != NULL) {
    irr_reload_database (name, uii);
    Delete (name);
  }
}

void uii_export_database (uii_connection_t *uii, char *name) {
  irr_database_t *db;

  db = find_database (name);

  if (IRR.ftp_dir == NULL) {
    uii_send_data (uii, "Error -- no ftp directory configured.\r\n");
    Delete (name);
    return;
  }

  if (db != NULL) {   
    uii_send_data (uii, "Exporting %s database to %s...\r\n", 
		   name, IRR.ftp_dir);
    if (irr_database_export (db) != 1) {
       uii_send_data (uii, "ERROR exporting %s database -- see log for more information\r\n", 
		      name);
    }
    else {
      uii_send_data (uii, "Export successful for %s\r\n", name);
      
      if (db->export_timer != NULL) 
	Timer_Reset_Time (db->export_timer);
    }
  }
  else 
    uii_send_data (uii, "Could not find %s database...\r\n", name);

  Delete (name);
  return;
}

void uii_irr_sync (uii_connection_t *uii, char *name) {
  irr_database_t *db;

  db = find_database (name);

  if (db != NULL) {   
    uii_send_data (uii, "Cleaning %s database...\r\n", name);
    irr_database_sync (db);
    if (db->clean_timer != NULL)
      Timer_Reset_Time (db->clean_timer);
  }
  else 
    uii_send_data (uii, "Could not find %s database...\r\n", name);

  Delete (name);
  return;
}

void uii_irr_mirror_last (uii_connection_t *uii, char *name) {
  uii_irr_mirror (uii, name, 0);
}

void uii_irr_mirror_serial (uii_connection_t *uii, char *name, int serial) {
  uii_irr_mirror (uii, name, serial);
}
void uii_irr_mirror (uii_connection_t *uii, char *name, int serial) {
  irr_database_t *database;

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

  if (database->mirror_prefix == NULL) {
    uii_send_data (uii, "Don't know how to mirror %s\r\n", database->name);
    return;
  }
  
  uii_send_data (uii, "Mirroring %s database...\r\n", name);
  uii_send_data (uii, "Current serial number %d\r\n", database->serial_number);
  if (serial > 0)
    uii_send_data (uii, "serial to: %d\n", serial);
  else
    uii_send_data (uii, "serial to: LAST\n");
  uii_send_data (uii, "Starting mirror in the background......\r\n", 
		 database->serial_number);

  if (request_mirror (database, uii, serial) == -1) {
    uii_send_data (uii, "\r\n** ERROR ** mirroring database!\r\n");
    uii_send_data (uii, "See logfile for more information\r\n");
    Delete (name);
    return;
  }

  if (database->mirror_timer != NULL) 
    Timer_Reset_Time (database->mirror_timer);

  Delete (name);
  return;
}



/* mainly for debugging, but would be nice to be able to set the serial number */
void uii_set_serial (uii_connection_t *uii, char *name, int serial) {
  irr_database_t *database;

  if ((database = find_database (name)) != NULL) {
    database->serial_number = serial;
    Delete (name);
    return;
  }
  
  uii_send_data (uii, "%s database not found\r\n", name);
  Delete (name);
}


void uii_show_ip (uii_connection_t *uii, prefix_t *prefix, int num, char *lessmore) {
  if (num == 0) {
    uii_show_ip_exact (uii, prefix);
    return;
  }

  if (!strcmp (lessmore, "less")) {
    uii_show_ip_less (uii, prefix, 1);
    return;
  }
  else {
    uii_show_ip_more (uii, prefix);
    return;
  }
  /*NOTREACHED*/
  return;
}


void uii_fetch_irr_object (uii_connection_t *uii, 
			   irr_database_t *database, u_long offset) {
  char buffer[512];
  
  fseek (database->fd, offset, SEEK_SET);
    
  while (fgets (buffer, sizeof (buffer)-1, database->fd) != NULL) {
    buffer[strlen(buffer) -1] = '\0'; /* lose newline */
    if (strlen (buffer) < 2) break;
    uii_add_bulk_output (uii, "%s\r\n", buffer);
  }
}

void uii_show_ip_exact (uii_connection_t *uii, prefix_t *prefix) {
  radix_node_t *node;
  LINKED_LIST *ll_attr;
  irr_route_object_t *attr;
  irr_database_t *database;
  int first = 1;

  LL_Iterate (IRR.ll_database, database) {
    irr_lock (database);

    node = radix_search_exact (database->radix, prefix);

    if (node != NULL) { 
      ll_attr = (LINKED_LIST *) node->data;
      LL_Iterate (ll_attr, attr) {
	if (first != 1) uii_add_bulk_output (uii, "\r\n");
	first = 0;
	uii_fetch_irr_object (uii, database, attr->offset);
      }
    }
    irr_unlock (database);
  }    
  
  uii_send_bulk_data (uii);
}



void uii_show_ip_less (uii_connection_t *uii, prefix_t *prefix, int flag) {
  radix_node_t *node;
  LINKED_LIST *ll_attr;
  irr_route_object_t *attr;
  irr_database_t *database;
  prefix_t *tmp_prefix;
  int first = 1;

  LL_Iterate (IRR.ll_database, database) {
    irr_lock (database);
    
    tmp_prefix = prefix;

    /* first check if this node exists */
    if ((node = radix_search_exact (database->radix, prefix)) != NULL) {
      ll_attr = (LINKED_LIST *) node->data;
      LL_Iterate (ll_attr, attr) {
	if (first != 1) uii_add_bulk_output (uii, "\r\n");
	first = 0;
	uii_fetch_irr_object (uii, database, attr->offset);
      }
    }

    /* now check all less specific */
    while ((flag == 1 || node == NULL) && 
	   (node = radix_search_best (database->radix, tmp_prefix, 0)) != NULL) {
      if (node != NULL) { 
	tmp_prefix = node->prefix;
	ll_attr = (LINKED_LIST *) node->data;
	LL_Iterate (ll_attr, attr) {
	  if (first != 1) uii_add_bulk_output (uii, "\r\n");
	  first = 0;
	  uii_fetch_irr_object (uii, database, attr->offset);
	}
      }
      /* break out after one loop for "l" case */
      if (flag == 0) break; 
    }
    irr_unlock (database);
  }
  
  uii_send_bulk_data (uii);
}




/* Route searches. M - all more specific eg, !r199.208.0.0/16,M */ 
/* Also does m - one level only more specific */ 
void uii_show_ip_more (uii_connection_t *uii, prefix_t *prefix) {
  radix_node_t *node, *start_node, *last_node;
  LINKED_LIST *ll_attr;
  irr_route_object_t *attr;
  irr_database_t *database;
  int first = 1;
  int len;

  LL_Iterate (IRR.ll_database, database) {
    irr_lock (database);

    last_node = NULL;
    start_node = NULL;
    node = NULL;    

    /* first, try to find this prefix exactly */
    start_node = radix_search_exact (database->radix, prefix);

    /* fine, try to find something less specific */
    if (start_node == NULL) {
      start_node = radix_search_best (database->radix, prefix, 0);
    }

    /* yuck -- walk up the radix tree until find more specific that
     * does not match
     */
    if (start_node == NULL) {
      prefix_t *tmp_prefix;
      len = prefix->bitlen;

      prefix->bitlen = 32;
      tmp_prefix = prefix;
      while ((start_node = radix_search_best (database->radix, tmp_prefix, 0)) != NULL) {
	if (!comp_with_mask ((void *) prefix_tochar (start_node->prefix), 
			     (void *) prefix_tochar (prefix),  len))
	  break;
	tmp_prefix = start_node->prefix;
	last_node = start_node;
      }
      prefix->bitlen = len;
      if (start_node == NULL) start_node = last_node;
    }

    if (start_node != NULL) {
      RADIX_WALK (start_node, node) {
	if ((node->prefix->bitlen >= prefix->bitlen) &&
	    (comp_with_mask ((void *) prefix_tochar (node->prefix), 
			     (void *) prefix_tochar (prefix),  prefix->bitlen))) {
	  ll_attr = (LINKED_LIST *) node->data;
	  LL_Iterate (ll_attr, attr) {
	    if (first != 1) {
	      uii_add_bulk_output (uii, "\r\n");
	    }
	    first = 0;
	    uii_fetch_irr_object (uii, database, attr->offset);
	  }
	}
      }
      RADIX_WALK_END;
    }
    irr_unlock (database);
  }
  
  uii_send_bulk_data (uii);
}


/* uii_delete_route
 * mainly for testing/debugging 
 */
int uii_delete_route (uii_connection_t *uii, char *name, prefix_t *prefix, int as) {
  irr_database_t *db;
  irr_object_t *irr_object;

  db = find_database (name);

  if (db == NULL) {   
    uii_send_data (uii, "Could not find %s database...\r\n", name);
    Delete (name);
    return (-1);
  }
  Delete (name);    

  irr_object = New (irr_object_t);
  irr_object->type = ROUTE;
  irr_object->name = prefix_toax (prefix);
  irr_object->origin = as;


  if ((irr_object = load_irr_object (db, irr_object)) == NULL) {
    uii_send_data (uii, "Could not find find %s...\r\n", prefix_toax (prefix));
    return (-1);
  }

  if (delete_irr_route (db, irr_object->name, irr_object)) 
    uii_send_data (uii, "Deleted\r\n");
  else
    uii_send_data (uii, "Deleted failed!\r\n");

  return (1);
}


int uii_read_update_file (uii_connection_t *uii, char *file, char *name) {
  irr_database_t *db;
  int new;
  FILE *fd;
  

  db = find_database (name);

  if (db == NULL) {   
    uii_send_data (uii, "Could not find %s database...\r\n", name);
    Delete (name);
    return (-1);
  }
  Delete (name); 

  if ((fd = fopen (file, "r")) == NULL) {
    uii_send_data (uii, "Could not open %s\r\n", file);
    return (-1);
  }

  /* just kill of the start line */
  valid_start_line (db, fd, &new);

  irr_lock (db);
#if (defined(USE_GDBM))
  dbm_fast (db);
#endif
  if (scan_irr_file (db, "load", 2, fd) != NULL) {
    trace (NORM, default_trace, 
	   "Read of updates failed: serial number unchanged: %d\n",
	   db->serial_number);
  }
  irr_unlock (db);
  
  fclose (fd);
#if (defined(USE_GDBM))
  dbm_slow (db);
#endif

  return (1);
}
