/***************************************
  $Revision: 1.3 $

  UP auto nic handling

  Status: NOT REVIEWED, NOT TESTED

  Author(s):       Denis Walker ( re-write of functions by Engin Gunduz )

  ******************/ /******************
  Modification History:
        engin (15/12/2000) first Created.
        denis (31/08/2001) Modified for new parser API
        denis (11/11/2002) re-write for re-structured dbupdate
  ******************/ /******************
  Copyright (c) 2001                              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 <glib.h>
#include <assert.h>
#include "rip.h"

#include "dbupdate.h"
#include "up_auto_nic.h"



/* Constructs an initials string from a given name (for NIC hdl generation) 
   The parser ensures that there are at least two words starting with a letter.
   Receives LG context
            person/role name
   Returns  initials string.
*/

char *up_find_initials(LG_context_t *lg_ctx, const char *person_role_name)
{
   char *temp, *pos;
   char *initials = NULL;
   int len, word_idx;
   char **vector;
   int count = 0;

   LG_log(lg_ctx, LG_FUNC,">find_initials: entered with name [%s]\n", person_role_name);

   temp = strdup(person_role_name);
   if ((pos = index(temp, '#')) != NULL)
   { /* delete the EOL comment */
     pos[0] = '\0';
   }

   vector = g_strsplit(temp, " ", 0);
   for (word_idx = 0; vector[word_idx] != NULL; word_idx++)
   {
     if ( (strlen(vector[word_idx]) > 0 ) && isalpha( (int)(vector[word_idx][0]) ) )
	 {
       /* this word starts with a letter */
       if ( count++ < 4 )
       {
         /* only take the first 4 initial letters */
         if (initials == NULL)
	     {
           initials = (char *)malloc(2);
           initials[0] = vector[word_idx][0]; 
		   initials[1] = '\0';
         }
	     else
	     {
           len = strlen(initials);
           initials = (char *)realloc(initials, len + 2 );
           initials[len] = vector[word_idx][0];
           initials[len + 1] = '\0';
         }
       }
     }
   }
   free(temp);
   g_strfreev(vector);
   LG_log(lg_ctx, LG_FUNC,"<find_initials: exiting with initials [%s]\n", initials);
   return initials;
}




/*  Gets the letter combination from the AUTO- nic-hdl if one has been specified,
    to be used in the automatically generated nic handle.
   Receives LG context
            AUTO- nic-hdl
   Returns  The letter combination in the AUTO- nic-handle, if specified.
            NULL otherwise
*/

char *up_get_combination_from_autonic(LG_context_t *lg_ctx, const char *autonic)
{
  GString *temp;
  char *str = NULL;
  char *pos;

  LG_log(lg_ctx, LG_FUNC,">up_get_combination_from_autonic: entered with autonic [%s]\n", autonic);

  temp = g_string_new(autonic);
  if ((pos = index(temp->str, '#')) != NULL)
  { 
    /* delete the EOL comment */
    pos[0] = '\0';
  }
  g_strstrip(temp->str);
  temp->len = strlen(temp->str);/* we directly played with temp->str, so adjust temp->len accordingly */
 
  temp = g_string_erase(temp, 0, strlen("AUTO-"));
  /* delete all digits from the beginning of the string */
  while (temp->len > 0 && ((temp->str)[0] >= '0' && (temp->str)[0] <= '9'))
  {
    temp = g_string_erase(temp, 0, 1);
  }

  if (temp->len < 2 )
  {
    /* should not happen as the parser rejects less than 2 chars as a syntax error 
       but I guess a double check does no harm */
    g_string_free(temp, TRUE);
    LG_log(lg_ctx, LG_FUNC,"<up_get_combination_from_autonic: exiting, less than 2 initials given, ignored\n");
    return NULL;
  }
  else
  {
    str = temp->str;
    g_string_free(temp, FALSE);
    if(strlen(str) > 4)
	{
      /* should not happen as the parser rejects more than 4 chars as a syntax error 
         but I guess a double check does no harm */
      str[4] = '\0'; 
    }
    LG_log(lg_ctx, LG_FUNC,"<up_get_combination_from_autonic: exiting with initials [%s]\n", str);
    return str;
  }
}




