/*
Copyright (c) 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	fsnmpm.c	The fsnmpm module in the fsnmp program.
*/



/**	Inside the fsnmpm.c module.
*/
#define FSNMPM_C	1
#include "fsnmp.h"
#include "dktools-version.h"




#line 53 "fsnmpm.ctr"




/**	Static strings used by the program.
*/
char *fsnmp_kw[] = {
/*  0 */ "Processing finished, exit code %d indicates: %s.",
/*  1 */ "Response from printer:\n",
/*  2 */ "Starting of filter",
/*  3 */ "Starting filter",
/*  4 */ "An error occured while initializing fsnmp. No filtering!",
/*  5 */ "Checking configuration file \"%s\" for configuration \"%s\".",
/*  6 */ "Failed to read configuration file!",
/*  7 */ "No printer name specified!",
/*  8 */ "Syntax problem in \"%s:%lu\"!",
/*  9 */ "Standard output is already connected to network socket.",
/* 10 */ "Creating file name for temporary file.",
/* 11 */ "Failed to create name for temporary file!",
/* 12 */ "Temporary file name \"%s\" was created.",
/* 13 */ "Retrieving pagecount %s.",
/* 14 */ "before data transfer",
/* 15 */ "after data transfer",
/* 16 */ "No SNMP community specified! Please correct the configuration file!",
/* 17 */ "Failed to create SNMP session!",
/* 18 */ "Failed to retrieve pagecount!",
/* 19 */ "Pagecount retrieved: %lu",
/* 20 */ "Printer not responding (offline).\nPRINTER REQUIRES MANUAL INTERVENTION!",
/* 21 */ "Error in SNMP response packet.",
/* 22 */ "Printer busy.",
/* 23 */ "Printer ready.",
/* 24 */ "Error while inspecting response packet!",
/* 25 */ "SNMP status responses: device = %s (%d), printer = %s (%d).",
/* 26 */ "unknown",
/* 27 */ "running",
/* 28 */ "warning",
/* 29 */ "testing",
/* 30 */ "down",
/* 31 */ "other",
/* 32 */ "unknown",
/* 33 */ "idle",
/* 34 */ "printing",
/* 35 */ "warmup",
/* 36 */ "PRINTER REQUIRES MANUAL INTERVENTION!",
/* 37 */ "Waiting for printer to start processing.",
/* 38 */ "Connecting to printer.",
/* 39 */ "Printer connection established, transferring data.",
/* 40 */ "Transferring data.",
/* 41 */ "Data transfer finished.",
/* 42 */ "Shutting down printer connection.",
/* 43 */ "Waiting for shutdown acknowledge.",
/* 44 */ "Printer connection closed.",
/* 45 */ "The of filter suspends itself.",
/* 46 */ "The of filter woke up.",
/* 47 */ "Error while writing to temporary file!",
/* 48 */ "Failed to write temporary file!",
/* 49 */ "Failed to create PDU for SNMP request!",
/* 50 */ "Error while transferring data to printer!",
/* 51 */ "Failed to read temporary file!",
/* 52 */ "Not enough memory (RAM/swap space)!",
/* 53 */ "A mathematical overflow occured!",
/* 54 */ "Host \"%s\" not found!",
/* 55 */ "No host name specified!",
/* 56 */ "Invalid port number \"%s\"!",
/* 57 */ "Missing port number!",
/* 58 */ "Missing host name and port number specification!",
/* 59 */ "No accounting destination specified!",
/* 60 */ "Empty accounting destination!",
/* 61 */ "Sending accounting data to \"%s\".",
/* 62 */ "Accounting data was sent successfully.",
/* 63 */ "Problem while sending accounting data!",
/* 64 */ "Failed to write accounting file!",
/* 65 */ "Failed to run accounting pipe command!",
/* 66 */ "Accounting host name \"%s\" not found!",
/* 67 */ "Accounting port number \"%s\" not found!",
/* 68 */ "Failed to open a socket for accounting!",
/* 69 */ "Failed to connect to accounting server \"%s\"!",
/* 70 */ "Failed to send accounting data!",
/* 71 */ "Problem while shutting down accounting network socket!",
/* 72 */ "Shutting down accounting network socket.",
/* 73 */ "Waiting for shutdown acknowledge from accounting server.",
/* 74 */ "Closing accounting network socket.",
/* 75 */ "Command line argument '-%c%s'.",
/* 76 */ "Successfully bound to local port %u.",
/* 77 */ "Failed to bind to local port range %u...%u!",
/* 78 */ "Failed to connect to printer!",
/* 79 */ "Failed to open a socket for data transfer!",
/* 80 */ "Problem while shutting down data transfer socket!",
/* 81 */ "Failed to mark local address for reuse.",
/* 82 */ VERSNUMB ,
/* 83 */ "This is fsnmp-%s.",
/* 84 */ "Accounting information was written to program \"%s\"",
/* 85 */ "Accounting information will be written to program \"%s\"",
NULL
};



