/* Printer status information */
/* Printer interface related operations */
/* Created 22.09.2005 T. Milius
   Changed 04.11.2005 T. Milius */
/* (c) Copyright 2005 by Thomas Milius Stade, Germany
   Source must not be altered without agreement of the owner.
   The owner of the source is allowed to use this code inside programs without
   publishing the code of this programs. These programs may be commercial.
   Other developers can use this source freely inside own software if the source
   code of this programs is made public to same conditions like valid to this code
   and no commercial profit is taken from the programs based on this code

   Code or parts of it are not allowed to be used within GPL code or
   similar licenses which are "infecting" other code and trying to "supersede"
   other licenses. */
/* RISC OS */

#ifndef printeractions_h
#define printeractions_h

/* !!!!!!!!!! libraries !!!!!!!!!! */
/* ---------- ANSI-C ---------- */
#include <ctype.h>
#include <stdbool.h>

/* ------------ own ------------ */
#include "common.h"
#include "error.h"
#include "network.h"
#include "variables.h"

/* !!!!!!!!!!! definitions !!!!!!!!!! */
#define CONNECTION_BROKEN        -3
#define CONNECTION_NOT_SUPPORTED -2
#define CONNECTION_INDETERMINED  -1
#define CONNECTION_NOT_SPECIAL    0
#define CONNECTION_PARALLEL       1
#define CONNECTION_USB            2
/* #define CONNECTION_NFS           3 */
/* Direct drive not supported due to lack of interface */
#define CONNECTION_ACCESS         4
/* File not supported for it makes no sense */

#define CONNECTION_TAG_DEVICE_1   "devices#"
#define CONNECTION_TAG_DEVICE_2   "devices:"
#define CONNECTION_TAG_PARALLEL_1 "parallel"
#define CONNECTION_TAG_PARALLEL_2 "fastparallel"
#define CONNECTION_TAG_USB_1      "usbd"
#define CONNECTION_TAG_USB_2      "usb"
#define CONNECTION_TAG_ACCESS     "share::_"

/* Special protcols */
#define PROTOCOL_NOT_SPECIAL 0
#define PROTOCOL_OUTPUT_ONLY 1
/* Parallel */
#define PROTOCOL_NIBBLE_MODE 2
#define PROTOCOL_ECP_MODE    3

#define PROTOCOL_TAG_NOT_SPECIAL "Output"
/* Parallel */
#define PROTOCOL_TAG_NIBBLE_MODE "Nibble"
#define PROTOCOL_TAG_ECP_MODE    "ECP"

/* internal Errors range from 0x40 ownwards */
#define ERROR_INVALID_CONNECTION       64
#define ERROR_MISSING_DEVICE_SEPARATOR 65
#define ERROR_UNKNOWN_USB_CLASS        66
#define ERROR_NETWORK_ADD_JOB          67
#define ERROR_NETWORK_BUILD_MESSAGE    68
#define ERROR_NETWORK_CONNECTION       69
#define ERROR_NETWORK_TRANSMISSION     70
#define ERROR_NETWORK_PROTOCOL         71
#define ERROR_FREEWAY_FILE             72
#define ERROR_FREEWAY_NO_DNS           73

/* Semaphor states */
#define SEMAPHOR_UNUSED 0
#define SEMAPHOR_WAIT   1
#define SEMAPHOR_EXIT   2

/* Semaphor lock responses */
#define SEMAPHOR_LOCK_INVALID 0
#define SEMAPHOR_LOCK_WAIT    1
#define SEMAPHOR_LOCK_EXIT    2
#define SEMAPHOR_LOCK_CLAIMED 3

/* Network request codes */
#define NETWORK_REQUEST_CONNECTION   0
#define NETWORK_REQUEST_PRINTER_TYPE 1
#define NETWORK_REQUEST_LOCK         2
#define NETWORK_REQUEST_UNLOCK       3
#define NETWORK_REQUEST_RESET        4
#define NETWORK_REQUEST_STATUS       5
#define NETWORK_REQUEST_SEND         6
#define NETWORK_REQUEST_GET          7

#define MAX_MESSAGE_ELEMENTS 10

#define MAX_PRINTER_NAME_LENGTH 40

#define FREEWAY_INFORMATION_FILE "<Wimp$ScrapDir>.PrtInfo.FWInfo"
#define FREEWAY_TAG_TYPE "Type "
#define FREEWAY_TAG_NAME "Name="
#define FREEWAY_TAG_DNS "Holder="

/* !!!!!!!!!! data structures !!!!!!!!!! */
struct printer_struct {
struct printer_struct *previous_printer;
struct printer_struct *next_printer;
char printer_name[MAX_PRINTER_NAME_LENGTH];
char connection_name[MAX_CONNECTION_NAME];
int connection_type;
int protocol_type;
/* physical interface to printer
   (differs from connection type at network printers) */
int real_connection_type;
char output_file[MAX_NAME_LENGTH];
char input_file[MAX_NAME_LENGTH];
char device_name[MAX_NAME_LENGTH];
/* usage semaphor */
unsigned long semaphor;
/* stored error for this connection */
struct error_struct error;
/* For shared or network printers. */
char dns[40];
};

struct {
struct printer_struct *first_printer;
unsigned long printers_task_handle;
bool printers_scan_flag;
} printeractions_common;

/* !!!!!!!!!! support functions !!!!!!!!!! */
bool drop_printer(struct printer_struct *printer)
{

if (!printer) return false;
if (printer->previous_printer) {
  printer->previous_printer->next_printer=printer->next_printer;
  }
else {
  printeractions_common.first_printer=printer->next_printer;
  }
if (printer->next_printer) {
  printer->next_printer->previous_printer=printer->previous_printer;
  }
free(printer);
return true;
}

struct printer_struct *add_printer(void)
{
struct printer_struct *printer;

if ((printer=malloc(sizeof(struct printer_struct))) == NULL) {
  return NULL;
  }
printer->previous_printer=NULL;
printer->next_printer=printeractions_common.first_printer;
if (printeractions_common.first_printer) {
  printeractions_common.first_printer->previous_printer=printer;
  }
printeractions_common.first_printer=printer;
printer->semaphor=SEMAPHOR_UNUSED;
return printer;
}

/* !!!!!!!!!! functions !!!!!!!!!! */
extern void printer_network_request(struct network_job_struct *job);

bool printer_initialize(void)
{

printeractions_common.first_printer=NULL;
return true;
}

bool printer_shutdown(void)
{

while (printeractions_common.first_printer) {
  drop_printer(printeractions_common.first_printer);
  }
return true;
}

extern bool printer_network_remote_call(struct network_job_struct *job,
                                        struct error_struct *error);

bool printer_unlock(struct printer_struct *printer)
{

if (!printer) return false;
switch(printer->connection_type) {
  case CONNECTION_BROKEN: {
    printer->semaphor=SEMAPHOR_UNUSED;
    return false;
    }
  break;
  case CONNECTION_NOT_SUPPORTED: {
    printer->semaphor=SEMAPHOR_UNUSED;
    return false;
    }
  break;
  case CONNECTION_ACCESS: {
    struct network_message_element_struct message_element[MAX_MESSAGE_ELEMENTS];
    struct network_job_struct *job;
    unsigned long number_of_elements;
    unsigned long message_number_of_elements;

    if ((job=network_job_add(NULL,
                             NETWORK_BUFFER_SIZE,
                             (unsigned long long) printer)) == NULL) {
      return false;
      }
    message_element[0].type=NETWORK_MESSAGE_ELEMENT_TYPE_VALUE;
    message_element[0].details.value=NETWORK_REQUEST_UNLOCK;
    message_element[1].type=NETWORK_MESSAGE_ELEMENT_TYPE_STRING;
    message_element[1].details.block.pointer=printer->printer_name;
    if (!network_build_message(job,
                               message_element,
                               2)) {
      network_job_drop(job);
      return false;
      }
    if (!printer_network_remote_call(job,
                                     NULL)) {
      return false;
      }
    message_element[0].type=NETWORK_MESSAGE_ELEMENT_TYPE_VALUE;
    message_element[1].type=NETWORK_MESSAGE_ELEMENT_TYPE_VALUE;
    if ((number_of_elements=network_extract_message(job,
                                                    message_element,
                                                    2,
                                                    0,
                                                    &message_number_of_elements)) < 2) {
      network_job_drop(job);
      return false;
      }
    network_job_drop(job);
    if (message_element[1].details.value) {
      return true;
      }
    else {
      return false;
      }
    }
  break;
  default: {
    printer->semaphor=SEMAPHOR_UNUSED;
    return true;
    }
  }
}

