/*
Copyright (c) 2000-2010, Dirk Krause
All rights reserved.

Redistribution and use in source and binary forms,
with or without modification, are permitted provided
that the following conditions are met:

* Redistributions of source code must retain the above
  copyright notice, this list of conditions and the
  following disclaimer.
* Redistributions in binary form must reproduce the above 
  opyright notice, this list of conditions and the following
  disclaimer in the documentation and/or other materials
  provided with the distribution.
* Neither the name of the Dirk Krause nor the names of
  contributors may be used to endorse or promote
  products derived from this software without specific
  prior written permission.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED.
IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
DAMAGE.
*/

/**	@file	dklogger.c	Dklogger program.
*/


#ifndef MAX_SERVERS
/**	Maximum number of remote hosts to log to. */
#define MAX_SERVERS 16
#endif

#include <dk.h>

#if DK_HAVE_STDLIB_H
#include <stdlib.h>
#endif
#if DK_HAVE_PROCESS_H
#include <process.h>
#endif

#include <dktypes.h>
#include <dkmem.h>
#include <dkstr.h>
#include <dkapp.h>
#include <dktcpip.h>
#include <dkslsupp.h>
#include <dklogc.h>
#include <dklic.h>
#include "dktools-version.h"


#line 66 "dklogger.ctr"




/**	Remote host description.
*/
typedef struct {
  unsigned long addr;		/**< IP4 address. */
  unsigned short portno;	/**< Port number. */
} remote_host;



/**	Array for remote hosts.
*/
static remote_host rh[MAX_SERVERS];


/**	Number of configured remote hosts.
*/
static int rh_used = 0;



/**	System configuration directory.
*/
static char sysconfdir[] = { DK_SYSCONFDIR };



/**	Application group name.
*/
static char packagename[] = { "dktools" };



/**	Message text array.
*/
static char *dklogger_str[11];



/**	Default message texts if localized texts not found.
*/
static dk_string_finder_t dklogger_find[] = {
  { "/m/01", &(dklogger_str[0]), "Host \"" },
  { "/m/02", &(dklogger_str[1]), "\" unknown!" },
  { "/m/03", &(dklogger_str[2]), "Failed to start socket!" },
  { "/m/04", &(dklogger_str[3]), "Failed to obtain socket!" },
  { "/m/05", &(dklogger_str[4]), "Missing message text!" },
  { "/m/06", &(dklogger_str[5]), "No destination host!" },
  { "/m/07", &(dklogger_str[6]), "No command line arguments!" },
  { "/m/08", &(dklogger_str[7]), "TCP/IP unavailable!" },
  { "/m/09", &(dklogger_str[8]), "<none>" },
  { "/m/10", &(dklogger_str[9]), "Current configuration:" },
  { "/m/11", &(dklogger_str[10]), "Default syslog feature and priority" },
  { NULL, NULL, NULL }
};



/**	Exit status. */
static int exval = 0;



/**	Application. */
static dk_app_t *app = NULL;



/**	Flag: Arguments used. */
static int  argsused    = 0;



/**	Pointer to array of command line arguments. */
static char **arguments = NULL;


/**	Pointer to one command line argument. */
typedef char *CPTR;



/**	Priority. */
static int the_priority = ((16<<3)+5);



/**	Pointer to priority string buffer.
*/
static char *pref_prio_buffer = NULL;



/**	Priority string.
*/
static char *prio_string = NULL;


/**	Preference key for priority.
*/
static char pk_syslog_priority[] = { "/syslog/priority" };



