/* aide, Advanced Intrusion Detection Environment
 *
 * Copyright (C) 1999 Rami Lehti, Pablo Virolainen
 *
 * 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 <string.h>
#include <sys/types.h>
#include <stdlib.h>
#include <time.h>

#include "types.h"
#include "base64.h"
#include "db_file.h"
#include "conf_yacc.h"
#include "util.h"
#include "aide.h"

#ifdef WITH_MHASH
#include <mhash.h>
#endif

#include "cipher.h"

extern int db_scan(); /* Rumaa.... */
extern char* dbtext; /* Todella rumaa... */
extern long db_lineno;

const static char* db_names[] = {
  "name", "bcount", "perm", "uid", "gid", "size", "atime", "ctime",
  "mtime", "inode", "count", "md5", "sha1", "rmd160", "tiger",
  "crc32", "haval", "rmd128","snefru","gost","crc32b", "unknown" }; 

const static int db_value[] = {
  db_filename, db_bcount, db_perm, db_uid, db_gid, db_size, db_atime,
  db_ctime, db_mtime, db_inode, db_lnkcount, db_md5, db_sha1,
  db_rmd160, db_tiger, db_crc32, db_haval, db_rmd128, db_snefru,
  db_gost, db_crc32b,
  db_unknown };


extern void db_buff(FILE*);

int db_file_read_spec(db_config* conf){
  
  int i=0;

  conf->db_in_order=(DB_FIELD*) malloc(1*sizeof(DB_FIELD));
  
  while ((i=db_scan())!=TNEWLINE){
    switch (i) {
      
    case TID : {
      int l;
      

      /* Yes... we do not check if realloc returns nonnull */

      conf->db_in_order=(DB_FIELD*)
	realloc((void*)conf->db_in_order,
		(conf->db_in_size+1)*sizeof(DB_FIELD));
      
      
      conf->db_in_order[conf->db_in_size]=db_unknown;
      
      for (l=0;l<db_unknown;l++){
	
	if (strcmp(db_names[l],dbtext)==0) {
	  
	  if (check_db_order(conf->db_in_order, conf->db_in_size,
			     db_value[l])==RETFAIL) {
	    error(0,"Field %s redefined in @@dbspec\n",dbtext);
	    conf->db_in_order[conf->db_in_size]=db_unknown;
	  } else {
	    conf->db_in_order[conf->db_in_size]=db_value[l];
	  }
	  conf->db_in_size++;
	  break;
	}
      }
      break;
    }
    
    case TDBSPEC : {
      error(0,"Only one @@dbspec in inputdatabase.\n");
      return RETFAIL;
      break;
    }
    
    default : {
      error(0,"Aide internal error while reading inputdatabase.\n");
      return RETFAIL;
    }
    }
  }
  return RETOK;
}