/* Gets an object whose nic-hdl is an AUTO- nic-hdl
   and  modifies the nic-hdl: attribute to be sent to RIPupdate.
   For example, "nic-hdl: AUTO-1" becomes "nic-hdl: HG*-RIPE 
   for a person with the initials HG
   Also, auto_nic_hdl is set to "AUTO-1" in this examle.
   auto_nic must be allocated enough memory before calling UP_replace_AUTO_NIC_hdl
   Receives RT context
            LG context
            options structure
            parsed object
            auto_nic pointer
   Returns  UP_OK if successful
            UP_FAIL otherwise
*/
   
int UP_replace_AUTO_nic_hdl(RT_context_t *rt_ctx, LG_context_t *lg_ctx,
                              options_struct_t *options, rpsl_object_t *preproc_obj,
                              char *auto_nic_hdl, char *obj_source)
{
  int retval = UP_OK;
  char *person_role_name = NULL;
  char *initials = NULL;
  char *value, *new_value = NULL;
  const char *type;
  int pos;
  GList *nichdl_item;
  rpsl_attr_t *attr;
  GList *class_attr;

  LG_log(lg_ctx, LG_FUNC,">UP_replace_AUTO_nic_hdl: entered\n");

  nichdl_item = rpsl_object_get_attr(preproc_obj, "nic-hdl"); /* list with one item only */

  value = rpsl_attr_get_clean_value((rpsl_attr_t *)(nichdl_item->data));
  LG_log(lg_ctx, LG_DEBUG,"UP_replace_AUTO_nic_hdl: AUTO- nic-hdl is [%s]", value);

  strcpy(auto_nic_hdl, value);
  g_strdown(auto_nic_hdl);
  pos = rpsl_attr_get_ofs((rpsl_attr_t *)(nichdl_item->data));

   /* if the letter combination is already specified, get it */
   /* use the original case sensitive value */
  initials = up_get_combination_from_autonic(lg_ctx, value);
  /* if the letter combination is not in the AUTO nichdl, obtain it from the name */
  if(initials == NULL)
  {
	type = rpsl_object_get_class(preproc_obj);
	class_attr = rpsl_object_get_attr(preproc_obj, type);
	person_role_name = rpsl_attr_get_clean_value((rpsl_attr_t *)(class_attr->data));
	rpsl_attr_delete_list(class_attr);
    initials = up_find_initials(lg_ctx, person_role_name);
	free(person_role_name);
  }
  LG_log(lg_ctx, LG_DEBUG,"UP_replace_AUTO_nic_hdl: initials [%s]", initials ? initials : "NULL");

  new_value = (char *)malloc(strlen(initials) + strlen(obj_source) + 3);
  strcpy(new_value, initials);
  strcat(new_value, "*-");
  strcat(new_value, obj_source);
  LG_log(lg_ctx, LG_DEBUG,"UP_replace_AUTO_nic_hdl: new nic-hdl value [%s]", new_value);

  /* now copy original attribute, replace value, remove old attr and add replacement */
  attr = rpsl_attr_copy((rpsl_attr_t *)(nichdl_item->data));
  rpsl_attr_replace_value(attr, new_value);
  free(initials);
  free(new_value);
  free(value);

  /* remove the attribute with the AUTO- */
  rpsl_object_remove_attr(preproc_obj, pos, NULL);

  /* insert new attribute with nic-hdl */
  rpsl_object_add_attr(preproc_obj, attr, pos, NULL);

  rpsl_attr_delete_list(nichdl_item);
  
  LG_log(lg_ctx, LG_FUNC,"<UP_replace_AUTO_nic_hdl: exiting with value [%s]\n", UP_ret2str(retval));
  return retval;
}



/* Replaces any refs to AUTO- nic hdls with the assigned ones.
   All AUTO- refs are processed, even after an error, so that multiple erors can be
   reported if necessary.
   Receives RT context
            LG context
            options structure
            parsed object
   Returns  UP_OK if successful
            UP_FAIL otherwise
*/

