#! /usr/bin/perl
use strict;
use Cwd;
use POSIX qw(tmpnam); # ... needed (this may not work on Windows).
use LWP; # For the http request and package downloading...
use File::Copy qw(copy);
use File::Basename qw(dirname basename);
use File::Path qw(rmtree mkpath);
use FileHandle; # Use saner OO style instead of IO style for print function.
use Getopt::Long 2.01;
use Fcntl;
use FindBin;
use lib "$FindBin::RealBin/../lib/perl5";
use lib "/tmp/c";
use RDB qw(RDB_parse_array RDB_parse_string RDB_splice_data);
use INF qw(read_inf inf_get_key inf_get_keys inf_has_key);
use inf_nt qw(parse_inf_nt);
use imp_gpg;
########################################################################
# #### Start of file scoped variables.
# Where to install packages to.
my $base_dir = undef; # look in cd_temp()
# Where to request from.
# my $base_url = "
http://code.and.org/cgi-bin/imp_retrieval.cgi";
# my $real_base_url = "
http://imprints.sourceforge.net/cgi-bin/imp_retrieval.cgi";
# my $real_base_url = "
http://imprints.samba.org/imprints/imp_retrieval.cgi";
my $real_base_url = "
http://imprints.samba.org/cgi-bin/imp_retrieval.cgi";
my $base_url = undef;
# Paths to external programs we use.
my $path_self = "$FindBin::RealBin/" . basename($0);
my $path_gzip = "gzip";
my $path_tar = "tar";
## commented out as the rpc_client_wrapper.pl script has been
## included in this file as a subroutine --jerry
# my $path_rpc_client_wrapper = "$FindBin::RealBin/../scripts/rpc_client_wrapper.pl";
# my $path_rpc_query_wrapper = "$FindBin::RealBin/../scripts/rpc_query_wrapper.pl";
my $path_rpcclient = "rpcclient";
my $path_smbclient = "smbclient";
# Extra options...
my $option_help = 0; # Do we show help.
my $option_version = 0; # Do we show the version.
my $option_cleanup = 1; # Do we cleanup the temp dir.
my $option_pubkey_install = -1; # -1 = default, 1 = yes, 0 = no
my $option_tmpdir = undef; # Change $ENV{'TMPDIR'}, tmpname() doesn't use it.
my $option_query = 0; # Just do a query for valid printer names.
my $option_rpcquery = 0; # Just do a query for samba stuff.
my $option_verbose = 0; # Show verbose messages during progress.
my $option_authfile = undef; # File for auth input
my $option_language = undef; # Language we want to printer drivers in.
my $option_cache_dir = undef; # Place to cache packages
my $option_pkg_name = undef; # local cached package
my $option_servers_file = undef; # File with a list of servers/languages.
my $DEBUG = 0;
# Parameters sent to the request server.
my @params = ();
my @download_params = ("action=get-printer-info");
my @query_params = ("action=list-printers");
my $tmp_dir = "";
# Error code variables...
my $error_code_default = 1;
my $error_code_request = 2;
my $error_code_query_6 = 3;
# #### End of file scoped variables.
########################################################################
########################################################################
# CD to a known safe directory.
# Params: none
# Returns: directory name.
sub cd_temp ()
{
my $tmp = undef;
# Cd to a known tmp dir...
do {
# Perl doesn't have mktemp() or tempnam().
$tmp = tmpnam();
} until (mkdir($tmp, 0700));
if (!chdir($tmp)) {
fatal_error ("Error: Cannot chdir($tmp): $!", 0);
}
$base_dir = "$tmp/install";
return ($tmp)
}
########################################################################
# Params:
# $base_url = The url from which the request should be made.
# $params = An array of parameters to put on the end of the url.
# $ua = A UserAgent object from LWP.
sub send_request
{
my ($base_url, $params, $ua) = @_;
my $req = HTTP::Request->new('GET' => $base_url . "?" . (join "&", @{$params}));
my $res = $ua->request($req);
if (!$res->is_success) {
fatal_error ("Error: REQ " . $res->status_line, 1, $error_code_request);
}
return ($res->content);
}
########################################################################
# Params:
# $tbl = A table object from RDB.
# Side Effect: Prints table or names and descriptions.
sub output_query_results
{
my ($tbl) = @_;
for (my $i=0; $i <= $#{$tbl->{data}->{"printer_name"}}; $i++) {
my $name = $tbl->{data}->{"printer_name"}[$i];
print("[printer_name]: $name\n");
}
}
########################################################################
# Params:
# $ua = A UserAgent object from LWP.
# $file_name = A Filename to download to.
# $req = A HTTP::Request object.
# Returns: Return value of $ua->request();
sub request_with_feedback
{
my ($ua, $file_name, $req) = @_;
my $expected_length = undef;
my $bytes_received = 0;
my $res = undef;
if (!open(PKG, "> $file_name")) {
return (undef);
}
my $last_timestamp = 0;
$res = $ua->request($req,
sub
{
my ($chunk, $res) = @_;
$bytes_received += length($chunk);
if (!defined ($expected_length)) {
$expected_length = $res->content_length || 0;
}
if ((time - $last_timestamp) > 2) {
$last_timestamp = time;
if ($expected_length) {
my $stats = sprintf ("%d - %d%%\n", $bytes_received,
100 * $bytes_received / $expected_length);
STDOUT->print("Download Status: " . $stats);
}
else {
my $stats = sprintf ("%d\n", $bytes_received);
STDOUT->print("Download Status: " . $stats);
}
}
PKG->print($chunk);
}
);
if (!close(PKG)) {
return (undef);
}
return ($res);
}
########################################################################
# Params:
# $ua = A UserAgent object from LWP.
# $tbl = A table object from RDB.
# Returns: Name of the new package.
sub download_package
{
my ($ua, $tbl) = @_;
my $res = undef;
my $loc = undef;
my $here_before = 0;
if (!scalar(@{$tbl->{data}->{"location_url"}})) {
fatal_error ("Error: No download locations.", 1);
}
while (scalar(@{$tbl->{data}->{"location_url"}})) {
if ($here_before) {
warn "Warn: PKG " . $loc . ": " . $res->status_line . "\n";
}
$here_before = 1;
my $pkg_name = $tbl->{data}->{"package_name"}[0];
# Take out non good chars.
$pkg_name =~ s/([^-a-zA-Z0-9 :._])/X/g;
if (defined ($option_cache_dir) &&
(-r "$option_cache_dir/$pkg_name") &&
(link("$option_cache_dir/$pkg_name", "$pkg_name") ||
copy("$option_cache_dir/$pkg_name", "$pkg_name")))
{
# It's assumed you have to GPG key
$option_pubkey_install = 0;
return ($pkg_name);
}
$loc = $tbl->{data}->{"location_url"}[0];
my $req = HTTP::Request->new('GET' => $loc);
if ($option_verbose) {
$res = request_with_feedback($ua, $pkg_name, $req);
}
else {
$res = $ua->request($req, $pkg_name);
}
if ($res->is_success) {
if (!$option_pubkey_install) {
return ($pkg_name);
}
$loc =~ s!/([^/]+)$!/!;
$loc .= "public_key";
$req = HTTP::Request->new('GET' => $loc);
$res = $ua->request($req, "./public_keys");
if (($option_pubkey_install != 1) || $res->is_success) {
return ($pkg_name);
}
unlink("$pkg_name");
}
RDB_splice_data($tbl, 0, 1);
}
fatal_error ("Error: PKG " . $loc . ": " . $res->status_line, 1);
}
########################################################################
# Params:
# $file_name = The filename of the package that we want to verify.
# $version = The GPG version ID.
# $sig = The GPG signature for the package.
# $crc = The GPG CRC for the signature.
sub verify_package
{
my ($file_name, $version, $sig, $crc) = @_;
my $installed_pubkey = 0;
my $rc = -1;
if ($option_pubkey_install == 1) {
$rc = impgpg_install_pubkey ("./public_keys");
if ($rc != 0) {
fatal_error ("Error: Installing public key. gpg ended "
. "with an exit code of $rc.", 1);
}
}
$rc = impgpg_print_sigfile ("signature", $version, $sig, $crc);
if ($rc != 0) {
fatal_error ("Error: Unable to write signature file.", 1);
}
$rc = impgpg_verify_signature_using_sigfile ($file_name, "signature");
if ($rc != 0) {
my $verify_rc = $rc;
if (-f "./signature") {
if (!$option_pubkey_install || ! -r "./public_keys") {
fatal_error ("Error: Verifying signature for file '$file_name'. "
. "gpg ended with an exit code of $rc.", 1);
return;
}
warn "Warn: Trying to install a newer public key.\n";
$rc = impgpg_install_pubkey ("./public_keys");
if ($rc == 0) {
$rc = impgpg_verify_signature_using_sigfile($file_name, "signature");
if ($rc == 0) {
goto make_cached_copy;
}
}
else {
$rc = $verify_rc;
}
}
fatal_error ("Error: Verifying signature for file '$file_name'. "
. "gpg ended with an exit code of $rc.", 1);
}
make_cached_copy:
if (defined ($option_cache_dir) &&
(! -r "$option_cache_dir/$file_name"))
{
if (!link("$file_name", "$option_cache_dir/$file_name")) {
copy("$file_name", "$option_cache_dir/$file_name");
}
}
}
########################################################################
# Params:
# $file_name = The filename of the package that we want to explode.
sub explode_package
{
my ($file_name) = @_;
if (system("$path_gzip -dc $file_name | $path_tar -xf -")) {
fatal_error ("Error: Couldn't explode package.\n", 1);
}
}
########################################################################
# Finds a case insensitive match to a filename.
# Params:
# $file_name = The filename that we want to find.
# $dir = The path that we find from.
sub get_real_filename
{
my ($file_name, $dir) = @_;
my @files = ();
if (!$file_name || !opendir(DIR, $dir)) {
return (undef);
}
@files = readdir(DIR);
closedir(DIR);
@files = grep (/^$file_name$/i, @files);
if (scalar(@files)) {
return ("$dir/$files[0]");
}
return (undef);
}
######################################################################
# read_authfile
#
## Need to return an array of values
sub read_authfile
{
my ($rpc_authfile, $printer_model_name) = @_;
my @lines = ();
my $rpc_printer_name = "";
my $rpc_share_samba_name = "";
my $rpc_port_name = "";
my $servername = "";
my @return_params = undef;
## read in the authorization file data
if (! open (IN, "< $rpc_authfile")) {
fatal_error("Error: Couldn't open Authorization file.\n");
}
@lines = <IN>;
if (!close (IN)) {
fatal_error("Error: Couldn't close Authorization file.\n");
}
chomp (@lines);
## we only need to handle the printer name, share name, port name,
## and server name. The username/password lines will be read directly
## by smbclient and rpcclient
for (@lines) {
if (/^\s*folder\s+share\s+name\s*=(.+)$/) {
$rpc_printer_name = $1;
}
if (/^\s*samba\s+share\s+name\s*=(.+)$/) {
$rpc_share_samba_name = $1;
}
if (/^\s*printer\s+port\s+name\s*=(.+)$/) {
$rpc_port_name = $1;
}
if (/^\s*server\s*=(.+)$/) {
$servername = $1;
}
}
if ($rpc_printer_name eq "<auto>") {
$rpc_printer_name = $printer_model_name;
}
@return_params = ($rpc_printer_name, $rpc_share_samba_name, $rpc_port_name, $servername);
}
######################################################################
# validate_credentials
#
## return a boolean depending on whether or not we could
## logon to the server
sub validate_credentials
{
my ($server, $authfile) = @_;
my $cmd = undef;
my @cmd_output = undef;
my $auth_failed = 0;
my $bad_hostname = 0;
my $session_request_failed = 0;
my $valid_credentials = 0;
$cmd = "$path_smbclient -L $server -A $authfile";
if ($DEBUG) { print STDERR "$cmd\n"; }
if (!open ( SMBCLIENT, "$cmd|")) {
warn "[rpc]: Unable to open smbclient session!\n";
return 0;
}
@cmd_output = <SMBCLIENT>;
close (SMBCLIENT);
for (@cmd_output) {
if ($DEBUG) { print STDERR "$_"; }
if ($_ =~ /session request.*failed/) {
print "[rpc]: $_";
$session_request_failed = 1;
}
if ($_ =~ /ERRbadpw/) {
$auth_failed = 1;
print "[rpc]: Bad username/password for host $server!.\n";
last;
}
if ($_ =~ /Connection to.*failed/) {
$bad_hostname = 1;
last;
}
if ($_ =~ /IPC\$.*IPC Service/) {
$valid_credentials = 1;
last;
}
}
## reset any failed session requests if we finally got it right
if ($valid_credentials) {
$session_request_failed = 0;
}
if ($session_request_failed) {
print "\n";
print "[rpc]: *****************************************************************\n";
print "[rpc]: A failed session request is usually indicative of\n";
print "[rpc]: some type of netbios name resolution problem. This can\n";
print "[rpc]: also be caused by hosts allow/deny lines in the Samba\n";
print "[rpc]: server's smb.conf(5) file.\n";
print "[rpc]: *****************************************************************\n\n";
}
## if it was a bad hostname, let's try again, but this time supplying
## the IP address as well if we can get it via a gethostbyname() call
if ($bad_hostname) {
my $name = undef;
my $aliases = undef;
my $addrtype = undef;
my $length = undef;
my @addrs = undef;
my ($a, $b, $c, $d);
print "Attempting to resolve $server via gethosybyname()...";
($name, $aliases, $addrtype, $length, @addrs) = gethostbyname($server);
if (defined($name)) {
for (@addrs) {
$bad_hostname = 0;
($a, $b, $c, $d) = unpack('C4', $_);
print "$a.$b.$c.$d...";
$cmd = "$path_smbclient -L $server -A $authfile -I $a.$b.$c.$d";
if ($DEBUG) { print STDERR "$cmd\n"; }
if (!open ( SMBCLIENT, "$cmd|")) {
warn "[rpc]: Unable to open smbclient session!\n";
return "";
}
@cmd_output = <SMBCLIENT>;
close (SMBCLIENT);
for (@cmd_output) {
if ($DEBUG) { print STDERR "$_"; }
if ($_ =~ /ERRbadpw/) {
$auth_failed = 1;
print "[rpc]: Bad username/password for host $server!.\n";
last;
}
if ($_ =~ /Connection to.*failed/) {
$bad_hostname = 1;
last;
}
}
}
}
print "\n";
if ($bad_hostname) {
print "[rpc]: Invalid Print Server name - $server!\n";
}
}
if ($auth_failed || $bad_hostname || $session_request_failed) {
return 0;
}
return 1;
}
######################################################################
# setup_credentials
#
sub setup_credentials
{
my ($tmpfile) = @_;
my $servername = undef;
my $username = undef;
my $password = undef;
if (!open(TMP, "> $tmpfile")) {
STDERR->print ("Unable to open $tmpfile for writing!\n");
return "";
}
##
## get input from user and create the authfile
##
print "Server: ";
$servername = <STDIN>;
chomp ($servername);
print "Username: ";
$username = <STDIN>;
chomp ($username);
print "Password: ";
$password = <STDIN>;
chomp ($password);
## save crendentials and close authfile since
## we will need to pass it to the validate_credentials() function
TMP->print("server=$servername\n");
TMP->print("username=$username\n");
TMP->print("password=$password\n");
close (TMP);
return $servername;
}
######################################################################
# build_authfile
#
## Need to return a filename
sub build_authfile
{
my $tmp_file_name = undef;
my $rpc_printer_name = undef;
my $rpc_share_samba_name = undef;
my $rpc_port_name = undef;
my $servername = undef;
my $username = undef;
my $password = undef;
my $cmd = undef;
my @rpc_output = ();
my ($tmp_str, $string) = undef;
my @port_list = ();
my $port_ok = undef;
## generate a temp file name which can be used as the authfile
## and write out the data
do {
$tmp_file_name = tmpnam();
} while (!sysopen(TMP, $tmp_file_name, O_RDWR|O_CREAT|O_EXCL, 0600));
## get the username, password and server tuple from user
$servername = setup_credentials($tmp_file_name);
if ("$servername" eq "") {
return "";
}
## since we have the servername, username and password,
print "Testing logon credentials...\n";
if (!validate_credentials($servername, $tmp_file_name)) {
## never leave a password lying around (even a bad one)
unlink ($tmp_file_name);
return "";
}
## get the remaining data since the servername/username/password
## tuple has been validated.
print "Printer name (press <ENTER> to default to the printer model name): ";
$rpc_printer_name = <STDIN>;
chomp($rpc_printer_name);
$rpc_printer_name =~ s/\s+//g;
if ( length ($rpc_printer_name) == 0 ) {
$rpc_printer_name = "<auto>";
}
## We can query the remote server also to get the valid port
## names and printer share names
print "Retrieving remote server information...\n";
@rpc_output = rpc_query_wrapper($tmp_file_name, $servername);
print "\tValid Printer Share Names:\n";
for (@rpc_output) {
if ($_ =~ /\[samba_share\]/) {
($tmp_str, $string) = split(/:/, $_);
$string =~ s/^\s+//;
print "\t\t$string\n";
}
}
print "\n";
print "\tValid Port Names:\n";
for (@rpc_output) {
if ($_ =~ /\[printer_port\]/) {
($tmp_str, $string) = split(/:/, $_);
$string =~ s/^\s+//;
chomp ($string);
print "\t\t$string\n";
push (@port_list, $string);
}
}
print "\n";
print "Samba share name: ";
$rpc_share_samba_name = <STDIN>;
chomp($rpc_share_samba_name);
## loop to verify the correctness of the portname
## against the list returned by the server
do {
$port_ok = 0;
print "Printer port: ";
$rpc_port_name = <STDIN>;
chomp($rpc_port_name);
for (@port_list) {
if ("$_" eq "$rpc_port_name") {
$port_ok = 1;
last;
}
}
if (!$port_ok) {
print "Invalid port name!\n";
}
} while (!$port_ok);
if (!open(TMP, ">> $tmp_file_name")) {
print "Unable to reopen the authentication file\n";
return "failed";
}
TMP->print("folder share name=$rpc_printer_name\n");
TMP->print("samba share name=$rpc_share_samba_name\n");
TMP->print("printer port name=$rpc_port_name\n");
close(TMP);
## return the filename
return $tmp_file_name;
}
######################################################################
# rpc_query_wrapper
#
sub rpc_query_wrapper
{
my ($rpc_authfile, $server) = @_;
my $cmd = undef;
my @info = ();
# get the shared printers
$cmd = "$path_smbclient -L $server -A $rpc_authfile";
if (!open (RPC_IN, "$cmd |")) {
fatal_error("Error: Couldn't run $cmd (get enum ports).\n");
}
my $state = 0;
while (<RPC_IN>) {
if (!$state && /^\s*Sharename\s+Type\s+Comment\s*$/) {
$state = 1;
}
last if ($state && /^\s*Server\s+Comment\s*$/);
if ($state && /^\s*(\S+)\s+Printer\s+/) {
push( @info, "[samba_share]: $1");
}
}
close (RPC_IN);
## get the ports
$cmd = "$path_rpcclient $server -d 1 -A $rpc_authfile -c \"enumports 1\"";
if (!open (RPC_IN, "$cmd |")) {
fatal_error("Error: Couldn't run $cmd (get enum ports).\n");
}
while (<RPC_IN>) {
if (/^\s*Port Name:\s*\[([^\]]+)\]\s*$/) {
push (@info, "[printer_port]: $1");
}
}
close (RPC_IN);
@info;
}
######################################################################
# rpc_client_wrapper
#
sub rpc_client_wrapper
{
my @args = @_;
my $cmd_file_name = undef; # We _will_ have to create this
my $cmd = undef; # Throw into
my $cmd_string = undef;
my $cmd_rpc = undef;
my $rpc_output = undef;
my $drv_file = undef;
my $authfile = undef;
my $ret = 1;
## save the current working directory
my $save_cwd = &cwd;
# Stuff that is always passed in as args...
my $printer_model_name = undef;
my $driver_file_name = undef;
my $data_file_name = undef;
my $config_file_name = undef;
my $help_file_name = undef;
my $language_monitor = undef; # unused
my $rpc_printer_name = undef;
my $rpc_share_samba_name = undef;
my $rpc_port_name = undef;
my $servername = undef;
my ($error, $error_string) = undef;
# Values got from rpcclient/smbclient
my $printer_upload_dir = undef; # Dir for first arg to smbclient
my $printer_upload_last = undef; # Dir for in last arg to smbclient
my %arch_map = ("W32X86" => "Windows NT x86",
"WIN40" => "Windows 4.0",
"W32MIPS" => "Windows NT R4000",
"W32ALPHA" => "Windows NT Alpha_AXP",
"W32PPC" => "Windows NT PowerPC");
my $arch = undef; # Right side of above mapping...
## first thing is to grab the authfile name
$authfile = shift (@args);
## make sure this is a valid architecture for printer drivers
if (exists($arch_map{$args[0]})) {
$arch = $arch_map{$args[0]};
shift(@args);
}
else {
fatal_error("Error: Not a supported arch for rpcclient.\n");
}
# grab the remaining fields for a DRIVER_INFO_3 struct
$printer_model_name = shift (@args);
$driver_file_name = shift(@args);
$data_file_name = shift(@args);
$config_file_name = shift(@args);
$help_file_name = shift(@args);
$language_monitor = shift(@args); # unused
## previous generate authfile code was here
( $rpc_printer_name,
$rpc_share_samba_name,
$rpc_port_name,
$servername) = read_authfile($authfile, $printer_model_name);
if (!chdir (dirname($args[0]))) {
fatal_error("ERROR! Couldn't chdir()\n");
}
for (@args) {
$_ = basename($_);
}
print "[rpc]: Installing $arch drivers for $printer_model_name...\n";
##
## Step #1 : get the upload directory for the driver files
##
## see the rpcclient(1) man page for more information
## on this MS-RPC
##
## e.g. getdriverdir "Windows NT x86"
## run the command
$cmd_string = "getdriverdir \\\"$arch\\\"";
$cmd = "$path_rpcclient $servername -d 1 -A $authfile -c \"$cmd_string\"";
if ($DEBUG) { print STDERR "$cmd\n"; }
if (!open (RPC_IN, "$cmd|")) {
fatal_error("ERROR! Couldn't run $cmd (get driver dir).\n");
}
while ($rpc_output = <RPC_IN>) {
if ($DEBUG) { print STDERR "$rpc_output"; }
if ($rpc_output =~ /^\s*Directory Name/) {
$printer_upload_dir = $rpc_output;
last;
}
}
$_ = $printer_upload_dir;
if (!defined ($_) || !s/\s*Directory Name:\[([^\]]+)]\s*/$1/) {
fatal_error("ERROR! Unable to locate printer upload dir.\n");
}
s/\\([^\\]+)$//;
$printer_upload_last = $1;
$printer_upload_dir = $_;
$printer_upload_dir =~ s/\\/\//g;
print "[rpc]: Printer Driver Upload Directory = $_\\$printer_upload_last\n";
##
## Step #2 : upload the driver files to $printer_upload_dir
##
## e.g. prompt; cd "W32X86"; put hp4000_6.ppd; put pscrptui.dll;
## put pscript.hlp; put pscript.dll
## run the command
$cmd_string = "prompt; cd $printer_upload_last";
foreach $drv_file (@args) {
$cmd_string .= "; put $drv_file";
}
$cmd = "$path_smbclient $printer_upload_dir -A $authfile -d 1 -c \"$cmd_string\"";
if ($DEBUG) { print STDERR "$cmd\n"; }
if (!open (RPC_IN, "$cmd|")) {
fatal_error("Error: Couldn't run $cmd (upload driver files).\n");
}
$error = 0;
$error_string = "";
while ($rpc_output = <RPC_IN>) {
if ($DEBUG) { print STDERR "$rpc_output"; }
if ($rpc_output =~ /Connection to.* failed/ ) {
$error = 1;
$error_string = "ERROR! $rpc_output";
last;
}
if ($rpc_output =~ /tree connect failed/) {
$error = 1;
$error_string = "ERROR! smbclient failed to connect to [$printer_upload_dir]";
$error_string .= " Check access settings on share.\n";
last;
}
if ($rpc_output =~ /ERRnosuchshare/ ) {
$error = 1;
$error_string = "ERROR! No access to [$printer_upload_dir]";
last;
}
if ($rpc_output =~ /ERRnoaccess/ ) {
$error = 1;
$error_string = "ERROR! No access to upload files!";
last;
}
if ($rpc_output =~ /^putting file/) {
print "[rpc]: $rpc_output";
}
}
close (RPC_IN);
if ($error) {
print "[rpc]: $error_string\n";
chdir ($save_cwd);
return 0;
}
##
## Step #3 : need an AddPrinterDriver() RPC
##
## see the rpcclient(1) man page for more information on the
## format of this MS-RPC
##
## e.g. adddriver "Windows NT x86" \
## "HP LaserJet 4000 Series PS:PSCRIPT.DLL:HP4000_6.PPD:PSCRPTUI.DLL:\
## PSCRIPT.HLP:NULL:RAW:hp4000_6.ppd,pscrptui.dll,pscript.hlp,\
## pscript.dll"
## run the client program
$cmd_string = "adddriver \\\"$arch\\\" " .
"\\\"$printer_model_name:$driver_file_name:$data_file_name:" .
"$config_file_name:$help_file_name:NULL:RAW:" .
(join (',', @args)) . "\\\"";
$cmd = "$path_rpcclient $servername -d 1 -A $authfile -c \"$cmd_string\"";
if ($DEBUG) { print STDERR "$cmd\n"; }
if (!open (RPC_IN, "$cmd|")) {
fatal_error("Error: Couldn't run $cmd (add printer driver).\n");
}
## parse the output
$error = 0;
$error_string = "";
while ($rpc_output = <RPC_IN>) {
if ($DEBUG) { print STDERR "$rpc_output"; }
## catch errors heres
## successful output
if ($rpc_output =~ /successfully installed/) {
print "[rpc]: $rpc_output";
last;
}
}
if ($error) {
print "[rpc]: $error_string\n";
chdir ($save_cwd);
return 0;
}
##
## Step #4 : invoke an AddPrinter() RPC
##
## e.g. addprinter "HP LaserJet 4000 Series PS" "" \
## "HP LaserJet 4000 Series PS" "Samba Printer Port"
##
## Possible errors include
## - invalid port name
## - bad share name (and unable to create new on on Samba server)
## run the command
$cmd_string = "addprinter " .
"\\\"$rpc_printer_name\\\"" . " " .
"\\\"$rpc_share_samba_name\\\"" . " " .
"\\\"$printer_model_name\\\"" . " " .
"\\\"$rpc_port_name\\\"";
$cmd = "$path_rpcclient $servername -d 1 -A $authfile -c \"$cmd_string\"";
if ($DEBUG) { print STDERR "$cmd\n"; }
if (!open (RPC_IN, "$cmd|")) {
fatal_error("Error: Couldn't run $cmd (add printer).\n");
}
## grab any output to print a message
$error = 0;
$error_string = "";
while ($rpc_output = <RPC_IN>) {
## enable the following line for debugging
if ($DEBUG) { print STDERR "$rpc_output"; }
if ($rpc_output =~ /Invalid port/) {
$error = 1;
$error_string = "ERROR! Invalid port ($rpc_port_name) specified in addprinter command";
last;
}
if ($rpc_output =~ /NT_STATUS/) {
chomp ($rpc_output);
$error = 1;
$error_string = "ERROR! Windows NT error code : [$rpc_output]";
}
if ($rpc_output =~ /successfully installed/) {
print "[rpc]: $rpc_output";
print "[rpc]: Installed arch: $arch\n";
last;
}
}
close (RPC_IN);
if ($error) {
print "[rpc]: $error_string\n";
chdir ($save_cwd);
return 0;
}
## return to the previous working directory
chdir ($save_cwd);
return 1;
}
########################################################################
# locate_client_programs
#
sub locate_client_programs
{
## let's verify that smbclient and rpcclient are actually
## in the search path and can be found
my @path = ();
my @dirs = ();
my $found = 0;
## look for rpcclient
push (@path, dirname($path_rpcclient));
@dirs = split(/:/, $ENV{'PATH'});
push (@path, @dirs);
for (@path) {
if (-f "$_/$path_rpcclient") {
$found = 1;
last;
}
}
if (! $found ) {
print "ERROR! Unable to locate rpcclient binary!\n";
print "Please make sure the following is a valid path\n";
print "(or is in your search path):\n";
print "\t$path_rpcclient\n";
return 0;
}
## look for smbclient
$found = 0;
@path = ();
push (@path, dirname($path_smbclient));
push (@path, @dirs);
for (@path) {
if (-f "$_/$path_smbclient") {
$found = 1;
last;
}
}
if (! $found ) {
print "ERROR! Unable to locate smbclient binary!\n";
print "Please make sure the following is a valid path\n";
print "(or is in your search path):\n";
print "\t$path_smbclient\n";
return 0;
}
return 1;
}
########################################################################
# Params:
# $inf = Return value from read_inf() call, on the control file for a package.
sub install_package
{
my ($inf) = @_;
my $tmp_file_name = undef;
my $ret = 1;
if (!locate_client_programs()) {
return 0;
}
## now onto more important stuff....
if (!inf_has_key($inf, "W32X86")) {
fatal_error ("ERROR! Package doesn't include a \"Windows NT x86\" driver set.\n", 1);
}
while ((!defined($option_authfile)) || (length($option_authfile)==0)) {
## generate a temp file name which can be used as the authfile
## and write out the data
do {
$tmp_file_name = tmpnam();
} while (!sysopen(TMP, $tmp_file_name, O_RDWR|O_CREAT|O_EXCL, 0600));
$tmp_file_name = build_authfile();
$option_authfile = $tmp_file_name;
}
for my $arch ("W32X86", "WIN40", "W32mips", "W32alpha", "W32ppc") {
if (!inf_has_key($inf, $arch)) {
next;
}
my $inf_arch = inf_get_key($inf, $arch);
my $arch_file_name = inf_get_key($inf_arch, "inf_fname");
my $inf_hack_arch = inf_get_key($inf, "W32X86");
if ($arch_file_name) {
my $tmp_inf = read_inf("$arch/$arch_file_name");
if (!defined($tmp_inf)) {
warn "Warn: Bad arch INF file: $arch.\n";
next;
}
my $model_name = inf_get_key($inf_arch, "model");
if ($arch eq "WIN40") {
# '9x uses NT arch model name.
$model_name = inf_get_key($inf_hack_arch, "model");
}
my %info = parse_inf_nt($tmp_inf,
inf_get_key($inf_arch, "manufacturer"),
inf_get_key($inf_arch, "model"),
$arch);
if (!%info) {
warn "Warn: Invalid arch INF file: $arch.\n";
next;
}
my @rpc_args = ();
my $arg_string = undef;
my $printer_model_name = inf_get_key($inf_arch, "model");
push (@rpc_args, $option_authfile);
push (@rpc_args, $arch);
push (@rpc_args, $printer_model_name);
print "[rpc]: Printer Driver Information : \n";
print "[rpc]: Printer Model = $rpc_args[$#rpc_args]\n";
print "[rpc]: Environment = $arch\n";
push (@rpc_args, $info{"DriverFile"});
push (@rpc_args, $info{"DataFile"});
push (@rpc_args, $info{"ConfigFile"});
push (@rpc_args, $info{"HelpFile"});
push (@rpc_args, $info{"LanguageMonitor"});
print "[rpc]: Driver Filename = " . $info{'DriverFile'} . "\n";
print "[rpc]: Data Filename = " . $info{'DataFile'} . "\n";
print "[rpc]: Config Filename = " . $info{'ConfigFile'} . "\n";
print "[rpc]: Help Filename = " . $info{'HelpFile'} . "\n";
# Install the files...
for my $filehash (@{$info{CopyFiles}}) {
# $src is not to be used.
my $dst = $filehash->{DstFilename};
my $src = get_real_filename($dst, $arch);
if (!defined($src)) {
warn "Warn: File not found: '$dst' in $arch.\n";
next;
}
$dst = lc($dst); # Because they are usually upper case.
# and $src contains the arch again.
my $loc = "$base_dir/$arch/$dst";
if (! -d dirname($loc) && !mkpath(dirname($loc), 0, 0755)) {
warn "Warn: mkpath: Couldn't create directories\n";
next;
}
if (!copy($src, $loc)) {
warn "Warn: copy: $!\n";
next;
}
push(@rpc_args, $loc);
}
if (!rpc_client_wrapper(@rpc_args)) {
## return an error
return 0;
}
}
}
if (defined($tmp_file_name)) {
unlink ($tmp_file_name);
}
return 1;
}
########################################################################
# Clean up the temporary directory and its files.
#-----------------------------------------------------------------
sub cleanup
{
if ($option_cleanup == 1) {
if (! chdir("/")) {
warn "Warn: Couldn't chdir to / $!\n";
return;
}
status_msg ("Cleaning up temporary files...");
rmtree($tmp_dir);
## very important to remove this file if it exists!!
#if (defined($option_authfile)) {
# unlink ($option_authfile);
#}
}
}
########################################################################
# handle_package
#
##
sub handle_package {
my ($pkg_name) = @_;
my $res = undef;
my $control_inf = undef;
status_msg ("Exploding package...");
explode_package($pkg_name);
$control_inf = read_inf("./control");
if (!defined($control_inf)) {
fatal_error ("Error: Bad control INF file.", 1);
}
status_msg ("Installing package...");
$res = install_package($control_inf);
return ($res);
}
########################################################################
# In case of an unrecoverable error, print a message to STDERR,
# optionally perform cleanup, and exit.
#-----------------------------------------------------------------
sub fatal_error
{
my ($msg, $do_cleanup, $error_code) = @_;
if (!defined ($error_code) || !$error_code) {
$error_code = $error_code_default;
}
# FIXME: output twice (GUI would like to see the real error _first_ atm.
STDERR->print ("$msg\n");
if ($do_cleanup != 0) {
cleanup ();
}
STDERR->print ("\n\n$msg\n");
exit ($error_code);
}
########################################################################
# If verbose mode is on, then print the status message to STDOUT.
#-----------------------------------------------------------------
sub status_msg
{
my ($msg) = @_;
if ($option_verbose == 1) {
print "$msg\n";
}
}
########################################################################
# #### Main #### #
########################################################################
my @saved_argv = @ARGV;
local($|) = 1;
# Don't let anyone mess with the files.
$tmp_dir = cd_temp();
# Getopt stuff...
# Getopt::Long::Configure ("bundling");
my $res = GetOptions('help|h' => \$option_help,
'base-url|b=s' => \$base_url,
'host=s' =>
sub
{
if ($_[1] ne '')
{
$base_url = $real_base_url;
$base_url =~
s!^http://([^/]+)/(.+)$!http://$_[1]/$2!;
}
else
{
$base_url = '';
}
},
'servers=s' => \$option_servers_file,
'query!' => \$option_query,
'rpcquery!' => \$option_rpcquery,
'cleanup!' => \$option_cleanup,
'verbose' => \$option_verbose,
'local-pkg=s' => \$option_pkg_name,
'authfile=s' => \$option_authfile,
'cache-dir=s' => \$option_cache_dir,
'language=s' => \$option_language,
'pubkey-install!' => \$option_pubkey_install,
'version|v' => \$option_version,
'debug|d' => \$DEBUG);
# Allow the query option to take no parameter,
# this will match all printers.
if (($option_query || $option_rpcquery) && (scalar (@ARGV) != 1)) {
$ARGV[0] = "";
}
if (!$option_help && (scalar (@ARGV) != 1)) {
$res = 0;
}
if (!$res || $option_help) {
STDERR->print(" Format: $0 [options] <name>\n");
STDERR->print(" --help -h - Print this message.\n");
STDERR->print(" --debug -d - log debug information to stderr.\n");
STDERR->print(" --version -v - Print the version.\n");
STDERR->print(" --base-url - Change the url to query requests from.\n");
STDERR->print(" --cache-dir=<dir> - Cache driver file packages in <dir>\n");
STDERR->print(" --local-pkg=<file> - Install a locally archived driver pkg\n");
STDERR->print(" --host - Change the host in the request url.\n");
STDERR->print(" --query - Do a query for fully qualified printer names.\n");
STDERR->print(" --rpcquery - Do an rpc query for samba details.\n");
STDERR->print(" --cleanup - Should we cleanup the tmp directory.\n");
STDERR->print(" --authfile - File which contains authorization information.\n");
STDERR->print(" --pubkey-install - Always install new public keys for the package.\n");
STDERR->print(" --verbose - Print progress messages during each program step.\n");
STDERR->print("\n");
cleanup ();
exit (!$res);
}
if ($option_version) {
print "$0: Version 1.0.0\n";
cleanup ();
exit (0);
}
if (!defined ($ENV{'TMPDIR'})) {
$ENV{'TMPDIR'} = "/tmp/";
}
if ($option_pkg_name) {
$res = handle_package("$option_cache_dir/$option_pkg_name");
cleanup();
exit ($res);
}
# #### MAIN server list code ####
if (!defined ($base_url) && defined($option_servers_file)) {
# So it works recursivly.
if (!open (SERV_IN, "< $option_servers_file")) {
fatal_error(" Couldn't open server list: $!\n", 1);
}
my @lines = <SERV_IN>;
chomp(@lines);
my $tbl = RDB_parse_array(@lines);
close(SERV_IN);
my $first = 1;
while (scalar(@{$tbl->{data}->{"host"}})) {
if (!$first) {
status_msg ("Auto restarting with the next server...");
}
$first = 0;
my @xtra_args = ();
if ($tbl->{data}->{"language"}[0] ne '') {
push(@xtra_args, "--language");
push(@xtra_args, $tbl->{data}->{"language"}[0]);
}
# Might still be undef.
elsif (defined($option_language)) {
push(@xtra_args, "--language");
if (!defined($option_language)) {
$option_language = '';
}
push(@xtra_args, $option_language);
}
if ($tbl->{data}->{"url"}[0] ne '') {
push(@xtra_args, "--base-url");
push(@xtra_args, $tbl->{data}->{"url"}[0]);
}
else {
push(@xtra_args, "--host");
push(@xtra_args, $tbl->{data}->{"host"}[0]);
}
$res = system($path_self, @saved_argv, @xtra_args);
$res /= 256;
if (($res != $error_code_query_6) && ($res != $error_code_request)) {
last;
}
RDB_splice_data($tbl, 0, 1);
}
cleanup();
exit ($res);
}
if (!defined ($base_url) || ($base_url eq '')) {
$base_url = $real_base_url;
}
# #### MAIN rpcquery code ####
if ($option_rpcquery) {
my $tmp_file_name = undef;
my $server = undef;
my @args = ();
my @rpc_output = ();
if (!locate_client_programs()) {
exit 0;
}
if (!defined ($option_authfile)) {
## generate a temp file name which can be used as the authfile
## and write out the data
do {
$tmp_file_name = tmpnam();
} while (!sysopen(TMP, $tmp_file_name, O_RDWR|O_CREAT|O_EXCL, 0600));
$server = setup_credentials($tmp_file_name);
$option_authfile = $tmp_file_name;
}
else {
@args = read_authfile($option_authfile);
$server = $args[3];
}
if (validate_credentials($server, $option_authfile)) {
@rpc_output = rpc_query_wrapper($option_authfile, $server);
for (@rpc_output) {
print "$_\n";
}
}
else {
print STDERR "Invalid credentials!\n";
}
if (defined($tmp_file_name)) {
unlink($tmp_file_name);
}
cleanup();
exit 0;
}
# #### MAIN query and install code ####
# http-ify the parameter.
$ARGV[0] =~ s/([^a-zA-Z0-9])/"%" . sprintf("%02x", ord($1))/eg;
if ($option_query) {
@params = @query_params;
if ($#ARGV >= 0) {
push (@params, "printer-name=" . $ARGV[0]);
}
}
else {
@params = @download_params;
push (@params, "printer-name=" . $ARGV[0]);
}
if (defined ($option_language) && ($option_language ne '')) {
# http-ify the parameter.
$option_language =~ s/([^a-zA-Z0-9])/"%" . sprintf("%x", ord($1))/eg;
push (@params, "lang=" . $option_language);
}
# Do downloads...
my $ua = LWP::UserAgent->new();
$ua->agent("Imprints-Client/1.0 " . $ua->agent);
$ua->env_proxy();
status_msg ("Sending request to server $base_url...");
my $request_data = send_request($base_url, \@params, $ua);
status_msg ("Parsing RDB table...");
my $tbl = RDB_parse_string($request_data);
if (!defined ($tbl)) {
fatal_error ("Error: Failed to parse information from retrieval server.", 1);
}
# Check for error_code in returned table.
if (exists ($tbl->{data}->{"error_code"})) {
my $error_code = $tbl->{data}->{"error_code"}[0];
my $error_desc = $tbl->{data}->{"error_desc"}[0];
my $err = $error_code_default;
# Not found error code...
if ($error_code == 6) {
$err = $error_code_query_6;
}
fatal_error ("Error: Query failed with return code=$error_code. $error_desc.", 1, $err);
}
if ($option_query) {
output_query_results($tbl);
cleanup ();
exit (0);
}
status_msg ("Downloading package...");
my $pkg_name = download_package($ua, $tbl);
# Deal with the package...
status_msg ("Verifying package using gpg...");
verify_package($pkg_name,
$tbl->{data}->{"gpg_version"}[0],
$tbl->{data}->{"gpg_sig"}[0],
$tbl->{data}->{"gpg_crc"}[0]);
$res = handle_package ($pkg_name);
# Cleanup temporary storage...
cleanup ();
if ($res) {
print("Installation completed successfully.\n");
}
else {
print("Installation experienced problems.\n");
}
exit ($res);