/**	Descriptions for the exit codes in LPRng.
*/
char *exit_code_meaning[] = {
  /*  0 */ "Success",
  "Processing failed (retry later)",
  "Processing aborted (do not retry)",
  "Remove job",
  "Unknown result",
  "Unknown result",
  "Hold job",
  "No spooling to this queue",
  "No printing to this queue",
  "Processing aborted by signal",
  /* 10 */ "Processing failed (do not retry)",
  NULL
};



/**	Flag: At least one signal occured.
*/
static
#if DK_HAVE_VOLATILE
volatile
#endif
int signal_received = 0;



/**	SIGPIPE handler.
	@param	i	Signal number (SIGPIPE).
*/
static
dk_signal_ret_t
sigpipe_handler DK_P1(int, i)
{
  dksignal_refresh(i, sigpipe_handler);
  signal_received = 1;
}



/**	SIGTERM handler.
	@param	i	Signal number (SIGTERM).
*/
static
dk_signal_ret_t
sigterm_handler DK_P1(int, i)
{
  dksignal_refresh(i, sigterm_handler);
  signal_received = 1;
}



/**	SIGINT handler.
	@param	i	Signal number (SIGINT).
*/
static
dk_signal_ret_t
sigint_handler DK_P1(int, i)
{
  dksignal_refresh(i, sigint_handler);
  signal_received = 1;
}




/**	Check whether or not we can continue.
	@param	fc	Fsnmp job.
	@return	1=can continue, 0=stop.
*/
int fsnmp_cc DK_P1(FC *,fc)
{
  int back = 1;
  if(signal_received) back = 0;
  if(fc->exit_code != EXIT_SUCCESS) back = 0;
  return back;
}



/**	Input buffer.
*/
static char buffer_in[FSNMP_BUFFER_SIZE];



/**	Output buffer.
*/
static char buffer_out[FSNMP_BUFFER_SIZE];



/**	Clean up fsnmp job.
	@param	fc	Fsnmp job to clean up.
*/
static 
void
cleanup DK_P1(FC *,fc)
{
  char *x;
  
  /* remove temporary file */
  dksf_remove_file(fc->temp_file);
  if(fc->a_hostport) {
    x = fc->a_hostport; dk_delete(x); fc->a_hostport = NULL;
  }
  if(fc->a_acctdest) {
    x = fc->a_acctdest; dk_delete(x); fc->a_acctdest = NULL;
  }
  if(fc->a_community) {
    x = fc->a_community; dk_delete(x); fc->a_community = NULL;
  }
  
}



/**	Report one command line argument to log file.
	@param	fc	Fsnmp job.
	@param	k	Option key to report.
*/
static
void
report_one_argument DK_P2(FC *,fc, char,k)
{
  char *v;
  v = fsnmpcmd_get_argv(fc, k);
  if(v) {
    fsnmplog(fc, PRIO_INFO, fsnmp_kw[75], k, v);
  }
}



/**	Report command line arguments.
	@param	fc	Fsnmp job.
*/
static
void
report_command_line_arguments DK_P1(FC *,fc)
{
  char c;
  for(c = 'a'; c <= 'z'; c++) {
    report_one_argument(fc, c);
  }
  for(c = 'A'; c <= 'Z'; c++) {
    report_one_argument(fc, c);
  }
}



/**	Default file name for log file.
*/
static char default_snmp_logfile_name[] = { "netsnmp.log" };