char** db_readline_file(db_config* conf){
  
  char** s=NULL;
  
  int i=0;
  int r;
  int a=0;
  int token=0;
  
  if (conf->db_in_size==0) {
    db_buff(conf->db_in);
    
    token=db_scan();
    while(token==TUNKNOWN){
      token=db_scan();
    }

    if (token!=TDBSPEC) {
      /*
       * error.. must be a @@dbspec line
       */
      
      switch (conf->db_in_url->type) {
      case url_file : {
	error(0,"Filedatabase must have one db_spec spesification\n");
	break;
      }

      case url_stdin : {
	error(0,"Pipedatabase must have one db_spec spesification\n");
	break;
      }

      case url_fd: {
	error(0,"FD-database must have one db_spec spesification\n");
	break;
      }

      default : {
	error(0,"db_readline_file():Unknown or unsupported db in type.\n");
	
	break;
      }
      
      }
      return s;
    }
    
    /*
     * Here we read da spec
     */
    
    if (db_file_read_spec(conf)!=0) {
      /* somethin went wrong */
      return s;
    }
    
  }
  
  s=(char**)malloc(sizeof(char*)*db_unknown);

  /* We NEED this to avoid Bus errors on Suns */
  for(i=0;i<db_unknown;i++){
    s[i]=NULL;
  }
  
  for(i=0;i<conf->db_in_size;i++){
    switch (r=db_scan()) {
      
    case TDBSPEC : {
      
      error(0,"Databasefile can have only one db_spec.\nTrying to continue on line %i\n",db_lineno);
      
      /*
	if (conf->db_in_order[i]!=db_unknown) {
	s[conf->db_in_order[i]]=(char*)strdup(dbtext);
      }
      */
      
      break;
    }
    case TNAME : {
      if (conf->db_in_order[i]!=db_unknown) {
	s[conf->db_in_order[i]]=(char*)strdup(dbtext);
      }
      break;
    }
    
    case TID : {
      if (conf->db_in_order[i]!=db_unknown) {
	s[conf->db_in_order[i]]=(char*)strdup(dbtext);
      }
      break;
    }
    
    case TNEWLINE : {
      
      if (i==0) {
	i--;
	break;
      }
      
      /*  */

      error(0,"Not enough parameters in db:%i. Trying to continue.\n",
	    db_lineno);
      for(a=0;a<i;a++){
	free(s[conf->db_in_order[a]]);
	s[conf->db_in_order[a]]=NULL;
      }
      i=0;
      break;

    }

    case TEOF : {
	
      /* This can be the first token on a line */
      if(i>0){
	error(0,"Not enough parameters in db:%i\n",db_lineno);
      };
      for(a=0;a<i;a++){
	free(s[conf->db_in_order[a]]);
      }
      free(s);
      return NULL;
      break;
    }
    
    default : {
      
      error(0,"Not implemented %i\n\"%s\"",r,dbtext);
      
      free(s);
      s=NULL;
      i=conf->db_in_size;
      break;
    }
    }
    
  }
  

  /*
   * If we don't get newline after reading all sells we print an error
   */
  a=db_scan();

  if (a!=TNEWLINE&&a!=TEOF) {
    error(0,"Newline exptexted in database. Readin until end of line\n");
    do {
      
      error(0,"Skipped value %s\n",dbtext);
      
      /*
       * Null statement
       */ 
      a=db_scan();
    }while(a!=TNEWLINE&&a!=TEOF);
    
  }
  
  return s;
  
}

int db_writechar(char* s,FILE* file,int i)
{
  char* r=NULL;
  int retval=0;

  if(i) {
    fprintf(file," ");
  }

  r=CLEANDUP(s);
  retval=fprintf(file,"%s",r);
  free(r);

  return retval;
}

int db_writeint(long i,FILE* file,int a)
{
  if(a) {
    fprintf(file," ");
  }
  
  return fprintf(file,"%li",i);
  
}

int db_write_byte_base64(byte*data,size_t len,FILE* file,int i )
{
  char* tmpstr=NULL;
  int retval=0;
  
  tmpstr=encode_base64(data,len);
  if(i){
    fprintf(file," ");
  }

  if(tmpstr){
    retval=fprintf(file,tmpstr);
    free(tmpstr);
    return retval;
  }else {
    return fprintf(file,"0");
  }
  return 0;

}

int db_write_time_base64(time_t i,FILE* file,int a)
{
  static char* ptr=NULL;
  char* tmpstr=NULL;
  int retval=0;

  if(a){
    fprintf(file," ");
  }

  if(i==0){
    retval=fprintf(file,"0");
    return retval;
  }


  ptr=(char*)malloc(sizeof(char)*TIMEBUFSIZE);
  if (ptr==NULL) {
    error(0,"\nCannot allocate memory..\n");
    abort();
  }
  memset((void*)ptr,0,sizeof(char)*TIMEBUFSIZE);

  sprintf(ptr,"%li",i);


  tmpstr=encode_base64(ptr,strlen(ptr));
  retval=fprintf(file,tmpstr);
  free(tmpstr);
  free(ptr);

  return retval;

}

int db_writeoct(long i, FILE* file,int a)
{
  if(a) {
    fprintf(file," ");
  }
  
  return fprintf(file,"%lo",i);
  
}

