/***************************************
  $Revision: 1.8 $

  Error reporting (er) er_paths.c - parser callback functions for path
                                    & filter creation/modification/deletion

  Status: NOT REVUED, PARTLY TESTED

  Design and implementation by: Marek Bukowy

  ******************/ /******************
  Copyright (c) 1999,2000,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 "rip.h"


/*++++++++++++++++++++++++++++++++++++++
  finds path by identifier
  
  er_path_t *
  er_find_path_byname  returns the pointer to it if found or NULL if not found
  
  char *key            path identifier

  ++++++++++++++++++++++++++++++++++++++*/
static
er_path_t *
er_find_path_byname(char *key)
{
  GList *pitem;
  er_path_t *pathptr;

  /* foreach path */
  for( pitem = g_list_first(er_pathlist);
       pitem != NULL;
       pitem = g_list_next(pitem)) {
    
    pathptr = (er_path_t *)pitem->data;
    
    if( strcmp(pathptr->name, key) == 0 ) {
      return pathptr;
    }
  } 

  return NULL;
}


/*++++++++++++++++++++++++++++++++++++++
  
  Updates the array of currently active aspects. Must be used after any change
  of filters/paths.
  
  The "asp" array describes the "OR" of all filters' aspects. This is to allow
  fast dropping of messages that would be dropped anyway 
  
  This function clears the array and regenerates it by going through
  all filters and setting appropriate bits of aspects per facility.

  ++++++++++++++++++++++++++++++++++++++*/
void
er_upd_asparray(void)
{
  GList *pitem, *fitem;
  er_fac_code_t f;

  /* clear */
  for(f=0; f<FAC_LAST; f++) {
    er_asparray[f] = 0;
  }

  /* foreach path */
  for( pitem = g_list_first(er_pathlist);
       pitem != NULL;
       pitem = g_list_next(pitem)) {
    
    er_path_t *pathptr = (er_path_t *)pitem->data;
    
    /* active paths only */
    if( pathptr->active ) {
      
      /* foreach filter on that path */
      for( fitem = g_list_first(pathptr->filters);
	   fitem != NULL;
	   fitem = g_list_next(fitem)) {
	
	er_filter_t *filtptr = (er_filter_t *) fitem->data;

	/* foreach facility in that filter */
	for(f=0; f<FAC_LAST; f++) {
	  if( MA_isset( filtptr->fac_mask, f ) ) {
	    er_asparray[f] |= filtptr->asp_mask;
	  }
	}
      }
    }
  }
}


/*++++++++++++++++++++++++++++++++++++++
  
  Adds a filter to the filter chain for the given path.
  
  er_ret_t 
  er_add_filter        always returns ER_OK.
  
  er_path_t *pathptr   pointer to path
  
  er_filter_t *filter  pointer to the filter
  ++++++++++++++++++++++++++++++++++++++*/
er_ret_t 
er_add_filter( er_path_t *pathptr, er_filter_t *filter )
{
  er_filter_t *ft = UT_malloc(sizeof(er_filter_t));
  
  memcpy(ft, filter, sizeof(er_filter_t));
  pathptr->filters =  g_list_append(pathptr->filters, ft);
 
  return ER_OK;
}


/*++++++++++++++++++++++++++++++++++++++
  
  Finds a path by identifier and adds a list of filters to the filter
  chain for that path.

  er_ret_t 
  er_attach_filter_chain    returns ER_INVKEY if the path cannot be found
                            or ER_OK on success.

  char *key           path identifier
			    
  GList *filterlist   list of filters
  ++++++++++++++++++++++++++++++++++++++*/
er_ret_t 
er_attach_filter_chain( char *key, GList *filterlist )
{ 
  er_path_t *pathptr;
  er_ret_t err;
  
  if( (pathptr=er_find_path_byname(key)) == NULL ) {
     return ER_INVKEY;
  }
  else {     
    GList  *fitem;
    for( fitem = g_list_first(filterlist);
	 fitem != NULL;
	 fitem = g_list_next(fitem)) {
	
      er_filter_t *filtptr = (er_filter_t *) fitem->data;
      
      if( !NOERR(err=er_add_filter( pathptr, filtptr)) ) {
	return err;
      }
    }
  }
  
  er_upd_asparray();

  return ER_OK;
}


/*++++++++++++++++++++++++++++++++++++++
  
  basic sanity checks for a path definition. Currently only checking
  if a specified socket exists.

  int
  er_path_safeguard      Returns 0 on success, -1 on failure 

  er_path_t *path        new path structure
  ++++++++++++++++++++++++++++++++++++++*/
static 
int
er_path_safeguard(er_path_t *path)
{

  switch ( path->type ) {

  case  ER_PATH_SOCK: /* the socket must exist */
    {
      char *n = SK_getpeername(path->descr.sock.fd);
      if( n == NULL ) {
	return -1;
      }
      else {
	UT_free(n);
      }
    }
    break;
  default:
    break;
  }

  return 0;
}


/*++++++++++++++++++++++++++++++++++++++
  
  Registers a path in the chain of paths.

er_ret_t
er_register_path    returns ER_DUPENT if a path with that identifier 
                    already exists, returns ER_INSANE if the sanity check
		    is not passed, or ER_OK on success.

  er_path_t *path   new path structure

  char *key         path identifier 
  ++++++++++++++++++++++++++++++++++++++*/
