#!/usr/bin/perl

# Created by elik on Jul 8 '13
use Getopt::Long;
use Switch;
use Net::SSH::Perl;
use Capture::Tiny 'capture';
use Net::LDAP;
use Expect;
use File::Slurp;

$logname = $ENV{'LOGNAME'};

if ($logname ne root) { $lpadmin = 'sudo /usr/sbin/lpadmin'; } else { $lpadmin = "/usr/sbin/lpadmin"; } #{ print "This script can only ran as root\n"; exit; }

#my $hp_ppdfiles = '/usr/share/foomatic/db/source/PPD/HP';
#my @ppdlist = sort grep { -f "$hp_ppdfiles/$_" } read_dir($hp_ppdfiles);

# List of directory's with HP PPD files 
@allppddir = ('/usr/share/foomatic/db/source/PPD/HP/', '/usr/share/ppd/SUNWfoomatic/HP/', '/usr/share/cups/model/SUNWhplip/', '/usr/share/ppd/SUNWhpijs/HP/');

foreach $allppdfiles (@allppddir) {
        my @hp_ppds = sort grep { -f "$allppdfiles/$_" } read_dir($allppdfiles);
                $ppddir = @allppddir[$_++];
                foreach (@hp_ppds) {
                        push (@ppdlist, $ppddir.$_);
                }
}

# Leave only remote hosts
my @print_server_list = ('print1', 'print2');
my $local_host = `hostname`;
chomp($local_host);
my ( $index )= grep { $print_server_list[$_] =~ /($local_host)/ } 0..$#print_server_list;
splice(@print_server_list, $index, 1);

if ($#ARGV eq -1) {
print <<EOF;
version 1.5.2
-----------
Please Note: This script could also be used in a non intractive mode
             For more information just run $0 -h

    This script could be used for
------------------------------------
Create local & remote print queus.
Create the printer LDAP entry.
Create the printer host LDAP entry.
Add printers to a printer class

EOF

     print "Adding a printer to a class?: [n] ";
     $add_class = <>;
	if ($add_class =~ "y") {
             print "Printer class name: ";
             $class = <>;
	     chomp($class);
             get_printer_name($add_class)
	} else {
             get_printer_name($add_class)
	}

    sub get_printer_name() {
	 my ($add_class) = @_;
         do {
             print "Printer Name: ";
             $printer = <>;
             chomp ($printer);
         } until $printer ne "";
      if ($add_class =~ "y") {
	    get_update_ldap_info($add_class) 
	} else {
	    get_ip_info() 
      }
    }

    sub get_ip_info() {
         do {
	     print "Printer IP Address (leave empty for virtual printers): ";
             $ip = <>;
                 if( $ip =~ m/^(\d\d?\d?)\.(\d\d?\d?)\.(\d\d?\d?)\.(\d\d?\d?)/ ) {
                     print("IP Address $ipaddr --> VALID FORMAT! \n");
                         if($1 <= 255 && $2 <= 255 && $3 <= 255 && $4 <= 255)
                     {
                           print("IP address: $1.$2.$3.$4 --> All octets within range\n");
                     } else {
                           print("One of the octets is out of range. All octets must contain a number between 0 and 255 \n");
                           $ip = '';
                     }
                     } else {
                     print("IP Address $ipaddr --> NOT IN VALID FORMAT! \n");
                     $ip = '';
                 }
             chomp ($ip);
         } until $ip ne "";
      get_update_ldap_info() 
    }

    sub get_update_ldap_info() {
	 my ($add_class) = @_;
         do {
             print "Update printer information in LDAP (host/ip/clas & printer) (y/n) [y]: ";
             $dontupdate = <>;
             chomp ($dontupdate);
             $dontupdate = "y" unless ($dontupdate);
                switch ($dontupdate) {
                    case "y"  { $dontupdate = "y"; }
                    case "n"  { $dontupdate = "n"; }
                    else      { print "Please make a vaild selection\n";
                                $dontupdate = ""; }
                }
         } until $dontupdate ne "";
      get_update_remote($add_class) 
    }

    sub get_update_remote() {
	 my ($add_class) = @_;
         do {
	     print "Update remote print server's (y/n) [y]: ";
	     $remote = <>;
             chomp ($remote);
             $remote = "y" unless ($remote);
                switch ($remote) {
                    case "y"  { $remote = "y"; }
                    case "n"  { $remote = "n"; }
                    else      { print "Please make a vaild selection\n";
                                $remote = ""; }
                }
         } until $remote ne "";

                if ($remote =~ "y") {
                   if ($add_class =~ "y") {
	                 get_remote_password($add_class) 
                   } else {
	                 get_remote_password() 
                   }
                } else {
                   if ($add_class =~ "y") {
	                 printer_class($printer, $class, $login, $password, $ip, $remote, $dontupdate, $start, @print_server_list);
	                 exit;
                   } else {
                   }
                }
    }

#         do {
#	     print "Please enetr a login ID with proper LDAP credentials: ";
#	     $login = <>;
#             chomp ($login);
#         } until $login ne "";
#
#         do {
#	     print "\nPlease enetr ${login}'s LDAP password: ";
#	     system('stty','-echo');
#	     $user_password = <>;
#	     chomp ($user_password);
#	     system('stty','echo'); 
#	     print "\n";
#         } until $user_password ne "";

    sub get_remote_password() {
	 my ($add_class) = @_;
         do {
	     print "\nPlease enetr the root password (needed for SSH): ";
	     system('stty','-echo');
	     $password = <>;
	     chomp ($password);
	     #chop($password=<>);
	     system('stty','echo'); 
	     print "\n";
         } until $password ne "";
      if ($add_class =~ "y") {
	    printer_class($printer, $class, $login, $password, $ip, $remote, $dontupdate, $start, @print_server_list);
	    exit;
      }
    }

print <<EOF;
  1) HP (All HP)
  2) Ithaca
  3) Zebra (All zebra S600/105SL/S4M)
  4) Optio (ODCS virtual printers)
  5) Raw - (All other printer models)