int UP_replace_refs_to_AUTO_nic_hdl(RT_context_t *rt_ctx, LG_context_t *lg_ctx,
                                         options_struct_t *options,
                                         rpsl_object_t *preproc_obj)
{
  int retval = UP_OK;
  char *nic_hdl = NULL;
  char *name;
  char *value;
  rpsl_attr_t *attr;
  const GList *attr_list = NULL;
  const GList *list_item = NULL;
  int pos;

  LG_log(lg_ctx, LG_FUNC,">UP_replace_refs_to_AUTO_nic_hdl: entered\n");

  attr_list = rpsl_object_get_all_attr(preproc_obj);
  
  list_item = attr_list;
  while (list_item != NULL)
  {
	name = strdup(rpsl_attr_get_name((rpsl_attr_t *)(list_item->data)));
	g_strdown(name);
	value = rpsl_attr_get_clean_value((rpsl_attr_t *)(list_item->data));
	g_strdown(value);
	pos = rpsl_attr_get_ofs((rpsl_attr_t *)(list_item->data));
  	if (    strstr(name, "admin-c") == name
	     || strstr(name, "tech-c")  == name
	     || strstr(name, "zone-c")  == name
	     || strstr(name, "author")  == name )
	{
	  /* attribute is admin-c, tech-c, zone-c or author */
	  if ( strstr(value, "auto-") != NULL)
	  {
        /* this attribute has an auto- nic-hdl */
        LG_log(lg_ctx, LG_DEBUG,"UP_replace_refs_to_AUTO_nic_hdl: attribute [%s] has auto_nic [%s]", name, value);

        if( (nic_hdl = (char *)g_hash_table_lookup(options->AUTO_nic_hdl_hash, value)) )
		{
          /* we have this AUTO- nic hdl in the hash, so replace it. */
          LG_log(lg_ctx, LG_DEBUG,"UP_replace_refs_to_AUTO_nic_hdl: actual nic-hdl found [%s]", nic_hdl);
		  /* create a new attribute with the auto nic handle */
		  attr = rpsl_attr_copy((rpsl_attr_t *)(list_item->data));
          rpsl_attr_replace_value(attr, nic_hdl);

		  /* remove the attribute with the auto- */
		  rpsl_object_remove_attr(preproc_obj, pos, NULL);

		  /* insert new attribute with nic-hdl */
		  rpsl_object_add_attr(preproc_obj, attr, pos, NULL);

          /* replacing the attr destroys the list, so start the list again */
          attr_list = rpsl_object_get_all_attr(preproc_obj);
          list_item = attr_list; 
        }
		else
		{
          /* unknown auto- nic-hdl referenced */
          LG_log(lg_ctx, LG_DEBUG,"UP_replace_refs_to_AUTO_nic_hdl: Unknown AUTO- nic handle referenced [%s]", value);
          RT_unknown_auto_nic_handle(rt_ctx, value);
		  retval = UP_FAIL;
        }
	  }
	}

	free(name);
	free (value);
	
	list_item = g_list_next(list_item);
  }
	
  LG_log(lg_ctx, LG_FUNC,"<UP_replace_refs_to_AUTO_nic_hdl: exiting with value [%s]\n", UP_ret2str(retval));
  return retval;
}




/* Replace the auto NIC handle (if there is one) of the object with
   its assigned NIC handle
   Receives RT context
            LG context
            auto_nic
            assigned nic-hdl
            postproc object
   Returns  UP_OK if no error
            UP_FAIL otherwise
*/

int UP_put_assigned_nic(RT_context_t *rt_ctx, LG_context_t *lg_ctx, 
                           options_struct_t *options,  char *auto_nic,
                           char *assigned_nic, rpsl_object_t *postproc_obj)
{
  int retval = UP_OK;
  char *value;
  int pos;
  rpsl_attr_t *attr;
  GList *nichdl_item;

  LG_log(lg_ctx, LG_FUNC,">UP_put_assigned_nic: entered with assigned nic-hdl [%s]\n", 
                            (assigned_nic && strlen(assigned_nic)) ? assigned_nic : "not set");

  nichdl_item = rpsl_object_get_attr(postproc_obj, "nic-hdl");  /* a list with only one item */

  if ( nichdl_item )
  {
    value = rpsl_attr_get_clean_value((rpsl_attr_t *)(nichdl_item->data));

    /* replace the AUTO-NIC hdl with the assigned one  */
    LG_log(lg_ctx, LG_DEBUG,"UP_put_assigned_nic: replacing auto- nic-hdl [%s]", value);

	/* copy original attribute, replace value */
	attr = rpsl_attr_copy((rpsl_attr_t *)(nichdl_item->data));
	rpsl_attr_replace_value(attr, assigned_nic);
    pos = rpsl_attr_get_ofs((rpsl_attr_t *)(nichdl_item->data));

	/* remove the attribute with the auto- */
	rpsl_object_remove_attr(postproc_obj, pos, NULL);

	/* insert new attribute with nic-hdl */
	rpsl_object_add_attr(postproc_obj, attr, pos, NULL);

    free(value);
    rpsl_attr_delete_list(nichdl_item);

    /* add the AUTO- nic-hdl to the hash */
    g_hash_table_insert(options->AUTO_nic_hdl_hash, strdup(auto_nic), strdup(assigned_nic));
    LG_log(lg_ctx, LG_DEBUG,"UP_put_assigned_nic: AUTO_nic_hdl_hash has %i pairs", 
                      g_hash_table_size(options->AUTO_nic_hdl_hash));
  }
  else
  {
    /* this object does not have a nic-hdl */
    LG_log(lg_ctx, LG_DEBUG,"UP_put_assigned_nic: object does not have a nic-hdl");
  }

  LG_log(lg_ctx, LG_FUNC,"<UP_put_assigned_nic: exiting with value [%s]\n", UP_ret2str(retval));
  return retval;
}