unsigned long printer_lock(struct printer_struct *printer,
                           unsigned long lock_mode)
{

if (!printer) return SEMAPHOR_LOCK_INVALID;
switch(printer->connection_type) {
  case CONNECTION_BROKEN: {
    return SEMAPHOR_LOCK_INVALID;
    }
  break;
  case CONNECTION_NOT_SUPPORTED: {
    return SEMAPHOR_LOCK_INVALID;
    }
  break;
  case CONNECTION_ACCESS: {
    struct network_message_element_struct message_element[MAX_MESSAGE_ELEMENTS];
    struct network_job_struct *job;
    unsigned long number_of_elements;
    unsigned long message_number_of_elements;

    if ((job=network_job_add(NULL,
                             NETWORK_BUFFER_SIZE,
                             (unsigned long long) printer)) == NULL) {
      return SEMAPHOR_LOCK_INVALID;
      }
    message_element[0].type=NETWORK_MESSAGE_ELEMENT_TYPE_VALUE;
    message_element[0].details.value=NETWORK_REQUEST_LOCK;
    message_element[1].type=NETWORK_MESSAGE_ELEMENT_TYPE_STRING;
    message_element[1].details.block.pointer=printer->printer_name;
    message_element[2].type=NETWORK_MESSAGE_ELEMENT_TYPE_VALUE;
    message_element[2].details.value=lock_mode;
    if (!network_build_message(job,
                               message_element,
                               3)) {
      network_job_drop(job);
      return SEMAPHOR_LOCK_INVALID;
      }
    if (!printer_network_remote_call(job,
                                     NULL)) {
      return SEMAPHOR_LOCK_INVALID;
      }
    message_element[0].type=NETWORK_MESSAGE_ELEMENT_TYPE_VALUE;
    message_element[1].type=NETWORK_MESSAGE_ELEMENT_TYPE_VALUE;
    if ((number_of_elements=network_extract_message(job,
                                                    message_element,
                                                    2,
                                                    0,
                                                    &message_number_of_elements)) < 2) {
      network_job_drop(job);
      return SEMAPHOR_LOCK_INVALID;
      }
    network_job_drop(job);
    return message_element[1].details.value;
    }
  break;
  default: {
    switch(printer->semaphor) {
      case SEMAPHOR_UNUSED: {
        printer->semaphor=lock_mode;
        return SEMAPHOR_LOCK_CLAIMED;
        }
      break;
      case SEMAPHOR_WAIT: {
        return SEMAPHOR_LOCK_WAIT;
        }
      break;
      case SEMAPHOR_EXIT: {
        return SEMAPHOR_LOCK_EXIT;
        }
      break;
      default: {
        return SEMAPHOR_LOCK_INVALID;
        }
      }
    }
  }
}

bool printer_send(struct printer_struct *printer,
                  char *sequence,
                  unsigned long size)
{

if ((!printer) || (!sequence)) return false;
switch(printer->connection_type) {
  case CONNECTION_BROKEN: {
    return false;
    }
  break;
  case CONNECTION_NOT_SUPPORTED: {
    return false;
    }
  break;
  case CONNECTION_ACCESS: {
    struct network_message_element_struct message_element[MAX_MESSAGE_ELEMENTS];
    struct network_job_struct *job;
    unsigned long number_of_elements;
    unsigned long message_number_of_elements;

    if ((job=network_job_add(NULL,
                             NETWORK_BUFFER_SIZE,
                             (unsigned long long) printer)) == NULL) {
      return false;
      }
    message_element[0].type=NETWORK_MESSAGE_ELEMENT_TYPE_VALUE;
    message_element[0].details.value=NETWORK_REQUEST_SEND;
    message_element[1].type=NETWORK_MESSAGE_ELEMENT_TYPE_STRING;
    message_element[1].details.block.pointer=printer->printer_name;
    message_element[2].type=NETWORK_MESSAGE_ELEMENT_TYPE_BLOCK;
    message_element[2].details.block.size=size;
    message_element[2].details.block.pointer=sequence;
    if (!network_build_message(job,
                               message_element,
                               3)) {
      network_job_drop(job);
      return false;
      }
    if (!printer_network_remote_call(job,
                                     NULL)) {
      return false;
      }
    message_element[0].type=NETWORK_MESSAGE_ELEMENT_TYPE_VALUE;
    message_element[1].type=NETWORK_MESSAGE_ELEMENT_TYPE_VALUE;
    if ((number_of_elements=network_extract_message(job,
                                                    message_element,
                                                    2,
                                                    0,
                                                    &message_number_of_elements)) < 2) {
      network_job_drop(job);
      return false;
      }
    network_job_drop(job);
    if (message_element[1].details.value == 0) {
      return false;
      }
    }
  break;
  default: {
    int output_channel;
    _kernel_swi_regs regs;

    regs.r[0]=0x8F;
    regs.r[1]=(int) printer->output_file;
    regs.r[2]=NULL;
    _kernel_swi(OS_Find, &regs, &regs);
    output_channel=regs.r[0];
    if ((output_channel) != 0) {
      regs.r[0]=2;
      regs.r[1]=output_channel;
      regs.r[2]=(int) sequence;
      regs.r[3]=size;
      _kernel_swi(OS_GBPB, &regs, &regs);
      regs.r[0]=0;
      regs.r[1]=output_channel;
      _kernel_swi(OS_Find, &regs, &regs);
      }
    else {
      return false;
      }
    }
  }
return true;
}