EOF
         do {
             print "Select a printer model type: ";
             $model = <>;
             chomp ($model);
                if ($model eq 0 or $model gt 5) {
                     print  "Please make a vaild selection\n";
                     $model = '';
                }
         } until $model ne "";

         create_intr_printer($model, $printer, $odcs_file, $class, $ip, $remote, $dontupdate, @print_server_list);

         sub create_intr_printer() {
                 my($model, $printer, $odcs_file, $class, $ip, $remote, $dontupdate, @print_server_list) = @_;
                 switch ($model) {
                           case "1" {
                                         my $lnum = 0;
                                         print "\nPlease enter a printer model number.\n";
                                         print "Note: The first in the list is the recommended driver\n";
                                         print "For example: for \"HP LaserJet 2200\", enter 2200: ";
                                         $select_model_num = <>;
                                         chomp ($select_model_num);
                                         foreach (@ppdlist) {
                                             chomp;
                                                  if( $_ =~ m/HP.*($select_model_num)/ig ) {
                                                      push (@hp_full_ppdlist, "$lnum) $_\n");
                                                      push (@all_models, $_);
                                                      $lnum++;    }
                                         }
					 if (!@hp_full_ppdlist) {
						print "\nYour model was not found in the list, Please try again.\n";
						print "Note: To add new print drivers to the system use \"ppdmgr\".\n";
						create_intr_printer($model, $printer, $odcs_file, $class, $ip, $remote, $dontupdate, @print_server_list);
					 }
                                             do {
                                                       print "@hp_full_ppdlist";
				                       $printer_count = $#hp_full_ppdlist;
                                                       print "Please select a printer model: ";
                                                 $model_num = <>;
                                                 chomp ($model_num);
                                                    switch ($model_num) {
                                                        case [0..$printer_count]  { $model_file = ("@all_models[$model_num]"); }
                                                        else      { print "Please make a vaild selection\n";
                                                                    $model_num = ""; }
                                                    }
                                             } until $model_num ne "";
	                              hp_printer($printer, "$hp_ppdfiles/$model_file", $login, $password, "1", $ip, $remote, $dontupdate, $start, @print_server_list); }
                           case "2" { ithaca_printer($printer, $model_file, $login, $password, "2", $ip, $remote, $dontupdate, $start, @print_server_list); }
                           case "3" { zebra_printer($printer, $model_file, $login, $password, "3", $ip, $remote, $dontupdate, $start, @print_server_list); }
                           case "4" { print "Please enter the odcs source file (example /spool/oeci/eci77/doc/${printer}.dcl): ";
			              $odcs_file = <>;
			               chomp ($odcs_file);
                                      optio_printer($printer, $odcs_file, $login, $password, "4", $ip, $remote, $dontupdate, $start, @print_server_list); }
                           case "5" { all_printers($printer, $model_file, $login, $password, "5", $ip, $remote, $dontupdate, $start, @print_server_list); }
                                 }
         }

} else {

         switch ($#ARGV) {
             case [0..5]   { &usage; }
             case [6..8,10..15] {}
	     else              { &usage; }
         }

     # Define variables
     my $ip             = ''; # Printers ip address
     my $login          = ''; # LDAP login credentials to use
     my $user_password  = ''; # User's LDAP password to use
     my $password       = ''; # Remote root SSH password to use
     my $remote         = ''; # Update remote print server information
     my $dontupdate     = ''; # Don't update ldap host & printer information
     my $type           = ''; # Printer type
     my $model_file     = ''; # Printer model PPD or Optio dcl file location
     my $help           = ''; # Help message
     my $verbose        = ''; # Verbose output
     my $odcs           = ''; # Optio (ODCS-dcl) file path
     my $class          = ''; # Printer class name

     GetOptions( 
                "ip=f" => \$ip,
                "login" => \$login,
                "user_password" => \$user_password,
                "password=s" => \$password,
                "remote" => \$remote,
                "start" => \$start,
                "dontupdate=s" => \$dontupdate,
                "type=s" => \$type,
                "model_file=s" => \$model_file,
                "h|help|?" => \&usage,
                "verbose" => \$verbose,
                "odcs_file=s" => \$odcs_file,
                "class=s" => \$class
               );

     my $printer = @ARGV[-1];
     create_printer($model_file ,$type, $printer, $odcs_file, $class, $ip, $remote, $dontupdate, $start, @print_server_list);

     sub create_printer() {
            my($model_file, $type, $printer, $odcs_file, $class, $ip, $remote, $dontupdate, $start, @print_server_list) = @_;
            switch ($type) {
                     case "1" { hp_printer($printer, $model_file, $login, $password, $type, $ip, $remote, $dontupdate, $start, @print_server_list); }
                     case "2" { ithaca_printer($printer, $model_file, $login, $password, $type, $ip, $remote, $dontupdate, $start, @print_server_list); }
                     case "3" { zebra_printer($printer, $model_file, $login, $password, $type, $ip, $remote, $dontupdate, $start, @print_server_list); }
                     case "4" { optio_printer($printer, $odcs_file, $login, $password, $type, $ip, $remote, $dontupdate, $start, @print_server_list); }
                     case "5" { all_printers($printer, $model_file, $login, $password, $type, $ip, $remote, $dontupdate, $start, @print_server_list); }
                     case "6" { printer_class($printer, $class, $login, $password, $ip, $remote, $dontupdate, $start, @print_server_list); }
                           }
                          }

}

