/*
  (c) 1998 netEstate (http://www.netestate.de/, info@netestate.de),
Michael Brunnbauer & Markus Hendel, Gesellschaft fuer Netzwerkkommunikation bR
Am Bluetenanger 8, 80995 Muenchen, Germany

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
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 "errorlib.h"

char* myperror()
{
  char buf[MAX_LINE_SIZE];

  if (sys_errlist[errno])
    {
      return sys_errlist[errno];
    }
  else
    {
      snprintf(buf,MAX_LINE_SIZE,"errno is: %i",errno);
      return buf;
    };
};

int log_to_stdlog(char* text)
{
  int handle;
  
  handle=open(LOG_STDLOG_PATH,O_RDWR|O_APPEND|O_CREAT,
	      S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH);
  if (handle==-1) { return 0; };
  write(handle,text,strlen(text));
  close(handle);
  return 1;
}

int log_to_errlog(char* text)
{
  int handle;
  
  handle=open(LOG_ERRLOG_PATH,O_RDWR|O_APPEND|O_CREAT,
              S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH);
  if (handle==-1) { return 0; };
  write(handle,text,strlen(text));
  close(handle);
  return 1;
}

void log_to_stderr(char* text)
{
  fputs(text,stderr);
}

void log_to_stdout(char* text)
{
  fputs(text,stdout);
}

int log_to_mail(char* text)
{
  FILE* mail;
  int fildes[2];
  int status;

  /* wir oeffnen die pipe */
  if (pipe(fildes)==-1) { return 0; };

  /* und forken */
  switch (fork())
    {
      /* was macht das kind ? */
    case 0:
      {
        if (close(STDIN_FILENO)) { return 0; };
        if (dup(fildes[0]) != STDIN_FILENO) { return 0; };
        if (close(fildes[0])) { return 0; };
        if (close(fildes[1])) { return 0; };
        
        /* es verwandelt sich in SENDMAIL */
        execlp(SENDMAIL,SENDMAIL,"-oi","-bm",LOG_MAIL_ADDRESS,(char *) 0);
	exit(1);
      };
    
    /* ging fork schief ? */
    case -1: { return 0; };
    };

  /* wir oeffnen einen stream zu SENDMAIL */
  mail=fdopen(fildes[1],"w");
  if (!mail) { return 0; };
  if (close(fildes[0])) { return 0; };

  /* und schreiben den header */
  fputs("Subject: ",mail);
  fputs("Message from ",mail);
  fputs(MYNAME,mail);
  fputs("\n\n",mail);

  /* und den body */
  fputs(text,mail);

  /* stream zu SENDMAIL schliessen */
  fclose(mail);

  /* exit-code von SENDMAIL abfragen */
  wait(&status);
  if (status) { return 0; };

  return 1;
}

void log_strcat(char* string1,char* string2)
{
  if (strlen(string1)+strlen(string2)+1 > MAX_LOG_SIZE)
    {
      if (!log_to_errlog("fatal error in log(), log-message gets too large\n"))
	{
	  log_to_stderr("fatal error in log(), log-message gets too large\n");
	};
      panic();
    }
  else
    {
      strcat(string1,string2);
    };
  return;
}

void log_addinfo(char* err,int type,int flags,int error,char* file,int line)
{
  char buf[MAX_LOG_SIZE];
  time_t now;
  struct tm* local_time;

  if (flags & LOG_FLAG_PRINT_NAME)
    {
      switch (type)
	{
	case LOG_NOTICE:
	  {
	    log_strcat(err,"notice / ");
	    break;
	  };
	case LOG_DEBUG:
          {
            log_strcat(err,"debug-notice / ");
            break;
          };
	case LOG_WARN:
          {
            log_strcat(err,"warning / ");
            break;
          };
	case LOG_ERROR:
          {
            log_strcat(err,"error / ");
            break;
          };
	case LOG_FATAL:
          {
            log_strcat(err,"fatal error / ");
            break;
          };
	default:
          {
            log_strcat(err,"unknown log-type, assuming LOG_FATAL / ");
            break;
          };
	};
    };

  if (flags & LOG_FLAG_PRINT_TIME)
    {
      now=time(NULL);
      local_time=localtime(&now);
      strftime(buf,MAX_LOG_SIZE,"%c / ",local_time);
      log_strcat(err,buf);
    };

  if (flags & LOG_FLAG_PRINT_POSITION)
    {
      snprintf(buf,MAX_LOG_SIZE,"file %s, line %i / ",file,line);
      log_strcat(err,buf);
    };

  if (flags & LOG_FLAG_PRINT_ERR)
    {
      switch(error)
	{
	case ERROR_NONE:
	  {
	    log_strcat(err,"err: none");
	    break;
	  };
	case ERROR_UNSPECIFIED:
	  {
	    log_strcat(err,"err: unspecified");
	    break;
	  };
	case ERROR_OUT_OF_MEM:
	  {
	    log_strcat(err,"err: out of mem");
	    break;
	  };
	case ERROR_VALUE_OUT_OF_RANGE:
	  {
	    log_strcat(err,"err: out of range");
	    break;
	  };
	case ERROR_WRONG_ARGUMENT:
	  {
	    log_strcat(err,"err: wrong argument");
	    break;
	  };
	case ERROR_SYNTAX_ERROR:
	  {
	    log_strcat(err,"err: syntax error");
	    break;
	  };
	default:
	  {
	    log_strcat(err,"err: unknown");
	    break;
	  };
	};
    };

  if ((flags & LOG_FLAG_PRINT_NAME) ||
      (flags & LOG_FLAG_PRINT_POSITION) ||
      (flags & LOG_FLAG_PRINT_TIME) ||
      (flags & LOG_FLAG_PRINT_ERR))
    {
      log_strcat(err,"\n");
    };

  return;
}

