/***************************************
  $Revision: 1.19 $

  Example code: A server for a client to connect to.

  Status: NOT REVUED, NOT TESTED

 Authors:       Chris Ottrey, Joao Damas

  +html+ <DL COMPACT>
  +html+ <DT>Online References:
  +html+ <DD><UL>
  +html+   <LI>Based on <A HREF="http://iii.ripe.net/dbase/coding/new.code/progress/ottrey/code/java/src/DBServer.java">DBServer.java</A>
  +html+ </UL>
  +html+ </DL>
 
  ******************/ /******************
  Modification History:
        ottrey (02/03/1999) Created.
        ottrey (08/03/1999) Modified.
        joao   (22/06/1999) Modified.
  ******************/ /******************
  Copyright (c) 1999                              RIPE NCC
 
  All Rights Reserved
  
  Permission to use, copy, modify, and distribute this software and its
  documentation for any purpose and without fee is hereby granted,
  provided that the above copyright notice appear in all copies and that
  both that copyright notice and this permission notice appear in
  supporting documentation, and that the name of the author not be
  used in advertising or publicity pertaining to distribution of the
  software without specific, written prior permission.
  
  THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
  ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS; IN NO EVENT SHALL
  AUTHOR BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY
  DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
  AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
  OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 ***************************************/
#include <sys/socket.h>
#include <netinet/in.h>

#include <sys/wait.h>
#include <ctype.h>

#include "thread.h"
#include "rxroutines.h"
#include "socket.h"
/*
#include "objects.h"
*/
#include "constants.h"
#include "mysql_driver.h"
#include "access_control.h"

#define RIPE_REG 17

static void put_inet_sql(rx_tree_t *mytree) {
  SQ_row_t *row;
  int retrieved_objects=0;

  SQ_connection_t *con;
  SQ_result_set_t *result;

  char *str=NULL;
  int no_cols;
  int objnr=1;

  /* Make connection */
  con = SQ_get_connection(CO_get_host(), CO_get_database_port(), CO_get_database(), CO_get_user(), CO_get_password());

  result = SQ_execute_query(SQ_STORE, con, CO_get_in_query()); 
  
  if (result == NULL) {
    fprintf(stderr, "ERROR %d: %s\n", SQ_errno(con), SQ_error(con));
  }
  else {
    printf("Initializing radix tree...   go get a coffee.\n");
    while ( (row = SQ_row_next(result)) != NULL ) {

      ip_range_t myrang;
      rx_dataleaf_t *leafptr;
      int in_id;
      int i;
      char *col[4];

      memset(&myrang, 0, sizeof(ip_range_t));
      myrang.begin.space =  myrang.end.space = IP_V4;

      for(i=0; i<4; i++) {
        col[i] = SQ_get_column_string(result, row, i);
        if (col[i] == NULL) {
          die;
        }
      }

      // get the data: range and payload (id and netname)
      
      // begin of range
      if( col[1] == NULL || 
          sscanf(col[1], "%u", &myrang.begin.words[0] ) < 1 ) {
        die;
      }

      // end of range
      
      if( col[2] == NULL || 
          sscanf(col[2], "%u", &myrang.end.words[0] ) < 1 ) {
        die;
      }
      
      // fulltext id
      if( col[0] == NULL || sscanf(col[0], "%u", &in_id ) < 1 ) {
        die;
      }
      
      // netname 
      if (col[3] == NULL) {
        /* XXX Dont die; */
        col[3] = "NULL";
      }

      /*** FILL IN ****************************************************/

      if( wr_calloc( (void **)& leafptr, sizeof(rx_dataleaf_t), 1) 
          != UT_OK) {
        die;
      }


      leafptr->data_key = in_id;

      // prepare output string for -K (inetnum: ip - ip \n)
      {
        char prstr[IP_RANGSTR_MAX];
        
        if( IP_rang_b2a( &myrang, prstr, IP_RANGSTR_MAX) != IP_OK ) {
          die; // program error.
        }
        
#define PAYLOAD_INETNUM_LENGTH strlen("inetnum:\t\n")
        
        leafptr->data_len = PAYLOAD_INETNUM_LENGTH + 1 + strlen(prstr);
        
        if( wr_calloc( (void **) & leafptr->data_ptr, leafptr->data_len, 1) 
            != UT_OK) {
          die;
        }
        
        if( snprintf(leafptr->data_ptr, leafptr->data_len, 
                     "inetnum:\t%s\n", prstr )
            > leafptr->data_len ) {
          // program error: the buffer is too short.
          die;
        }
      }

      /*      
        leafptr->data_ptr = col[3];
        leafptr->data_len = strlen(str)+1;
      */
      
      if( RX_inum_node( RX_OPER_CRE, &myrang, mytree, leafptr ) != RX_OK ) {
        fprintf(stderr,"%d:\t%d\t%s\n", objnr, in_id, str);
        die;
      }

      /*
      printf("%d \t %s\n", in_id, str);
      */
 
      /*** FREE  ****************************************************/

      for(i=0;i<4;i++) {
        free(col[i]);
      }
      objnr++;
    }
  }
  SQ_free_result(result);

  /* Close connection */
  SQ_close_connection(con);

} /* put_inet_sql() */