sub hp_printer() {
     my($printer, $model_file, $login, $password, $type, $ip, $remote, $dontupdate, $start, @print_server_list) = @_;
	update_ldap ($printer, $ip, $login, $password, $dontupdate);
        check_remote ($start);
                my ($out, $err) = capture { system("$lpadmin -p $printer -E -v socket://$printer -P $model_file") };
                print_error_output($err, $out);

             enable_printer($printer);
             print_error_output($err, $out, "1");

     $cmd = qq~/spool/printadm -i $ip -s r -p $password -t $type -m $model_file $printer~;
     create_remote_printer($login, $password, $cmd, $remote, @print_server_list);
}

sub ithaca_printer() {
     my($printer, $model_file, $login, $password, $type, $ip, $remote, $dontupdate, $start, @print_server_list) = @_;
	update_ldap ($printer, $ip, $login, $password, $dontupdate);
        check_remote ($start);
                my ($out, $err) = capture { system("$lpadmin -p $printer -E -v socket://$printer:9100 -m raw") };
                print_error_output($err, $out);

             enable_printer($printer);
             print_error_output($err, $out, "1");

     $cmd = qq~/spool/printadm -i $ip -s r -p $password -t $type -m netstandard $printer~;
     create_remote_printer($login, $password, $cmd, $remote, @print_server_list);
}