bool printer_get(struct printer_struct *printer,
                 int terminator,
                 char *response,
                 unsigned long *size)
{
char *c, *cs;
int i;
int input_channel;
unsigned long max_size;
unsigned long min_time;
_kernel_swi_regs regs;
bool get_data;

if ((!printer) || (!size) || (!response)) return false;
max_size=*size;
switch(printer->connection_type) {
  case CONNECTION_BROKEN: {
    *size=0;
    return false;
    }
  break;
  case CONNECTION_NOT_SUPPORTED: {
    *size=0;
    return false;
    }
  break;
  case CONNECTION_ACCESS: {
    struct network_message_element_struct message_element[MAX_MESSAGE_ELEMENTS];
    struct network_job_struct *job;
    unsigned long number_of_elements;
    unsigned long message_number_of_elements;

    if ((job=network_job_add(NULL,
                             NETWORK_BUFFER_SIZE,
                             (unsigned long long) printer)) == NULL) {
      *size=0;
      return false;
      }
    message_element[0].type=NETWORK_MESSAGE_ELEMENT_TYPE_VALUE;
    message_element[0].details.value=NETWORK_REQUEST_GET;
    message_element[1].type=NETWORK_MESSAGE_ELEMENT_TYPE_STRING;
    message_element[1].details.block.pointer=printer->printer_name;
    message_element[2].type=NETWORK_MESSAGE_ELEMENT_TYPE_VALUE;
    message_element[2].details.value=terminator;
    message_element[3].type=NETWORK_MESSAGE_ELEMENT_TYPE_VALUE;
    message_element[3].details.value=*size;
    if (!network_build_message(job,
                               message_element,
                               4)) {
      network_job_drop(job);
      *size=0;
      return false;
      }
    if (!printer_network_remote_call(job,
                                     NULL)) {
      *size=0;
      return false;
      }
    message_element[0].type=NETWORK_MESSAGE_ELEMENT_TYPE_VALUE;
    message_element[1].type=NETWORK_MESSAGE_ELEMENT_TYPE_VALUE;
    message_element[2].type=NETWORK_MESSAGE_ELEMENT_TYPE_STRING;
    message_element[2].details.block.size=*size;
    message_element[2].details.block.pointer=response;
    if ((number_of_elements=network_extract_message(job,
                                                    message_element,
                                                    3,
                                                    0,
                                                    &message_number_of_elements)) < 2) {
      network_job_drop(job);
      *size=0;
      return false;
      }
    network_job_drop(job);
    if (!message_element[1].details.value) {
      *size=0;
      return false;
      }
    *size=message_element[2].details.block.size;
    }
  break;
  default: {
    regs.r[0]=0x4F;
    regs.r[1]=(int) printer->input_file;
    regs.r[2]=NULL;
    _kernel_swi(OS_Find, &regs, &regs);
    input_channel=regs.r[0];
    if (input_channel != 0) {
      response[*size-1]='\0';
      response[0]='\0';
      _kernel_swi(OS_ReadMonotonicTime, &regs, &regs);
      if (printer->connection_type == CONNECTION_USB) {
        /* 2 Seconds response delay */
        min_time=regs.r[0]+200;
        }
      else {
        min_time=regs.r[0]+50;
        }
      do {
        if (printer->connection_type == CONNECTION_USB) {
          get_data=true;
          }
        else {
          get_data=true;
          }
        if (get_data) {
          regs.r[0]=4;
          regs.r[1]=input_channel;
          regs.r[2]=(int) response;
          regs.r[3]=max_size-1;
          _kernel_swi(OS_GBPB, &regs, &regs);
          *size=(max_size-1) - regs.r[3];
          }
        else {
          *size=0;
          }
        _kernel_swi(OS_ReadMonotonicTime, &regs, &regs);
        }
      while ((*size == 0) &&
             (regs.r[0] < min_time));
      regs.r[0]=0;
      regs.r[1]=input_channel;
      _kernel_swi(OS_Find, &regs, &regs);
      /* If size is given at start then set size accordingly and
         shift string */
      if (terminator < 0) {
        switch (terminator) {
          /* All Big endian. Size bytes included */
          case -1: {
            *size=((unsigned long) *response) - 1;
            }
          break;
          case -2: {
            *size=((unsigned long) *((unsigned char *) response)<<8 | *((unsigned char *) response + 1)) - 2;
            }
          break;
          case -4: {
            *size=((unsigned long)  *((unsigned char *) response)<<24 | *((unsigned char *) response + 1)<<16 | *((unsigned char *) response + 2)<<8 | *((unsigned char *) response + 3)) - 4;
            }
          break;
          default: {
            *size=0;
            *response='\0';
            return false;
            }
          }
        if (*size >= MAX_RESPONSE_LENGTH) {
          *size=MAX_RESPONSE_LENGTH-1;
          response[*size]='\0';
          return false;
          }
        /* shift response string */
        cs=response + (-terminator);
        c=response;
        for(i=0; i < *size; i++) {
          *c=*cs;
          c++;
          cs++;
          }
        response[*size]='\0';
        }
      /* Terminate at terminator (if given) and make 0 Blanks. */
      c=response;
      i=0;
      while((i < *size) &&
            (((int) *c) != terminator)) {
        if (*c == '\0') {
          *c=' ';
          }
        c++;
        i++;
        }
      *size=i;
      *c='\0';
      return true;
      }
    else {
      *size=0;
      *response='\0';
      return false;
      }
    }
  }
return true;
}

bool printer_status(struct printer_struct *printer,
                    struct error_struct *error,
                    int *error_status)
{

if (!printer) {
  if (error) {
    error->number=ERROR_WRONG_CALL;
    error->number_of_parameters=2;
    strcpy(error->parameter[0], "printer_status");
    strcpy(error->parameter[1], "printer");
    }
  return false;
  }
if (!error_status) {
  if (error) {
    error->number=ERROR_WRONG_CALL;
    error->number_of_parameters=2;
    strcpy(error->parameter[0], "printer_status");
    strcpy(error->parameter[1], "error_status");
    }
  return false;
  }
/* Perform standard operation */
switch(printer->connection_type) {
  case CONNECTION_BROKEN: {
    if (error) {
      *error=printer->error;
      }
    return false;
    }
  break;
  case CONNECTION_NOT_SUPPORTED: {
    if (error) {
      *error=printer->error;
      }
    return false;
    }
  break;
  case CONNECTION_PARALLEL: {
    _kernel_swi_regs regs;

    /* Use RISC OS Standard SWI */
    regs.r[0]=0;
    _kernel_swi(Parallel_Op, &regs, &regs);
    *error_status=regs.r[2] & 0x00000038;
    }
  break;
  case CONNECTION_USB: {
    _kernel_swi_regs regs;
    char result_buffer;

    /* Use according class request */
    regs.r[0]=1<<31;
    regs.r[1]=(int) printer->device_name;
    regs.r[3]=161 | (1<<8) | (0<<16);
    regs.r[4]=0 | (1<<16);
    regs.r[5]=(int) &result_buffer;
    regs.r[6]=0;
    regs.r[7]=NULL;
    regs.r[8]=NULL;
    _kernel_swi(DeviceFS_CallDevice, &regs, &regs);
    *error_status=result_buffer;
    }
  break;
  case CONNECTION_ACCESS: {
    struct network_message_element_struct message_element[MAX_MESSAGE_ELEMENTS];
    struct network_job_struct *job;
    unsigned long number_of_elements;
    unsigned long message_number_of_elements;

    if ((job=network_job_add(NULL,
                             NETWORK_BUFFER_SIZE,
                             (unsigned long long) printer)) == NULL) {
      if (error) {
        error->number=ERROR_NETWORK_ADD_JOB;
        error->number_of_parameters=0;
        }
      return false;
      }
    message_element[0].type=NETWORK_MESSAGE_ELEMENT_TYPE_VALUE;
    message_element[0].details.value=NETWORK_REQUEST_STATUS;
    message_element[1].type=NETWORK_MESSAGE_ELEMENT_TYPE_STRING;
    message_element[1].details.block.pointer=printer->printer_name;
    if (!network_build_message(job,
                               message_element,
                               2)) {
      network_job_drop(job);
      if (error) {
        error->number=ERROR_NETWORK_BUILD_MESSAGE;
        error->number_of_parameters=0;
        }
      return false;
      }
    if (!printer_network_remote_call(job,
                                     error)) {
      return false;
      }
    message_element[0].type=NETWORK_MESSAGE_ELEMENT_TYPE_VALUE;
    message_element[1].type=NETWORK_MESSAGE_ELEMENT_TYPE_VALUE;
    if ((number_of_elements=network_extract_message(job,
                                                    message_element,
                                                    2,
                                                    0,
                                                    &message_number_of_elements)) < 2) {
      network_job_drop(job);
      if (error) {
        error->number=ERROR_NETWORK_PROTOCOL;
        error->number_of_parameters=1;
        strcpy(error->parameter[0], printer->dns);
        }
      return false;
      }
    network_job_drop(job);
    *error_status=message_element[1].details.value;
    }
  break;
  }
return true;
}