er_ret_t
er_register_path(  er_path_t *path, char *key )
{
  er_path_t *ft;
  er_path_t *pathptr;
  
  if( (pathptr=er_find_path_byname(key)) != NULL ) {
    return ER_DUPENT; /* duplicate !!! */
  }
  if( er_path_safeguard(path) < 0 ) {
    return ER_INSANE;
  }
  
  ft = UT_calloc(sizeof(er_path_t),1);
  memcpy(ft, path, sizeof(er_path_t));
  strncpy(ft->name, key, 31);
  er_pathlist = g_list_append(er_pathlist, ft);

  er_upd_asparray();
  
  return ER_OK;
}


/*++++++++++++++++++++++++++++++++++++++
  
  Finds the path by identified and replaces its definition without touching
  the filters

  er_ret_t
  er_modify_path     returns ER_INVKEY if the path cannot be found
                     or ER_OK on success.

  er_path_t *newpath  new path structure

  char *key         path identifier 
  ++++++++++++++++++++++++++++++++++++++*/ 
er_ret_t
er_modify_path(  er_path_t *newpath, char *key )
{
  er_path_t *pathptr;

  if( (pathptr=er_find_path_byname(key)) == NULL ) {
     return ER_INVKEY;
  }
  else {
    /* name stays the same */
    pathptr->active = newpath->active;
    pathptr->format = newpath->format;
    pathptr->mutex  = newpath->mutex;
    pathptr->type   = newpath->type;
    pathptr->descr  = newpath->descr;
    /* filters stay the same */
    
    er_upd_asparray();
  
    return ER_OK;
  }
}


/*++++++++++++++++++++++++++++++++++++++
  
  Deletes a filter from the list of filters of the path specified by
  identifier.  The filter is specified by its position in the list,
  starting with 0.

  er_ret_t
  er_delete_filter    returns ER_INVKEY if the path or filter cannot be found

  char *key           path identifier

  unsigned  filterid  filter position
  ++++++++++++++++++++++++++++++++++++++*/
er_ret_t
er_delete_filter( char *key, unsigned  filterid ) 
{
  er_path_t *pathptr;
  er_filter_t *filtptr;

  if( (pathptr=er_find_path_byname(key)) == NULL ) {
     return ER_INVKEY;
  }
  else {
    int numfilters = g_list_length(pathptr->filters);
    
    if( filterid >= numfilters ) {
      return ER_INVKEY;
    }
    
    filtptr = g_list_nth_data(pathptr->filters, (unsigned) filterid);
    /* free filter structure */
    UT_free(filtptr);
    /* remove filter link from list */
    pathptr->filters = g_list_remove(pathptr->filters, filtptr);
    /* update arrays */
    er_upd_asparray();  

    return ER_OK;
  }
}


/*++++++++++++++++++++++++++++++++++++++
  
  Adds an argument to a dynamically build argv array of arguments for
  a path of EXEC type.

  er_path_t *pathptr    path structure 

  char *arg             new argument
  ++++++++++++++++++++++++++++++++++++++*/
void
er_add_exec_arg(er_path_t *pathptr, char *arg)
{
  int len = 0;
  char **argv = pathptr->descr.exec.argv;
  char **newargv;

  if( argv != NULL ) {
    while( argv[len] != NULL ) {
      len++;
    }
  }

  newargv = UT_calloc( sizeof(char **) * (len+2), 1 );
  if( len > 0 ) {
    memcpy( newargv, argv, sizeof(char **) * len);
  }
  newargv[len] = UT_strdup(arg);
  
  pathptr->descr.exec.argv = newargv;

  if( argv != NULL ) {
    UT_free(argv);
  }
}
 


/*++++++++++++++++++++++++++++++++++++++
  
  free dynamic elements of the path structure 

  er_path_t *pathptr    path structure 

  ++++++++++++++++++++++++++++++++++++++*/
void er_free_dynadescr( er_path_t *pathptr )
{
  if(pathptr->type == ER_PATH_EXEC ) {
    char **argv = pathptr->descr.exec.argv;
    int len=0;
  
    if( argv != NULL ) {
      while( argv[len] != NULL ) {
	UT_free( argv[len] );
	len++;
      }
    }    
    if( argv != NULL ) {
      UT_free(argv);
    }
  }
}



/*++++++++++++++++++++++++++++++++++++++
  
  finds and removes a path identified by identifier
  
  er_ret_t
  er_delete_path      ER_OK on success, ER_INVKEY if path not found
  
  char *key           path identifier
  ++++++++++++++++++++++++++++++++++++++*/
er_ret_t
er_delete_path(char *key )
{
  er_path_t *pathptr;

  if( (pathptr=er_find_path_byname(key)) == NULL ) {
     return ER_INVKEY;
  }
  else {
    /* remove filters */
    wr_clear_list( &(pathptr->filters) );
    /* delete dynamic elements */
    er_free_dynadescr( pathptr );
    /* free path structure */
    UT_free(pathptr);
    /* remove path link from list */
    er_pathlist = g_list_remove(er_pathlist, pathptr);
    
    /* update arrays */
    er_upd_asparray();  

    return ER_OK;
  }
}