int db_writespec_file(db_config* conf)
{
  int i=0;
  int j=0;
  int retval=1;
  struct tm* st;
  time_t tim=time(&tim);
  st=localtime(&tim);

  retval=fprintf(conf->db_out,
		 "# This file was generated by Aide, version %s\n"
		 "# Time of generation was %0.4u-%0.2u-%0.2u %0.2u:%0.2u:%0.2u\n",
		 AIDEVERSION,
		 st->tm_year+1900, st->tm_mon+1, st->tm_mday,
		 st->tm_hour, st->tm_min, st->tm_sec
		 );
  if(retval==0){
    return RETFAIL;
  }

  retval=fprintf(conf->db_out,"@@db_spec ");
  if(retval==0){
    return RETFAIL;
  }
  for(i=0;i<conf->db_out_size;i++){
    for(j=0;j<db_unknown;j++){
      if(db_value[j]==conf->db_out_order[i]){
	retval=fprintf(conf->db_out,"%s ",db_names[j]);
	if(retval==0){
	  return RETFAIL;
	}
	break;
      }
    }
  }
  retval=fprintf(conf->db_out,"\n");
  if(retval==0){
    return RETFAIL;
  }
  return RETOK;
}

int db_writeline_file(db_line* line,db_config* conf){
  int i;

  for(i=0;i<conf->db_out_size;i++){
    switch (conf->db_out_order[i]) {
    case db_filename : {
      db_writechar(line->filename,conf->db_out,i);
      break;
    }
    case db_bcount : {
      db_writeint(line->bcount,conf->db_out,i);
      break;
    }

    case db_mtime : {
      db_write_time_base64(line->mtime,conf->db_out,i);
      break;
    }
    case db_atime : {
      db_write_time_base64(line->atime,conf->db_out,i);
      break;
    }
    case db_ctime : {
      db_write_time_base64(line->ctime,conf->db_out,i);
      break;
    }
    case db_inode : {
      db_writeint(line->inode,conf->db_out,i);
      break;
    }
    case db_lnkcount : {
      db_writeint(line->nlink,conf->db_out,i);
      break;
    }
    case db_uid : {
      db_writeint(line->uid,conf->db_out,i);
      break;
    }
    case db_gid : {
      db_writeint(line->gid,conf->db_out,i);
      break;
    }
    case db_size : {
      db_writeint(line->size,conf->db_out,i);
      break;
    }
    case db_md5 : {
      db_write_byte_base64(line->md5,
			   md_digest_length(DIGEST_ALGO_MD5),conf->db_out,i);
      break;
    }
    case db_sha1 : {
      db_write_byte_base64(line->sha1,
			   md_digest_length(DIGEST_ALGO_SHA1),conf->db_out,i);
      break;
    }
    case db_rmd160 : {
      db_write_byte_base64(line->rmd160,
			   md_digest_length(DIGEST_ALGO_RMD160),
			   conf->db_out,i);
      break;
    }
    case db_tiger : {
      db_write_byte_base64(line->tiger,
			   md_digest_length(DIGEST_ALGO_TIGER),
			   conf->db_out,i);
      break;
    }
    case db_perm : {
      db_writeoct(line->perm,conf->db_out,i);
      break;
    }
#ifdef WITH_MHASH
    case db_crc32 : {
      db_write_byte_base64(line->crc32,
			   mhash_get_block_size(MHASH_CRC32),
			   conf->db_out,i);
      break;
    }
    case db_crc32b : {
      db_write_byte_base64(line->crc32b,
			   mhash_get_block_size(MHASH_CRC32B),
			   conf->db_out,i);
      break;
    }
    case db_haval : {
      db_write_byte_base64(line->haval,
			   mhash_get_block_size(MHASH_HAVAL),
			   conf->db_out,i);
      break;
    }
    case db_rmd128 : {
      db_write_byte_base64(line->rmd128,
			   mhash_get_block_size(MHASH_RIPEMD128),
			   conf->db_out,i);
      break;
    }
    case db_snefru : {
      db_write_byte_base64(line->snefru,
			   mhash_get_block_size(MHASH_SNEFRU),
			   conf->db_out,i);
      break;
    }
    case db_gost : {
      db_write_byte_base64(line->gost ,
			   mhash_get_block_size(MHASH_GOST),
			   conf->db_out,i);
      break;
    }
#endif
    default : {
      error(0,"Not implemented in db_writeline_file %i\n",
	    conf->db_out_order[i]);
      return RETFAIL;
    }
    
    }
    
  }

  fprintf(conf->db_out,"\n");
  return RETOK;
}