/**	Get configuration.
*/
static void get_config DK_P0()
{
  int lfd;
  char keybuffer[32];
  char valbuffer[256];
  char *ptr;
  unsigned short us;
  unsigned u;
  dk_ip_addr_t ipaddr;
  char *errmsg[4];

  if(dkapp_get_pref(app, pk_syslog_priority, valbuffer, sizeof(valbuffer), 0)) {
    pref_prio_buffer = dkstr_dup(valbuffer);
    prio_string = pref_prio_buffer;
  }
  rh_used = 0;
  for(lfd = 1; lfd <= MAX_SERVERS; lfd++) {
    sprintf(keybuffer, "/syslog/host/%03d/destination", lfd);
    if(dkapp_get_pref(app, keybuffer, valbuffer, sizeof(valbuffer), 0)) {
      ptr = dkstr_chr(valbuffer, ':');
      if(ptr) { *(ptr++) = '\0'; }
      if(dktcpipaddr_set_ip_byname(&ipaddr, valbuffer, NULL)) {
        rh[rh_used].addr = ipaddr.ip4.sin.sin_addr.s_addr;
	us = 514;
        if(ptr) {
	  if(sscanf(ptr, "%u", &u) == 1) {
	    us = (unsigned short)u;
	  }
	}
	rh[rh_used++].portno = htons(us);
      } else {
	errmsg[0] = dklogger_str[0];
	errmsg[1] = valbuffer;
	errmsg[2] = dklogger_str[1];
	dkapp_log_msg(app, DK_LOG_LEVEL_ERROR, errmsg, 3);
      }
    } else {
      lfd = MAX_SERVERS + 1;
    }
  }
}



/**	Release configuration buffer.
*/
static void release_config DK_P0()
{
  if(pref_prio_buffer) {
    dk_delete(pref_prio_buffer); pref_prio_buffer = NULL;
  }
}



/**	Send a message.
	@param	msgbuf	Buffer containing the message to send.
*/
static void send_the_message DK_P1(char *, msgbuf)
{
  dk_tcpip_t *sock;
  dk_ip_addr_t *addr; size_t lgt;
  int i;
  char *errmsg[4];

  
  sock = dktcpip_new();
  if(sock) {
    dktcpip_set_reuse(sock, 1);
    addr = dktcpip_get_addr(sock, DK_IP_ADDR_LOCAL_WISHED);
    dktcpip_addr_set_port(addr, 0, 0);
    dktcpipaddr_set_ip_any(addr);
    dktcpip_set_connectionless(sock, 1);
    addr = dktcpip_get_addr(sock, DK_IP_ADDR_REMOTE_WISHED);
    if(dktcpip_up(sock)) {
      lgt = strlen(msgbuf);
      
      for(i = 0; i < rh_used; i++) {
	(addr->ip4).sin.sin_addr.s_addr = rh[i].addr;
	dktcpip_addr_set_port(addr, rh[i].portno, rh[i].portno);
	dktcpip_write(sock, msgbuf, &lgt);
      }
      exval = 1;
      dktcpip_down(sock);
    } else {
      errmsg[0] = dklogger_str[2];
      dkapp_log_msg(app, DK_LOG_LEVEL_ERROR, errmsg, 1);
    }
    dktcpip_delete(sock); sock = NULL;
  } else {
    errmsg[0] = dklogger_str[3];
    dkapp_log_msg(app, DK_LOG_LEVEL_ERROR, errmsg, 1);
  }
  
}



/**	Default help text if dklogger.txt is not found.
*/
static char *help_text[] = {
  "dklogger <message-text>",
  "dklogger -p <feature>.<priority> <message-text>",
  "",
  "Example:",
  "dklogger -p auth.notice User joe logged in.",
  NULL
};


/**	Text to show version number and used libraries.
*/
static char *version_text[] = {
  "",
  "dklogger (part of the dktools collection, version " VERSNUMB ")",
  "Copyright (C) 2002-2010 - Dipl.-Ing. D. Krause",
  "http://dktools.sourceforge.net/",
  "",
"Redistribution and use in source and binary forms, with or without",
"modification, are permitted provided that the following conditions are met:",
"* Redistributions of source code must retain the above copyright notice, this",
"  list of conditions and the following disclaimer.",
"* Redistributions in binary form must reproduce the above copyright notice,",
"  this list of conditions and the following disclaimer in the documentation",
"  and/or other materials provided with the distribution.",
"* Neither the name of the Dirk Krause nor the names of other contributors may",
"  be used to endorse or promote products derived from this software without",
"  specific prior written permission.",
"THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\"",
"AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE",
"IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE",
"ARE DISCLAIMED.",
"IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY",
"DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES",
"(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;",
"LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND",
"ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT",
"(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS",
"SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.",
NULL
};