bool printer_reset(struct printer_struct *printer,
                   struct error_struct *error)
{

if (!printer) {
  if (error) {
    error->number=ERROR_WRONG_CALL;
    error->number_of_parameters=2;
    strcpy(error->parameter[0], "printer_reset");
    strcpy(error->parameter[1], "printer");
    }
  return false;
  }
/* Perform standard operation */
switch(printer->connection_type) {
  case CONNECTION_BROKEN: {
    if (error) {
      *error=printer->error;
      }
    return false;
    }
  break;
  case CONNECTION_NOT_SUPPORTED: {
    if (error) {
      *error=printer->error;
      }
    return false;
    }
  break;
  case CONNECTION_PARALLEL: {
    _kernel_swi_regs regs;
    int min_time;

    /* Use RISC OS Standard SWI */
    /* Set INIT 0 for reset */
    regs.r[0]=2;
    regs.r[1]=0x00000000;
    regs.r[2]=0xFFFFFFFB;
    _kernel_swi(Parallel_Op, &regs, &regs);
    /* min 50us low for reset */
    _kernel_swi(OS_ReadMonotonicTime, &regs, &regs);
    min_time=regs.r[0]+1;
    do {
      _kernel_swi(OS_ReadMonotonicTime, &regs, &regs);
      }
    while (regs.r[0] < min_time);
    /* Set INTI 1 again */
    regs.r[0]=2;
    regs.r[1]=0x00000004;
    regs.r[2]=0xFFFFFFFB;
    _kernel_swi(Parallel_Op, &regs, &regs);
    }
  break;
  case CONNECTION_USB: {
    _kernel_swi_regs regs;

    /* Use according class request */
    regs.r[0]=1<<31;
    regs.r[1]=(int) printer->device_name;
    regs.r[3]=33 | (2<<8) | (0<<16);
    regs.r[4]=0 | (0<<16);
    regs.r[5]=0;
    regs.r[6]=0;
    regs.r[7]=NULL;
    regs.r[8]=NULL;
    _kernel_swi(DeviceFS_CallDevice, &regs, &regs);
    }
  break;
  case CONNECTION_ACCESS: {
    struct network_message_element_struct message_element[MAX_MESSAGE_ELEMENTS];
    struct network_job_struct *job;

    if ((job=network_job_add(NULL,
                             NETWORK_BUFFER_SIZE,
                             (unsigned long long) printer)) == NULL) {
      if (error) {
        error->number=ERROR_NETWORK_ADD_JOB;
        error->number_of_parameters=0;
        }
      return false;
      }
    message_element[0].type=NETWORK_MESSAGE_ELEMENT_TYPE_VALUE;
    message_element[0].details.value=NETWORK_REQUEST_RESET;
    message_element[1].type=NETWORK_MESSAGE_ELEMENT_TYPE_STRING;
    message_element[1].details.block.pointer=printer->printer_name;
    if (!network_build_message(job,
                               message_element,
                               2)) {
      network_job_drop(job);
      if (error) {
        error->number=ERROR_NETWORK_BUILD_MESSAGE;
        error->number_of_parameters=0;
        }
      return false;
      }
    if (!printer_network_remote_call(job,
                                     error)) {
      return false;
      }
    network_job_drop(job);
    }
  break;
  }
return true;
}

struct printer_struct *printer_find(char *printer_name)
{
struct printer_struct *printer;

if (!printer_name) return NULL;
printer=printeractions_common.first_printer;
while(printer) {
  if (strcmp(printer_name, printer->printer_name) == 0) {
    return printer;
    }
  printer=printer->next_printer;
  }
return NULL;
}