sub zebra_printer() {
     my($printer, $model_file, $login, $password, $type, $ip, $remote, $dontupdate, $start, @print_server_list) = @_;
	update_ldap ($printer, $ip, $login, $password, $dontupdate);
        check_remote ($start);
                my ($out, $err) = capture { system("$lpadmin -p $printer -E -v socket://$printer:9100 -m raw") };
                print_error_output($err, $out);

             enable_printer($printer);
             print_error_output($err, $out, "1");

     $cmd = qq~/spool/printadm -i $ip -s r -p $password -t $type -m netstandard $printer~;
     create_remote_printer($login, $password, $cmd, $remote, @print_server_list);
}

sub optio_printer() {
     my($printer, $odcs_file, $login, $password, $type, $ip, $remote, $dontupdate, $start, @print_server_list) = @_;
        if (!$start) {
		LdapLookup ("ldap_printer", $printer, $ip, $login, $password);
	}
        check_remote ($start);
                my ($out, $err) = capture { system("/spool/oeci/bin/eivpadm -a $printer -f $odcs_file") };
                print_error_output($err, $out);

     print_error_output($err, $out, "1");
     $cmd = qq~ksh -c \". /etc/profile; /spool/printadm -s r -p $password -t $type -o $odcs_file $printer\"~;
     create_remote_printer($login, $password, $cmd, $remote, @print_server_list);
}

sub all_printers() {
     my($printer, $model_file, $login, $password, $type, $ip, $remote, $dontupdate, $start, @print_server_list) = @_;
	update_ldap ($printer, $ip, $login, $password, $dontupdate);
        check_remote ($start);
                my ($out, $err) = capture { system("$lpadmin -p $printer -E -v socket://$printer:9100 -m raw") };
                print_error_output($err, $out);

             enable_printer($printer);
             print_error_output($err, $out, "1");

     $cmd = qq~/spool/printadm -i $ip -s r -p $password -t $type -m raw $printer~;
     create_remote_printer($login, $password, $cmd, $remote, @print_server_list);
}

#sub all_printers() {
#     print "Not implmenated yet\n";
#     exit;
#}

sub printer_class() {
        check_remote ($start);
	    my($printer, $class, $login, $password, $ip, $remote, $dontupdate, $start, @print_server_list) = @_;
	    update_ldap ($printer, $ip, $login, $password, $dontupdate, $class);
            my ($out, $err) = capture { system("$lpadmin -p $printer -c $class -o printer-is-shared=true") };
            print_error_output($err, $out, "1");

     $cmd = qq~/spool/printadm -c $class -s r -t 6 -p $password $printer~;
     create_remote_printer($login, $password, $cmd, $remote, @print_server_list);
}

sub enable_printer() {
     my($printer) = @_;
     my ($out, $err) = capture { system ("/usr/bin/enable $printer") };
	print_error_output($err, "$out.enable");
     my ($out, $err) = capture { system ("/usr/sbin/accept $printer") };
	print_error_output($err, "$out.accept");
}

sub check_remote() {
     my($start) = @_;
            if (!$start) {
                print "Updating printer information on print server ",`hostname`;
                print "Please wait.... ";
            }
}