static void put_route_sql(rx_tree_t *mytree) {
  SQ_row_t *row;
  int retrieved_objects=0;

  SQ_connection_t *con;
  SQ_result_set_t *result;

  int objnr=1;

  /* Make connection */
  con = SQ_get_connection(CO_get_host(), CO_get_database_port(), CO_get_database(), CO_get_user(), CO_get_password());

  //  compose the query

  result = SQ_execute_query(SQ_STORE, con, CO_get_rt_query()); 
  
  if (result == NULL) {
    fprintf(stderr, "ERROR %d: %s\n", SQ_errno(con), SQ_error(con));
  }
  else {
    while ( (row = SQ_row_next(result)) != NULL ) {

      ip_prefix_t mypref;
      rx_dataleaf_t *leafptr;
      int rt_id;
      char *col[4];
      int i;
      
      memset(&mypref, 0, sizeof(ip_prefix_t));
      mypref.ip.space = IP_V4;

      for(i=0; i<4; i++) {
        col[i] = SQ_get_column_string(result, row, i);
        if (col[i] == NULL) {
          die;
        }
      }

      // get the data: prefix and payload (id and origin)
      
      // prefix ip
      if( sscanf(col[1], "%u", &mypref.ip.words[0] ) < 1 ) {
        die;
      }

      // prefix length
      if( sscanf(col[2], "%u", &mypref.bits ) < 1 ) {
        die;
      }
      
      // fulltext id
      if( sscanf(col[0], "%u", &rt_id ) < 1 ) {
        die;
      }
      
      /*** FILL IN ****************************************************/

      // payload: goes into a dataleaf
      if( wr_calloc( (void **)& leafptr, sizeof(rx_dataleaf_t), 1) 
          != UT_OK) {
        die;
      }
      
      leafptr->data_key = rt_id;

      // prepare output string for -K (route: prefix/len\norigin:col[3])
      {
        char prstr[IP_PREFSTR_MAX];
        
        if( IP_pref_b2a( &mypref, prstr, IP_PREFSTR_MAX) != IP_OK ) {
          die; // program error.
        }
        
#define PAYLOAD_ROUTE_LENGTH strlen("route:\t/\norigin:\t\n")
        
        leafptr->data_len = PAYLOAD_ROUTE_LENGTH + 1 
          + strlen(prstr) + strlen(col[3]);
        
        
        if( wr_calloc( (void **) & leafptr->data_ptr, leafptr->data_len, 1) 
            != UT_OK) {
          die;
        }
        
        if( snprintf(leafptr->data_ptr, leafptr->data_len, 
                     "route:\t%s\norigin:\t%s\n", prstr, col[3] )
            > leafptr->data_len ) {
          // program error: the buffer is too short.
          die;
        }
      }
      

      if( RX_bin_node( RX_OPER_CRE, &mypref, mytree, leafptr ) != RX_OK ) {
        fprintf(stderr,"%d:\t%d\t%s\n", objnr, rt_id, col[3]);
        die;
      }

      /*** FREE  ****************************************************/

      for(i=0;i<4;i++) {
        free(col[i]);
      }

      objnr++;
    }
  }
  SQ_free_result(result);

  /* Close connection */
  SQ_close_connection(con);

} /* put_route_sql() */

