/* 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 <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/stat.h>
#include <signal.h>
#include <ctype.h>


#ifndef MAXHOSTNAMELEN
#define MAXHOSTNAMELEN 256
#endif

#include "report.h"
#include "db_config.h"
#include "util.h"
#include "aide.h"

#define URL_UNSAFE " <>\"#%{}|\\^~[]`@:\033"
#define ISPRINT(c) (isascii(c) && isprint(c))

static const char* url_name[] = { 
  "file", "stdin", "stdout", "stderr", "fd"};

static const int url_value[] = {
  url_file, url_stdin, url_stdout,url_stderr,url_fd };

const int url_ntypes=sizeof(url_value)/sizeof(int);

int cmpurl(url_t* u1,url_t* u2)
{
  if(u1->type!= u2->type){
    return RETFAIL;
  };
  if(strcmp(u1->value,u2->value)!=0){
    return RETFAIL;
  }

  return RETOK;
};

url_t* parse_url(char* val)
{
  url_t* u=NULL;
  char* r=NULL;
  int i=0;

  if(val==NULL){
    return NULL;
  }
  
  u=(url_t*)malloc(sizeof(url_t));
  
  for(r=val;r[0]!=':'&&r[0]!='\0';r++);
  
  if(r[0]!='\0'){
    r[0]='\0';
    r++;
  }
  u->type=url_unknown;
  for(i=0;i<url_ntypes;i++){
    if(strcmp(val,url_name[i])==0){
      u->type=url_value[i];
      break;
    }
  }
  
  
  switch (u->type) {
  case url_file : {
    if(r[0]=='/'&&(r+1)[0]=='/'&&(r+2)[0]=='/'){
      u->value=strdup(r+2);
      break;
    }
    if(r[0]=='/'&&(r+1)[0]=='/'&&(r+2)[0]!='/'){
      char*hostname=(char*)malloc(sizeof(char)*MAXHOSTNAMELEN);
      char* t=r+2;
      r+=2;
      for(i=0;r[0]!='/'&&r[0]!='\0';r++,i++);
      if(r[0]=='\0'){
	error(10,"Invalid file-URL,no path after hostname: file:%s\n",t);
	return NULL;
      }
      u->value=strdup(r);
      r[0]='\0';
      if(gethostname(hostname,MAXHOSTNAMELEN)==-1){
	free(hostname);
	hostname="localhost";
      }
      if( (strcmp(t,"localhost")==0)||(strcmp(t,hostname)==0)){
	free(hostname);
	break;
      } else {
	error(10,"Invalid file-URL, cannot use hostname other than localhost or %s: file:%s\n",hostname,u->value);
	free(hostname);
	return NULL;
      }
      free(hostname);
      break;
    }
    u->value=strdup(r);
    
    break;
  }
  case url_unknown : {
    error(20,"Unknown URL-type:%s\n",val);
    break;
  }
  default : {
    u->value=strdup(r);
    break;
  }
  }

  return u;
}

/* Returns 1 if the string contains unsafe characters, 0 otherwise.  */
int contains_unsafe (const char *s)
{
  for (; *s; s++)
    if (strchr (URL_UNSAFE,(int) *s)||!ISPRINT((int)*s))
      return 1;
  return 0;
}
 
/* Decodes the forms %xy in a URL to the character the hexadecimal
   code of which is xy.  xy are hexadecimal digits from
   [0123456789ABCDEF] (case-insensitive).  If x or y are not
   hex-digits or `%' precedes `\0', the sequence is inserted
   literally.  */
 
void decode_string (char* s)
{
  char *p = s;
 
  for (; *s; s++, p++)
    {
      if (*s != '%')
        *p = *s;
      else
        {
          /* Do nothing if at the end of the string, or if the chars
             are not hex-digits.  */
          if (!*(s + 1) || !*(s + 2)
              || !(ISXDIGIT (*(s + 1)) && ISXDIGIT (*(s + 2))))
            {
              *p = *s;
              continue;
            }
          *p = (ASC2HEXD (*(s + 1)) << 4) + ASC2HEXD (*(s + 2));
          s += 2;
        }
    }
  *p = '\0';
}
 
/* Encodes the unsafe characters (listed in URL_UNSAFE) in a given
   string, returning a malloc-ed %XX encoded string.  */