bool determine_printer_connection(struct printer_struct *printer,
                                  char *connection_name)
{
char *c;

if ((!printer) || (!connection_name)) return false;
printer->error.number=ERROR_NO_ERROR;
printer->error.number_of_parameters=0;
printer->protocol_type=PROTOCOL_NOT_SPECIAL;
strncpy(printer->connection_name, connection_name, MAX_CONNECTION_NAME-1);
/* convert to lower to simplify comparation */
c=connection_name;
while (*c != '\0') {
  *c=tolower(*c);
  c++;
  }
/* Test for Printer Spooler */
if ((strstr(connection_name, ".!scrap.scrapdirs.") != NULL) &&
    (strstr(connection_name, ".printspool.created.") != NULL)) {
  char printer_type_path[20];

  /* In this case the connection name is obtained
     from the printer type maintained in PrintSpool$OrigType */
  if (!getenv("PrintSpool$OrigType")) {
    printer->connection_type=CONNECTION_NOT_SUPPORTED;
    printer->real_connection_type=printer->connection_type;
    printer->error.number=ERROR_INVALID_CONNECTION;
    printer->error.number_of_parameters=1;
    strcpy(printer->error.parameter[0], connection_name);
    return false;
    }
  sprintf(printer_type_path,
          "PrinterType$%s",
          getenv("PrintSpool$OrigType"));
  if (!getenv(printer_type_path)) {
    printer->connection_type=CONNECTION_NOT_SUPPORTED;
    printer->real_connection_type=printer->connection_type;
    printer->error.number=ERROR_INVALID_CONNECTION;
    printer->error.number_of_parameters=1;
    strcpy(printer->error.parameter[0], connection_name);
    return false;
    }
  strcpy(connection_name, getenv(printer_type_path));
  strncpy(printer->connection_name, connection_name, MAX_CONNECTION_NAME-1);
  /* convert to lower to simplify comparation */
  c=connection_name;
  while (*c != '\0') {
    *c=tolower(*c);
    c++;
    }
  }
/* check for access connection */
if (strncmp(connection_name, CONNECTION_TAG_ACCESS, strlen(CONNECTION_TAG_ACCESS)) == 0) {
  char *c, *start;
  char input_line[MAX_STRING_LENGTH];
  char command[MAX_STRING_LENGTH];
  FILE *freeway_info;
  int freeway_type_section;
  struct network_message_element_struct message_element[MAX_MESSAGE_ELEMENTS];
  struct network_job_struct *job;
  unsigned long number_of_elements;
  unsigned long message_number_of_elements;

  strcpy(printer->output_file, c);
  /* Input handled over network */
  strcpy(printer->input_file, "");
  /* determine server */
  sprintf(command,
          "fwshow { > %s }",
          FREEWAY_INFORMATION_FILE);
  system(command);
  if ((freeway_info=fopen(FREEWAY_INFORMATION_FILE, "r")) == NULL) {

    printer->connection_type=CONNECTION_NOT_SUPPORTED;
    printer->real_connection_type=printer->connection_type;
    printer->error.number=ERROR_FREEWAY_FILE;
    printer->error.number_of_parameters=1;
    strcpy(printer->error.parameter[0], FREEWAY_INFORMATION_FILE);
    return false;
    }
  strcpy(printer->dns, "");
  freeway_type_section=0;
  while ((!feof(freeway_info)) &&
         (strcmp(printer->dns, "") == 0)) {
    strcpy(input_line, "");
    fgets(input_line, MAX_STRING_LENGTH - 1, freeway_info);
    if (!feof(freeway_info)) {
      c=&input_line[strlen(input_line) - 1];
      while ((c >= input_line) &&
             ((*c == '\r') ||
              (*c == '\n') ||
              (isspace(*c)))) {
        *c='\0';
        c--;
        }
      start=input_line;
      if (*start == '\r') {
        start++;
        }
      if (strncmp(start, FREEWAY_TAG_TYPE, strlen(FREEWAY_TAG_TYPE)) == 0)  {
        if ((c=strchr(start, ':')) != NULL) {
          *c='\0';
          freeway_type_section=atoi(start + strlen(FREEWAY_TAG_TYPE));
          }
        }
      else if (freeway_type_section == 2) {
        if ((start=strstr(start, FREEWAY_TAG_NAME)) != NULL) {
          start=start + strlen(FREEWAY_TAG_NAME);
          if ((c=strchr(start, ' ')) != NULL) {
            /* Found a shared printer name */
            *c='\0';
            if (strcmp(start, printer->printer_name) == 0) {
              if ((start=strstr(c + 1, FREEWAY_TAG_DNS)) != NULL) {
                strcpy(printer->dns, start + strlen(FREEWAY_TAG_DNS));
                }
              }
            }
          }
        }
      }
    }
  fclose(freeway_info);
  if (strcmp(printer->dns, "") == 0) {
    printer->connection_type=CONNECTION_NOT_SUPPORTED;
    printer->real_connection_type=printer->connection_type;
    printer->error.number=ERROR_FREEWAY_NO_DNS;
    printer->error.number_of_parameters=0;
    return false;
    }
  printer->connection_type=CONNECTION_ACCESS;
  /* check for real connection type on remote side */
  if ((job=network_job_add(NULL,
                           NETWORK_BUFFER_SIZE,
                           (unsigned long long) printer)) == NULL) {
    printer->connection_type=CONNECTION_BROKEN;
    printer->real_connection_type=printer->connection_type;
    printer->error.number=ERROR_NETWORK_ADD_JOB;
    printer->error.number_of_parameters=0;
    return false;
    }
  message_element[0].type=NETWORK_MESSAGE_ELEMENT_TYPE_VALUE;
  message_element[0].details.value=NETWORK_REQUEST_CONNECTION;
  message_element[1].type=NETWORK_MESSAGE_ELEMENT_TYPE_STRING;
  message_element[1].details.block.pointer=printer->printer_name;
  if (!network_build_message(job,
                             message_element,
                             2)) {
    network_job_drop(job);
    printer->connection_type=CONNECTION_BROKEN;
    printer->real_connection_type=printer->connection_type;
    printer->error.number=ERROR_NETWORK_BUILD_MESSAGE;
    printer->error.number_of_parameters=0;
    return false;
    }
  if (!printer_network_remote_call(job,
                                   &printer->error)) {
    printer->real_connection_type=printer->connection_type;
    return false;
    }
  message_element[0].type=NETWORK_MESSAGE_ELEMENT_TYPE_VALUE;
  message_element[1].type=NETWORK_MESSAGE_ELEMENT_TYPE_VALUE;
  if ((number_of_elements=network_extract_message(job,
                                                  message_element,
                                                  2,
                                                  0,
                                                  &message_number_of_elements)) < 2) {
    network_job_drop(job);
    printer->connection_type=CONNECTION_BROKEN;
    printer->real_connection_type=printer->connection_type;
    printer->error.number=ERROR_NETWORK_PROTOCOL;
    printer->error.number_of_parameters=1;
    strcpy(printer->error.parameter[0], printer->dns);
    return false;
    }
  network_job_drop(job);
  printer->real_connection_type=message_element[1].details.value;
  return true;
  }
/* All other supported connections are devices */
if ((strncmp(connection_name, CONNECTION_TAG_DEVICE_1, strlen(CONNECTION_TAG_DEVICE_1)) != 0) &&
    (strncmp(connection_name, CONNECTION_TAG_DEVICE_2, strlen(CONNECTION_TAG_DEVICE_1)) != 0)) {
  printer->connection_type=CONNECTION_NOT_SUPPORTED;
  printer->real_connection_type=printer->connection_type;
  printer->error.number=ERROR_INVALID_CONNECTION;
  printer->error.number_of_parameters=1;
  strcpy(printer->error.parameter[0], connection_name);
  return false;
  }
/* Find device name */
if (!(c=strrchr(connection_name, ':'))) {
  printer->connection_type=CONNECTION_NOT_SUPPORTED;
  printer->real_connection_type=printer->connection_type;
  printer->error.number=ERROR_MISSING_DEVICE_SEPARATOR;
  printer->error.number_of_parameters=1;
  strcpy(printer->error.parameter[0], connection_name);
  return false;
  }
c++;
if (*c == '$') {
  c+=2;
  }
strcpy(printer->device_name, c);
if ((strcmp(c, CONNECTION_TAG_PARALLEL_1) == 0) ||
    (strcmp(c, CONNECTION_TAG_PARALLEL_2) == 0)) {
  /* Parallel */
  sprintf(printer->output_file, "%s%s", CONNECTION_TAG_DEVICE_2, c);
  /* Fastparallel is not input and parallel is a propretary protcol
     in input mode. The file is opened as output for protection purposes
     and then protocol handling for input is done by software. */
  sprintf(printer->input_file,
          "%s%s",
          CONNECTION_TAG_DEVICE_2, c);
  printer->connection_type=CONNECTION_PARALLEL;
  printer->real_connection_type=printer->connection_type;
  if (strcmp(c, CONNECTION_TAG_PARALLEL_1) == 0) {
    printer->protocol_type=PROTOCOL_NIBBLE_MODE;
    }
  else {
    printer->protocol_type=PROTOCOL_ECP_MODE;
    }
  return true;
  }
else if ((strncmp(c, CONNECTION_TAG_USB_1, strlen(CONNECTION_TAG_USB_1)) == 0) ||
         (strncmp(c, CONNECTION_TAG_USB_2, strlen(CONNECTION_TAG_USB_2)) == 0)) {
  /* USB */
  char result_buffer[1024];
  char *descriptor_data;
  _kernel_swi_regs regs;

  /* Protocol is required */
  regs.r[0]=1<<31;
  regs.r[1]=(int) printer->device_name;
  regs.r[3]=128 | (6<<8) | ((2<<8 | 0)<<16);
  regs.r[4]=0 | (1024<<16);
  regs.r[5]=(int) result_buffer;
  regs.r[6]=0;
  regs.r[7]=NULL;
  regs.r[8]=NULL;
  _kernel_swi(DeviceFS_CallDevice, &regs, &regs);
  descriptor_data=result_buffer;
  /* Skip configuration. Move to first interface */
  descriptor_data+=descriptor_data[0];
  if (descriptor_data[5] == 7) {
    if (descriptor_data[6] == 1) {
      switch(descriptor_data[7]) {
        case 1: {
          sprintf(printer->output_file,
                  "%sendpoint1:%s",
                  CONNECTION_TAG_DEVICE_1,
                  c);
          strcpy(printer->input_file, "null:");
          printer->connection_type=CONNECTION_USB;
          printer->protocol_type=PROTOCOL_OUTPUT_ONLY;
          printer->real_connection_type=printer->connection_type;
          return true;
          }
        break;
        case 2: {
          sprintf(printer->output_file,
                  "%sendpoint1:%s",
                  CONNECTION_TAG_DEVICE_1,
                  c);
          sprintf(printer->input_file,
                  "%sendpoint2:%s",
                  CONNECTION_TAG_DEVICE_1,
                  c);
          printer->connection_type=CONNECTION_USB;
          printer->real_connection_type=printer->connection_type;
          return true;
          }
        break;
        case 3: {
          sprintf(printer->output_file,
                  "%sendpoint2:%s",
                  CONNECTION_TAG_DEVICE_1,
                  c);
          sprintf(printer->input_file,
                  "%sendpoint1:%s",
                  CONNECTION_TAG_DEVICE_1,
                  c);
          printer->connection_type=CONNECTION_USB;
          printer->real_connection_type=printer->connection_type;
          return true;
          }
        break;
        }
      }
    }
  printer->connection_type=CONNECTION_NOT_SUPPORTED;
  printer->real_connection_type=printer->connection_type;
  printer->error.number=ERROR_UNKNOWN_USB_CLASS;
  printer->error.number_of_parameters=1;
  sprintf(printer->error.parameter[0],
          "%d-%d-%d",
          descriptor_data[5],
          descriptor_data[6],
          descriptor_data[7]);
  return false;
  }
sprintf(printer->output_file, "%s%s", CONNECTION_TAG_DEVICE_2, c);
sprintf(printer->input_file, "%s%s", CONNECTION_TAG_DEVICE_2, c);
printer->connection_type=CONNECTION_NOT_SPECIAL;
printer->real_connection_type=printer->connection_type;
return true;
}

