/***************************************
  $Revision: 1.15 $

  Radix payload (rp) - user level functions for storing data in radix trees

  rp_load = loading the radix trees with data on startup

  Status: NOT REVIEWED, TESTED
  
  Design and implementation by: Marek Bukowy
  
  ******************/ /******************
  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 <rp.h>
#include <mysql_driver.h>
#include <constants.h>

#include "ca_configFns.h"
#include "ca_dictSyms.h"
#include "ca_macros.h"
#include "ca_srcAttribs.h"

static
er_ret_t
make_sql2pack(SQ_result_set_t *result, SQ_row_t *row, 
	      rp_upd_pack_t *pack, rp_attr_t  attr, int colcount)
{
  er_ret_t   conv = RP_OK;
  rp_uni_t   *uniptr = &(pack->uni);
  char       *idptr; /* initially set to the 0'th column */
  char       *col[4];
  int        i;

  dieif(colcount>4); /* size of the col array */

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

  idptr = col[0];

  pack->type = attr;
  pack->d.origin = NULL;
  switch( attr ) {
  case A_IN:
    /*
      read 0-2 from inetnum
      0 - objectid
      1 - begin   
      2 - end     
    */
    uniptr->space = IP_V4;
    conv = IP_rang_f2b_v4( &(uniptr->u.in), col[1], col[2] );
    break;
  case A_RT:
    /*
      read 0-3 from route
      0 - objectid 
      1 - prefix    
      2 - prefix_length   
      3 - origin
    */
    uniptr->space = IP_V4;
    if( NOERR(conv = IP_pref_f2b_v4( &(uniptr->u.rt), col[1], col[2] ))) {
      dieif(wr_malloc( (void **) &(pack->d.origin), strlen(col[3])+1)
	    != UT_OK);

      strcpy(pack->d.origin, col[3]);
    }
    break;
  case A_DN:
    /*
      read 0-3 from inaddr
      0 - objectid 
      1 - prefix
      2 - prefix_length   
      3 - domain
    */
    conv = IP_pref_f2b_v4( &(uniptr->u.rt), col[1], col[2] );
    uniptr->space = IP_pref_b2_space( &(uniptr->u.rt) );
    dieif(wr_malloc( (void **) &(pack->d.domain), strlen(col[3])+1)
	    != UT_OK);

    strcpy(pack->d.domain, col[3]);
    break;
  case A_I6: 
    /*
      read 0-3 from inaddr
      0 - objectid 
      1 - msb
      2 - lsb
      3 - prefix_length 
    */
    conv = IP_pref_f2b_v6( &(uniptr->u.rt), col[1], col[2], col[3]);
    uniptr->space = IP_pref_b2_space( &(uniptr->u.rt) );
    break;
  default:
    /*    die; / * shouldn't have got here */
    conv = IP_INVARG;
  }
  
  if( sscanf(idptr, "%lu", &(pack->key) ) < 1 ) {
    conv = IP_INVARG;
  }
  

  for(i=0; i<colcount; i++) {
    /*    wr_free(col[i]);*/ ;
  }
  
  return conv;
}

er_ret_t
RP_sql_load_attr_space( int maxobj, int operation, 
			char *qry,
			rp_attr_t attr, ip_space_t space, 
			rp_regid_t reg_id, SQ_connection_t *con
			)
{
  SQ_row_t *row;
  SQ_result_set_t *result;
  int objnr=0;
  rx_tree_t   *mytree;
  rp_upd_pack_t pack;
  int colcount;
  int sizedebug = ER_is_traced(FAC_RP, ASP_RP_LOAD_DET);

  dieif( RP_tree_get ( &mytree, reg_id, space, attr ) != RP_OK );
 
  
  ER_inf_va(FAC_RP, ASP_RP_LOAD_GEN, "loading %s", qry);
  
  ER_dbg_va(FAC_RP, ASP_RP_LOAD_GEN, "size before query = %x", sbrk(0));
  
  if ( SQ_execute_query(con, qry, &result) == -1 ) {
    fprintf(stderr, "ERROR %d: %s\n", SQ_errno(con), SQ_error(con));
    die;
  }
  else { 
    colcount = SQ_get_column_count(result);
    
    ER_dbg_va(FAC_RP, ASP_RP_LOAD_GEN, 
	      "size after query = %x; columns = %d", sbrk(0), colcount);
    
    /* XXX LOCKED when created */
    /*TH_acquire_write_lock( &(mytree->rwlock) );*/
    
    while ( (row = SQ_row_next(result)) != NULL 
	    && SQ_errno(con) == 0
	    && objnr<=maxobj) {
      
      dieif( ! NOERR(make_sql2pack(result, row, &pack, attr, colcount)) );
      
      if( ! NOERR(RP_pack_node_l(operation, &pack, mytree))) {
	fprintf(stderr,"%d:\t%ld\n", objnr, pack.key);
	die;
      }
      
      /* free allocated memory */
      if( pack.d.origin != NULL ) {
	wr_free(pack.d.origin);
	pack.d.origin == NULL;
      }
      
      
      
      objnr++;
      
      if( sizedebug ) {
	ER_dbg_va(FAC_RP, ASP_RP_LOAD_DET, "size after object %d = %x", 
		  objnr, sbrk(0));
      }
      
    }
    /* XXX UNLOCK */
    TH_release_write_lock( &(mytree->rwlock) );
  }

  if( SQ_errno(con) == 0 ) {
      SQ_free_result(result);
  } else {
      die;
  }

  ER_inf_va(FAC_RP, ASP_RP_LOAD_GEN, "loaded %d objects into %s", objnr,
	    DF_get_attribute_code(attr) );
 
  
  return RP_OK;
}