char* encode_string (const char* s)
{
  const char *b;
  char *p, *res;
  int i;
 
  b = s;
  for (i = 0; *s; s++, i++){
    if (strchr (URL_UNSAFE,(int) *s)||!ISPRINT((int)*s)){
      i += 2; /* Two more characters (hex digits) */
    }
  }

  res = (char *)malloc (i + 1);
  s = b;
  for (p = res; *s; s++){
    if (strchr (URL_UNSAFE, *s)||!ISPRINT((int)*s))
      {
        const unsigned char c = *s;
        *p++ = '%';
        *p++ = HEXD2ASC (c >> 4);
        *p++ = HEXD2ASC (c & 0xf);
      }
    else {
      *p++ = *s;
    }
  }
  *p = '\0';
  return res;
}

char* perm_to_char(mode_t perm)
{
  char*pc=NULL;
  int i=0;
  
  pc=(char*)malloc(sizeof(char)*11);
  for(i=0;i<10;i++){
    pc[i]='-';
  }
  pc[10]='\0';

  if(S_ISDIR(perm))
    pc[0]='d';
#ifdef S_ISFIFO
  if(S_ISFIFO(perm))
    pc[0]='p';
#endif
  if(S_ISLNK(perm))
    pc[0]='l';
  if(S_ISBLK(perm))
    pc[0]='b';
  if(S_ISCHR(perm))
    pc[0]='c';
#ifdef S_ISDOOR
  if(S_ISDOOR(perm))
    pc[0]='|';
#endif
#ifdef S_ISSOCK
  if(S_ISSOCK(perm))
    pc[0]='s';
#endif
  
  if((S_IRUSR&perm)==S_IRUSR){
    pc[1]='r';
  }
  if((S_IWUSR&perm)==S_IWUSR){
    pc[2]='w';
  }
  if((S_IXUSR&perm)==S_IXUSR){
    pc[3]='x';
  }
  if((S_IRGRP&perm)==S_IRGRP){
    pc[4]='r';
  }
  if((S_IWGRP&perm)==S_IWGRP){
    pc[5]='w';
  }
  if((S_IXGRP&perm)==S_IXGRP){
    pc[6]='x';
  }
  if((S_IROTH&perm)==S_IROTH){
    pc[7]='r';
  }
  if((S_IWOTH&perm)==S_IWOTH){
    pc[8]='w';
  }
  if((S_IXOTH&perm)==S_IXOTH){
    pc[9]='x';
  }

  if((S_ISUID&perm)==S_ISUID){
    if((S_IXUSR&perm)==S_IXUSR){
      pc[3]='s';
    } else {
      pc[3]='S';
    }
  }
  if((S_ISGID&perm)==S_ISGID){
    if((S_IXGRP&perm)==S_IXGRP){
      pc[6]='s';
    } else {
      pc[6]='l';
    }
  }
  if((S_ISVTX&perm)==S_ISVTX){
    if((S_IXOTH&perm)==S_IXOTH){
      pc[9]='t';
    } else {
      pc[9]='T';
    }
  }

  error(230,"perm_to_char(): %i -> %s\n",perm,pc);

  return pc;
}

void init_sighandler()
{
  signal(SIGBUS,sig_handler);
  signal(SIGTERM,sig_handler);
  signal(SIGUSR1,sig_handler);
  signal(SIGUSR2,sig_handler);
  signal(SIGHUP,sig_handler);

  return;
}

void sig_handler(int signal)
{
  switch(signal){
  case SIGBUS  : 
  case SIGSEGV :{
    error(200,"Caught SIGBUS/SIGSEGV\n");
    if(conf->catch_mmap==1){
      error(5,"Caught SIGBUS/SEGV while mmapping. File was truncated while aide was running?\n");
      conf->catch_mmap=0;
    } else {
      error(0,"Caught SIGBUS/SEGV. Exiting\n");
      exit(1);
    }
    break;
  }
  case SIGHUP : {
    error(5,"Caught SIGHUP\n");
    break;
  }
  case SIGTERM : {
    error(5,"Caught SIGTERM\nUse SIGKILL to terminate\n");
    break;
  }
  case SIGUSR1 : {
    error(5,"Setting output to debug level according to signal\n");
    conf->verbose_level=220;
    break;
  }
  case SIGUSR2 : {
    error(5,"Setting output to normal level according to signal\n");
    conf->verbose_level=5;
    break;
  }
  }
  error(220,"Caught signal %d\n",signal);
  init_sighandler();

  return;
}