/**	Run the program.
	@param	fc	Fsnmp job.
*/
static
void
run DK_P1(FC *,fc)
{
  /* variables to store original signal handlers */
  dk_signal_disp_t disp_term = NULL;
  dk_signal_disp_t disp_pipe = NULL;
  dk_signal_disp_t disp_int  = NULL;
#if DK_HAVE_NETSNMP_ENABLE_FILELOG
  netsnmp_log_handler *logh = NULL;
#endif
  

  /* set signal handlers */
  disp_term = dksignal_set(SIGTERM, sigterm_handler);
  disp_pipe = dksignal_set(SIGPIPE, sigpipe_handler);
  disp_int  = dksignal_set(SIGINT,  sigint_handler);
#if DK_HAVE_NETSNMP_ENABLE_FILELOG
  logh = netsnmp_register_loghandler(NETSNMP_LOGHANDLER_FILE, 5);
  if(logh) {
    logh->pri_max = 5;
    logh->token = strdup(default_snmp_logfile_name);
    if(logh->token) {
      netsnmp_enable_filelog(logh, 0);
    }
  }
#endif
  /* Check, whether or not to use stdout */
  fsnmp_check_peer(fc);

  /* read configuration file */
  fsnmpcmd_read_config_file(fc);

  /* create name for temporary file */
  fsnmpcmd_create_temp_file_name(fc);

  /* run filter or of */
  if(fsnmp_cc(fc)) {
    fsnmplog(fc, PRIO_PROGRESS, fsnmp_kw[83], fsnmp_kw[82]);
    if((fc->flags) & FSNMP_FLAG_OF) {
      fsnmplog(fc, PRIO_PROGRESS, fsnmp_kw[2]);
      report_command_line_arguments(fc);
      fsnmp_of(fc);
    } else {
      fsnmplog(fc, PRIO_PROGRESS, fsnmp_kw[3]);
      report_command_line_arguments(fc);
      fsnmp_filter(fc);
    }
  } else {
    fsnmplog(fc, PRIO_ERROR, fsnmp_kw[4]);
  }
#if DK_HAVE_NETSNMP_ENABLE_FILELOG
  if(logh) {
    snmp_disable_filelog();
    netsnmp_remove_loghandler(logh);
    if(logh->token) {
      SNMP_FREE((logh->token)); logh->token = NULL;
    }
    free(logh);
  }
#endif
  /* restore original signal handlers */
  dksignal_set(SIGINT,  disp_int);
  dksignal_set(SIGPIPE, disp_pipe);
  dksignal_set(SIGTERM, disp_term);

  /* cleanup */
  cleanup(fc);

  
}



/**	The main function of the fsnmp 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_PROTOTYPES
int main(int argc, char *argv[])
#else
int main(argc, argv) int argc; char *argv[];
#endif
{
  FC fc;
  char *command_line_arguments[52];	/* 2 * 26 = a-z,A-Z */
  char temp_file_name_buffer[128];
  oid oid_ds[MAX_OID_LEN];
  oid oid_ps[MAX_OID_LEN];
  oid oid_pc[MAX_OID_LEN];
  
#line 406 "fsnmpm.ctr"

  

  /* initialize fc */
  fsnmpcmd_init(&fc);
  fc.argv = command_line_arguments;
  fc.buffer_in = buffer_in; fc.sz_buffer_in = sizeof(buffer_in);
  fc.buffer_out = buffer_out; fc.sz_buffer_out = sizeof(buffer_out);
  fc.temp_file = temp_file_name_buffer;
  fc.sz_temp_file = sizeof(temp_file_name_buffer);
  { int i; for(i = 0; i < 52; i++) command_line_arguments[i] = NULL; }
  fc.oid_ds = oid_ds; fc.oid_ps = oid_ps; fc.oid_pc = oid_pc;
  fc.sz_oid_ds = fc.sz_oid_ps = fc.sz_oid_pc = MAX_OID_LEN;

  /* process command line arguments */
  fsnmpcmd_apply_argv(&fc, argc, argv);

  /* run the program */
  run(&fc);
  fsnmplog(
    &fc, PRIO_INFO, fsnmp_kw[0], fc.exit_code,
    exit_code_meaning[((fc.exit_code >= 0) && (fc.exit_code <= 10)) ? fc.exit_code : 3]
  );

  
  
#line 431 "fsnmpm.ctr"

  exit(fc.exit_code); return fc.exit_code;
}



