#!/usr/bin/perl

#
# $Revision: 1.17 $
# Copyright Teleflora 2010
#

use strict;
use warnings;
use Getopt::Long;
use IO::Socket;


my $HELP = 0;
my $VERSION = 0;
my $LIST = 0;
my $SAMBA = 0;
my $JETDIRECT = 0;
my $LPD = 0;
my $ADD = ""; 
my $DELETE = ""; 
my $CLEAR = ""; 
my $USER = ""; 
my $PASSWORD = ""; 
my $SHARE = ""; 
my $WKGRP = ""; 
my $FASTPRINT = ""; 


GetOptions(
"help" => \$HELP,
"version" => \$VERSION,
"list" => \$LIST,
"samba" => \$SAMBA,
"jetdirect" => \$JETDIRECT,
"lpd" => \$LPD,
"add=s" => \$ADD,
"delete=s" => \$DELETE,
"clear=s" => \$CLEAR,
"user=s" => \$USER,
"password=s" => \$PASSWORD,
"fastprint=s" => \$FASTPRINT,
"share=s" => \$SHARE,
"workgroup=s" => \$WKGRP,
);


# --version
if($VERSION != 0) {
	print('$Revision: 1.17 $' . "\n");
	exit(0);
}

# --help
if($HELP != 0) {
	usage();
	exit(0);
}

# --list
if($LIST != 0) {
	my $printer = "";
	my %printers = ();
	%printers = list_printers();

	foreach $printer (sort(keys(%printers))) {
		printf("%-20s\t(%03d Jobs)\n", $printer, $printers{$printer});
	}

	exit(0);
}

# --add spoolname:printer.ip.address
if("$ADD" ne "") {
	exit(add_printer($ADD));
}

# --delete spoolname
if("$DELETE" ne "") {
	exit(delete_printer($DELETE));
}

# --delete spoolname
if("$CLEAR" ne "") {
	exit(clear_printqueues($CLEAR));
}


# --fastprint=p1name
# --fastprint=p2name,p2name,p3name,...
if("$FASTPRINT" ne "") {
	exit(print_to_fastest_printer($FASTPRINT));
}

usage();
exit(0);

#################################################################
#################################################################
#################################################################

sub usage
{
	print(<< "EOF");
Usage:
$0 --help
$0 --version
$0 --list
sudo $0 --add spoolname:/dev/lp0
sudo $0 --add spoolname:/dev/ttyS0
sudo $0 --add spoolname:printer.ip.address
sudo $0 --jetdirect --samba --lpd --add spoolname:printer.ip.address
sudo $0 --user=username --password=pw --share=sharename --workgroup=smbwkgrp --add spoolname:printer.ip.address
sudo $0 --delete spoolname
sudo $0 --clear spoolname
echo "Stuff to print" | $0 --fastprint spoolname
echo "Stuff to print" | $0 --fastprint spoolname,spool2name,spool3name,...

EOF
}



sub add_printer
{
	my $fullname =  $_[0];
	my $port = "";
	my $spoolname = "";
	my $ipaddr = "";
	my $protocol = "";
	my $thiskey = "";
	my $remote;
	my $found = 0;
	my %services = ();


	if(! $fullname) {
		return(-1);
	}
	if("$fullname" eq "") {
		return(-2);
	}
	if("$fullname" !~ /:/) {
		print("Error: Invalid spoolname:printer.ip.address specified.\n");
		return(-3);
	}


	# spoolname:printer.ip.address -> spoolname, ipaddr
	($spoolname, $ipaddr) = split(/:/, $fullname);
	if(! defined $spoolname) {
	}
	if(! defined $ipaddr) {
	}

	# Don't add a spool which already exists.
	if(does_spool_exist($spoolname) != 0) {
		print("Error: Spool \"$spoolname\" Already Exists.\n");
		return(3);
	}



	# Parallel Prionter.
	if("$ipaddr" =~ /\/dev\/lp/) {
		print("Creating \"$spoolname\" to \"parallel://$ipaddr\"\n");
		system("lpadmin -p \"$spoolname\" -D \"Teleflora Parallel Printer\" -E -v \"parallel://$ipaddr\" -m raw");

	# Serial Printer
	# http://www.cups.org/doc-1.1/sam.html
	} elsif ("$ipaddr" =~ /\/dev\/tty[^U]/) {
		print("Creating \"$spoolname\" to \"serial://$ipaddr\"\n");
		system("lpadmin -p \"$spoolname\" -D \"Serial Printer\" -E -v \"serial://$ipaddr\" -m raw");

	# Local USB Printer.
	} elsif ("$ipaddr" =~ /\/dev\/ttyUSB/) {
		print("Creating \"$spoolname\" to \"usb://$ipaddr\"\n");
		system("lpadmin -p \"$spoolname\" -D \"Teleflora USB Printer\" -E -v \"usb://$ipaddr\" -m raw");

	# Network Printer
	} else {



		# Magically guess which connection type to use.
		if(($SAMBA == 0) && ($JETDIRECT == 0) && ($LPD == 0)) {
			$services{"01 socket"} = 9100;
			$services{"02 smb"} = 445;
			$services{"03 lpd"} = 515;
			$services{"04 ipp"} = 631;
			$services{"05 http"} = 80;

		# We are "forcing" to search for a particular printer type.
		# ie: "--smb" , "--samba", "--jetdirect"
		} else {
			if($SAMBA != 0) {
				$services{"02 smb"} = 445;
			}
			if($JETDIRECT != 0) {
				$services{"01 socket"} = 9100;
			}
			if($LPD != 0) {
				$services{"03 lpd"} = 515;
			}
		}
		foreach $thiskey (sort keys(%services)) {

			# "01 keyname" -> "keyname"
			$protocol = $thiskey;
			$protocol =~ s/^\d\d\s//g;

			
			# Can we find the printer at this protocol?
			$remote = IO::Socket::INET->new(
				Proto => "tcp",
				PeerAddr => "$ipaddr",
				PeerPort => "$services{$thiskey}",
				Timeout => 1
			);
			next unless($remote);
			close($remote);

			print("Creating \"$spoolname\" to \"$protocol://$ipaddr\"\n");


			# lpadmin ... -v smb://ipaddress/SHARE -m raw
			# lpadmin ... -v smb://USER@WKGRP/ipaddress/SHARE -m raw
			# lpadmin ... -v smb://USER:PASSWORD@WKGRP/ipaddress/SHARE -m raw
			if("$protocol" eq "smb") {
				my $command = "";
				
				$command = "/usr/sbin/lpadmin -p \"$spoolname\" -D \"Teleflora Samba Printer\" -E -v ";
				$command .= "smb://";

				if("$USER" ne "") {
					$command .= "$USER";
					if("$PASSWORD" ne "") {
						$command .= ":$PASSWORD";
					}
					if("$WKGRP" ne "") {
						$command .= "\@$WKGRP";
						$command .= "/";
					}
				}
				$command .= "$ipaddr";
				if("$SHARE" ne "") {
					$command .= "/$SHARE";
				}
				$command .= " -m raw";
				system("$command");
			} else {
				system("/usr/sbin/lpadmin -p \"$spoolname\" -D \"Teleflora Network Printer\" -E -v \"$protocol://$ipaddr\" -m raw");
			}

			$found = 1;
			last;
		}

		# If we never created the printer, then indicate so.
		if($found == 0) {
			print("Host \"$ipaddr\" does not appear to be a printer.\n");
			return(1);
		}
	}

	return(0);
}