bool determine_connection(struct printer_struct *printer,
                          struct error_struct *error,
                          char *interface,
                          char *protocol)
{

if (!printer) {
  if (error) {
    error->number=ERROR_WRONG_CALL;
    error->number_of_parameters=2;
    strcpy(error->parameter[0], "determine_connection");
    strcpy(error->parameter[1], "printer");
    }
  return false;
  }
if (!interface) {
  if (error) {
    error->number=ERROR_WRONG_CALL;
    error->number_of_parameters=2;
    strcpy(error->parameter[0], "determine_connection");
    strcpy(error->parameter[1], "interface");
    }
  return false;
  }
if (!protocol) {
  if (error) {
    error->number=ERROR_WRONG_CALL;
    error->number_of_parameters=2;
    strcpy(error->parameter[0], "determine_connection");
    strcpy(error->parameter[1], "protocol");
    }
  return false;
  }
strcpy(protocol, "");
strcpy(interface, "");
switch(printer->real_connection_type) {
  case CONNECTION_BROKEN: {
    if (error) {
      *error=printer->error;
      }
    return false;
    }
  break;
  case CONNECTION_NOT_SUPPORTED: {
    if (error) {
      *error=printer->error;
      }
    return false;
    }
  break;
  case CONNECTION_INDETERMINED: {
    return false;
    }
  break;
  case CONNECTION_NOT_SPECIAL: {
    strcpy(interface, ACTION_MODE_TAG_NOT_SPECIAL);
    }
  break;
  case CONNECTION_PARALLEL: {
    strcpy(interface, ACTION_MODE_TAG_PARALLEL);
    }
  break;
  case CONNECTION_USB: {
    strcpy(interface, ACTION_MODE_TAG_USB);
    }
  break;
  default : {
    return false;
    }
  }
switch(printer->protocol_type) {
  case PROTOCOL_OUTPUT_ONLY: {
    strcpy(protocol, PROTOCOL_TAG_NOT_SPECIAL);
    }
  break;
  case PROTOCOL_NIBBLE_MODE: {
    strcpy(protocol, PROTOCOL_TAG_NIBBLE_MODE);
    }
  break;
  case PROTOCOL_ECP_MODE: {
    strcpy(protocol, PROTOCOL_TAG_ECP_MODE);
    }
  break;
  }
return true;
}

bool determine_printer_type(struct printer_struct *printer,
                            char *manufacturer,
                            char *printer_type)
{
bool found_flag;

found_flag=false;
switch(printer->connection_type) {
  case CONNECTION_USB: {
    char *c;
    char *tag_end;
    char result_buffer[1024];
    _kernel_swi_regs regs;

    /* If USB use IEEE identifier according Printer class
       to determine manufacturer and printer type */
    regs.r[0]=1<<31;
    regs.r[1]=(int) printer->device_name;
    regs.r[3]=161 | (0<<8) | (0<<16);
    regs.r[4]=0 | (1024<<16);
    regs.r[5]=(int) result_buffer;
    regs.r[6]=0;
    regs.r[7]=NULL;
    regs.r[8]=NULL;
    _kernel_swi(DeviceFS_CallDevice, &regs, &regs);
    /* First two bytes are containg the length as big endian */
    result_buffer[result_buffer[0]*256 + result_buffer[1]]='\0';
    /* Determine Manufacturer */
    c=strstr(&result_buffer[2], "MFG:");
    if (c) {
      c+=strlen("MFG:");
      tag_end=strchr(c, ';');
      found_flag=true;
      if (tag_end) {
        *tag_end='\0';
        strcpy(manufacturer, c);
        *tag_end=';';
        }
      else {
        strcpy(manufacturer, c);
        }
      c=manufacturer;
      while (*c != '\0') {
        if (isspace(*c)) {
          *c='_';
          }
        c++;
        }
      }
    /* Determine Printer type */
    c=strstr(&result_buffer[2], "MDL:");
    if (c) {
      c+=strlen("MDL:");
      tag_end=strchr(c, ';');
      found_flag=true;
      if (tag_end) {
        *tag_end='\0';
        strcpy(printer_type, c);
        *tag_end=';';
        }
      else {
        strcpy(printer_type, c);
        }
      c=printer_type;
      while (*c != '\0') {
        if (isspace(*c)) {
          *c='_';
          }
        c++;
        }
      }
    }
  break;
  case CONNECTION_ACCESS: {
    struct network_message_element_struct message_element[MAX_MESSAGE_ELEMENTS];
    struct network_job_struct *job;
    unsigned long number_of_elements;
    unsigned long message_number_of_elements;

    if ((job=network_job_add(NULL,
                             NETWORK_BUFFER_SIZE,
                             (unsigned long long) printer)) == NULL) {
      printer->error.number=ERROR_NETWORK_ADD_JOB;
      printer->error.number_of_parameters=0;
      return false;
      }
    message_element[0].type=NETWORK_MESSAGE_ELEMENT_TYPE_VALUE;
    message_element[0].details.value=NETWORK_REQUEST_PRINTER_TYPE;
    message_element[1].type=NETWORK_MESSAGE_ELEMENT_TYPE_STRING;
    message_element[1].details.block.pointer=printer->printer_name;
    if (!network_build_message(job,
                               message_element,
                               2)) {
      network_job_drop(job);
      printer->error.number=ERROR_NETWORK_BUILD_MESSAGE;
      printer->error.number_of_parameters=0;
      return false;
      }
    if (!printer_network_remote_call(job,
                                     &printer->error)) {
      return false;
      }
    message_element[0].type=NETWORK_MESSAGE_ELEMENT_TYPE_VALUE;
    message_element[1].type=NETWORK_MESSAGE_ELEMENT_TYPE_VALUE;
    message_element[2].type=NETWORK_MESSAGE_ELEMENT_TYPE_STRING;
    message_element[2].details.block.size=MAX_MESSAGE_LENGTH;
    message_element[2].details.block.pointer=manufacturer;
    message_element[3].type=NETWORK_MESSAGE_ELEMENT_TYPE_STRING;
    message_element[3].details.block.size=MAX_MESSAGE_LENGTH;
    message_element[3].details.block.pointer=printer_type;
    if ((number_of_elements=network_extract_message(job,
                                                    message_element,
                                                    4,
                                                    0,
                                                    &message_number_of_elements)) < 4) {
      network_job_drop(job);
      printer->error.number=ERROR_NETWORK_PROTOCOL;
      printer->error.number_of_parameters=1;
      strcpy(printer->error.parameter[0], printer->dns);
      return false;
      }
    network_job_drop(job);
    if (message_element[1].details.value == 0) {
      found_flag=false;
      }
    else {
      found_flag=true;
      }
    }
  break;
  }
return found_flag;
}

/* ---------- network ---------- */
/* Format of message:
   Bytes Meaning
   4     Total size           (handled internally)
   4     Number of parameters (handled internally)
   4     Command code
   X     Printername
         Command parameters

   Response:
   4     Total size           (handled internally)
   4     Number of parameters (handled internally)
   4     Error code
         Result values or error parameters */
void printer_network_remote_call_final(struct network_job_struct *job)
{

/* Dont do anything if there is a transmission fault.
   This is handled at the base procedure */
if (network_common.trace_file) {
  fprintf(network_common.trace_file,
          "printer_network_remote_call_final(%lx)\n",
          (unsigned long) job);
  fflush(network_common.trace_file);
  }
if (job->status != NETWORK_JOB_STATUS_ERROR) {
  /* Simply mark request as done */
  job->status=NETWORK_JOB_STATUS_DONE;
  }
job->final_function=NULL;
}

void printer_network_remote_call_switch(struct network_job_struct *job)
{

if (network_common.trace_file) {
  fprintf(network_common.trace_file,
          "printer_network_remote_call_switch(%lx)\n",
          (unsigned long) job);
  fflush(network_common.trace_file);
  }
/* Dont do anything if there is a transmission fault.
   This is handled at the base procedure */
if (job->status != NETWORK_JOB_STATUS_ERROR) {
  if (network_common.trace_file) {
    fprintf(network_common.trace_file,
            "switch to receiving\n");
    fflush(network_common.trace_file);
    }
  /* Simply swap request from sending to recieving */
  job->status=NETWORK_JOB_STATUS_RECEIVING;
  job->actual_transfered_size=0;
  job->message_size=0;
  job->final_function=printer_network_remote_call_final;
  }
}