/* Takes a parsed object and checks if the nic-hdl contains an AUTO-.
   Receives LG context
            parsed object
   Returns  1 if AUTO- found
            0 otherwise
*/

int UP_has_AUTO_nic_hdl(LG_context_t *lg_ctx, const rpsl_object_t *preproc_obj)
{
  int retval = 0;
  char *nic_hdl;
  GList *nichdl_item = NULL;

  LG_log(lg_ctx, LG_FUNC,">UP_has_AUTO_nic_hdl: entered\n");

  nichdl_item = rpsl_object_get_attr(preproc_obj, "nic-hdl");  /* a list with only one item */
  if (nichdl_item != NULL)
  {
    if ( UP_strstr_in_attr_list(lg_ctx, nichdl_item, "AUTO-") )
	{ 
      /* it contains an AUTO- nic */
      nic_hdl = rpsl_attr_get_clean_value((rpsl_attr_t *)(nichdl_item->data));
      LG_log(lg_ctx, LG_DEBUG,"UP_has_AUTO_nic_hdl: %s nic_hdl found", nic_hdl);
      free(nic_hdl);
      retval = 1;
    }
    rpsl_attr_delete_list(nichdl_item);
  }

  LG_log(lg_ctx, LG_FUNC,"<UP_has_AUTO_nic_hdl: exiting, %s\n", 
                      retval ? "object contains AUTO- nic-hdl" : "no AUTO- nic-hdl found");
  return retval; 
}



/* Takes a parsed object and checks if this object contains
   a reference to an AUTO nic handle.
   Receives LG context
            parsed object
   Returns  1 if reference found
            0 if NO reference found
*/

int UP_has_ref_to_AUTO_nic_hdl(LG_context_t *lg_ctx, rpsl_object_t *object)
{
  GList *attributes = NULL;
  int list_idx;
  char *attr_list[] = { "admin-c",
                        "tech-c",
                        "zone-c",
                        "author"
                      };

  LG_log(lg_ctx, LG_FUNC,">UP_has_ref_to_AUTO_nic_hdl: entered\n");

  if ( ! rpsl_object_is_deleted(object) )
  {
    for ( list_idx=0; list_idx < sizeof(attr_list)/sizeof(char *); list_idx++ )
    {
      attributes = rpsl_object_get_attr(object, attr_list[list_idx]);
      rpsl_attr_split_multiple(&attributes);
      if (attributes != NULL)
	  {
        if (UP_strstr_in_attr_list(lg_ctx, attributes, "AUTO-") == 1)
	    { 
          /* it contains a ref to AUTO- nic */
          rpsl_attr_delete_list(attributes);
          LG_log(lg_ctx, LG_FUNC,"<UP_has_ref_to_AUTO_nic_hdl: exiting with value 1 (%s has AUTO-)", attr_list[list_idx]);
          return 1;
        }
      }
      rpsl_attr_delete_list(attributes);
    }
    LG_log(lg_ctx, LG_FUNC,"<UP_has_ref_to_AUTO_nic_hdl: exiting with value 0 (no refs found)\n");
    return 0;        
  }
  else
  {  
    /* it is to be deleted so it doesn't matter if 
       it contains refs to AUTO NIC hdls. */
    LG_log(lg_ctx, LG_FUNC,"<up_has_ref_to_AUTO_nic_hdl: exiting with value 0 (to be deleted)\n");
    return 0;        
  }
}



/**************************************************************************************/    
/*                     TEMPORARY CODE TO USE EXISTING RIPUPD                          */
  
/* Gets assigned NIC hdl from the string that is returned from RIPupdate */