/**	Long options.
*/
static char *long_options[] = {
  /* 00 */ "h$elp",
  /* 01 */ "v$ersion",
  /* 02 */ "c$onfigure",
  /* 03 */ "u$nconfigure",
  /* 04 */ "r$eset",
  /* 05 */ "s$how-configuration",
  /* 06 */ "p$riority",
  NULL
};



/**	Text containing three spaces.
*/
static char three_spaces[] = { "   " };



/**	Show configuration.
*/
static void show_conf DK_P0()
{
  dkapp_stdout(app, dklogger_str[9]);
  fputc('\n', stdout);
  fputs("-p", stdout);
  fputs(three_spaces, stdout);
  if(prio_string) {
    dkapp_stdout(app, prio_string);
  } else {
    dkapp_stdout(app, dklogger_str[8]);
  }
  fputs(three_spaces, stdout);
  dkapp_stdout(app, dklogger_str[10]);
  fputc('\n', stdout);
}



/**	The main() function of the dklogger program.
	@param	argc	Number of command line arguments.
	@param	argv	Command line arguments array.
	@return	0 on success, any other value indicates an error.
*/
#if DK_HAVE_PROTO
int main(int argc, char *argv[])
#else
int main(argc,argv) int argc; char *argv[];
#endif
{
  int helponly, versiononly;
  int xargc, lfd; char *msgbuf, *optarg, *thisarg, **xargv, **argptr;
  int priofound;
  int do_conf, do_unconf, do_showconf;
  size_t sz;
  char *errmsg[4];

  
#line 378 "dklogger.ctr"

  app = dkapp_open_ext1(argc,argv,packagename, sysconfdir, 0, 0);
  do_conf = do_unconf = do_showconf = 0;
  if(app) {
    dkapp_find_multi(app, dklogger_find, "dklogger");
    if(dktcpip_start()) {
      get_config();
      helponly = versiononly = 0;
      xargc = dkapp_get_argc(app); xargv = dkapp_get_argv(app);
      argptr = xargv; lfd = 0;
      argptr++; lfd++;
      if(xargc) {
        arguments = dk_new(CPTR,xargc);
        if(arguments) {
	  for(argsused = 0; argsused < xargc; argsused++) {
	    arguments[argsused] = NULL;
	  }
	  argsused = 0;
	  priofound = 0;
          while(lfd < xargc) {
	    thisarg = *argptr;
	    if(*thisarg == '-') {
	      thisarg++;
	      switch(*thisarg) {
		case '-' : {
		  thisarg++;
		  optarg = dkstr_chr(thisarg, '=');
		  if(optarg) { *(optarg++) = '\0'; }
		  switch(dkstr_array_abbr(long_options, thisarg, '$', 1)) {
		    case 0: {
		      helponly = 1;
		    } break;
		    case 1: {
		      versiononly = 1;
		    } break;
		    case 2: {
		      do_conf = 1;
		    } break;
		    case 3: {
		      do_conf = do_unconf = 1;
		    } break;
		    case 4: {
		      prio_string = NULL; priofound = 0;
		    } break;
		    case 5: {
		      do_showconf = 1;
		    } break;
		    case 6: {
		      prio_string = optarg;
		      if(optarg) priofound = 1;
		    } break;
		  }
		} break;
		case 'v' : {
		  versiononly = 1;
		} break;
		case 'h' : {
		  helponly = 1;
		} break;
		case 'p' : {
		  thisarg++;
		  if(!(*thisarg)) {
		    if((lfd+1) < argc) {
		      argptr++; lfd++;
		      thisarg = *argptr;
		    }
		  }
		  if(*thisarg) {
		    if(!priofound) {
		      prio_string = thisarg; priofound = 1;
		    }
		  }
		} break;
		case 'c': {
		  do_conf = 1;
		} break;
		case 'u': {
		  do_unconf = do_conf = 1;
		} break;
		case 'C': {
		  do_showconf = 1;
		} break;
		case 'r': {
		  prio_string = NULL; priofound = 0;
		} break;
	      }
	    } else {
	      arguments[argsused++] = thisarg;
	    }
	    lfd++; argptr++;
	  }
          if(helponly || versiononly || do_conf || do_showconf) {
	    if(versiononly || helponly) {
	      char **ptr;
	      ptr = version_text;
	      while(*ptr) {
		printf("%s\n", *(ptr++));
	      }
              printf("\nLibraries used:\n\n");
              ptr = dklic_get();
              while(*ptr) {
                fputs(*(ptr++), stdout); fputc('\n', stdout);
              }
              fputc('\n', stdout);
	      if(helponly) {
	        dkapp_help(app, "dklogger.txt", help_text);
	      }
	    } else {
	      if(do_unconf) {
	        dkapp_unconfigure(app);
	      } else {
	        if(do_conf) {
		  if(prio_string) {
		  dkapp_set_pref(app, pk_syslog_priority, prio_string);
		  } else {
		    dkapp_set_pref(app, pk_syslog_priority, "");
		  }
		}
		show_conf();
	      }
	    }
	  } else {		/* if(helponly || versiononly) */
	    if(rh_used) {
	      if(argsused) {
	        sz = 0;
	        for(lfd = 0; lfd < argsused; lfd++) {
	          sz += strlen(arguments[lfd]);
	        }
	        sz += argsused;	/* space before each string */
	        sz += 13; 		/* <xx> and finalizing byte */
		msgbuf = dk_new(char,sz);
		if(msgbuf) {
		  the_priority = dkslsupp_get_code(prio_string);
		  sprintf(msgbuf, "<%d>", the_priority);
		  for(lfd = 0; lfd < argsused; lfd++) {
		    strcat(msgbuf, " ");
		    strcat(msgbuf, arguments[lfd]);
		  }
		  send_the_message(msgbuf);
		  dk_delete(msgbuf); msgbuf = NULL;
		} else {	/* if(msgbuf) */
		  dkapp_err_memory(app,1,sz);
		}		/* if(msgbuf) */
	      } else {	/* if(argsused) */
		errmsg[0] = dklogger_str[4];
		dkapp_log_msg(app, DK_LOG_LEVEL_ERROR, errmsg, 1);
	      }		/* if(argsused) */
	    } else {		/* if(rh_used) */
	      errmsg[0] = dklogger_str[5];
	      dkapp_log_msg(app, DK_LOG_LEVEL_ERROR, errmsg, 1);
	    }			/* if(rh_used) */
	  }			/* if(helponly || versiononly) */
	  dk_delete(arguments); arguments = NULL;
        } else {	/* if(arguments) */
	  dkapp_err_memory(app,sizeof(CPTR),xargc);
        }		/* if(arguments) */
      } else {	/* if(xargc) */
	errmsg[0] = dklogger_str[6];
	dkapp_log_msg(app, DK_LOG_LEVEL_ERROR, errmsg, 1);
      }		/* if(xargc) */
      release_config();
      (void)dktcpip_end();
    } else {	/* if(dktcpip_start()) */
      errmsg[0] = dklogger_str[7];
      dkapp_log_msg(app, DK_LOG_LEVEL_ERROR, errmsg, 1);
    }		/* if(dktcpip_start()) */
    dkapp_close(app); app = NULL;
  } else {
    fprintf(stderr, "Failed to initialize application!\n");
    fflush(stderr);
  }
  exval = (exval ? 0 : 1);
  
  
#line 551 "dklogger.ctr"

  exit(exval); return exval;
}


