/*
 * FILE NAME:  npipe/prosd.c
 *
 * Printer driver for Axis print servers using the PROS 2.1 protocol.
 *
 * This version installed as a daemon.
 *
 * Usage:
 * prosd hostname pipe print-server-name printername password &
 *
 * (C) Copyright 1994, Axis Communications AB, LUND, SWEDEN.
 *
 * Version 1.5 April 25, 1994 RW
 *
 * Define ALWAYS_OPEN during compilation to leave the named pipe open
 * between print jobs. (Required on some systems).
 * 
 * Define TRY_FOREVER if attempt to connect to print server should not be
 * aborted if print server never seems to go ready.
 */
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/time.h>
#include <netinet/in.h>
#include <netdb.h>
#include <fcntl.h>
#include <stdio.h>
#include <errno.h>
#include <string.h>
#include <sys/wait.h>
#include <signal.h>
#include <time.h>

#define PROS_PORT       35
#define MAX_LOOPS       200
#define MAX_STATBUF     4096
#define PIPESIZE        4096
#define MAX_OUTBUF      256

/* PROS protocol 2.1 */

/* server -> host error messages */

#define PROSERR_HDR    0  /* header error */
#define PROSERR_MEM    1  /* out of memory */
#define PROSERR_NOA    2  /* No access */
#define PROSERR_POC    3  /* Printer occupied */
#define PROSERR_BAN    4  /* Bad printer name */
#define PROSERR_OLD    5  /* Old protocol */
#define PROSERR_NOI    6  /* Printer not installed */
#define PROSERR_OFL    7  /* Printer off line */
#define PROSERR_EOP    8  /* Printer out of paper */
#define PROSERR_BSY    9  /* Printer busy */
#define PROSERR_PRO   10  /* Protocol error */
#define PROSERR_UND   11  /* Undefined error */

/* host -> server informational messages */

#define PROSMSG_EOF   32   /* End-of-file: no more print data */
#define PROSMSG_UID   33   /* user name */
#define PROSMSG_HST   34   /* host name */
#define PROSMSG_PRN   35   /* printer name */
#define PROSMSG_PAS   36   /* password */
#define PROSMSG_DTP   37   /* data to printer */
#define PROSMSG_NOP   38   /* NOP - ignored */

/* server -> host informational messages */

#define PROSMSG_JOK   48   /* Job ended ok */
#define PROSMSG_JST   49   /* Job started */
#define PROSMSG_ACC   50   /* accounting data */
#define PROSMSG_DFP   51   /* data from printer */

/* status byte bits */

#define PROSBIT_FATAL  0x40   /* fatal error bit */
#define PROSBIT_DATA   0x80   /* data record follows */

int sock;
int mothid;
char indata[PIPESIZE+3];
char outdata[MAX_OUTBUF];
unsigned char statbuf[MAX_STATBUF];

void cancel();

void cancel(dummy)
int dummy;
{
  int status;

  wait(&status);
  close(sock);
  exit(status >> 8);
}

