/* aide, Advanced Intrusion Detection Environment
 *
 * Copyright (C) 1999 Rami Lehti
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License as
 * published by the Free Software Foundation; either version 2 of the
 * License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
 * General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>
#include <sys/param.h>

#include "aide.h"
#include "conf_lex.h"
#include "conf_yacc.h"
#include "db_config.h"
#include "gen_list.h"
#include "symboltable.h"
#include "util.h"


#define BUFSIZE 4096

#ifndef MAXHOSTNAMELEN
#define MAXHOSTNAMELEN 256
#endif

int commandconf(const char mode,const char* line)
{
  static char* before=NULL;
  static char* config=NULL;
  static char* after=NULL;
  char* all=NULL;
  char* tmp=NULL;
  int l=0;

  switch(mode){
  case 'B':{
    if(before==NULL){
      before=strdup(line);
    }
    else {
      tmp=(char*)malloc(sizeof(char)
			*(strlen(before)+strlen(line)+2));
      strcat(tmp,before);
      strcat(tmp,"\n");
      strcat(tmp,line);
      free(before);
      before=tmp;
    }
    break;
  }
  case 'C':{
    config=strdup(line);
    break;
  }
  case 'A':{
    if(after==NULL){
      after=strdup(line);
    }
    else {
      tmp=(char*)malloc(sizeof(char)
			*(strlen(after)+strlen(line)+2));
      strcat(tmp,after);
      strcat(tmp,"\n");
      strcat(tmp,line);
      free(after);
      after=tmp;
    }
    break;
  }
  case 'D': {
    /* Let's do it */
    int rv=-1;
    
    if (config!=NULL && strcmp(config,"-")==0) {
      error(255,"Config from stdin\n");
      rv=0;
    } else {
      
      rv=access(config,R_OK);
      if(rv==-1){
	error(0,"Cannot access config file:%s:%s\n",config,strerror(errno));
      }
    }
    
    if(before==NULL&&after==NULL&&
       (config==NULL||strcmp(config,"")==0||rv==-1)){
      error(0,"No config defined\n");
      return RETFAIL;
    }
    if(before!=NULL) {
      l+=strlen(before);
    }
    if(config!=NULL) {
      l+=strlen(config);
    }
    if(after!=NULL) {
      l+=strlen(after);
    }
    l+=strlen("@@include \n\n\n"+1);
    
    all=(char*)malloc(sizeof(char)*l);

    memset(all,0,l);
    if(before!=NULL){
      strcat(all,before);
      strcat(all,"\n");
    }
    strcat(all,"@@include ");
    strcat(all,config);
    strcat(all,"\n");
    if(after!=NULL){
      strcat(all,after);
      strcat(all,"\n");
    }
    
    error(200,"commandconf():%s\n",all);
    
    conf_scan_string(all);
    
    if(confparse()){
      return RETFAIL;
    }
    
    break;
  }
  default: {
    error(0,"Illegal argument %c to commmandconf()\n",mode);
    break;
  }
  }
  return RETOK;
}

int check_db_order(DB_FIELD* d,int size, DB_FIELD a)
{
  int i=0;
  for(i=0;i<size;i++){
    if(d[i]==a)
      return RETFAIL;
  }
  return RETOK;
}