sub update_ldap() {
     my($printer_name, $printer_ip, $user, $user_password, $dontupdate, $class) = @_;
	  if ($dontupdate) {
	      if ($dontupdate eq "y" ) {
                #Add Printer host to LDAP if not exsists
		if (!$class) {
                LdapLookup ("ldap_host", $printer_name, $printer_ip, $user, $user_password);
		}
                #Add Printer to LDAP if not exsists
		if (!$class) {
                LdapLookup ("ldap_printer", $printer_name, $printer_ip, $user, $user_password);
		}
		if ($class) {
		$printer_name = $class;
                LdapLookup ("ldap_printer", $printer_name, $printer_ip, $user, $user_password);
		}
	      }
	  }
}

sub print_error_output() {
    my($err, $out, $job_done) = @_;
       if ($err) {
           print "Failed\n";
           print "$err\n";
	   exit;
        } else {
                if ($job_done) {
                    print "Successful.\n-------------\n";
        		print "$out";
                }
        }
}

sub create_remote_printer () {
     my($login, $password, $cmd, $remote, @print_server_list) = @_;
	if (!$remote) {
             exit;
	} elsif ($remote eq "n") {
             exit; 
	}
     foreach $print_server (@print_server_list) {
	 print "Updating printer information on print server $print_server\n";
	 print "Please wait... ";
         @KEYFILE = ("/root/.ssh/id_rsa");
         my $ssh = Net::SSH::Perl->new($print_server, identity_files=>\@KEYFILE);
         $ssh->login(root, $password);
         #my $ssh = Net::SSH::Perl->new($print_server);
        # $ssh->login(root, $password);

         my($stdout, $stderr, $exit) = $ssh->cmd($cmd);

             if (!$stderr) {
                 #print "Successful.\n";
             } else {
                 print "Failed\n";
                 print "\n$stderr";
		 exit;
             }
             if ($stdout) {
                 print "$stdout";
             }
     }
exit;
}

# LDAP related subroutines
sub LdapLookup {
    my($ldap_host, $printer_name, $printer_ip, $user, $user_password) = @_;
    if ( $_[0] =~ "ldap_host" ) {
        $base_string = q~ou=hosts,o=domain.com,dc=subdomain,dc=com~;
        $filter_string = qq~cn=$printer_name~;
	@attributes     = qw( dn ipHostNumber cn );
    }

	print "printer_name - $printer_name\n";
    if ( $_[0] =~ "ldap_printer" ) {
        $base_string = q~ou=printers,o=domain.com,dc=subdomain,dc=com~; ;
        $filter_string = qq~printer-uri=$printer_name~;
	@attributes     = qw( dn printer-name );
    }

	$admindn = "uid=password_reset,o=domain.com,dc=subdomain,dc=com";
	$adminpw = "passwd";

        $ldapserverone = "ldap1.domain.com";
        $ldapservertwo = "ldap2.domain.com";

    unless ( $ldap =
            Net::LDAP->new($ldapserverone,
                            port=>389,timeout=>5) )
           {
            $ldap = Net::LDAP->new($ldapservertwo,
                                     port=>389,timeout=>20) or
            die "Can't connect to $ldapserverone or $ldapservertwo via LDAP: $@";
           }

        $mesg = $ldap->bind(dn => $admindn,password => $adminpw, version=>3);

        $mesg = $ldap->search( # perform a search
                               base   => "$base_string",
                               filter => "$filter_string",
                               attrs  => [ @attributes ]
                             );
	$mesg->code && die $mesg->error;

    @entries = $mesg->entries;
    foreach $entry (@entries) {
	$dn = $entry->dn;
	$cn = $entry->get_value("cn");
	$ipHostNumber = $entry->get_value("ipHostNumber");
	$printername = $entry->get_value('printer-name');
    }

		#Define undifned varbles
        	$dn = "Not_Found" unless ($dn);
        	$printername = "Not Defined" unless ($printername);
        	$ipHostNumber = "Not Defined" unless ($ipHostNumber);
        	$cn = "Not Defined" unless ($cn);

      if ( $_[0] =~ "ldap_host" ) {
	  if (("$cn" =~ "$printer_name") && ("$ipHostNumber" =~ "$printer_ip")) {
	           print "LDAP host entry exsist, continuing...\n","-------------\n";
	       } elsif (("$cn" =~ "$printer_name") && ! ("$ipHostNumber" =~ "$printer_ip")) {
	           print "LDAP Printer IP entry doesn't match existing printer IP\n","----------------\n","Removing old printer/ip information...\n";
		   $mesg = $ldap->delete($dn);
	           print "Adding new LDAP host ip information...\n","----------------\n";
		   #Update LDAP with new Host
		   LdapHostUpdate ($printer_name, $printer_ip);
               } else {
		   print "Adding LDAP Host/IP entry\n","-----------------\n";
		   #Update LDAP with new Host
		   LdapHostUpdate ($printer_name, $printer_ip);
               }
      }

      if ( $_[0] =~ "ldap_printer" ) {
            if ( "$printername" =~ "$printer_name" ) {
  	         print "LDAP printer entry exsist, continuing...\n","-------------\n";
               } else {
	           print "Adding LDAP printer entry\n","-------------\n";
		   #Update LDAP with new Printer
		   LdapPrinterUpdate ($printer_name);
               }
      }

	if ( $mesg->code ) {
   	     $errstr = $mesg->code;
	     print "An error has ocuerd the error code is $errstr\n";
        } else {
	}

        $mesg = $ldap->unbind;   # take down session
}