int get_assigned_nic(RT_context_t *rt_ctx, LG_context_t *lg_ctx, 
                              options_struct_t *options,
                              char *string, char *nic_hdl )
{
   int retval = UP_OK;
   char **temp = NULL;
   int i=1;

  LG_log(lg_ctx, LG_FUNC,">get_assigned_nic: entered with string [%s]\n", string);

  /* split the string into lines */
  temp = g_strsplit(string , "\n", 0);

  /* look for assigned NIC hdl */
  /* in the second line RIPupdate returns for example "I[65][EK3-RIPE]" We
     need to extract EK3-RIPE part */
  nic_hdl = strncpy(nic_hdl, rindex(temp[i],'[') + 1 ,  
                             rindex(temp[i],']') - rindex(temp[i],'[') - 1);
  nic_hdl[rindex(temp[i],']') - rindex(temp[i],'[') - 1] = '\0';

  LG_log(lg_ctx, LG_DEBUG,"get_assigned_nic: nic-hdl [%s]\n", nic_hdl);

  g_strfreev(temp);

  LG_log(lg_ctx, LG_FUNC,"<get_assigned_nic: exiting with value [%s]\n", UP_ret2str(retval));
  return retval;
}
/**************************************************************************************/    



/* Gets assigned NIC hdl from the string that is returned from RIPupdate 
   Receives RT context
            LG context
            options structure
            result string from ripupd
            pointer to assigned_nic
   Returns  UP_OK if successful
            UP_FAIL otherwise
            sets assigned_NIC
*/

int UP_get_assigned_nic(RT_context_t *rt_ctx, LG_context_t *lg_ctx, 
                              options_struct_t *options,
                              char *string, char *nic_hdl )
{
  int retval = UP_OK;
  int line_idx, char_idx;
  int num_idx = 0;
  int attr_type = 0;
  char attr_type_str[10];
  char *pos = NULL;
  char **temp = NULL;

  LG_log(lg_ctx, LG_FUNC,">UP_get_assigned_nic: entered with string [%s]\n", string);

  /* Find the line starting with 'I' followed by the attr_type [<A_NH>] */
  /* split the string into lines */
  temp = g_strsplit(string , "\n", 0);
  for (line_idx = 0; temp[line_idx] != NULL; line_idx++)
  {
    if ( strstr(temp[line_idx], "I") == temp[line_idx] )
    {
      /* Found a line starting with 'I'.
         Check for the nic-hdl attr_type A_NH */
      for ( char_idx=1; temp[line_idx][char_idx] != '\0' && num_idx<9; char_idx++ )
      {
        if ( isdigit((int)temp[line_idx][char_idx]) )
        {
          /* save the digits */
          attr_type_str[num_idx++] = temp[line_idx][char_idx];
        }
        else if ( num_idx )
        {
          /* first non digit AFTER the number */
          break;
        }
      }
      assert(num_idx);  /* should always find a number */
      attr_type_str[num_idx] = '\0';
      attr_type = atoi(attr_type_str);
      if ( attr_type == A_NH )
      {
        /* look for assigned NIC hdl 
           RIPupdate returns for example "I[<A_NH>]:EK3-RIPE" 
           We need to extract EK3-RIPE part */
        temp[line_idx] = g_strchomp(temp[line_idx]); 
        pos = strchr(temp[line_idx], ':');
        if ( pos )
        {
          strcpy(nic_hdl, pos+1);
          LG_log(lg_ctx, LG_DEBUG,"UP_get_assigned_nic:nic-hdl [%s]", nic_hdl);
        }
        else
        {
          /* there is no nic-hdl, or the colon was missing, fatal error,
             this should not happen, but just in case */
          LG_log(lg_ctx, LG_FATAL, "UP_get_assigned_nic: no :nic-hdl returned from ripupdate [%s]", temp[line_idx]);
          UP_internal_error(rt_ctx, lg_ctx, options, "UP_get_assigned_nic: no :nic-hdl returned from ripupdate\n");
        }
        break;
      }
    }
  }
  g_strfreev(temp);
  
  if ( attr_type != A_NH )
  {
    /* no nic-hdl found, fatal error */
    LG_log(lg_ctx, LG_FATAL,"UP_get_assigned_nic: no nic-hdl attribute type returned from ripupdate");
    UP_internal_error(rt_ctx, lg_ctx, options, "UP_get_assigned_nic: no nic-hdl attribute type returned from ripupdate\n");
  }

  LG_log(lg_ctx, LG_FUNC,"<UP_get_assigned_nic: exiting with value [%s]\n", UP_ret2str(retval));
  return retval;
}