/* XXX void radix_init(char *database) { */
static void radix_init() {
  er_path_t erlogstr;
  rx_tree_t  *mytree;

  if (RX_tree_cre(RIPE_REG, IP_V4, RX_FAM_IN, "0.0.0.0/0", RX_MEM_RAMONLY, RX_SUB_NONE, &mytree) != RX_OK) {
    puts("error!!!");
  }
  else {
    
    put_inet_sql(mytree);
    RX_attach2forest(mytree);

    if (RX_tree_cre(RIPE_REG, IP_V4, RX_FAM_RT, "0.0.0.0/0", RX_MEM_RAMONLY, RX_SUB_NONE, &mytree) != RX_OK) {
      puts("error!!!");
    }
    else {
      
      put_route_sql(mytree); 
      RX_attach2forest(mytree);
    }
  }

  erlogstr.fdes = stderr;
  /*
  erlogstr.asp  = ASP_RX_SRCH_DET | ASP_RX_STKBLD_DET ;
  */
  erlogstr.asp  = 0;  /* No debugging info. */
  erlogstr.sev  = ER_SEV_W;
  erlogstr.mode = ER_M_SEVCHAR | ER_M_TEXTLONG;

  ER_setpath(& erlogstr);  

} /* radix_init() */


/* SV_start() */
/*++++++++++++++++++++++++++++++++++++++

  Start the server.

  More:
  +html+ <PRE>
  Authors:
        ottrey
        joao
  +html+ </PRE>
  +html+ Starts up the server.
  +html+ <OL>
  +html+   <LI> Create sockets on the necessary ports (whois, config and mirror)
  +html+   <LI> Start new threads for each service.
  +html+ </OL>
  +html+ <A HREF=".DBrc">.properties</A>

  ++++++++++++++++++++++++++++++++++++++*/
void SV_start() {
  int status;
  int whois_sock,config_sock /* ,mirror_sock */;
  /* uint32_t whois_addr,sock_addr,mirror_addr; */
  int whois_port = -1;
  int config_port = -1;
  /* int mirror_port = -1; */

  /* Initialise the access control list. */
  AC_build();
  AC_acc_load();

  /* Initialise the radix tree before allowing any socket connections. */
  radix_init();

  /* XXX I have no idea how this is working!! It's wrong! - ottrey 30/7/99 */
  /* Get port information for each service */
  whois_port = SK_atoport(CO_get_whois_port(), "tcp");
  printf("XXX htons(whois_port)=%d\n", htons(whois_port));
  if(whois_port == -1) {
    printf("Invalid service/port: %d\n", htons(whois_port));
    exit(-1);
  }

  /* XXX I have no idea how this is working!! It's wrong! - ottrey 30/7/99 */
  config_port = SK_atoport(CO_get_config_port(), "tcp");
  printf("XXX htons(config_port)=%d\n", htons(config_port));
  if(config_port == -1) {
    printf("Invalid service/port: %d\n", htons(config_port));
    exit(-1); 
  }
/* Commented out for now. Remove comment when enabling mirroring
  mirror_port = SK_atoport(CO_get_mirror_port(), "tcp");
  if(mirror_port == -1) {
    printf("Invalid service/port: %s\n", mirror_port);
    exit(-1);
  }
*/

  /* 6. Create a socket on the necessary ports/addresses and bind to them. */
  /* whois socket */
  whois_sock = SK_getsock(SOCK_STREAM, whois_port, INADDR_ANY);
/* Currently binds to INADDR_ANY. Will need to get specific address */
/*  whois_sock = SK_getsock(SOCK_STREAM,whois_port,whois_addr); */
  /* config interface socket */
  config_sock = SK_getsock(SOCK_STREAM, config_port, INADDR_ANY);
  /* nrt socket */
/*  mirror_sock = SK_getsock(SOCK_STREAM,mirror_sock,mirror_addr); Remove comment when enabling mirroring */

  /* Now.... accept() calls block until they get a connection
     so to listen on more than one port we need more
     than one thread */

  /* Create master thread for whois threads */
  TH_run(whois_sock, (void *)TH_do_whois);
  /* Create master thread for config threads */
  TH_run(config_sock, (void *)TH_do_config);
  /* Create master thread for mirror threads */
  /* Remove comment when enabling mirroring
  TH_run(mirror_sock, (void *)TH_do_mirror);
  */

  /* XXX Is this needed? */
  pthread_exit(&status);

} /* SV_start() */
