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

  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                             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 "erroutines.h"
#include "memwrap.h"


/* find path by name, returns the pointer to it if found or NULL. */
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;
}

/* the "asp" array describes the "OR" of all filters' aspects. This is to allow
   fast dropping of messages that would be dropped anyway 
   
   when invoked, clears the array and then goes through all filters
   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;
	  }
	}
      }
    }
  }
}



er_ret_t 
er_add_filter( er_path_t *pathptr, er_filter_t *filter )
{
  er_filter_t *ft = malloc(sizeof(er_filter_t));
  
  memcpy(ft, filter, sizeof(er_filter_t));
  pathptr->filters =  g_list_append(pathptr->filters, ft);
 
  return ER_OK;
}


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;
      }
    }
  }

  return ER_OK;
}


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 !!! */
  }
  
  ft = 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;
}

/* find the path by name and replace its definition without touching
   the filters
*/
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;
  }
}

er_ret_t
er_delete_filter( char *key, unsigned int 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, filterid);
    /* free filter structure */
    free(filtptr);
    /* remove filter link from list */
    pathptr->filters = g_list_remove(pathptr->filters, filtptr);
    /* update arrays */
    er_upd_asparray();  

    return ER_OK;
  }
}


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 = calloc( sizeof(char **) * (len+2), 1 );
  if( len > 0 ) {
    memcpy( newargv, argv, sizeof(char **) * len);
  }
  newargv[len] = strdup(arg);
  
  pathptr->descr.exec.argv = newargv;

  if( argv != NULL ) {
    free(argv);
  }
}
 
/* free dynamic elements of the path description */ 
void er_free_dynadescr( er_path_t *pathptr )
{
  char **argv = pathptr->descr.exec.argv;
  int len=0;
  
  if( argv != NULL ) {
    while( argv[len] != NULL ) {
      free( argv[len] );
      len++;
    }
  }

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


/* finds and removes a path identified by the key (name).
   Also removes all associated filters.

   returns:
   ER_OK on success 
   
*/
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 */
    free(pathptr);
    /* remove path link from list */
    er_pathlist = g_list_remove(er_pathlist, pathptr);
    
    /* update arrays */
    er_upd_asparray();  

    return ER_OK;
  }
}


/************************************************************/
static
void er_print_format(int format, GString *g_reply )
{
  int i;

  for(i=0;  er_formarr[i].n != NULL; i++) {
    if( format & er_formarr[i].v ) {
      g_string_sprintfa(g_reply, "%s|",er_formarr[i].n);
    }
  }
  /* cut the last "|" */
  g_string_truncate(g_reply, strlen(g_reply->str)-1);
}


static
void er_print_one_path_descr(er_path_t *pathptr, GString *g_reply )
{
  er_path_descr_t *d = &(pathptr->descr);

  switch(pathptr->type) {
  case  ER_PATH_NAME:
    g_string_sprintfa(g_reply, "NAME %s%s", d->name.filename, 
		      d->name.date ? " DATE" : ""
		      );
    break;
  case  ER_PATH_SOCK:
    g_string_sprintfa(g_reply, "SOCK %d", d->sock.fd );

    break;

  case  ER_PATH_EXEC:
    g_string_sprintfa(g_reply, "EXEC ");
    if( d->exec.usepath ) {
      g_string_sprintfa(g_reply, "PATH ");
    }
    {
      char **argv = d->exec.argv;
      int len=0;
  
      if( argv != NULL ) {
	while( argv[len] != NULL ) {
	  g_string_sprintfa(g_reply, "%s ", argv[len]);
	  len++;
	}
      }
    }
    break;

  default:
    /* XXX other path descriptions missing */
    break;
  }
}

static
void er_print_aspmask(mask_t facmask, unsigned aspmask, GString *g_reply)
{
  int i = 31;

  while(i >= 0) {
    if( aspmask & (1<<i) ) {
      er_getaspsym(facmask, 1<<i, g_reply);
      g_string_append(g_reply, "|");
    }
    
    i--;
  }
  /* cut the last "|" */
  g_string_truncate(g_reply, strlen(g_reply->str)-1);
}

static
void er_print_facmask(mask_t facmask, GString *g_reply)
{
  int i = FAC_NONE;
  
  while( ++i != FAC_LAST ) {
    if( MA_isset(facmask, er_fac_err[i].code) ) {
      g_string_sprintfa(g_reply, "%s|", er_fac_err[i].name);
    }
  }
  /* cut the last "|" */
  g_string_truncate(g_reply, strlen(g_reply->str)-1);

}

static
void er_print_one_filter(er_filter_t *filtptr, GString *g_reply )
{ 
  g_string_sprintfa(g_reply, "( FAC ");
  er_print_facmask( filtptr->fac_mask, g_reply);
  
  if( filtptr->asp_mask != 0 ) {
    g_string_sprintfa(g_reply, "  ASP ");
    er_print_aspmask(  filtptr->fac_mask, filtptr->asp_mask, g_reply);
  }

  g_string_sprintfa(g_reply, "  SEV %s-%s ",
		    er_getsevsym( filtptr->sev_min, ER_M_SEVCHAR),
		    er_getsevsym( filtptr->sev_max, ER_M_SEVCHAR)
		    );
  if( filtptr->thr_id != 0 ) {
    g_string_sprintfa(g_reply, "   THR %u ", filtptr->thr_id);
  }
  g_string_sprintfa(g_reply, " )" );
}

static
void er_print_one_path(er_path_t *pathptr, GString *g_reply )
{
  GList   *qitem;
  int f=1;
  
  g_string_sprintfa(g_reply,"%s { ", pathptr->name );
  g_string_sprintfa(g_reply," FORMAT ");
  er_print_format(pathptr->format, g_reply ); 
  g_string_sprintfa(g_reply,"  ");

  er_print_one_path_descr(pathptr, g_reply);
  g_string_sprintfa(g_reply," }\n");

  for(qitem = g_list_first(pathptr->filters);
      qitem != NULL;
      qitem = g_list_next(qitem)) {
    er_filter_t *filtptr = (er_filter_t *) qitem -> data;
    
    g_string_sprintfa(g_reply,"\t");
    er_print_one_filter(filtptr, g_reply) ;
    g_string_sprintfa(g_reply,"\n");
    f++;
  }

}

void er_print_paths(char **retbuf)
{
  GList   *qitem;
  GString *g_reply = g_string_sized_new(2048); /* initial size */

  for( qitem = g_list_first(er_pathlist);
       qitem != NULL;
       qitem = g_list_next(qitem)) {
    er_path_t *pathptr = qitem -> data;

    /*   g_string_sprintfa(g_reply, "path type %d (%s) with %d filters\n",
	   pathptr->type, er_pathtypes[pathptr->type],
	   g_list_length(pathptr->filters));
    */
    er_print_one_path(pathptr, g_reply);

  }

  *retbuf = g_reply->str;  

  /* we must duplicate the string allocated by glib - it can be freed 
     only by glib internal free function :-( */
  g_string_free( g_reply, /* CONSTCOND */ FALSE);
}