int check_dboo(DB_FIELD a){
  return check_db_order(conf->db_out_order,conf->db_out_size,a);
}
void update_db_out_order(int a)
{
  if((a&DB_PERM) && (check_dboo(db_perm)!=RETFAIL)){
    conf->db_out_order[conf->db_out_size++]=db_perm;
  }
  if((a&DB_BCOUNT) && (check_dboo(db_bcount)!=RETFAIL)){
    conf->db_out_order[conf->db_out_size++]=db_bcount;
  }
  if((a&DB_UID) && (check_dboo(db_uid)!=RETFAIL)){
    conf->db_out_order[conf->db_out_size++]=db_uid;
  }
  if((a&DB_GID) && (check_dboo(db_gid)!=RETFAIL)){
    conf->db_out_order[conf->db_out_size++]=db_gid;
  }
  if((a&DB_SIZE) && (check_dboo(db_size)!=RETFAIL)){
    conf->db_out_order[conf->db_out_size++]=db_size;
  }
  if((a&DB_SIZEG) && (check_dboo(db_size)!=RETFAIL)){
    conf->db_out_order[conf->db_out_size++]=db_size;
  }
  if((a&DB_ATIME) && (check_dboo(db_atime)!=RETFAIL)){
    conf->db_out_order[conf->db_out_size++]=db_atime;
  }
  if((a&DB_MTIME) && (check_dboo(db_mtime)!=RETFAIL)){
    conf->db_out_order[conf->db_out_size++]=db_mtime;
  }
  if((a&DB_CTIME) && (check_dboo(db_ctime)!=RETFAIL)){
    conf->db_out_order[conf->db_out_size++]=db_ctime;
  }
  if((a&DB_INODE) && (check_dboo(db_inode)!=RETFAIL)){
    conf->db_out_order[conf->db_out_size++]=db_inode;
  }
  if((a&DB_LNKCOUNT) && (check_dboo(db_lnkcount)!=RETFAIL)){
    conf->db_out_order[conf->db_out_size++]=db_lnkcount;
  }
  if((a&DB_MD5) && (check_dboo(db_md5)!=RETFAIL)){
    conf->db_out_order[conf->db_out_size++]=db_md5;
  }
  if((a&DB_SHA1) && (check_dboo(db_sha1)!=RETFAIL)){
    conf->db_out_order[conf->db_out_size++]=db_sha1;
  }
  if((a&DB_RMD160) && (check_dboo(db_rmd160)!=RETFAIL)){
    conf->db_out_order[conf->db_out_size++]=db_rmd160;
  }
  if((a&DB_TIGER) && (check_dboo(db_tiger)!=RETFAIL)){
    conf->db_out_order[conf->db_out_size++]=db_tiger;
  }
#ifdef WITH_MHASH
  if((a&DB_CRC32) && (check_dboo(db_crc32)!=RETFAIL)){
    conf->db_out_order[conf->db_out_size++]=db_crc32;
  }
  if((a&DB_HAVAL) && (check_dboo(db_haval)!=RETFAIL)){
    conf->db_out_order[conf->db_out_size++]=db_haval;
  }
  if((a&DB_RMD128) && (check_dboo(db_rmd128)!=RETFAIL)){
    conf->db_out_order[conf->db_out_size++]=db_rmd128;
  }
  if((a&DB_SNEFRU) && (check_dboo(db_snefru)!=RETFAIL)){
    conf->db_out_order[conf->db_out_size++]=db_snefru;
  }
  if((a&DB_GOST) && (check_dboo(db_gost)!=RETFAIL)){
    conf->db_out_order[conf->db_out_size++]=db_gost;
  }
  if((a&DB_CRC32B) && (check_dboo(db_crc32b)!=RETFAIL)){
    conf->db_out_order[conf->db_out_size++]=db_crc32b;
  }
#endif
}


char* get_variable_value(char* var)
{
  list* r=NULL;
  
  if((r=list_find(var,conf->defsyms))){
    return (((symba*)r->data)->value);
  };

  return NULL;
}

void putbackvariable(char* var)
{
  char* a=NULL;
  
  char* v=strdup(var);
  
  char* subst_begin=strstr(v,"@@{");
  char* subst_end=strstr(subst_begin,"}");
  
  char* tmp=(char*)malloc((subst_end-subst_begin)+1);
  
  tmp = strncpy(tmp,subst_begin+3,subst_end-subst_begin-3);
  
  tmp[subst_end-subst_begin-3]='\0';
  
  conf_put_token(subst_end+1);
  
  if((a=get_variable_value(tmp))!=NULL){
    conf_put_token(a);
  }
  else {
    /*
     * We can use nondefined variable
     */ 
  }
  
  subst_begin[0]='\0';
  
  conf_put_token(v);
  conf_put_token("\n");
  free(v);

}


void do_define(char* name, char* value)
{
  symba* s=NULL;
  list* l=NULL;

  if(!(l=list_find(name,conf->defsyms))){
    s=(symba*)malloc(sizeof(symba));
    s->name=name;
    s->value=value;
    conf->defsyms=list_append(conf->defsyms,(void*)s);
  }
  else {
    free(((symba*)l->data)->value);
    ((symba*)l->data)->value=NULL;
    ((symba*)l->data)->value=value;
  }
}

void do_undefine(char* name)
{
  list*r=NULL;

  if((r=list_find(name,conf->defsyms))){
    free(((symba*)r->data)->value);
    free((symba*)r->data);
    r->data=NULL;
    conf->defsyms=list_delete_item(r);
  }
}