bool printer_network_remote_call(struct network_job_struct *job,
                                 struct error_struct *error)
{
int i;
struct printer_struct *printer;
struct network_message_element_struct message_element[MAX_MESSAGE_ELEMENTS];
unsigned long number_of_elements;
unsigned long message_number_of_elements;

if (!job) return false;
printer=(struct printer_struct *) job->identification;
job->final_function=printer_network_remote_call_switch;
if (!network_connection_establish(job,
                                  printer->dns)) {
  network_job_drop(job);
  printer->connection_type=CONNECTION_BROKEN;
  printer->error.number=ERROR_NETWORK_CONNECTION;
  printer->error.number_of_parameters=1;
  strcpy(printer->error.parameter[0], printer->dns);
  if (error) {
    *error=printer->error;
    }
  return false;
  }
while ((job->status != NETWORK_JOB_STATUS_ERROR) &&
       (job->status != NETWORK_JOB_STATUS_DONE)) {
  my_poll(POLL_NETWORK);
  }
if (job->status == NETWORK_JOB_STATUS_ERROR) {
  network_job_drop(job);
  printer->connection_type=CONNECTION_BROKEN;
  printer->error.number=ERROR_NETWORK_CONNECTION;
  printer->error.number_of_parameters=1;
  strcpy(printer->error.parameter[0], printer->dns);
  if (error) {
    *error=printer->error;
    }
  return false;
  }
message_element[0].type=NETWORK_MESSAGE_ELEMENT_TYPE_VALUE;
if ((number_of_elements=network_extract_message(job,
                                                message_element,
                                                1,
                                                0,
                                                &message_number_of_elements)) < 1) {
  network_job_drop(job);
  return false;
  }
if (message_element[0].details.value != ERROR_NO_ERROR) {
  network_job_drop(job);
  if (error) {
    message_element[1].type=NETWORK_MESSAGE_ELEMENT_TYPE_VALUE;
    for (i=0; i < MAX_ERROR_PARAMETERS; i++) {
      message_element[i + 2].type=NETWORK_MESSAGE_ELEMENT_TYPE_STRING;
      message_element[i + 2].details.block.size=MAX_STRING_LENGTH;
      message_element[i + 2].details.block.pointer=error->parameter[i];
      }
    if (network_extract_message(job,
                                message_element,
                                2 + MAX_ERROR_PARAMETERS,
                                1,
                                &message_number_of_elements) < 2) {
      }
    printer->error.number_of_parameters=message_element[1].details.value;
    }
  return false;
  }
return true;
}

bool printer_network_error_response(struct error_struct *error,
                                    struct network_job_struct *job,
                                    struct network_message_element_struct *message_element,
                                    unsigned long max_number_of_elements)
{
int i;

if ((error->number_of_parameters + 2) > max_number_of_elements) {
  network_job_drop(job);
  return false;
  }
message_element[0].type=NETWORK_MESSAGE_ELEMENT_TYPE_VALUE;
message_element[0].details.value=error->number;
message_element[1].type=NETWORK_MESSAGE_ELEMENT_TYPE_VALUE;
message_element[1].details.value=error->number_of_parameters;
for (i=0; i < error->number_of_parameters; i++) {
  message_element[i + 2].type=NETWORK_MESSAGE_ELEMENT_TYPE_STRING;
  message_element[i + 2].details.block.pointer=error->parameter[i];
  }
if (!network_build_message(job,
                           message_element,
                           error->number_of_parameters + 2)) {
  network_job_drop(job);
  return false;
  }
return true;
}

extern bool scan_printers(void);

