/* Lpq.c */

/*--------------------------------------------------------------------------*/

#include <sys/types.h>
#include <setjmp.h>
#include <stdio.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <signal.h>
#include <errno.h>
#include <strings.h>
#include "common.h"
#include "zprstat.h"
#include "Lpq.h"
#include "Prntr.h"
#include "Pstat.h"
#include "substr.h"

/*--------------------------------------------------------------------------*/
#ifdef SOLARIS
#define index  strchr
#define rindex strrchr
#endif

#define	TIMEOUT_SECONDS	30

/*--------------------------------------------------------------------------*/

extern	int	errno;
extern	char	*sys_errlist[];
static	jmp_buf	env;
static	char	buffer[1024];
static	char	lpq_data_buffer[16384];
static	char	error_buffer[1024];
static	Prntr*	this_prntr;
static	int	fd;

static	char*	string_cr =		"%s\n";
static	char*	timeout_msg =		"No response after 30 seconds.";
static	char*	no_remote_host =	"No remote host for printer %s.";
static	char*	no_remote_host_connect ="No remote host to connect to.";
static	char*	unable_to_connect =	"Unable to connect to server %s.";
static	char*	lost_connection =	"Lost connection to server %s.";
static	char*	read_error =		"Read error (%d).";
static	char*	debug_read_bytes =	"Read %d bytes (from %s).\n";

/*--------------------------------------------------------------------------*/

void	alarm_handler();
char*	get_queue();
void	getport();

/*--------------------------------------------------------------------------*/

void	Lpq_Error(pstat, reason)
Pstat	pstat;
char*	reason;
{
    Prntr_Set_Curr_Status(this_prntr, pstat);
    Prntr_Set_Reason(this_prntr, reason);
}

/*--------------------------------------------------------------------------*/

void	alarm_handler(a)
int	a;
{
    if (fd >= 0)
      (void)close(fd);

    longjmp(env, 1);
}

/*--------------------------------------------------------------------------*/

void	Lpq_Init_Handler()
{
    signal(SIGALRM, alarm_handler);
}

/*--------------------------------------------------------------------------*/

char*	get_queue(prntr)
Prntr*	prntr;
{
    register int i;
    char* name;
    char* machine;
    char* ret;

    name = Prntr_Name(prntr);
    ret = lpq_data_buffer;

    if (Prntr_Pcap_Data(prntr) == NULL)
      {
	  Lpq_Error(Pstat_UNKNOWN, empty_string);
	  return NULL;
      }

    if ((machine = Prntr_Machine(prntr)) == NULL)
      {
	  sprintf(error_buffer, no_remote_host, name);

	  if (debug)
	    fprintf(stderr, string_cr, error_buffer);

	  Lpq_Error(Pstat_UNKNOWN, error_buffer);
	  return NULL;
      }
  
    getport(Prntr_Socket_Address(prntr));

    if (fd < 0)
      {
	  sprintf(error_buffer, unable_to_connect, machine);
	  
	  if (debug)
	    fprintf(stderr, string_cr, error_buffer);
	  
	  Lpq_Error(Pstat_DOWN, error_buffer);
	  return NULL;
      }
    else
      {
	  (void)sprintf(buffer, "%c%s\n", '\3', name);
	  i = strlen(buffer);
	  if (write(fd, buffer, i) != i)
	    {
		sprintf(error_buffer, lost_connection, machine);

		if (debug)
		  fprintf(stderr, string_cr, error_buffer);

		(void)close(fd);
		Lpq_Error(Pstat_DOWN, error_buffer);
		return NULL;
	    }
	  while ((i = read(fd, ret, 1024)) > 0) 
	    {
		if (debug)
		  fprintf(stderr, debug_read_bytes, i, machine);
		ret += i;
	    }
	  if (i < 0)
	    {
		int err = errno;
		sprintf(error_buffer, read_error, err);

		if (debug)
		  fprintf(stderr, string_cr, error_buffer);

		(void)close(fd);
		Lpq_Error(Pstat_DOWN, error_buffer);
		return NULL;
	    }
	  (void)close(fd);
	  *ret = EOS;
      }
    return lpq_data_buffer;
}

/*--------------------------------------------------------------------------*/

char*	Lpq_Get_Queue(prntr)
Prntr*	prntr;
{
    char* retval;
    this_prntr = prntr;
    lpq_data_buffer[0] = EOS;

    fd = -1;
    alarm(TIMEOUT_SECONDS);

    if (setjmp(env) != 0)
      {
	  Lpq_Error(Pstat_TIMEOUT, timeout_msg);
	  return NULL;
      }

    retval = get_queue(prntr);
    alarm(0);
    return retval;
}

/*--------------------------------------------------------------------------*/

void	Lpq_Parse_Queue(prntr, data)
Prntr*	prntr;
char*	data;
{
    int len;
    int n = 0;
    char* ptr;

    char first_line[1024];
    char last_line[1024];

    this_prntr = prntr;

    Lpq_Error(Pstat_UP, empty_string);

    if ((ptr = index(data, '\n')) == NULL)
      {
	  strcpy(first_line, data);
	  strcpy(last_line, empty_string);
      }
    else
      {
	  len = (int)(ptr - data);
	  strncpy(first_line, data, len);
	  first_line[len] = EOS;

	  *rindex(data, '\n') = EOS;
	  if ((index(data, '\n') != NULL) &&
	      ((ptr = rindex(data, '\n')) != NULL))
	    strcpy(last_line, ptr+1);
	  else
	    strcpy(last_line, empty_string);
      }

    if ((substr(first_line, "waiting") >= 0) ||
	(substr(first_line, "Error") >= 0) ||
	(substr(first_line, "Warning") >= 0) ||
	(substr(first_line, "unable to connect") >= 0))
      Lpq_Error(Pstat_DOWN, first_line);

    if (substr(first_line, "unknown printer") >= 0)
      Lpq_Error(Pstat_UNKNOWN, first_line);

    if (sscanf(last_line, "%d", &n) != 1)
      n = 0;

    Prntr_Set_Num_Jobs(prntr, n);
}

/*--------------------------------------------------------------------------*/

/*
 * Create a connection to the remote printer server.
 * Most of this code comes from rcmd.c.
 */

void	getport(sin)
struct sockaddr_in* sin;
{
    int rcnt = 0, lport = IPPORT_RESERVED - 1;
    int err;

    /*
     * Try connecting to the server.
     */
    
 retry:
    fd = rresvport(&lport);
    if (fd < 0)
      {
	  if (debug)
	    fprintf(stderr, "rresvport failed! errno=%d\n", errno);
	  return;
      }

    if (connect(fd, sin, sizeof(struct sockaddr_in)) < 0)
      {
	  err = errno;
	  (void) close(fd);
	  errno = err;
	  if (errno == EADDRINUSE)
	    {
		lport--;
		goto retry;
	    }
	  if (errno == ECONNREFUSED && rcnt < 3)
	    {
		rcnt++;
		goto retry;
	    }

	  if (debug)
	    fprintf(stderr, "connect failed! errno=%d\n", errno);
      }
}

/*--------------------------------------------------------------------------*/