main(argc, argv)
int argc;
char **argv;
{
  int ok, loops;
  int stat_len;
  struct sockaddr_in server;
  struct hostent *hp, *gethostbyname();
  char *outdata_data;
  int doit, daughter, status;
  char *hostname, *userid, *pipefile, *boxname, *printername, *password;
  unsigned char status_code;
  char eof_marker = PROSMSG_EOF;
  int inpipe;
  int bytes_read;
  int byte_counter;
  int feedback = 0;
  time_t current_time;

  if (argc < 6)
  {
    fprintf(stderr, "Usage: prosd hostname pipe print-server-name log-printer pr-password\n");
    exit(1);
  }
  hostname = argv[1];
  pipefile = argv[2];
  boxname = argv[3];
  printername = argv[4];
  password = argv[5];

#ifdef ALWAYS_OPEN
    inpipe = open(pipefile, O_RDONLY);
    if (inpipe < 0)
    {
      fprintf(stderr, "Error trying to open input pipe!\n");
      exit(1);
    }
#endif

  while (1)
  {
#ifndef ALWAYS_OPEN
    inpipe = open(pipefile, O_RDONLY);
    if (inpipe < 0)
    {
      fprintf(stderr, "Error trying to open input pipe!\n");
      exit(1);
    }
#endif

#ifdef ALWAYS_OPEN
    while ((bytes_read = read(inpipe, indata+3, PIPESIZE)) == 0)
      sleep(1);
#else
    bytes_read = read(inpipe, indata+3, PIPESIZE);
#endif
     
    if (bytes_read < 0)
    {
      fprintf(stderr, "Error reading input pipe!\n");
      exit(1);
    }

    current_time = time(NULL);
    fprintf(stderr, "\nJob processing started at %s", ctime(&current_time)); 
    doit = fork();
    if (doit != 0) /* grandmother */
    {
      if (doit == -1) /* fork failed */
      {
        fprintf(stderr, "Main fork failed - insufficient resources!\n");
        exit(1);
      }
      wait(&status);
#ifndef ALWAYS_OPEN
      close(inpipe);
#endif
      current_time = time(NULL);
      fprintf(stderr, "Finished processing at %s", ctime(&current_time)); 
      fprintf(stderr, "Job exit status is 0x%04x\n", status);
    }
    else /* mother; open connection to print server, then read from pipe */
    {
      userid = "prosd";
      fprintf(stderr, "Trying to start new job on %s:%s initiated by %s@%s\n",
              boxname, printername, userid, hostname);
      byte_counter = 0;
      server.sin_family = AF_INET;
      hp = gethostbyname(boxname);
      if (hp == 0)
      {
        fprintf(stderr, "%s: unknown host\n", boxname);
        exit(2);
      }
      memcpy((char *) &server.sin_addr, (char *) hp->h_addr, hp->h_length);
      server.sin_port = htons(PROS_PORT);
      ok = 0;
      loops = 0;
      do
      {
        sock = socket(AF_INET, SOCK_STREAM, 0);
        if (sock < 0)
        {
          fprintf(stderr, "Error creating socket\n");
          exit(1);
        }
 
        if (connect(sock, (struct sockaddr *) & server, sizeof server) < 0)
        {
#ifndef TRY_FOREVER
          loops++;
#endif
          if (errno == ECONNREFUSED)
          {
            close(sock);
            sleep(10);
            fprintf(stderr, "Print server busy; retrying...\n");
          }
          else if (errno == ETIMEDOUT)
          {
            close(sock);
            fprintf(stderr, "Print server not responding; retrying...\n");
          }
          else
          {
            fprintf(stderr, "Unable to contact printer server; Unix errno is %d\n",
                    errno);
            exit(2);
          }
        }
        else
          ok++;
      } while (!ok && loops < MAX_LOOPS);
      if (!ok)
      {
        fprintf(stderr, "Connection refused; print server might be hung\n");
        exit(1);
      }
          
      mothid = getpid();
      daughter = fork();
      if (daughter != 0)
      {
        if (daughter == -1)
        {
          fprintf(stderr, "Fork failed - insufficient resources!\n");
          exit(1);
        }
        (void) signal(SIGINT, cancel);
        send_hdr(sock, PROSMSG_HST, hostname);
        send_hdr(sock, PROSMSG_UID, userid);
        send_hdr(sock, PROSMSG_PRN, printername);
        send_hdr(sock, PROSMSG_PAS, password);
          
        outdata_data = indata;
        while (bytes_read > 0)
        {
          outdata_data[0] = PROSMSG_DTP | PROSBIT_DATA;
          outdata_data[1] = bytes_read >> 8;
          outdata_data[2] = bytes_read & 255;
          if (write(sock, outdata_data, bytes_read + 3) < 0)
          {
            fprintf(stderr, "Error writing data to socket\n");
            exit(1);
          }
          byte_counter += bytes_read;
          bytes_read = read(inpipe, indata + 3, PIPESIZE);
          if (bytes_read < 0)
          {
            fprintf(stderr, "Can't read from pipe!");
            break;
          }
        }
        if (write(sock, &eof_marker, 1) < 0)
        {
          fprintf(stderr, "Error writing EOF to socket\n");
          exit(1);
        }
        fprintf(stderr, "EOF sent; sent %d bytes in this job\n",
                byte_counter);
        while (1)
        {
          sleep(60);
          outdata[0] = PROSMSG_NOP;
          if (write(sock, outdata, 1) < 0)
          {
            fprintf(stderr, "Error writing NOP to socket\n");
            exit(1);
          }
        }
      }
      else
      {
        while (1)
        {
          get_stat(&status_code, 1);
          if (status_code & PROSBIT_DATA)
          {
            get_stat(statbuf, 2);
            stat_len = (statbuf[0] << 8) + statbuf[1];
            get_stat(statbuf, stat_len);
            statbuf[stat_len] = 0;
          }
          else
          {
            strcpy((char *)statbuf, "<no message>");
            stat_len = 0;
          }

          if ( status_code == (PROSMSG_DFP | PROSBIT_DATA) )
          {
            if( feedback == 0 )
            {
              fprintf(stderr, "Received printer feedback!\n");
              feedback = 1;
            }
            fprintf(stdout, "%s", statbuf);
          }
          else
          {
            if ( feedback != 0 )
              feedback = 0;
        
            fprintf(stderr, "Msg: (%d, l=%d) %s%s\n",
              status_code, stat_len, statbuf,
              (status_code & PROSBIT_FATAL) ? " (FATAL)" : "");
          }

          if ((status_code & 0x3f) == PROSMSG_JOK)
          {
            kill(mothid, SIGINT);
            exit(0);
          }
          if (status_code & PROSBIT_FATAL)
          {
            kill(mothid, SIGINT);
            exit(2);
          }
        }
      }
    } /* end if 1. fork */
  } /* end main while */
}  
    
send_hdr(sock, msg_code, msg_data)
int sock;
int msg_code;
char *msg_data;
{
  int str_len;

  str_len = strlen(msg_data);
  outdata[0] = msg_code | PROSBIT_DATA;
  outdata[1] = str_len >> 8;
  outdata[2] = str_len & 255;
  strcpy(&outdata[3], msg_data);

  if (write(sock, outdata, str_len + 3) < 0)
  {
    fprintf(stderr, "Error writing header to socket\n");
    exit(1);
  }
}

get_stat(buffer, len)
char *buffer;
int len;
{
  int rval;

  do
  {
    if ((rval = read(sock, buffer, len)) < 0)
    {
      fprintf(stderr, "Error reading from print server\n");
      kill(mothid, SIGINT);
      exit(2);
    }
    else if (rval == 0)
    {
      fprintf(stderr, "Unexpected close by print server\n");
      kill(mothid, SIGINT);
      exit(2);
    }
    len -= rval;
    buffer += rval;
  } while (len > 0);
}