void printer_network_request(struct network_job_struct *job)
{
char printer_name[MAX_PRINTER_NAME_LENGTH];
struct printer_struct *printer;
struct network_message_element_struct message_element[MAX_MESSAGE_ELEMENTS];
unsigned long number_of_elements;
unsigned long message_number_of_elements;
struct error_struct error;

if (job->status == NETWORK_JOB_STATUS_ERROR) {
  /* Simply ignore faulty transmissions */
  network_job_drop(job);
  return;
  }
/* Connection is still open for replies in the moment */
/* Determine common part of request */
message_element[0].type=NETWORK_MESSAGE_ELEMENT_TYPE_VALUE;
message_element[1].type=NETWORK_MESSAGE_ELEMENT_TYPE_STRING;
message_element[1].details.block.size=MAX_PRINTER_NAME_LENGTH;
message_element[1].details.block.pointer=printer_name;
number_of_elements=network_extract_message(job,
                                           message_element,
                                           2,
                                           0,
                                           &message_number_of_elements);
if (number_of_elements < 2) {
  if (network_common.trace_file) {
    fprintf(network_common.trace_file,
            "Received message has too few parameters (%ld)\n",
            number_of_elements);
    fflush(network_common.trace_file);
    }
  /* Simply ignore faulty transmissions */
  network_job_drop(job);
  return;
  }
/* determine related printer */
if ((printer=printer_find(printer_name)) == NULL) {
  /* Scan all printers in case that the printer has been registered yet */
  if (network_common.trace_file) {
    fprintf(network_common.trace_file,
            "Printer \"%s\" not known. Rescanning\n",
           printer_name);
    fflush(network_common.trace_file);
    }
  scan_printers();
  if ((printer=printer_find(printer_name)) == NULL) {
    if (network_common.trace_file) {
      fprintf(network_common.trace_file,
              "Printer \"%s\" invalid.\n",
              printer_name);
      fflush(network_common.trace_file);
      }
    /* Simply ignore request for invalid printer */
    network_job_drop(job);
    return;
    }
  }
if (network_common.trace_file) {
  fprintf(network_common.trace_file,
          "Request %ld for printer %s received.\n",
          message_element[0].details.value,
          printer_name);
  fflush(network_common.trace_file);
  }
switch(message_element[0].details.value) {
  case NETWORK_REQUEST_CONNECTION: {
    if (message_number_of_elements != 2) {
      if (network_common.trace_file) {
        fprintf(network_common.trace_file,
                "Wrong number of elements for connection request (%ld).\n",
                message_number_of_elements);
        fflush(network_common.trace_file);
        }
      /* Simply ignore faulty transmissions */
      network_job_drop(job);
      return;
      }
    message_element[0].type=NETWORK_MESSAGE_ELEMENT_TYPE_VALUE;
    message_element[0].details.value=ERROR_NO_ERROR;
    message_element[1].type=NETWORK_MESSAGE_ELEMENT_TYPE_VALUE;
    message_element[1].details.value=printer->connection_type;
    if (!network_build_message(job,
                               message_element,
                               2)) {
      network_job_drop(job);
      return;
      }
    }
  break;
  case NETWORK_REQUEST_PRINTER_TYPE: {
    bool result;
    char manufacturer[MAX_MESSAGE_LENGTH];
    char printer_type[MAX_MESSAGE_LENGTH];

    if (message_number_of_elements != 2) {
      if (network_common.trace_file) {
        fprintf(network_common.trace_file,
                "Wrong number of elements for determine printer type request (%ld).\n",
                message_number_of_elements);
        fflush(network_common.trace_file);
        }
      /* Simply ignore faulty transmissions */
      network_job_drop(job);
      return;
      }
    strcpy(manufacturer, "");
    strcpy(printer_type, "");
    result=determine_printer_type(printer,
                                  manufacturer,
                                  printer_type);
    message_element[0].type=NETWORK_MESSAGE_ELEMENT_TYPE_VALUE;
    message_element[0].details.value=ERROR_NO_ERROR;
    message_element[1].type=NETWORK_MESSAGE_ELEMENT_TYPE_VALUE;
    if (result) {
      message_element[1].details.value=1;
      }
    else {
      message_element[1].details.value=0;
      }
    message_element[2].type=NETWORK_MESSAGE_ELEMENT_TYPE_STRING;
    message_element[2].details.block.pointer=manufacturer;
    message_element[3].type=NETWORK_MESSAGE_ELEMENT_TYPE_STRING;
    message_element[3].details.block.pointer=printer_type;
    if (!network_build_message(job,
                               message_element,
                               4)) {
      network_job_drop(job);
      return;
      }
    }
  break;
  case NETWORK_REQUEST_LOCK: {
    unsigned long lock_result;

    if (message_number_of_elements != 3) {
      if (network_common.trace_file) {
        fprintf(network_common.trace_file,
                "Wrong number of elements for lock request (%ld).\n",
                message_number_of_elements);
        fflush(network_common.trace_file);
        }
      /* Simply ignore faulty transmissions */
      network_job_drop(job);
      return;
      }
    message_element[3].type=NETWORK_MESSAGE_ELEMENT_TYPE_VALUE;
    number_of_elements=network_extract_message(job,
                                               message_element,
                                               3,
                                               2,
                                               &message_number_of_elements);
    lock_result=printer_lock(printer,
                             message_element[2].details.value);
    message_element[0].type=NETWORK_MESSAGE_ELEMENT_TYPE_VALUE;
    message_element[0].details.value=ERROR_NO_ERROR;
    message_element[1].type=NETWORK_MESSAGE_ELEMENT_TYPE_VALUE;
    message_element[1].details.value=lock_result;
    if (!network_build_message(job,
                               message_element,
                               2)) {
      network_job_drop(job);
      return;
      }
    }
  break;
  case NETWORK_REQUEST_UNLOCK: {
    bool result;

    if (message_number_of_elements != 2) {
      if (network_common.trace_file) {
        fprintf(network_common.trace_file,
                "Wrong number of elements for unlock request (%ld).\n",
                message_number_of_elements);
        fflush(network_common.trace_file);
        }
      /* Simply ignore faulty transmissions */
      network_job_drop(job);
      return;
      }
    result=printer_unlock(printer);
    message_element[0].type=NETWORK_MESSAGE_ELEMENT_TYPE_VALUE;
    message_element[0].details.value=ERROR_NO_ERROR;
    message_element[1].type=NETWORK_MESSAGE_ELEMENT_TYPE_VALUE;
    if (result) {
      message_element[1].details.value=1;
      }
    else {
      message_element[1].details.value=0;
      }
    if (!network_build_message(job,
                               message_element,
                               2)) {
      network_job_drop(job);
      return;
      }
    }
  break;
  case NETWORK_REQUEST_RESET: {
    if (message_number_of_elements != 2) {
      if (network_common.trace_file) {
        fprintf(network_common.trace_file,
                "Wrong number of elements for reset request (%ld).\n",
                message_number_of_elements);
        fflush(network_common.trace_file);
        }
      /* Simply ignore faulty transmissions */
      network_job_drop(job);
      return;
      }
    if (printer_reset(printer,
                      &error)) {
      message_element[0].type=NETWORK_MESSAGE_ELEMENT_TYPE_VALUE;
      message_element[0].details.value=ERROR_NO_ERROR;
      if (!network_build_message(job,
                                 message_element,
                                 1)) {
        network_job_drop(job);
        return;
        }
      }
    else {
      if (!printer_network_error_response(&error,
                                          job,
                                          message_element,
                                          MAX_MESSAGE_ELEMENTS)) {
        return;
        }
      }
    }
  break;
  case NETWORK_REQUEST_STATUS: {
    int error_status;

    if (message_number_of_elements != 2) {
      if (network_common.trace_file) {
        fprintf(network_common.trace_file,
                "Wrong number of elements for status request (%ld).\n",
                message_number_of_elements);
        fflush(network_common.trace_file);
        }
      /* Simply ignore faulty transmissions */
      network_job_drop(job);
      return;
      }
    if (printer_status(printer,
                       &error,
                       &error_status)) {
      message_element[0].type=NETWORK_MESSAGE_ELEMENT_TYPE_VALUE;
      message_element[0].details.value=ERROR_NO_ERROR;
      message_element[1].type=NETWORK_MESSAGE_ELEMENT_TYPE_VALUE;
      message_element[1].details.value=error_status;
      if (!network_build_message(job,
                                 message_element,
                                 2)) {
        network_job_drop(job);
        return;
        }
      }
    else {
      if (!printer_network_error_response(&error,
                                          job,
                                          message_element,
                                          MAX_MESSAGE_ELEMENTS)) {
        return;
        }
      }
    }
  break;
  case NETWORK_REQUEST_SEND: {
    bool result;

    if (message_number_of_elements != 3) {
      /* Simply ignore faulty transmissions */
      network_job_drop(job);
      return;
      }
    message_element[2].type=NETWORK_MESSAGE_ELEMENT_TYPE_BLOCK;
    message_element[2].details.block.size=0;
    message_element[2].details.block.pointer=NULL;
    number_of_elements=network_extract_message(job,
                                               message_element,
                                               3,
                                               2,
                                               &message_number_of_elements);
    result=printer_send(printer,
                        message_element[2].details.block.pointer,
                        message_element[2].details.block.size);
    message_element[0].type=NETWORK_MESSAGE_ELEMENT_TYPE_VALUE;
    message_element[0].details.value=ERROR_NO_ERROR;
    message_element[1].type=NETWORK_MESSAGE_ELEMENT_TYPE_VALUE;
    if (result) {
      message_element[1].details.value=1;
      }
    else {
      message_element[1].details.value=0;
      }
    if (!network_build_message(job,
                               message_element,
                               2)) {
      network_job_drop(job);
      return;
      }
    }
  break;
  case NETWORK_REQUEST_GET: {
    char *response;
    unsigned long size;
    bool result;

    if (message_number_of_elements != 4) {
      /* Simply ignore faulty transmissions */
      network_job_drop(job);
      return;
      }
    message_element[2].type=NETWORK_MESSAGE_ELEMENT_TYPE_VALUE;
    message_element[3].type=NETWORK_MESSAGE_ELEMENT_TYPE_VALUE;
    number_of_elements=network_extract_message(job,
                                               message_element,
                                               4,
                                               2,
                                               &message_number_of_elements);
    size=message_element[3].details.value;
    if ((response=malloc(size)) == NULL) {
      network_job_drop(job);
      return;
      }
    result=printer_get(printer,
                       message_element[2].details.value,
                       response,
                       &size);
    message_element[0].type=NETWORK_MESSAGE_ELEMENT_TYPE_VALUE;
    message_element[0].details.value=ERROR_NO_ERROR;
    message_element[1].type=NETWORK_MESSAGE_ELEMENT_TYPE_VALUE;
    if (result) {
      message_element[1].details.value=1;
      }
    else {
      message_element[1].details.value=0;
      }
    message_element[2].type=NETWORK_MESSAGE_ELEMENT_TYPE_BLOCK;
    message_element[2].details.block.size=size;
    message_element[2].details.block.pointer=response;
    if (!network_build_message(job,
                               message_element,
                               3)) {
      free(response);
      network_job_drop(job);
      return;
      }
    free(response);
    }
  break;
  default: {
    if (network_common.trace_file) {
      fprintf(network_common.trace_file,
              "Wrong number of elements for lock request (%ld).\n",
              message_number_of_elements);
      fflush(network_common.trace_file);
      }
    /* Simply ignore unknown requests */
    network_job_drop(job);
    return;
    }
  }
if (network_common.trace_file) {
  fprintf(network_common.trace_file,
          "Sending response to request.\n");
  fflush(network_common.trace_file);
  }
/* Send response */
/* Dropping the job after doing the work is the default */
job->final_function=NULL;
job->actual_transfered_size=0;
job->status=NETWORK_JOB_STATUS_SENDING;
}

#endif