sub LdapPrinterUpdate {

        my ($printer_name) = @_;
        $mesg = $ldap->add("printer-uri=$printer_name,ou=printers,o=domain.com,dc=subdomain,dc=com",
           attr => [ 'printer-uri' => $printer_name,
                     'sun-printer-kvp' => "printer-uri-supported=lpd\\://print.domain.com/printers/$printer_name#Solaris",
                     'printer-name' => $printer_name,
                     'sun-printer-bsdaddr' => "print,$printer_name,Solaris",
                     'objectclass' => [ 'top', 'printerService', 'printerAbstract', 'sunPrinter' ]
                   ]
           );

        if ( $mesg->code ) {
             $errstr = $mesg->code;
             print "An error has ocuerd the error code is $errstr\n";
        } else {
        }
}

sub LdapHostUpdate {

        my ($printer_name, $printer_ip) = @_;
	$mesg = $ldap->add("cn=$printer_name\+ipHostNumber=$printer_ip,ou=Hosts,o=domain.com,dc=subdomain,dc=com",
                attr => [ 'cn' => $printer_name,
                          'ipHostNumber' => $printer_ip,
                          'objectclass' => [ 'top', 'ipHost', 'device' ]
                        ]
           );

        $mesg->code;

        if ( $mesg->code ) {
             $errstr = $mesg->code;
             print "An error has ocuerd the error code is $errstr\n";
        } else {
        }
}

sub usage()
{
        print STDERR << "EOF";

Please select a vaild option...

usage: $0 [-v] -hilupmrdtv printer_name
        $0 [-v] -hlupc printer_name

     -h               : This (help) message
     -v               : verbose output
     -i printer ip    : printers ip address
     -l login         : LDAP login credentials to use
     -u password      : User's LDAP password to use
     -p password      : Remote root SSH password to use
     -r               : Update remote server information
     -d y|n           : Update LDAP host & printer information
     -t 1-5           : Printer type 
           		- Avalbile printer types are -
			1) HP (All HP)
			2) Ithaca
			3) Zebra (All zebra S600/105SL/S4M)
			4) Optio (ODCS virtual printers) Warning: it will restart lp!!!
			5) Other (All other printer models)
			6) Create or Add a printer to a Class
     -m printer model : Printer model PPD file location or Optio dcl file location

     -c printer class : Printer class name

example: $0 -i 10.10.10.10 -p 'pAssWor\$' -r -d y -t 1 -m /var/lp/ppd/user/HP/HP-LaserJet_4350-hpijs.ppd.gz is1
example: $0 -p 'pAssWor\$' -r -t 4 -m /spool/oeci/eci77/doc/pos.dcl ship.dcl
example: $0 -c is1_C -t 6 -r -p 'pAssWor\$' is1
EOF
    exit;
};