void log_doit(char* err,int flags)
{
  /* -1 = nicht erforderlich, 1=erfolgreich, 0= nicht erfolgreich */
  int stdlog=-1,errlog=-1,mail=-1;
  int stdout_ok=0;
  
  if (flags & LOG_FLAG_STDLOG) { stdlog=log_to_stdlog(err); };
  if (flags & LOG_FLAG_ERRLOG) { errlog=log_to_errlog(err); };
  if (flags & LOG_FLAG_MAIL) { mail=log_to_mail(err); };

  if (!mail)
    {
      if (!log_to_errlog("could not log to MAIL\n"))
        {
          log_to_stderr("could not log to MAIL\n");
        };
    };
  
  if (!stdlog)
    {
      if (!log_to_errlog("could not log to STDLOG\n"))
	{
	  log_to_stderr("could not log to STDLOG\n");
	};
    }

  if (!errlog)
    {
      log_to_stderr("could not log to ERRLOG\n");
    }

  if (flags & LOG_FLAG_STDOUT)
    {
      log_to_stdout(err);
      stdout_ok=1;
    };
  
  if (
      (!errlog && !stdout_ok) ||
      (!stdlog && !stdout_ok) ||
      (flags & LOG_FLAG_STDERR))
    {
      log_to_stderr(err);
    };
  
  return;
}

void log(int type,char* file,int line,int error,char* fmt,...)
{
  char err[MAX_LOG_SIZE];
  char text[MAX_LOG_SIZE];
  va_list arg;

  if (type==LOG_DEBUG && !debug) { return; };

  /* fehlertext bauen */
  text[0]=0;
  if (fmt)
    {
      va_start(arg,fmt);
      vsnprintf(text,MAX_LOG_SIZE-1,fmt,arg);
      va_end(arg);
      strcat(text,"\n");
    }

  /* los gehts */
  err[0]=0;
  switch (type)
    {
    case LOG_NOTICE:
      {
	log_addinfo(err,type,LOG_FLAGS_NOTICE,error,file,line);
	log_strcat(err,text);
	log_doit(err,LOG_FLAGS_NOTICE);
	break;
      };
    case LOG_DEBUG:
      {
	if (debug)
	  {
	    log_addinfo(err,type,LOG_FLAGS_DEBUG,error,file,line);
	    log_strcat(err,text);
	    log_doit(err,LOG_FLAGS_DEBUG);
	  };
	break;
      };
    case LOG_WARN:
      {
        log_addinfo(err,type,LOG_FLAGS_WARN,error,file,line);
        log_strcat(err,text);
        log_doit(err,LOG_FLAGS_WARN);
        break;
      };
    case LOG_ERROR:
      {
        log_addinfo(err,type,LOG_FLAGS_ERROR,error,file,line);
        log_strcat(err,text);
        log_doit(err,LOG_FLAGS_ERROR);
	break;
      };
    case LOG_FATAL:
      {
        log_addinfo(err,type,LOG_FLAGS_FATAL,error,file,line);
        log_strcat(err,text);
        log_doit(err,LOG_FLAGS_FATAL);
	panic();
        break;
      };
    default :
      {
	log_addinfo(err,type,LOG_FLAGS_FATAL,error,file,line);
        log_strcat(err,text);
        log_doit(err,LOG_FLAGS_FATAL);
        panic();
	break;
      };
    };

  return;
}