sub delete_printer
{
	my $spoolname =  $_[0];

	if(does_spool_exist($spoolname) == 0) {
		print("Printer \"$spoolname\" Does not exist.\n");
		return(0);
	}

	system("/usr/sbin/lpadmin -x \"$spoolname\"");
	if(does_spool_exist($spoolname) == 0) {
		print("Printer \"$spoolname\" Removed Successfully.\n");
		return(0);
	} else {
		print("Error Removing Printer \"$spoolname\".\n");
		return(1);
	}

	
	return(2);
}


# Put together a list of printers and their queue sizes.
sub list_printers
{
	my @array = ();
	my $count = 0;
	my $printer = "";
	my %results = ();

	open(PRINTERS, "lpstat -a 2>&1 |");
	while(<PRINTERS>) {
		next if (/No destinations added/);
		@array = split(/\s+/);
		$printer = $array[0];

		$count = 0;
		open(JOBS, "lpq -P $printer |");
		while(<JOBS>) {
			next if (/^Rank/);
			$count++;
		}
		close(JOBS);
		$results{$printer} = $count;
	}
	close(PRINTERS);


	return(%results);
}


# Given a list of "candidate" printers, print the contents of STDIN to 
# thta printer who's queue is smallest.
# This allows us to sort of "load balance" printers.
sub print_to_fastest_printer
{
	my $destprinters = $_[0];
	my @candidates = ();
	my %printers = ();
	my $printer = "";
	my $keyval = "";
	my $queuesize = 0;


	# We were given just one printer.
	if($destprinters !~ /,/) {
		$printer = $destprinters;

	# We were given more than one printer to send to in the form of a comma separated list.
	} else {


		# "pr1,pr2,pr3" -> @candidates
		@candidates = split(/,/, $destprinters);
		%printers = list_printers();

		$queuesize = 1000000000; # Infinite.
		$printer = $candidates[0];
		foreach $keyval (keys(%printers)) {
			next until grep(/^$keyval$/, @candidates);

			if( $printers{$keyval} <= $queuesize) {
				$printer = $keyval;
				$queuesize = $printers{$keyval};
			}
		}
	}


	# Read from stdin, write to an 'lp' job for our printer.
	open(PIPE, "| lp -d$printer -s 2> /dev/null");
	while(<STDIN>) {
		print(PIPE);
	}
	close(PIPE);

	return(0);
}



sub clear_printqueues
{
	my $spoolname =  $_[0];

	if(does_spool_exist($spoolname) == 0) {
		print("Printer \"$spoolname\" Does not exist.\n");
		return(0);
	}

	system("cancel -a $spoolname");
	print("Queue Cleared for printer \"$spoolname\"");
	return(0);
}


sub does_spool_exist
{
	my $spoolname = $_[0];
	my @array = ();
	my $found = 0;

	open(PRINTERS, "lpstat -a 2>&1 |");
	while(<PRINTERS>) {
		next if (/No destinations added/);
		@array = split(/\s+/);
		if("$spoolname" eq $array[0]) {
			$found = 1;
			last;
		}
	}
	close(PRINTERS);
	
	return($found);
}

sub pinghost
{
	my $hostname = $_[0];

	my $p = Net::Ping->new("icmp", 2);
	$p->ping($hostname);

}