int handle_endif(int doit,int allow_else){
  
  if(doit){
    int count=1;
    error(230,"\nEating until @@endif\n");
    do {
      int i = conflex();
      switch (i) {
      case TIFDEF : {
	count++;
	break;
      }
      case TIFNDEF : {
	count++;
	break;
      }
      case TENDIF : {
	count--;
	break;
      }

      case TIFHOST : {
	count++;
	break;
      }

      case TIFNHOST : {
	count++;
	break;
      }
      
      case TELSE : {
	
	if (count==1) {
	  /*
	   * We have done enough 
	   */ 
	  if (allow_else) {
	    return 0;
	  }
	  else {
	    conferror("Ambigous else");
	    return -1;
	  }
	}
	
	break;
      }
      
      case 0 : {
	conferror("@@endif or @@else expected");
	return -1;
	count=0;
      }
      
      default : {
	/*
 	 * empty default
	 */
      }
      }
      
      
    } while (count!=0);
    
    conf_put_token("\n@@endif\n");
    error(230,"\nEating done\n");
  }
  
  return 0;
  
}

int do_ifxdef(int mode,char* name)
{
  int doit;
  doit=mode;

  if((list_find(name,conf->defsyms))){
    doit=1-doit;
  }
  
  return (handle_endif(doit,1));

}

int do_ifxhost(int mode,char* name)
{
  int doit;
  char* s=NULL;
  doit=mode;
  s=(char*)malloc(sizeof(char)*MAXHOSTNAMELEN+1);

  if(gethostname(s,MAXHOSTNAMELEN)==-1){
    error(0,"Couldn't get hostname %s",name);
    return -1;
  }  
  if(strcmp(name,s)==0)
    doit=1-doit;

  return (handle_endif(doit,1));
}

list* append_rxlist(char* rx,int attr,list* rxlst)
{
  rx_rule* r=NULL;
  r=(rx_rule*)malloc(sizeof(rx_rule));
  r->rx=rx;
  r->attr=attr;
  update_db_out_order(r->attr);
  rxlst=list_append(rxlst,(void*)r);
  
  return rxlst;
}

void do_groupdef(char* group,int value)
{
  list* r=NULL;
  symba* s=NULL;

  if((r=list_find(group,conf->groupsyms))){
      ((symba*)r->data)->ival=value;
      return;
  }
  /* This is a new group */
  s=(symba*)malloc(sizeof(symba));
  s->name=group;
  s->ival=value;
  conf->groupsyms=list_append(conf->groupsyms,(void*)s);
}

int get_groupval(char* group)
{
  list* r=NULL;
  if((r=list_find(group,conf->groupsyms))){
    return (((symba*)r->data)->ival);
  }
  return -1;
}



void do_dbindef(char* val)
{
  url_t* u=NULL;

  if(conf->db_in_url==NULL){
    u=parse_url(val);
    /* FIXME Check the URL if you add support for databases that cannot be 
     * both input and output urls */
    if(u==NULL||u->type==url_unknown||u->type==url_stdout
       ||u->type==url_stderr) {
      error(0,"Unsupported input URL-type:%s\n",val);
    }
    else {
      conf->db_in_url=u;
    }
  }

  free(val);
}

void do_dboutdef(char* val)
{
  url_t* u=NULL;

  error(200,"Setting output database \"%s\"\n",val);

  if(conf->db_out_url==NULL){
    u=parse_url(val);
    /* FIXME Check the URL if you add support for databases that cannot be 
     * both input and output urls */
    if(u==NULL||u->type==url_unknown||u->type==url_stdin){
      error(0,"Unsupported output URL-type:%s\n",val);
    }
    else{
      conf->db_out_url=u;
      error(200,"Output database set to \"%s\" \"%s\"\n",val,u->value);
    }
  } else {
    error(200,"Output database already set\n");
  }

  free(val);
}

void do_repurldef(char* val)
{
  url_t* u=NULL;

  
  u=parse_url(val);
  /* FIXME Check the URL if you add support for databases that cannot be 
   * both input and output urls */
  if(u==NULL||u->type==url_unknown||u->type==url_stdin){
    error(0,"Unsupported output URL-type:%s\n",val);
  } else {
    error_init(u);
  }
  
}

void do_verbdef(char* val)
{
  char* err=NULL;
  long a=0;
  
  a=strtol(val,&err,10);
  if(*err!='\0' || a>255 || a<0 || errno==ERANGE){
    error(0, "Illegal verbosity level:%s\n",val);
    error(10,"Using previous value:%i\n",conf->verbose_level);
    return;
  }    
  else {
    if(conf->verbose_level==-1){
      conf->verbose_level=a;
    }else {
      error(210,"Verbosity already defined to %i\n",conf->verbose_level);
    }
  }
}