er_ret_t
RP_sql_load_reg(rp_regid_t reg_id) 
{
  unsigned maxline = 999999999;
  er_ret_t err;
  SQ_connection_t *con;
  char *dbhost = ca_get_srcdbmachine(reg_id);
  char *dbname = ca_get_srcdbname(reg_id);
  char *dbuser = ca_get_srcdbuser(reg_id);
  char *dbpass = ca_get_srcdbpassword(reg_id);
  char *srcnam = ca_get_srcname(reg_id);


  /* Make connection */
  /*
  con = SQ_get_connection(HOST, DATABASE_PORT, DATABASE, USER, PASSWD);
  */


  con = SQ_get_connection( dbhost, ca_get_srcdbport(reg_id),
				      dbname, dbuser, dbpass );
#if 0
  con = SQ_get_connection(CO_get_host(), 
			  CO_get_database_port(), 
			  CO_get_database(), /* XXX for this regid */
			  CO_get_user(), 
			  CO_get_password());
#endif

  dieif ( SQ_execute_query(con, "LOCK TABLES    " 
     "route READ, inetnum READ, inet6num READ, inaddr_arpa READ, domain READ ",
			   NULL) == -1 );

  do {
   
    if( !NOERR(err=RP_sql_load_attr_space(maxline, RX_OPER_CRE, 
 "SELECT  object_id,prefix,prefix_length,origin   FROM route ",
					  A_RT, IP_V4, reg_id, con))) {
      break;
    }
    
#if 0
    {
      er_path_t erlogstr;
      
      erlogstr.fdes = stderr;
      erlogstr.asp  = 0xffff0000;
      erlogstr.fac  = FAC_RP; /* FAC_QI; */
      erlogstr.sev  = ER_SEV_I;
      erlogstr.mode = ER_M_SEVCHAR | ER_M_FACSYMB | ER_M_TEXTLONG;
      
      ER_setpath(& erlogstr);  
      
      wr_log_set(0);
    }
#endif
 
    if( !NOERR(err=RP_sql_load_attr_space(maxline, RX_OPER_CRE, 
"SELECT  object_id,begin_in,end_in       FROM inetnum ",
					  A_IN, IP_V4, reg_id, con))) {
      break;
    }
    if( !NOERR(err=RP_sql_load_attr_space(maxline, RX_OPER_CRE, 
					 
"SELECT object_id,i6_msb,i6_lsb,prefix_length FROM inet6num",

					  A_I6, IP_V6, reg_id, con))) {
      break;
    }
    if( !NOERR(err=RP_sql_load_attr_space(maxline, RX_OPER_CRE, 

"SELECT domain.object_id,prefix,prefix_length,domain FROM inaddr_arpa,domain WHERE domain.object_id = inaddr_arpa.object_id",
					  
					  A_DN, IP_V4, reg_id, con))) {
      break;
    }

    /* CONSTCOND */
  }while(0);

  dieif ( SQ_execute_query(con, "UNLOCK TABLES    ", NULL) == -1 );

  /* Close connection */
  SQ_close_connection(con);

  /* free junk */
  wr_free(dbhost);
  wr_free(dbname);
  wr_free(dbuser);
  wr_free(dbpass);
  wr_free(srcnam);
  return err;
}


er_ret_t
RP_asc_load(char *filename, int maxobj, int operation, 
	    rp_regid_t reg_id)
{
  er_ret_t err;
  FILE *fp;
  char buf[1024];
  char fulltext[65536];
  int objnr = 0;
  int len, oldlen=0;
  int ranlen;
  char rangstr[IP_RANGSTR_MAX];
  int parsed = 0;
  int eor; /* end of record */

  
  if( (fp = fopen(filename,"r")) == NULL ) {
    perror(filename);
    die; 
  }
 
  do {
    fgets(buf, 128, fp);

    eor = ( strlen(buf) <= 1 || feof(fp) );
      
    if( strlen(buf) > 1 ) {
      len = strlen(buf);
      dieif( oldlen+len+1 > 65536 ); /* object too long */
      memcpy( fulltext+oldlen, buf, len);
      oldlen+=len;
      
      fulltext[oldlen]=0;
    }
    
    if( eor ) {              /* end of object: put into the database. */
      parsed++;
      
      /* see if it was just some whitespace junk and nothing more */
      if( *fulltext==0 ) {
	continue;  /* discard */
      }

      /* check if it's a radix object */
      do {
	char attrname[3];
	A_Type_t attrcode;
	
	if( fulltext[0] == '*' &&  fulltext[3] == ':' ) {
	  strncpy(attrname, fulltext+1, 2);
	  attrname[2]=0;
	  
	  if(strcmp(attrname, "XX") == 0 ) {
	    /* object deleted */
	    break;
	  }
	  
	  if( (attrcode = DF_attribute_code2type( attrname )) == -1 ) {
	    fprintf(stderr,"discarding a non-object:\n%s\n", fulltext);
	    break;
	  }
	  
	  if( DF_attrcode_has_radix_lookup(attrcode) == 0 ) {
	    /* no interest to radix */
	    break;
	  }
	
	  /* copy and translate the range */
	  ranlen = index(fulltext+5,'\n')-fulltext-5;
	  strncpy(rangstr, fulltext+5, ranlen);
	  rangstr[ranlen]=0;
	       
	  if( NOERR(err=RP_asc_node(operation, rangstr, attrcode, reg_id,  
				    fulltext, strlen(fulltext)+1, 0L )) ) {
	    objnr++;
	  }
	  else {
	    die; /* error putting into the radix tree */
	    return err;
	  }
	  
	}
	/* CONSTCOND */
      } while(0);
      
      *fulltext=0;
      oldlen=0;
    }
  }
  while(!feof(fp) && objnr<maxobj);  

  return RP_OK;
}
