# -*- perl -*-
#
# $Revision: 1.14 $
package printer;

require Exporter;
@ISA = qw(Exporter);
@EXPORT_OK = qw(&print_usage &print_version &service_test);
use lib $main::runtimedir;
use csl_utils qw($TIMEOUT %ERRORS);
use strict;

use Net::SNMP;

sub print_usage ();
sub print_version ();
sub service_test (%);

sub print_usage () {

    print "
Usage:  Check printer service.
Options:
    -H <hostname> -o os -c community [--verbose]
    --help
    --version
    --usage
";
    return $ERRORS{'OK'};
}

sub print_version () {
    my $version = '$Revision: 1.14 $';
    $version =~ s/^\$Revision: //;
    $version =~ s/ \$\s*$//;

    print "csl_printer.pm - $version\n";
    return $ERRORS{'OK'};
}

sub service_test (%) {

    my %options = @_;

    my $timeout=5;
    my $retries=3;
    my $error;
    my $response;
    my $alert_response;
    my $alert_status;
    my $display;
    my $session;
    my $status = 0;
    my $entry;

    my %PRINTER;

    my $i;
    my $id;
    my $door_open=0;
    my $id_oid = ".1.3.6.1.2.1.25.3.2.1.3.1";
    my $pr_oid = ".1.3.6.1.2.1.25.3.5.1.2.1";
    my $door_oid = ".1.3.6.1.2.1.43.6.1.1.3.1";
    my $alert_oid = ".1.3.6.1.2.1.43.18.1.1.8";
    my $old_hp_display_oid = ".1.3.6.1.4.1.11.2.3.9.1.1.3.0";
    my $xerox_display_oid = ".1.3.6.1.2.1.43.16.5.1.2.1";
    my $lanier_scan_oid = ".1.3.6.1.4.1.367.3.2.1.2.2.18.0";
    my $lanier_copy_oid = ".1.3.6.1.4.1.367.3.2.1.2.2.22.0";
    my $rfc_oid = ".1.3.6.1.2.1.43.18.1.1.8.1";
    my $pr_error;
    my $pr_status;
    my $rfc_errors;
    my @rfc_printable;

    my @statuses = ("OK", "WARNING", "CRITICAL", "UNKNOWN");
    my @status = ("low paper", "no paper", "low toner", "no toner", 
        "door open", "paper jam or other maintenance", "printer offline", 
        "service requested:", "input tray missing", 
        "output tray missing", "marker supply missing", 
        "output tray nearly full", "output tray full", 
        "input tray empty", "overdue preventative maintenance");
    my @priorities = ( 0, 1, 0, 1, 2, 2, 1, 2, 2, 2, 2, 2, 2, 2, 2);
    my @pr_status_array = ();
    my @maint_array = ();
    my @lanier_scan_status = ("paper on contact glass", "ADF lifted up", 
        "ADF jam", "ADF cover open", "other error", "user maintenance",
        "service call", "power off", "in use", "other warning");
    my @lanier_scan_priorities = (0, 0, 2, 1, 2, 2, 2, 0, 0, 2);
    my @lanier_scan_status_array={};
    my @lanier_copy_status = ("other alert", "toner low", "occupied", 
        "warming up", "other error", "scanner cover open",
        "plotter cover open", "paper end", "scanner paper jam", 
        "plotter paper jam", "toner out", "service call");
    my @lanier_copy_priorities = (0, 0, 0, 0, 2, 2, 2, 2, 2, 2, 2, 2);
    my @lanier_copy_status_array={};

    if ($options{verbose}) {
        foreach my $opt (sort keys(%options)) {
            if ($options{$opt}) {
                print "$opt - $options{$opt}\n";
            }
        }
    }

    my $host = $options{"opt_H"};

    if ((!$host) || ($host eq "")) {
        print "\nHost name not specified.\n";
        &print_usage();
        return $ERRORS{'OK'};
    }

    my $os = $options{"opt_o"};
    ($os) || ($os = "HP");

    my $community=$options{"opt_c"};
    ($community) || ($community = "CSL");

    if ($os eq "xeroxcolor") {
        $os = "XEROX";
        $community = "public";
    }

    if (!(defined($PRINTER{$os}))) {
        $os = "HP";
    }

    print "os = $os\n" if $options{verbose};

    print "Create session: $host - $community - $timeout - $retries\n" 
        if ($options{verbose});
    #create the SNMP session
    ($session, $error) = Net::SNMP->session(-hostname=>$host,
                                            -community=>$community,
                                            -timeout=>$timeout,
                                            -retries=>$retries);

    print "Querying $PRINTER{$os}{status_oid}\n" if $options{verbose};

    if ( $host =~ /^olm\..*$/ || $host =~ /^hpljet\..*$/ ) {
        $os = "old-HP";
    } else {
        $response = $session->get_request($id_oid);
        $id = $response->{$id_oid};

        print "Printer ID: $id\n" if $options{verbose};

        if ($id =~ /Xerox/ || $id =~ /Tektronix.*Phaser/) {
            $os = "XEROX";
        } elsif ($id =~ /HP.LaserJet/) {
            $os = "HP";
        } elsif ($id =~ /LANIER/) {
            $os = "LANIER";
        }
    }

    print "Printer type: $os\n" if $options{verbose};

    if ( ! ($os =~ /old-HP/) ) {
        $response = $session->get_table($door_oid);
        foreach (sort keys (%$response)) {
            if ( $response->{$_} == 3 ) {
                $door_open=1;
                print "Found open door\n" if $options{verbose};
            }
        }

        $response = $session->get_request($pr_oid);
        $pr_error = $session->error;

        if ($pr_error) {
            $status = 3;
            print "@statuses[$status]: $pr_error\n";
            print "Exit status: @statuses[$status]\n" if $options{verbose};
        } else {
            $pr_status = $response->{$pr_oid};
            $pr_status = 0 if ($pr_status eq "");
            print "Printer status string: $pr_status\n" if $options{verbose};
            foreach ($i=0; $i<15; $i++) {
                print "Checking for 2**$i: " . (2**$i) . " " .
                    (hex($pr_status) & (2**$i)) . "\n" if $options{verbose};

                if ( (hex($pr_status) & (2**$i)) != 0 ) {
                    print "2**$i state exists\n" if $options{verbose};
                    push(@pr_status_array, $i);
                    if ($i == 7) {
                        print "\$i is 7\n" if $options{verbose};
                        # Only go warning for low-paper conditions, critical
                        # for all else
                        $alert_response = $session->get_table($alert_oid);
                        foreach $entry (keys(%$alert_response)) {
                            print "processing alert $alert_response->{$entry}\n"
                                if $options{verbose};
                            if (($alert_response->{$entry} =~ /Low.Paper/i ) || 
                                ($alert_response->{$entry} =~ /Warming.Up/i ) ||
                                ($alert_response->{$entry} =~ /Sleep.Mode/i ) ||
                                ($alert_response->{$entry} =~ /ORDER.CARTRIDGE/i ) || 
                                ($alert_response->{$entry} =~ /POWER.*SAVE/i )) {

                                # no need to get excited about this one
                                $status = 0;
                                push(@maint_array, $alert_response->{$entry});
                            } elsif (($alert_response->{$entry} =~ /Tray.*Empty/i )) {
                                # warning only
                                #  $status = 1;
                                push(@maint_array, $alert_response->{$entry});
                            } else {
                                $status = 2;
                                push(@maint_array, $alert_response->{$entry});
                            }
                        }
                    } else {
                        $status = @priorities[$_];
                        print "Status now set to $status\n" if $options{verbose};
                    }
                }
            }
        }

        # stupid Xerox Phaser printers won't put maintenance kit
        # information in RFC printer status OID...
        # (phaser 850 won't report being low on ink sticks, either)
        if ($os eq "XEROX") {
            $response = $session->get_table($xerox_display_oid);
            foreach (sort keys %$response) {
                if ($response->{$_} =~ /Maintenance.Kit/) {
                    push(@pr_status_array, 7);
                    push(@pr_status_array, 14);
                    $status = 2;
                }
                if ($response->{$_} =~ /Ink.low/) {
                    push(@pr_status_array, 3);
                    $status = 2;
                }
                if ($response->{$_} =~ /Replace.*tray/) {
                    push(@pr_status_array, 8);
                    $status = 2;
                }
                if ($response->{$_} =~ /Maintenance.*missing/) {
                    push(@pr_status_array, 7);
                    push(@maint_array, "maintenance tray missing");
                    $status = 2;
                }
            }
        }
    }

    if ($id =~ /LANIER/i) {
        # run through Ricoh-specific status bits for print engine
        # and ADF/scanner (if applicable)
        print "status at start of Lanier checks: $status\n" if $options{verbose};
        if ( $id =~ /LD[0-9]/ ) {
            # check scanner pieces
            $response = $session->get_request($lanier_scan_oid);
            $pr_status = $response->{$lanier_scan_oid};
            print "scanner status: $pr_status\n" if $options{verbose};
            foreach ($i=0; $i<15; $i++) {
                print "status before checking for 2**$i: $status\n" .
                    "Scanner: Checking for 2**$i: " . (2**$i) . " "  .
                    (($pr_status) & (2**$i)) . "\n" if $options{verbose};
                if ( ($pr_status & (2**$i)) != 0 ) {
                    print "2**$i state exists\n" .  "status value for 2**$i: " .
                        "@lanier_scan_priorities[$i]\n" if $options{verbose};
                    push(@lanier_scan_status_array, $i);
                    #$status = @priorities[$i];
                    if (@lanier_scan_priorities[$i] > $status) {
                        $status = @lanier_scan_priorities[$i];
                    }
                }
            }
        }

        print "status after lanier_scan: $status\n" if $options{verbose};

        if ( $id =~ /LD[0-9]/ ) {
            # check copier/printer pieces
            $response = $session->get_request($lanier_copy_oid);
            $pr_status = $response->{$lanier_copy_oid};
            if ($options{verbose}) {
                print "printer status: $pr_status\n";
            }
            foreach ($i=0; $i<15; $i++) {
                if ($options{verbose}) {
                    print "Printer: Checking for 2**$i: " . (2**$i) . " "  .  (($pr_status) & (2**$i))  . "\n";
                }
                if ( ($pr_status & (2**$i)) != 0 ) {
                    if ($options{verbose}) {
                        print "2**$i state exists\n";
                    }
                    push(@lanier_copy_status_array, $i);
                    if (@lanier_copy_priorities[$i] > $status) {
                        $status = @lanier_copy_priorities[$i];
                    }
                }
            }
        }
        if ($options{verbose}) {
            print "status after lanier_copy: $status\n";
        }
    }

    if ($os =~ /old-HP/ ) {
      # old HP printers don't handle RFC1179 SNMP OID's
      # parse the display instead
      $response = $session->get_request($old_hp_display_oid);
      $pr_status = $response->{$old_hp_display_oid};
      if ($options{verbose}) {
print "old-HP display: $pr_status\n";
      }
      if ($pr_status =~ /TONER/ || $pr_status =~ /JAM/ ) {
$status = 2;
push(@rfc_printable,$pr_status);
      } elsif ($pr_status =~ /LOAD.*TRAY/ ||
       $pr_status =~ /CLOSE/ || $pr_status =~ /OPEN/ || 
       $pr_status =~ /INSERT.*TRAY/ ) {
if ($status < 1) {
  $status = 1;
}
push(@rfc_printable,$pr_status);
      }
    }

    #
    # get RFC1179 printer alerts
    #
    $rfc_errors = $session->get_table($rfc_oid);
    for $entry (keys(%$rfc_errors)) {
      # put up "warning" for toner almost empty or
      # general door-open situations
      if ( $rfc_errors->{$entry} =~ /Offline/i ||
           $rfc_errors->{$entry} =~ /Door.open/i ||
           $rfc_errors->{$entry} =~ /^Close/i ||
           $rfc_errors->{$entry} =~ /Door.open/i ||
           $rfc_errors->{$entry} =~ /Toner.Cartridge.is.almost.empty/i ||
           $rfc_errors->{$entry} =~ /^Insert/i ) {
if ($status < 1) {
  $status = 1;
}
      } elsif ($rfc_errors->{$entry} =~ /Energy.Save/i ||
         # don't warn for out-of-paper conditions...users should
         # be able to figure this one out
       $rfc_errors->{$entry} =~ /Low.Power.Mode\s/i ||
       $rfc_errors->{$entry} =~ /Full:.Tray\s/i ||
       $rfc_errors->{$entry} =~ /MANUALLY.FEED/i ||
       $rfc_errors->{$entry} =~ /ORDER.CARTRIDGE/i ||
       $rfc_errors->{$entry} =~ /Power.*Save/i ||
       $rfc_errors->{$entry} =~ /Sleep.+mode.+on/i ||
       $rfc_errors->{$entry} =~ /in.use/i ||
       $rfc_errors->{$entry} =~ /ADF.lifted/i ||
       $rfc_errors->{$entry} =~ /Panel.Off/i ||
         $rfc_errors->{$entry} =~ /Tray.*Empty/i ||
         $rfc_errors->{$entry} =~ /Tray.*load/i ||
         $rfc_errors->{$entry} =~ /OUT.OF.PAPER/i ||
         $rfc_errors->{$entry} =~ /No.Paper/i ||
         $rfc_errors->{$entry} =~ /Warming.up/i ||
         $rfc_errors->{$entry} =~ /Paper.Is.Low/i ||
         $rfc_errors->{$entry} =~ /Low.Paper/i ||
       $rfc_errors->{$entry} =~ /In.Use:/i ) {
# Don't do anything for low-paper or manual feed
# Don't raise flags for out-of-paper conditions, either
      } elsif ($rfc_errors->{$entry} =~ /TONER.LOW/i ||
       $rfc_errors->{$entry} =~ /Jam/i ||
       $rfc_errors->{$entry} =~ /Duplexer/i ||
       $rfc_errors->{$entry} =~ /Stapler/i  ) {
# go "critical" for toner low/out
if ($status < 2) {
  $status = 2;
}
      } else {
# what do we have here???
if ($status < 2) {
  $status = 2;
}
      }
      push (@rfc_printable, $rfc_errors->{$entry});
    }

    # all done with SNMP queries, we hope
    $session->close;

    if ($door_open) {
      $status = 2;
    }

    if ($options{verbose}) {
      print "maint_array: @maint_array\n";
    }

    if ( ($os =~ /old-HP/) && ($status == 0) ) {
      print "OK ";
    } else {
      print @statuses[$status] . " ";
    }

    if ($door_open) {
      print "Door open; ";
    }
    if (@pr_status_array) {
      foreach (@pr_status_array) {
print " ";
if (($_ == 7) && @maint_array) {
  foreach $entry (@maint_array) {
    print "$entry; ";
  }
} else {
  print @status[$_] . "; ";
}
      }
    }

    if ($id =~ /LANIER/) {
      # go through Lanier-specific status bits
      if ($id =~ /LD060/ || $id =~ /LD045/) {
foreach (@lanier_scan_status_array) {
  print $lanier_scan_status[$_] . "; ";
}
      }
    }

    if ($id =~ /LANIER/) {
      # go through Lanier-specific status bits
      if ($id =~ /LD060/ || $id =~ /LD045/ || $id =~ /2145/) {
foreach (@lanier_copy_status_array) {
  print $lanier_copy_status[$_] . "; ";
}
      }
    }

    foreach (@rfc_printable) {
      print $_ . "; ";
    }

    print "\n";
    print "Exit status: @statuses[$status]\n" if $options{verbose};

    exit $status;
  }
