Viewing file: foomatic-configure (133.59 KB) -rwxr-xr-x Select action/file-type: (+) | (+) | (+) | Code (+) | Session (+) | (+) | SDB (+) | (+) | (+) | (+) | (+) | (+) |
#!/usr/bin/perl -w use strict; # -*- perl -*-
# This is foomatic-configure, a program to establish and configure # print queues, drivers, spoolers, etc using the foomatic database and # companion filters.
# It also comprises half of a programatic API for user tools: you can # learn and control everything about the static properties of print # queues here. With the sister program foomatic-printjob, you can do # everything related to print queue dynamic state: submit jobs, and # query, cancel, reorder, and redirect them.
use Foomatic::Defaults; use Foomatic::DB; use Data::Dumper;
# Connect syntax: # # This differs a tad from CUPS's, partly because everything is # supposed to be a file, and CUPS doesn't entirely reflect that. # But I'm not really very particular... # # If a certain URI is not supported by all the spoolers, the spoolers # which support it are listed in parantheses, "direct" means direct, # spooler-less printing. # # usb:/path/device # Local USB printer # usb://make/model?serial=xxx # Printer-bound USB connection (CUPS) # parallel:/path/device # Local parallel printer # serial:/path/device # Local serial printer # file:/path/file # includes usb, lp, named pipes, other # hp:/bus/model?serial=xxx # HPLIP print queue (hpinkjet.sf.net) # hpfax:/bus/model?serial=xxx # HPLIP fax queue (hpinkjet.sf.net) # ptal:/provider:bus:name # HPOJ MLC protocol (hpoj.sf.net,obsolete) # mtink:/path/device # Epson inkjet through mtink daemon # # (for ink level monitoring when printing, # # http://xwtools.automatix.de/) # lpd://host/queue # LPD protocol # lpd://host # LPD protocol (default queue, CUPS only) # socket://host:port # TCP aka appsocket # socket://host # TCP aka appsocket (port 9100) # ncp://user:pass@host/queue # Netware (LPD, LPRng, direct) # smb://user:pass@wgrp/host/queue # Windows (CUPS, PPR, LPD, LPRng, direct) # stdout # Standard output (direct) # postpipe:"<command line>" # Free-formed backend command line # # (LPD, LPRng, direct) #
# Read out the program name with which we were called, but discard the path
$0 =~ m!/([^/]+)\s*$!; my $progname = ($1 || $0); my $debug = 0;
# We use the library Getopt::Long here, so that we can have more than # one "-o" option on one command line.
my($opt_q, $opt_f, $opt_w, $opt_n, $opt_N, $opt_L, $opt_ppd, $opt_d, $opt_p, $opt_s, $opt_C, $opt_R, $opt_D, $opt_Q, $opt_P, $opt_O, $opt_X, $opt_c, @opt_o, $opt_r, $opt_dd, $opt_nodd, $opt_att, $opt_delay, $opt_h); use Getopt::Long; Getopt::Long::Configure("no_ignore_case"); GetOptions("q" => \$opt_q, # Quiet, non-interactive operation "f" => \$opt_f, # Force rebuild of PPD from database "w" => \$opt_w, # Cut GUI strings in the PPD to 39 # characters (for CUPS Windows driver) "n=s" => \$opt_n, # queue Name "N=s" => \$opt_N, # human-readable Name (Model, # Description) "L=s" => \$opt_L, # Location "ppd=s" => \$opt_ppd, # PPD file "d=s" => \$opt_d, # Driver "p=s" => \$opt_p, # Printer "s=s" => \$opt_s, # Spooler "C" => \$opt_C, # Copy queue "R" => \$opt_R, # Remove queue "D" => \$opt_D, # set Default queue "Q" => \$opt_Q, # Query queue info "P" => \$opt_P, # Perl queue/printer/driver info output "O" => \$opt_O, # get printer support Overview "X" => \$opt_X, # query XML printer/driver/combo info "c=s" => \$opt_c, # printer Connection type "o=s" => \@opt_o, # default printing Options "r" => \$opt_r, # list Remote queues "backend-dont-disable=s" => \$opt_dd, # Do not disable CUPS # backends "backend-attempts=s" => \$opt_att, # Try that often when backend # fails "backend-delay=s" => \$opt_delay, # Delay in seconds between # retries of failed backend "h" => \$opt_h, # Help! "help"=> \$opt_h) || help();
help() if $opt_h;
my $db = new Foomatic::DB;
overview() if $opt_O;
get_xml() if $opt_X;
my $force = ($opt_f ? 1 : 0);
my $shortgui = ($opt_w ? 1 : 0);
my $in_config = {'queue' => $opt_n, 'desc' => $opt_N, 'loc' => $opt_L, 'ppdfile' => $opt_ppd, 'driver' => $opt_d, 'printer' => $opt_p, 'spooler' => $opt_s, 'connect' => $opt_c, 'options' => \@opt_o, 'force' => $force, 'shortgui' => $shortgui, 'dd' => $opt_dd, 'att' => $opt_att, 'delay' => $opt_delay, 'foomatic' => 1};
# If description and location contain only whitespace, use an empty string # instead
if ((defined($in_config->{'desc'})) && ($in_config->{'desc'} =~ m!^\s*$!)) { $in_config->{'desc'} = ""; } if ((defined($in_config->{'loc'})) && ($in_config->{'loc'} =~ m!^\s*$!)) { $in_config->{'loc'} = ""; }
my $action = ($opt_R ? 'delete' : 'configure'); $action = ($opt_D ? 'default' : $action); $action = ($opt_Q ? 'query' : $action); $action = ($opt_P ? 'query' : $action);
my $procs = { 'lpd' => { 'delete' => \&delete_lpd, 'configure' => \&setup_lpd, 'default' => \&default_lpd, 'query' => \&query_lpd }, 'lprng'=>{ 'delete' => \&delete_lpd, 'query' => \&query_lpd, 'default' => \&default_lprng, 'configure' => \&setup_lpd }, 'cups' =>{ 'delete' => \&delete_cups, 'query' => \&query_cups, 'default' => \&default_cups, 'configure' => \&setup_cups }, 'pdq' =>{ 'delete' => \&delete_pdq, 'query' => \&query_pdq, 'default' => \&default_pdq, 'configure' => \&setup_pdq }, 'ppr' =>{ 'delete' => \&delete_ppr, 'query' => \&query_ppr, 'default' => \&default_ppr, 'configure' => \&setup_ppr }, 'direct'=>{'delete' => \&delete_direct, 'query' => \&query_direct, 'default' => \&default_direct, 'configure' => \&setup_direct } };
if (!($opt_Q or $opt_P or defined($in_config->{'queue'}))) { # No queue manipulation without knowing the name of the queue print STDERR "You must specify a queue name with -n!\n"; help(); exit 1; }
if (!defined($in_config->{'spooler'})) {
my $takenfromconfigfile = 0;
# Personal default spooler my $s; if (($> != 0) && (-f "$ENV{'HOME'}/.defaultspooler")) { $s = `cat $ENV{'HOME'}/.defaultspooler`; chomp $s; $takenfromconfigfile = 1; } # System default spooler if ((!defined($s)) && (-f "$sysdeps->{'foo-etc'}/defaultspooler")) { $s = `cat $sysdeps->{'foo-etc'}/defaultspooler`; chomp $s; $takenfromconfigfile = 1; } if (!defined($s)) { $s = detect_spooler(); }
die "Unable to identify spooler, please specify with -s\n" unless $s;
if ((!$opt_q) && (!$takenfromconfigfile)) { print STDERR "You appear to be using $s. Correct? "; my $yn = <STDIN>; die "\n" if ($yn !~ m!^y!i); }
$in_config->{'spooler'} = $s; }
if ($in_config->{'printer'}) { # If the user supplies an old numerical printer ID, translate it to # a new clear-text ID $in_config->{'printer'} = Foomatic::DB::translate_printer_id($in_config->{'printer'}); }
# Call proper proc &{$procs->{$in_config->{'spooler'}}{$action}}($in_config); exit(0);
# Common parts for queue creation/modification functions
sub getoldqueuedata {
my ($config, $reconf) = @_; my ($sourcespooler, $sourcequeue, $olddatablob, $beh);
# Copy a queue if ($opt_C) { if ($#ARGV == 0) { # 1 argument -> queue from same spooler $sourcespooler = $config->{'spooler'}; $sourcequeue = $ARGV[0]; } elsif ($#ARGV == 1) { # 2 arguments -> queue from given spooler $sourcespooler = $ARGV[0]; $sourcequeue = $ARGV[1]; } else { die "Unsufficient options to copy a queue, " . "try \"$progname -h\"!\n"; } # Read data from source queue if (!($olddatablob = load_datablob($sourcespooler, $sourcequeue))) { # It is not possible to copy the given source queue die "The source queue $sourcequeue does not exist " . "or is corrupted!\n"; } # PPD file of the source queue, if it exists, and if the user # does not insist on using another PPD file, we must copy it my $sourceppd = $olddatablob->{'ppdfile'}; if ((-r $sourceppd) && (!$config->{'ppdfile'})) { $config->{'ppdfile'} = $sourceppd; } # Stuff data into the $config structure, all items must be defined, # so that an old queue gets overwritten if ($olddatablob->{'queuedata'}) { my $i; for $i (('desc', 'loc', 'printer', 'driver', 'connect', 'ppdfile', 'dd', 'att', 'delay')) { if (!defined($config->{$i})) { if ($olddatablob->{'queuedata'}{$i}){ $config->{$i} = $olddatablob->{'queuedata'}{$i}; } elsif ($i eq 'dd') { $config->{$i} = 0; } elsif ($i eq 'att') { $config->{$i} = 1; } elsif ($i eq 'delay') { $config->{$i} = 30; } else { $config->{$i} = ""; } } } # Check consistency of the printer/driver settings if ((($config->{'driver'} eq "") || ($config->{'driver'} eq "raw") || # No new driver, printer, ($config->{'printer'} eq "")) && # PPD file ($config->{'ppdfile'} eq "") && ((!defined($olddatablob->{'args'})) || # No existing options ($#{$olddatablob->{'args'}} < 0))) { # -> source queue raw $config->{'driver'} = "raw"; $config->{'printer'} = undef; } # We do not need the queue data block any more delete($olddatablob->{'queuedata'}); } else { # No Foomatic/PPD data $olddatablob = undef; } } else { # Load the datablob of the former configuration if ($reconf) { if ($olddatablob = load_datablob($config->{'spooler'}, $config->{'queue'})) { # If the user has supplied only a printer or only a driver # fill in the second of the two fields in $config if ((!$config->{'ppdfile'}) && ($olddatablob->{'queuedata'}{'foomatic'})) { if ((!$config->{'driver'}) && ($config->{'printer'})) { $config->{'driver'} = $olddatablob->{'driver'}; } if ((!$config->{'printer'}) && ($config->{'driver'})) { $config->{'printer'} = $olddatablob->{'id'}; } } # Extract URI and backend error handling data if ($config->{'spooler'} eq "cups") { $beh->{'uri'} = $olddatablob->{'queuedata'}{'connect'}; $beh->{'dd'} = $olddatablob->{'queuedata'}{'dd'}; $beh->{'att'} = $olddatablob->{'queuedata'}{'att'}; $beh->{'delay'} = $olddatablob->{'queuedata'}{'delay'}; } # We do not need the queue data block here delete($olddatablob->{'queuedata'}); } else { $olddatablob = undef; } } } # If the user does not supply info about his printer and/or driver # and the queue did not exist before we assume that he wants to set up a # raw queue. To make a raw queue out of a formerly filtered one, one # has to use the driver name "raw". $config->{'driver'} = "" if not defined $config->{'driver'}; $config->{'printer'} = "" if not defined $config->{'printer'}; $config->{'ppdfile'} = "" if not defined $config->{'ppdfile'}; my $nodriver = (((!$config->{'driver'}) && (!$config->{'printer'}) && (!$config->{'ppdfile'})) || ($config->{'driver'} eq "raw"));
# Set to 1 when we retrieve a data set from the Foomatic database my $newfoomaticdata = 0; if ($nodriver) { if ($olddatablob) { if ($config->{'driver'} ne "raw") { # We couldn't determine a certain driver, probably we had a # native PostScript PPD file $db->{'dat'} = $olddatablob; } else { # For a raw queue overtake at least the $postpipe if (defined($olddatablob->{'postpipe'})) { $db->{'dat'}{'postpipe'} = $olddatablob->{'postpipe'}; } } } } elsif ($config->{'ppdfile'}) { if (! -r $config->{'ppdfile'}) { die "The PPD file \'$config->{'ppdfile'}\' does not exist or is " . "readable.\n"; } # Load the data from the PPD file $db->getdatfromppd($config->{'ppdfile'}); # Overtake the former default settings if ($olddatablob) {overtake_defaults($olddatablob)}; # Overtake the former $postpipe if (defined($olddatablob->{'postpipe'})) { $db->{'dat'}{'postpipe'} = $olddatablob->{'postpipe'}; } } else { if (($olddatablob) && ($olddatablob->{'driver'} eq $config->{'driver'}) && ($olddatablob->{'id'} eq $config->{'printer'}) && (!$config->{'force'})) { # Overtake data from the former configuration $db->{'dat'} = $olddatablob; } else { # Retrieve data from the Foomatic database if (!$config->{'driver'}) { die "You also need to specify a driver with \"-d\"!\n"; } if (!$config->{'printer'}) { die "You also need to specify a printer with \"-p\"!\n"; } # The printer is supported by the chosen driver? If yes, load # its data my $possible = $db->getdat($config->{'driver'}, $config->{'printer'}); die "That printer and driver combination is not possible.\n" if (!$possible); die "There is neither a custom PPD file nor the driver database entry contains sufficient data to build a PPD file.\n" if (!$db->{'dat'}{'cmd'}) && (!$db->{'dat'}{'ppdfile'}); $newfoomaticdata = 1; # Overtake the former default settings if ($olddatablob) {overtake_defaults($olddatablob)}; # Overtake the former $postpipe if (defined($olddatablob->{'postpipe'})) { $db->{'dat'}{'postpipe'} = $olddatablob->{'postpipe'}; } } }
# When we have no arguments in the current configuration, we must have # a raw queue my $rawqueue = ((!defined($db->{'dat'}{'args'})) || ($#{$db->{'dat'}{'args'}} < 0));
# Set the default printing options supplied on the command line if (!$rawqueue) { set_default_options($config, $db->{'dat'}); }
# Printer model name (for comment field of the queue configuration) my ($make, $model, $makemodel); if (defined($db->{'dat'})) { $make = $db->{'dat'}{'make'}; $model = $db->{'dat'}{'model'}; $makemodel = $db->{'dat'}{'makemodel'}; if (($make) && ($model)) { $makemodel = "$make $model"; } } return ($rawqueue, $newfoomaticdata, $makemodel, ($config->{'spooler'} eq "cups" ? $beh : ())); }
#fix to work on Ubuntu, where cupd runs not as root, but as cupsys user #like system ("chown cupsys $ppdfile"), but #changeowner function changes owner only if user exists on system sub changeowner {
my ($username, $file) = @_;
my ($uid,$gid) = (-1, -1); my $l; $l = getpwnam($username); $uid = $l if defined($l); $l = getgrnam($username); $gid = $l if defined($l); chown $uid, $gid, $file;
}
sub writeppdfile {
my ($config, $ppdfile, $rawqueue, $newfoomaticdata) = @_;
# Save old $ppdfile, if any system("cp -f \'$ppdfile\' \'$ppdfile.old\'") if (-f $ppdfile); if ($rawqueue) { # Raw queue with $postpipe, use a "PPD" only containing the # $postpipe (LPRng, LPD, and no spooler only) if (((defined $db->{'dat'}{'postpipe'} && $db->{'dat'}{'postpipe'} ne "") && (($config->{'spooler'} eq 'lprng') || ($config->{'spooler'} eq 'lpd'))) || ($config->{'spooler'} eq 'direct')) { open PPDFILE, "> $ppdfile" or die "Cannot write \'$ppdfile\'!\n"; print PPDFILE "*PPD-Adobe: \"4.3\"\n*%\n"; print PPDFILE "*% This is a raw (driverless/unfiltered) " . "queue, this PPD file only carries\n" . "*% the postpipe.\n*%\n"; close PPDFILE; $db->ppdsetdefaults($ppdfile); chmod 0644, $ppdfile; #fix to work on Ubuntu, where cupd runs not as root, but as cupsys user #system ("chown cupsys $ppdfile"); #changeowner function changes owner only if user exists on system changeowner("cupsys", $ppdfile); } else { if (-f $ppdfile) { unlink "$ppdfile" or die "Cannot delete \'$ppdfile\'!\n"; } } } else { if ($config->{'ppdfile'}) { # Copy in the PPD file specified on the command line if ($config->{'ppdfile'} !~ /\.gz$/i) { # Uncompressed PPD file system("cp -f \'$config->{'ppdfile'}\' \'$ppdfile\'") and die "Cannot copy \'$config->{'ppdfile'}\' to \'$ppdfile\'!\n"; } else { # Compressed PPD file system("$sysdeps->{'gzip'} -dc " . "\'$config->{'ppdfile'}\' > " . "\'$ppdfile\'") and die "Cannot copy \'$config->{'ppdfile'}\' to \'$ppdfile\'!\n"; } # Set default option settings and $postpipe $db->ppdsetdefaults($ppdfile); } elsif ($newfoomaticdata) { # Generate the PPD file from the Foomatic database open PPDFILE, "> $ppdfile" or die "Cannot write \'$ppdfile\'!\n"; print PPDFILE $db->getppd($config->{'shortgui'}); close PPDFILE; } else { # Keep the previous PPD file, only set the options and the # $postpipe $db->ppdsetdefaults($ppdfile); } # Correct the permissions of the PPD file chmod 0644, $ppdfile; #fix to work on Ubuntu, where cupd runs not as root, but as cupsys user #system ("chown cupsys $ppdfile"); #changeowner function changes owner only if user exists on system changeowner("cupsys", $ppdfile); } }
### Queue manipulation functions for both LPD and LPRng
sub setup_lpd { my ($config) = $_[0];
# Read the previous /etc/printcap my $pcap = load_lpd_printcap();
my ($ppdfile, $entry, $reconf, $p); for $p (@{$pcap}) { if ($p->{'names'}[0] eq $config->{'queue'}) { $entry = $p; $reconf = 1; print "Reconfigure of ", Dumper($p) if $debug; last; } }
# PPD file name $ppdfile = sprintf('%s/lpd/%s.ppd', $sysdeps->{'foo-etc'}, $config->{'queue'}) if !$ppdfile;
# Get the data from the former queue if we reconfigure or copy a queue # do also some checking of the user-supplied parameters my ($rawqueue, $newfoomaticdata, $makemodel) = getoldqueuedata($config, $reconf);
# Set the printer queue name line in /etc/printcap if (!$reconf) { if (!$rawqueue) { $entry->{'names'}[0] = $config->{'queue'}; $entry->{'names'}[1] = $config->{'desc'}; $entry->{'names'}[2] = "$makemodel"; $entry->{'names'}[3] = $config->{'loc'}; } else { $entry->{'names'}[0] = $config->{'queue'}; $entry->{'names'}[1] = $config->{'desc'}; $entry->{'names'}[2] = "Raw queue"; $entry->{'names'}[3] = $config->{'loc'}; } } else { if (!$rawqueue) { $entry->{'names'}[2] = "$makemodel"; } else { if (($entry->{'names'}[2] eq "Raw queue") || ($config->{'driver'} eq "raw")) { $rawqueue = 1; $entry->{'names'}[2] = "Raw queue"; } } if (defined($config->{'desc'})) { $entry->{'names'}[1] = $config->{'desc'}; } if (defined($config->{'loc'})) { $entry->{'names'}[3] = $config->{'loc'}; } }
# These lines are always in /etc/printcap $entry->{'str'}{'sd'} = sprintf('%s/%s', $sysdeps->{'lpd-dir'}, $config->{'queue'}); $entry->{'str'}{'lf'} = $sysdeps->{'lpd-log'}; $entry->{'num'}{'mx'} = '0'; $entry->{'bool'}{'sh'} = 1;
# Lines depending on the printer/spooler if (!$rawqueue) { if ($config->{'spooler'} eq "lpd") { $entry->{'str'}{'ppdfile'} = $ppdfile; # For the GPR printing GUI delete $entry->{'str'}{'ppd'}; $entry->{'str'}{'if'} = $sysdeps->{'foomatic-rip'}; $entry->{'str'}{'af'} = $ppdfile; delete $entry->{'bool'}{'force_localhost'}; delete $entry->{'str'}{'filter_options'}; } elsif ($config->{'spooler'} eq "lprng") { $entry->{'str'}{'ppd'} = $ppdfile; # for LPRng PPD support $entry->{'str'}{'if'} = $sysdeps->{'foomatic-rip'}; $entry->{'bool'}{'force_localhost'} = 1; delete $entry->{'str'}{'ppdfile'}; delete $entry->{'str'}{'af'}; delete $entry->{'str'}{'filter_options'}; } else { die "The spooler $config->{'spooler'} is not supported " . "by this function!\n"; } } else { delete $entry->{'str'}{'if'}; delete $entry->{'str'}{'af'}; delete $entry->{'str'}{'filter_options'}; delete $entry->{'str'}{'ppd'}; if ($config->{'spooler'} eq "lpd") { delete $entry->{'bool'}{'force_localhost'}; } elsif ($config->{'spooler'} eq "lprng") { $entry->{'bool'}{'force_localhost'} = 1; } else { die "The spooler $config->{'spooler'} is not supported " . "by this function!\n"; } }
# If printing job has to be passed through a special program, put the # command line into $postpipe (for example for Socket, Samba, ...) my $postpipe = "";
if ((!$reconf) or ($config->{'connect'})) { # Set up connection type
# Remove "rm" and "rp" tags to avoid problems when overwriting a # raw queue delete $entry->{'str'}{'rm'}; delete $entry->{'str'}{'rp'};
# All URIs ("-c" option) have the same syntax as URIs in CUPS ("-v" # option of "lpadmin"). my $file; if ($config->{'connect'} =~ m!^(file|usb|parallel|serial):(.*)!) { # Local printer or printing to a file $file = $2; if ($config->{'connect'} =~ m!^usb://!) { # Queue with printer-bound USB URI transferred from CUPS, # as LPD/LPRng does not support these URIs, translate it # back to a standard USB device URI $file = cups_usb_printer_uri_to_device_uri($file); } if (! -e $file) { warn "The device or file $file doesn't exist? " . "Working anyway.\n"; } if (($file =~ m!^$sysdeps->{'ptal-pipes'}/(.+)$!) || ($file =~ m!^/dev/ptal-printd/(.+)$!) || ($file =~ m!^/var/run/ptal-printd/(.+)$!)) { # Translate URI for ptal-printd to postpipe using the # "ptal-connect" command my $devname = $1; $devname =~ s/_/:/; $devname =~ s/_/:/; $postpipe = "$sysdeps->{'ptal-connect'} $devname -print"; $entry->{'str'}{'lp'} = "/dev/null"; } else { $entry->{'str'}{'lp'} = $file; } } elsif ($config->{'connect'} =~ m!^ptal://?([^/].*)$!) { # HPOJ MLC protocol my $devname = $1; $postpipe = "$sysdeps->{'ptal-connect'} $devname -print"; $entry->{'str'}{'lp'} = "/dev/null"; } elsif ($config->{'connect'} =~ m!^mtink:/(.+)$!) { # Printing through "mtinkd" $entry->{'str'}{'lp'} = "$sysdeps->{'mtink-pipes'}/$1"; } elsif ($config->{'connect'} =~ m!^lpd://([^/]+)/([^/]+)$!) { # Remote LPD my $remhost = $1; my $remqueue = $2; if (($rawqueue) && ($config->{'spooler'} eq "lpd")) { $entry->{'str'}{'rm'} = $remhost; $entry->{'str'}{'rp'} = $remqueue; delete $entry->{'str'}{'lp'}; } elsif( ($config->{'spooler'} eq "lprng")) { delete $entry->{'str'}{'rm'}; delete $entry->{'str'}{'rp'}; $entry->{'str'}{'lp'} = "$remqueue\@$remhost"; } else { # classic LPD does not support sending jobs to a server with the # "rm" and "rp" tags in /etc/printcap and filtering it # before ("if" tag). So when we do not set up a raw queue, # we do not # # $entry->{'str'}{'rm'} = $remhost; # $entry->{'str'}{'rp'} = $remqueue; # # but use "rlpr" in a $postpipe. Note that "rlpr" prints a # banner page by default, "-h" suppresses it. "rlpr" must # be SUID "root". $postpipe = "$sysdeps->{'rlpr'} -q -h -P " . "$remqueue\@$remhost"; $entry->{'str'}{'lp'} = "/dev/null"; } } elsif ($config->{'connect'} =~ m!^socket://([^/:]+):([0-9]+)/?$!) { # Socket (AppSocket/HP JetDirect) my $remhost = $1; my $remport = $2; if( ($config->{'spooler'} eq "lprng")) { $entry->{'str'}{'lp'} = "$remhost\%$remport"; } else { $postpipe = "$sysdeps->{'nc'} -w 1 $remhost $remport"; $entry->{'str'}{'lp'} = "/dev/null"; } } elsif ($config->{'connect'} =~ m!^smb://(.*)$!) { # SMB (Printer on Windows server) my $parameters = $1; # Get the user's login and password from the URI my $smbuser = ""; my $smbpassword = ""; if ($parameters =~ m!([^@]*)@([^@]+)!) { my $login = $1; $parameters = $2; if ($login =~ m!([^:]*):([^:]*)!) { $smbuser = $1; $smbpassword = $2; } else { $smbuser = $login; $smbpassword = ""; } } else { $smbuser = "GUEST"; $smbpassword = ""; } # Get the workgroup, server, and share name my $workgroup = ""; my $smbserver = ""; my $smbshare = ""; if ($parameters =~ m!([^/]*)/([^/]+)/([^/]+)$!) { $workgroup = $1; $smbserver = $2; $smbshare = $3; } elsif ($parameters =~ m!([^/]+)/([^/]+)$!) { $workgroup = ""; $smbserver = $1; $smbshare = $2; } else { die "The \"smb://\" URI must at least contain the " . "server name and the share name!\n"; } # Set up the command line for printing on the SMB server $postpipe = "$sysdeps->{'smbclient'} '//$smbserver/$smbshare'"; if ($smbpassword ne "") { warn("WARNING: smbclient password is visible in PPD file\n"); $postpipe .= " '$smbpassword'"; } if ($smbuser ne "") {$postpipe .= " -U '$smbuser'";} if ($workgroup ne "") {$postpipe .= " -W '$workgroup'";} $postpipe .= " -N -P -c 'print -' "; $entry->{'str'}{'lp'} = "/dev/null"; } elsif ($config->{'connect'} =~ m!^ncp://(.*)$!) { my $parameters = $1; # Get the user's login and password from the URI my $ncpuser = ""; my $ncppassword = ""; if ($parameters =~ m!([^@]*)@([^@]+)!) { my $login = $1; $parameters = $2; if ($login =~ m!([^:]*):([^:]*)!) { $ncpuser = $1; $ncppassword = $2; } else { $ncpuser = $login; $ncppassword = ""; } } else { $ncpuser = ""; $ncppassword = ""; } # Get the server and share name my $ncpserver = ""; my $ncpqueue = ""; if ($parameters =~ m!([^/]+)/([^/]+)$!) { $ncpserver = $1; $ncpqueue = $2; } else { die "The \"ncp://\" URI must at least contain the " . "server name and the queue name!\n"; } # Set up the command line for printing on the Netware server $postpipe = "$sysdeps->{'nprint'} -S $ncpserver"; if ($ncpuser ne "") { $postpipe .= " -U $ncpuser"; if ($ncppassword ne "") { warn("WARNING: ncp password is visible in PPD file\n"); $postpipe .= " -P $ncppassword"; } else { $postpipe .= " -n"; } } $postpipe .= " -q $ncpqueue -N - 2>/dev/null"; $entry->{'str'}{'lp'} = "/dev/null"; } elsif ($config->{'connect'} =~ m!^postpipe:(.*)$!) { # Pipe output into a command $postpipe = $1; $entry->{'str'}{'lp'} = "/dev/null"; } elsif ($config->{'connect'}) { $entry->{'str'}{'lp'} = '/dev/null'; die ("The URI \"$config->{'connect'}\" is not supported " . "for LPD/LPRng or you have\nmistyped.\n"); } else { print STDERR "You must specify a connection with -c.\n"; help(); exit(1); } # Put $postpipe into the data structure, so that it will be # inserted into the PPD file if ($postpipe ne "") { $postpipe = "| $postpipe"; $db->{'dat'}{'postpipe'} = $postpipe; } else { undef $db->{'dat'}{'postpipe'}; } } else { # Keep previous connection type # Use previous $postpipe if (defined($db->{'dat'}{'postpipe'})) { $postpipe = $db->{'dat'}{'postpipe'}; } }
# When we have a $postpipe we never write to a device if ($postpipe ne "") { $entry->{'str'}{'lp'} = '/dev/null'; if ($config->{'spooler'} eq "lpd") { $entry->{'str'}{'if'} = $sysdeps->{'foomatic-rip'}; $entry->{'str'}{'af'} = $ppdfile; } elsif ($config->{'spooler'} eq "lprng") { $entry->{'str'}{'if'} = $sysdeps->{'foomatic-rip'}; $entry->{'str'}{'ppd'} = $ppdfile; $entry->{'bool'}{'force_localhost'} = 1; } else { die "The spooler $config->{'spooler'} is not supported " . "by this function!\n"; } }
# Various file setup mkdir $sysdeps->{'foo-etc'}, 0755; mkdir "$sysdeps->{'foo-etc'}/lpd", 0755; mkdir $entry->{'str'}{'sd'}, 0755;
# Lead with a blank line for new entries push (@{$entry->{'comments'}}, "\n") if (!$reconf);
# Put in a useful comment for both new and old entries push (@{$entry->{'comments'}}, sprintf ("\# Entry edited %s by $progname.", scalar(localtime(time))), "\# Additional configuration atop $ppdfile");
# Add to the printcap if a new entry if (!$reconf) { push(@{$pcap}, $entry); }
# Generate/write te PPD file writeppdfile($config, $ppdfile, $rawqueue, $newfoomaticdata);
# Make sure that /var/spool/lp-errs exists system "touch $sysdeps->{'lpd-log'}"; chmod 0600, $sysdeps->{'lpd-log'}; my ($lpuid, $lpgid) = (-1, -1); my $l; $l = getpwnam("lp"); $lpuid = $l if defined($l); $l = getgrnam("lp"); $lpgid = $l if defined($l); chown $lpuid, $lpgid, $sysdeps->{'lpd-log'};
# Write back /etc/printcap my $printcap = $sysdeps->{'lpd-pcap'}; rename $printcap, "$printcap.old" or die "Cannot backup $printcap!\n"; open PRINTCAP, "> $printcap" or die "Cannot write $printcap!\n"; print PRINTCAP dump_lpd_printcap($config, $pcap); close PRINTCAP; chmod 0644, $printcap;
# In case of LPRng, give SIGHUP to the daemon, LPRng needs this to # recognize a new queue if ($config->{'spooler'} eq "lprng") { # first check configuration system("$sysdeps->{'lprng-checkpc'} -f > /dev/null 2>&1"); # now signal to use it system("$sysdeps->{'lpd-lpc'} reread > /dev/null 2>&1"); }
return 1; }
sub default_lpd { my ($config) = $_[0];
my $name = $config->{'queue'};
my $pcap = load_lpd_printcap();
# Add the alias "lp" to the /etc/printcap entry to make LPD considering # the chosen printer as default printer
# Some stuff for renaming a queue named "lp" my $nppdfile = undef; my $newname = undef; my $rawqueue = 0;
my @newcap; for (@{$pcap}) { my $p = $_; if ($p->{'names'}[0] eq $name) { $p->{'names'}[4] = 'lp'; } else { # Rename a printer whose first name is 'lp' if ($p->{'names'}[0] eq 'lp') { # Do we have a raw queue? if ((!defined($p->{'str'}{'if'})) || ($p->{'str'}{'if'} ne $sysdeps->{'foomatic-rip'})) { $rawqueue = 1; } # Search for a free name my $i = 0; my $namefound = 0; while(!$namefound) { my $pp; my $nameinuse = 0; for $pp (@{$pcap}) { if (defined($pp->{'names'})) { my $n; for $n (@{$pp->{'names'}}) { if ($n eq "lp$i") { $nameinuse = 1; last; } } if ($nameinuse) { $i++; last; } } } $namefound = 1 - $nameinuse; } $newname = "lp$i";
# Old PPD file name my $ppdfile = sprintf('%s/lpd/lp.ppd', $sysdeps->{'foo-etc'}); # New PPD file name my $nppdfile = sprintf('%s/lpd/%s.ppd', $sysdeps->{'foo-etc'}, $newname); # Rename the printer $p->{'names'}[0] = $newname; my $oldspooldir = $p->{'str'}{'sd'}; $p->{'str'}{'sd'} = sprintf('%s/%s', $sysdeps->{'lpd-dir'}, $newname); if ($p->{'str'}{'af'} =~ /\.ppd$/) { $p->{'str'}{'af'} = $nppdfile; }
# Rename old $ppdfile, if any rename $ppdfile, $nppdfile if (-f $ppdfile); # Rename the spool directory rename $oldspooldir, $p->{'str'}{'sd'} if (-d $oldspooldir);
# Put out warning warn("WARNING: Printer \"lp\" renamed to \"$newname\".\n"); } # Remove 'lp' as alias name my $n; for $n (@{$p->{'names'}}) { if ($n eq 'lp') { $n = ''; } } } push (@newcap, $p); }
my @newprintcap = dump_lpd_printcap($config, \@newcap);
my $printcap = $sysdeps->{'lpd-pcap'}; rename $printcap, "$printcap.old" or die "Cannot backup $printcap!\n"; open PRINTCAP, "> $printcap" or die "Cannot write $printcap!\n"; print PRINTCAP @newprintcap; close PRINTCAP; chmod 0644, $printcap;
return 1; }
sub default_lprng { my ($config) = $_[0];
my $name = $config->{'queue'};
my $pcap = load_lpd_printcap();
# Move the /etc/printcap entry for the chosen printer to the first place # so that LPRng considers it as the default printer
my @newcap; for (@{$pcap}) { push (@newcap, $_) if ($_->{'names'}[0] eq $name); } for (@{$pcap}) { push (@newcap, $_) unless ($_->{'names'}[0] eq $name); }
my @newprintcap = dump_lpd_printcap($config, \@newcap);
my $printcap = $sysdeps->{'lpd-pcap'}; rename $printcap, "$printcap.old" or die "Cannot backup $printcap!\n"; open PRINTCAP, "> $printcap" or die "Cannot write $printcap!\n"; print PRINTCAP @newprintcap; close PRINTCAP; chmod 0644, $printcap;
# In case of LPRng, give SIGHUP to the daemon, LPRng needs this to # recognize the changes if ($config->{'spooler'} eq "lprng") { system("$sysdeps->{'lpd-lpc'} reread > /dev/null 2>&1"); system("$sysdeps->{'lprng-checkpc'} -f > /dev/null 2>&1"); }
return 1; }
sub delete_lpd { my ($config) = $_[0];
my $name = $config->{'queue'};
my $pcap = load_lpd_printcap();
my @newcap; for (@{$pcap}) { push (@newcap, $_) unless ($_->{'names'}[0] eq $name); }
my @newprintcap = dump_lpd_printcap($config, \@newcap);
my $printcap = $sysdeps->{'lpd-pcap'}; rename $printcap, "$printcap.old" or die "Cannot backup $printcap!\n"; open PRINTCAP, "> $printcap" or die "Cannot write $printcap!\n"; print PRINTCAP @newprintcap; close PRINTCAP; chmod 0644, $printcap;
# PPD file name my $ppdfile = sprintf('%s/lpd/%s.ppd', $sysdeps->{'foo-etc'}, $config->{'queue'});
# Rename old $ppdfile, if any rename $ppdfile, "$ppdfile.old" if (-f $ppdfile);
# In case of LPRng, give SIGHUP to the daemon, LPRng needs this to # recognize the changes if ($config->{'spooler'} eq "lprng") { system("$sysdeps->{'lpd-lpc'} reread > /dev/null 2>&1"); system("$sysdeps->{'lprng-checkpc'} -f > /dev/null 2>&1"); }
return 1; }
sub query_lpd { my ($config) = @_;
# User requests data of a printer/driver combo to see the options before # installing a queue if (($opt_P) && ((($config->{'driver'}) && ($config->{'printer'}) && ($config->{'driver'} ne "raw")) || ($config->{'ppdfile'}))) { if ($opt_n) { my $olddatablob = load_lpd_datablob($opt_n); print_perl_combo_data($config, $olddatablob); } else { print_perl_combo_data($config); } return; }
my $i = $ARGV[0]; if (!defined($i)) {$i = 0;}
my $pcap = load_lpd_printcap(); my $p;
if (!$opt_P) { print "<queues>\n"; }
# Query the default printer my $default; if (!defined($config->{'queue'})) { if ($config->{'spooler'} eq "lpd") { # Under LPD the default printer is the printer which has # "lp" as its name or as an alias name my $def_firstname = undef; for $p (@{$pcap}) { if (defined($p->{'names'})) { my $n; for $n (@{$p->{'names'}}) { if ($n eq 'lp') { $def_firstname = $p->{'names'}[0]; last; } } if (defined($def_firstname)) { last; } } } if (defined($def_firstname)) { $default = $def_firstname; if (!$opt_P) { print "<defaultqueue>$def_firstname</defaultqueue>\n"; } } } else { # Under LPRng the default printer is the first entry in # /etc/printcap for $p (@{$pcap}) { if (defined($p->{'names'})) { $default = $p->{'names'}[0]; if (!$opt_P) { print "<defaultqueue>$p->{'names'}[0]" . "</defaultqueue>\n"; } last; } } } }
for $p (@{$pcap}) { # enpty end entry for trailing comments next if !defined($p->{'names'}); # were we invoked for only one queue? next if (defined($config->{'queue'}) and $config->{'queue'} ne $p->{'names'}[0]);
# load the queue data $db->{'dat'} = load_lpd_datablob($p->{'names'}[0]);
# extract the queue data block my $c = $db->{'dat'}{'queuedata'};
if ($opt_P) { if ($p->{'names'}[0] eq $default) { $db->{'dat'}{'queuedata'}{'default'} = 1; } else { $db->{'dat'}{'queuedata'}{'default'} = 0; } $db->{'dat'}{'queuedata'}{'remote'} = 0; my $asciidata = $db->getascii(); $asciidata =~ s/\$VAR1/\$QUEUES[$i]/g; print $asciidata; $i ++; } else { # and get it to standard output dump_config($c); } }
if (!$opt_P) { print "</queues>\n"; }
return; }
### Queue manipulation functions for CUPS
sub setup_cups { my ($config) = $_[0];
# PPD file name # (/etc/foomatic/cups/ will be a link to /etc/cups/ppd/) my $ppdfile = sprintf('%s/ppd/%s.ppd', $sysdeps->{'cups-etc'}, $config->{'queue'});
# Get the data from the former queue if we reconfigure or copy a queue # do also some checking of the user-supplied parameters my ($rawqueue, $newfoomaticdata, $makemodel, $beh) = getoldqueuedata($config, 1);
# Here we set up the command line for the "lpadmin" command my $lpadminline = "$sysdeps->{'cups-admin'} -p \"$config->{'queue'}\" -E";
# Use manufacturer and model as description when no description is # provided if (defined($config->{'desc'})) { $lpadminline .= " -D \"$config->{'desc'}\""; } else { # Before we overwrite the description field with manufacturer # and model, check if there is some old contents my $pconf = load_cups_printersconf(); my $p; my $olddesc; for $p (@{$pconf}) { next if (defined($config->{'queue'}) and $config->{'queue'} ne $p->{'name'}); $olddesc = $p->{'Info'}; } if (!$olddesc) { if (!$rawqueue) { $lpadminline .= " -D \"$makemodel\""; } else { $lpadminline .= " -D \"Raw queue\""; } } }
# Fill in the "location" field if something for it is provided. if (defined($config->{'loc'})) { $lpadminline .= " -L \"$config->{'loc'}\""; }
# PPD file argument for the printer if (!$rawqueue) { $lpadminline .= " -P \'$ppdfile\'"; }
# All URIs ("-c" option) have the same syntax as URIs in CUPS # ("-v" option of "lpadmin"). Here the old "file:/" URIs are # translated to the form which CUPS needs. All other URIs are # simply passed to lpadmin.
my $cupsuri = ""; if (defined($config->{'connect'})) { if ($config->{'connect'} =~ m!^(file|usb|parallel|serial):(.*)$!) { # Translate "file:/" into the prefix needed by CUPS, if # necessary $cupsuri = $2; if ((($cupsuri =~ m!$sysdeps->{'ptal-pipes'}/(.+)$!) || ($cupsuri =~ m!/dev/ptal-printd/(.+)$!) || ($cupsuri =~ m!/var/run/ptal-printd/(.+)$!)) && (-x "$sysdeps->{'cups-backends'}/ptal")) { # Translate URI for ptal-printd (does not work with CUPS # 1.1.12 and newer) to URI for the "ptal" CUPS backend # script (if the script is there) my $devname = $1; $devname =~ s/_/:/; $devname =~ s/_/:/; $cupsuri = "ptal:/$devname"; } elsif ((($cupsuri =~ m!^$sysdeps->{'mtink-pipes'}/(.+)$!) || ($cupsuri =~ m!^/var/mtink/(.+)$!)) && (-x "$sysdeps->{'cups-backends'}/mtink")) { # Translate URI for mtinkd (does not work with CUPS # 1.1.12 and newer) to URI for the "mtink" CUPS backend # script (if the script is there) $cupsuri = "mtink:/$1"; } elsif ($config->{'connect'} =~ m!usb!i) { $cupsuri = cups_usb_device_uri_to_printer_uri($cupsuri); $cupsuri = "usb:$cupsuri"; } elsif (($cupsuri =~ m!lp[0-9]!) || ($cupsuri =~ m!LP[0-9]!)|| ($cupsuri =~ m!parallel!)) { $cupsuri = "parallel:$cupsuri"; } elsif (($cupsuri =~ m!tty!) || ($cupsuri =~ m!TTY!) || ($cupsuri =~ m!serial!)) { $cupsuri = "serial:$cupsuri"; } else { $cupsuri = "file:$cupsuri"; } } elsif (($config->{'connect'} =~ m!^ptal://?([^/].*)$!) && (!-x "$sysdeps->{'cups-backends'}/ptal")) { # If there is no "ptal" backend script for CUPS, use an URI # pointing to the pipe set up by ptal-printd. my $devname = $1; $devname =~ tr/:/_/; $cupsuri = "file:$sysdeps->{'ptal-pipes'}/$devname"; } elsif (($config->{'connect'} =~ m!^mtink:/(.*)$!) && (!-x "$sysdeps->{'cups-backends'}/mtink")) { # If there is no "mtink" backend script for CUPS, use an URI # pointing to the pipe set up by mtinkd. $cupsuri = "file:$sysdeps->{'mtink-pipes'}/$1"; } else { $cupsuri=$config->{'connect'}; } # Correct PTAL URIs: "ptal:/..." for HPOJ 0.9, "ptal://..." for newer # HPOJ if ($cupsuri =~ m!^ptal:/!) { $cupsuri = cups_correct_ptal_uri($cupsuri); } }
# Are there changes in the error handling of the backend? if (((defined($config->{'dd'})) && (((defined($beh->{'dd'})) && ($config->{'dd'} ne $beh->{'dd'})) || ($config->{'dd'} != 0))) || ((defined($config->{'att'})) && (((defined($beh->{'att'})) && ($config->{'att'} ne $beh->{'att'})) || ($config->{'att'} != 1))) || ((defined($config->{'delay'})) && (((defined($beh->{'delay'})) && ($config->{'delay'} ne $beh->{'delay'})) || ($config->{'delay'} != 30)))) { if (!defined($config->{'dd'})) { $config->{'dd'} = (defined($beh->{'dd'}) ? $beh->{'dd'} : 0); } if (!defined($config->{'att'})) { $config->{'att'} = (defined($beh->{'att'}) ? $beh->{'att'} : 1); } if (!defined($config->{'delay'})) { $config->{'delay'} = (defined($beh->{'delay'}) ? $beh->{'delay'} : 30); } $cupsuri = $beh->{'uri'} if !$cupsuri; # Do only add the "beh" wrapper backend when it is really needed # (More than one retry and/or no disabling) and if the queue is not # using the HPLIP ("hp") backend, as otherwise the "hp-toolbox" # will not list the printer any more. HPLIP does infinite retries # in 30-sec intervals anyway. if (($cupsuri) && ($cupsuri !~ m!^hp(fax|):/!) && (($config->{'dd'} != 0) || ($config->{'att'} != 1))) { $cupsuri = sprintf("beh:/%d/%d/%d/%s", $config->{'dd'}, $config->{'att'}, $config->{'delay'}, $cupsuri); } }
if ($cupsuri) { $lpadminline .= " -v \"$cupsuri\""; }
# Directory setup, let the Foomatic PPD directory for CUPS be the same # as /etc/cups/ppd/ (where CUPS stores the PPDs of the installed queues) mkdir $sysdeps->{'foo-etc'}, 0755; symlink "$sysdeps->{'cups-etc'}/ppd/", "$sysdeps->{'foo-etc'}/cups";
# In CUPS we never have a $postpipe # (when we get a $postpipe from a source PPD file from another # spooler, we don't need to remove it really, because it will be # ignored by foomatic-rip, uncomment this to remove it)
#$db->{'dat'}{'postpipe'} = "";
# Generate/write te PPD file writeppdfile($config, $ppdfile, $rawqueue, $newfoomaticdata);
# Execute the lpadmin command to set up the new queue
if (system $lpadminline) { # Remove the config files unlink "$ppdfile" if (-f "$ppdfile"); # Revert changed config files rename "$ppdfile.old", "$ppdfile" if (-f "$ppdfile.old"); die "Could not set up/change the queue \"$config->{'queue'}\"!\n"; }
return 1; }
sub default_cups { my ($config) = $_[0]; if ($< == 0) { # (/etc/cups/printers.conf can only be manipulated by root) # This line sets the default printer in /etc/cups/printers.conf my $command = "$sysdeps->{'cups-admin'} -d " . "\"$config->{'queue'}\" > /dev/null"; # Do it! (Ignore errors silently) system $command; } # This line sets the default printer in /etc/cups/lpoptions # (required for setting a remote queue as default) my $command = "$sysdeps->{'cups-lpoptions'} -d " . "\"$config->{'queue'}\" > /dev/null"; # Do it! system $command and die "Unable to set queue \"$config->{'queue'}\" as default!\n"; }
sub delete_cups { my ($config) = $_[0];
# This line deletes the old printer queue my $queuedeleteline = "$sysdeps->{'cups-admin'} -x \"$config->{'queue'}\"";
# Do it! system $queuedeleteline and die "Unable to delete queue \"$config->{'queue'}\"!\n";
return 1; }
sub query_cups { my ($config) = @_;
# User requests data of a printer/driver combo to see the options before # installing a queue if (($opt_P) && ((($config->{'driver'}) && ($config->{'printer'}) && ($config->{'driver'} ne "raw")) || ($config->{'ppdfile'}))) { if ($opt_n) { my $olddatablob = load_cups_datablob($opt_n); print_perl_combo_data($config, $olddatablob); } else { print_perl_combo_data($config); } return; }
my $i = $ARGV[0]; if (!defined($i)) {$i = 0;}
my $pconf = load_cups_printersconf(); if (defined($opt_r)) {$opt_r = undef;} my $p;
if (!$opt_P) { print "<queues>\n"; }
# Query the default printer my $default = ''; if (!defined($config->{'queue'})) { open DEFAULT, "$sysdeps->{'cups-lpstat'} -d |" or die "Could not run $sysdeps->{'cups-lpstat'}!\n"; my $defaultstr = <DEFAULT>; close DEFAULT; if ($defaultstr =~ m!\S+:\s+(\S+)$!) { $default = $1; if (!$opt_P) { print "<defaultqueue>$default</defaultqueue>\n"; } } }
for $p (@{$pconf}) { # were we invoked for only one queue? next if (defined($config->{'queue'}) and $config->{'queue'} ne $p->{'name'});
# load the queue data $db->{'dat'} = load_cups_datablob($p->{'name'});
# Enter info for remote queue if ($p->{'remote'}) { $db->{'dat'}{'queuedata'}{'foomatic'} = 0; $db->{'dat'}{'queuedata'}{'spooler'} = 'cups'; $db->{'dat'}{'queuedata'}{'queue'} = $p->{'name'}; $db->{'dat'}{'queuedata'}{'connect'} = $p->{'DeviceURI'}; $db->{'dat'}{'queuedata'}{'description'} = $p->{'Info'}; $db->{'dat'}{'queuedata'}{'loc'} = $p->{'Location'}; $db->{'dat'}{'queuedata'}{'remote'} = 1; } else { $db->{'dat'}{'queuedata'}{'remote'} = 0; }
# extract the queue data block my $c = $db->{'dat'}{'queuedata'};
if ($opt_P) { if ($p->{'name'} eq $default) { $db->{'dat'}{'queuedata'}{'default'} = 1; } else { $db->{'dat'}{'queuedata'}{'default'} = 0; } my $asciidata = $db->getascii(); $asciidata =~ s/\$VAR1/\$QUEUES[$i]/g; print $asciidata; $i ++; } else { # and get it to standard output dump_config($c); } }
if (!$opt_P) { print "</queues>\n"; } return; }
### Queue manipulation functions for PDQ
sub setup_pdq { my ($config) = $_[0];
# Read the previous /usr/lib/pdq/printrc my $printrc = load_pdq_printrc();
my ($ppdfile, $driverfile, $entry, $reconf, $p); $reconf = 0; for $p (@{$printrc}) { if ((defined($p->{'name'})) && ($p->{'name'} eq $config->{'queue'})) { $entry = $p; $reconf = 1; last;
use Data::Dumper; print "Reconfigure of ", Dumper($p); } }
# Config file names $ppdfile = sprintf('%s/pdq/%s.ppd', $sysdeps->{'foo-etc'}, $config->{'queue'}); $driverfile = sprintf('%s/pdq/driverdescr/%s.pdq', $sysdeps->{'foo-etc'}, $config->{'queue'});
# Get the data from the former queue if we reconfigure or copy a queue # do also some checking of the user-supplied parameters my ($rawqueue, $newfoomaticdata, $makemodel) = getoldqueuedata($config, $reconf);
# Set the initial line of the "printer" block in /usr/lib/pdq/printrc $entry->{'name'} = $config->{'queue'};
# Location field if ((defined($config->{'loc'})) || (!$reconf)) { $entry->{'location'} = "\"$config->{'loc'}\""; }
# Model/Description field if (defined($config->{'desc'})) { $entry->{'model'} = "\"$config->{'desc'}\""; } elsif (!$entry->{'model'}) { if (!$rawqueue) { $entry->{'model'} = "\"$makemodel\""; } else { $entry->{'model'} = "\"Raw printer\""; } }
# Create directories mkdir $sysdeps->{'foo-etc'}, 0755; mkdir $sysdeps->{'foo-etc'} . '/pdq', 0755; mkdir $sysdeps->{'foo-etc'} . '/pdq/driverdescr', 0755; # Make the printer driver descriptions in /etc/foomatic/pdq visible # for PDQ # symlink $sysdeps->{'foo-etc'} . '/pdq', $sysdeps->{'pdq-foomatic'};
# Save old driver file, use the "~" to make it appear an editor # backup so that PDQ does not parse it. # Save old $driverfile, if any rename $driverfile, "$driverfile.old~" if (-f $driverfile);
# Generate/write the PPD file writeppdfile($config, $ppdfile, $rawqueue, $newfoomaticdata);
# Create driver description file if ($rawqueue) { system("$sysdeps->{'foomatic-rip'} --genrawpdq $driverfile") and die "Cannot create $driverfile!\n"; } else { system("$sysdeps->{'foomatic-rip'} --ppd \'$ppdfile\' --genpdq " . "$driverfile") and die "Cannot create $driverfile!\n"; }
# PDQ configuration file
# Driver fields
# Extract driver name my $driverdesc = `cat $driverfile`; $driverdesc =~ m!^\s*driver\s*(\"\S*\-\d+\")!m;
# Driver-specific entries $entry->{'driver'} = $1; $entry->{'driver_opts'} = "\{ \}"; $entry->{'driver_args'} = "\{ \}";
# Interface fields
# All URIs ("-c" option) have the same syntax as URIs in CUPS ("-v" # option of "lpadmin"). if ($config->{'connect'} =~ m!^(file|usb|parallel|serial):(.*)!) { # Local printer or printing to a file my $file = $2; if ($config->{'connect'} =~ m!^usb://!) { # Queue with printer-bound USB URI transferred from CUPS, # as PDQ does not support these URIs, translate it # back to a standard USB device URI $file = cups_usb_printer_uri_to_device_uri($file); } if (! -e $file) { warn "The device or file $file doesn't exist? " . "Working anyway.\n"; } $entry->{'interface'} = "\"local-port\""; $entry->{'interface_opts'} = "\{ \}"; $entry->{'interface_args'} = "\{ \"PORT\" = \"$file\" \}"; } elsif ($config->{'connect'} =~ m!^ptal://?([^/].*)$!) { # HPOJ MLC protocol my $devname = $1; $devname =~ tr/:/_/; $entry->{'interface'} = "\"local-port\""; $entry->{'interface_opts'} = "\{ \}"; $entry->{'interface_args'} = "\{ \"PORT\" = " . "\"$sysdeps->{'ptal-pipes'}/$devname\" \}"; } elsif ($config->{'connect'} =~ m!^mtink:/(.+)$!) { # Printing through "mtinkd" $entry->{'interface'} = "\"local-port\""; $entry->{'interface_opts'} = "\{ \}"; $entry->{'interface_args'} = "\{ \"PORT\" = " . "\"$sysdeps->{'mtink-pipes'}/$1\" \}"; } elsif ($config->{'connect'} =~ m!^lpd://([^/]+)/([^/]+)$!) { # Remote LPD my $remhost = $1; my $remqueue = $2; $entry->{'interface'} = "\"bsd-lpd\""; $entry->{'interface_opts'} = "\{ \}"; $entry->{'interface_args'} = "\{ \"QUEUE\" = \"$remqueue\", \"REMOTE_HOST\" = " . "\"$remhost\" \}"; } elsif ($config->{'connect'} =~ m!^socket://([^/:]+):([0-9]+)/?$!) { # Socket (AppSocket/HP JetDirect) my $remhost = $1; my $remport = $2; $entry->{'interface'} = "\"tcp-port\""; $entry->{'interface_opts'} = "\{ \}"; $entry->{'interface_args'} = "\{ \"REMOTE_PORT\" = \"$remport\", \"REMOTE_HOST\" = " . "\"$remhost\" \}"; } elsif ($config->{'connect'}) { die ("The URI \"$config->{'connect'}\" is not supported " . "for PDQ or you have\nmistyped.\n"); } elsif (!$reconf) { die "You must specify a connection with -c.\n"; }
# Add to the printrc if it is a new entry if (!$reconf) { push(@{$printrc}, $entry); }
# Write back the modified printrc file my $printrcname = $sysdeps->{'pdq-printrc'}; rename $printrcname, "$printrcname.old" or die "Cannot backup $printrcname!\n"; open PRINTRC, "> $printrcname" or die "Cannot write $printrcname!\n"; print PRINTRC dump_pdq_printrc($printrc); close PRINTRC; chmod 0644, $printrcname;
return 1; }
sub default_pdq { my ($config) = $_[0];
# Determine the name of the config file to modify my $printrcname = ""; if ($< == 0) { $printrcname = "$sysdeps->{'pdq-printrc'}"; if (!(-f $printrcname)) {die "No file $printrcname!"}; } else { $printrcname = "$ENV{HOME}/.printrc"; if (!(-f $printrcname)) {system "touch $printrcname"}; }
# Read the config file open PRINTRC, "$printrcname" or die "Cannot open $printrcname!"; my @printrc = <PRINTRC>; close PRINTRC;
# Remove all valid "default_printer" lines ($_ =~ /^\s*default_printer/ and $_="") foreach @printrc; # Insert the new "default_printer" line push @printrc, "default_printer $config->{'queue'}\n";
# Write back the modified config file open PRINTRC, "> $printrcname" or die "Cannot open $printrcname!"; print PRINTRC @printrc; close PRINTRC;
}
sub delete_pdq { my ($config) = $_[0];
my $name = $config->{'queue'};
my $printrc = load_pdq_printrc();
my @newrc; for (@{$printrc}) { push (@newrc, $_) unless (defined($_->{'name'}) && ($_->{'name'} eq $name)); }
my @newprintrc = dump_pdq_printrc(\@newrc);
my $printrcname = $sysdeps->{'pdq-printrc'}; rename $printrcname, "$printrcname.old" or die "Cannot backup $printrcname!\n"; open PRINTRC, "> $printrcname" or die "Cannot write $printrcname!\n"; print PRINTRC @newprintrc; close PRINTRC; chmod 0644, $printrcname;
# Config file names my $ppdfile = sprintf('%s/pdq/%s.ppd', $sysdeps->{'foo-etc'}, $config->{'queue'}); my $driverfile = sprintf('%s/pdq/driverdescr/%s.pdq', $sysdeps->{'foo-etc'}, $config->{'queue'});
# Rename old $ppdfile, if any rename $ppdfile, "$ppdfile.old" if (-f $ppdfile); # Rename old driverfile, if any, use the "~" to make it appear an # editor backup so that PDQ does not parse it. # Rename old $driverfile, if any rename $driverfile, "$driverfile.old~" if (-f $driverfile);
return 1; }
sub query_pdq { my ($config) = @_;
# User requests data of a printer/driver combo to see the options before # installing a queue if (($opt_P) && ((($config->{'driver'}) && ($config->{'printer'}) && ($config->{'driver'} ne "raw")) || ($config->{'ppdfile'}))) { if ($opt_n) { my $olddatablob = load_pdq_datablob($opt_n); print_perl_combo_data($config, $olddatablob); } else { print_perl_combo_data($config); } return; }
my $i = $ARGV[0]; if (!defined($i)) {$i = 0;}
my $printrc = load_pdq_printrc(); my $p;
if (!$opt_P) { print "<queues>\n"; }
# Query the default printer my $default; if (!defined($config->{'queue'})) { open DEFAULT, "$sysdeps->{'pdq-print'} -h 2>&1 |" or die "Could not run $sysdeps->{'pdq-print'}!\n"; my $defaultstr = join('', <DEFAULT>); close DEFAULT; if ($defaultstr =~ m!The\s+default\s+printer\s+is\s+(\S+)$!m) { $default = $1; if (!$opt_P) { print "<defaultqueue>$default</defaultqueue>\n"; } } }
for $p (@{$printrc}) {
# Omit non-printer-block items next if (!(defined($p->{'name'}))); # were we invoked for only one queue? next if (defined($config->{'queue'}) and $config->{'queue'} ne $p->{'name'});
# load the queue data $db->{'dat'} = load_pdq_datablob($p->{'name'});
# extract the queue data block my $c = $db->{'dat'}{'queuedata'};
if ($opt_P) { if ($p->{'name'} eq $default) { $db->{'dat'}{'queuedata'}{'default'} = 1; } else { $db->{'dat'}{'queuedata'}{'default'} = 0; } $db->{'dat'}{'queuedata'}{'remote'} = 0; my $asciidata = $db->getascii(); $asciidata =~ s/\$VAR1/\$QUEUES[$i]/g; print $asciidata; $i ++; } else { # and get it to standard output dump_config($c); } }
if (!$opt_P) { print "</queues>\n"; } return; }
### Queue manipulation functions for PPR
sub setup_ppr { my ($config) = $_[0];
# Read the previous configuration my $printrc = load_ppr_printers_conf();
my ($ppdfile, $entry, $reconf, $p); $reconf = 0; for $p (@{$printrc}) { if ((defined($p->{'name'})) && ($p->{'name'} eq $config->{'queue'})) { $entry = $p; $reconf = 1; last;
use Data::Dumper; print "Reconfigure of ", Dumper($p); } }
# PPD file name $ppdfile = sprintf('%s/ppr/%s.ppd', $sysdeps->{'foo-etc'}, $config->{'queue'});
# Determine the PPR version in use my $pprversion; if (open VER, "$sysdeps->{'ppr-pprd'} --version |") { my $ver = <VER>; close VER; $ver =~ /^\D*(\d+)\.(\d+)(\.(\d+)|)((a|alpha|b|beta|r|rc)(\d+|)|)/; $pprversion = (1e8 * $1 + 1e6 * $2 + 1e4 * $4 + ($5 ? 100 * (ord(uc($6)) - 64) + $7 : 9999)) / 1e8; } else { # Could not determine version, so we set it to 0 (oldest possible) $pprversion = 0; }
# Get the data from the former queue if we reconfigure or copy a queue # do also some checking of the user-supplied parameters my ($rawqueue, $newfoomaticdata, $makemodel) = getoldqueuedata($config, $reconf);
# Read out previous interface settings my $interface = ""; my $address = ""; my $options = ""; my $interface_options = ""; if ($reconf) { $interface = $entry->{'Interface'}; $address = $entry->{'Address'}; $interface_options = $entry->{'Options'}; if (($interface eq "foomatic-rip") || ($interface eq "ppromatic")) { if ($interface_options =~ /backend=(\S+)/) { $interface = $1; $interface_options =~ s/backend=(\S+)//; if ($interface_options =~ /^\s*$/) { $interface_options = ""; } } else { $interface = ""; } } }
# All URIs ("-c" option) have the same syntax as URIs in CUPS ("-v" # option of "lpadmin").
if (defined($config->{'connect'})) { $interface_options =~ s/smbuser=(\S+)//; $interface_options =~ s/smbpassword=(\S+)//; if ($config->{'connect'} =~ m!^(file|usb|parallel|serial):(.*)!) { # Local printer or printing to a file $address = $2; if ($config->{'connect'} =~ m!^usb://!) { # Queue with printer-bound USB URI transferred from CUPS, # as PPR does not support these URIs, translate it # back to a standard USB device URI $address = cups_usb_printer_uri_to_device_uri($address); } if (! -e $address) { warn "The device or file $address doesn't exist? " . "Working anyway.\n"; } if (($address =~ m!usb!) || ($address =~ m!USB!) || ($address =~ m!$sysdeps->{'ptal-pipes'}!) || ($address =~ m!/dev/ptal-printd!) || ($address =~ m!/var/run/ptal-printd!) || ($address =~ m!$sysdeps->{'mtink-pipes'}!) || ($address =~ m!/var/mtink!)) { $interface = "simple"; } elsif (($address =~ m!lp[0-9]!) || ($address =~ m!LP[0-9]!) || ($address =~ m!parallel!)) { $interface = "parallel"; } elsif (($address =~ m!tty!) || ($address =~ m!TTY!) || ($address =~ m!serial!)) { $interface = "serial"; } else { $interface = "dummy"; } $options = ""; } elsif ($config->{'connect'} =~ m!^ptal://?([^/].*)$!) { # HPOJ MLC protocol my $devname = $1; $devname =~ tr/:/_/; $address = "$sysdeps->{'ptal-pipes'}/$devname"; $interface = "simple"; $options = ""; } elsif ($config->{'connect'} =~ m!^mtink:/(.+)$!) { # Printing through "mtinkd" $address = "$sysdeps->{'mtink-pipes'}/$1"; $interface = "simple"; $options = ""; } elsif ($config->{'connect'} =~ m!^lpd://([^/]+)/([^/]+)$!) { # Remote LPD my $remhost = $1; my $remqueue = $2; $address = "${remqueue}\@${remhost}"; $interface = "lpr"; $options = ""; } elsif ($config->{'connect'} =~ m!^socket://([^/:]+):([0-9]+)/?$!) { # Socket (AppSocket/HP JetDirect) my $remhost = $1; my $remport = $2; $address = "$remhost:$remport"; $interface = "tcpip"; $options = ""; } elsif ($config->{'connect'} =~ m!^smb://(.*)$!) { # SMB (Printer on Windows server) my $parameters = $1; # Get the user's login and password from the URI my $smbuser = ""; my $smbpassword = ""; if ($parameters =~ m!([^@]*)@([^@]+)!) { my $login = $1; $parameters = $2; if ($login =~ m!([^:]*):([^:]*)!) { $smbuser = $1; $smbpassword = $2; } else { $smbuser = $login; $smbpassword = ""; } } else { $smbuser = "GUEST"; $smbpassword = ""; } # When a password is given, a user name should be given, too. if (($smbpassword ne "") && ($smbuser eq "")) { $smbuser = "GUEST"; } # The "smb" interface of PPR uses "ppr" as the SMB user when no # user name is given. Usually one does not have such a user name # under Windows. So use "GUEST" if no user name is given. if ($smbuser eq "") { $smbuser = "GUEST"; } # Set the options for PPR's "smb" interface $options = ""; if ($smbuser ne "") { $options = "smbuser=\"$smbuser\""; if ($smbpassword ne "") { $options .= " smbpassword=\"$smbpassword\""; } } # Get the workgroup, server, and share name my $workgroup = ""; my $smbserver = ""; my $smbshare = ""; if ($parameters =~ m!([^/]*)/([^/]+)/([^/]+)$!) { $workgroup = $1; $smbserver = $2; $smbshare = $3; } elsif ($parameters =~ m!([^/]+)/([^/]+)$!) { $workgroup = ""; $smbserver = $1; $smbshare = $2; } else { die "The \"smb://\" URI must at least contain the " . "server name and the share name!\n"; } $address = "//$smbserver/$smbshare"; $interface = "smb"; } else { die ("The URI \"$config->{'connect'}\" is not supported for " . "PPR or you have\nmistyped.\n"); } } elsif (!$reconf) { die "You must specify a connection with -c.\n"; }
# Here we set up the command line for the "ppad interface" and the # "ppad options" commands my $ppad_interface = ""; my $ppad_options = ""; my $ppad_rip = ""; if ($rawqueue) { $ppad_interface = "$sysdeps->{'ppr-ppad'} interface " . "\"$config->{'queue'}\" $interface \"$address\""; $ppad_options = "$sysdeps->{'ppr-ppad'} options " . "\"$config->{'queue'}\" $options $interface_options"; $ppad_rip = "$sysdeps->{'ppr-ppad'} " . "rip \"$config->{'queue'}\""; } else { if ($pprversion >= 1.50000102 ) { #1.50a2 $ppad_interface = "$sysdeps->{'ppr-ppad'} interface " . "\"$config->{'queue'}\" $interface \"$address\""; $ppad_options = "$sysdeps->{'ppr-ppad'} options " . "\"$config->{'queue'}\" $options $interface_options"; if ($db->{'dat'}{'id'}) { $ppad_rip = "$sysdeps->{'ppr-ppad'} " . "rip \"$config->{'queue'}\" foomatic-rip x" . # PPR 1.50a2 has a bug and needs at least one option for # the command line of the PPR RIP, therefore we add the # "0" in this case. The number is very likely not the # name of any boolean option, so it will be ignored by # foomatic-rip (($pprversion < 1.50000103 ) ? " 0" : ""); } else { $ppad_rip = "$sysdeps->{'ppr-ppad'} " . "rip \"$config->{'queue'}\""; } } else { $ppad_interface = "$sysdeps->{'ppr-ppad'} interface " . "\"$config->{'queue'}\" foomatic-rip \"$address\""; $ppad_options = "$sysdeps->{'ppr-ppad'} options " . "\"$config->{'queue'}\" backend=\"$interface\" " . "$options $interface_options"; $ppad_rip = "$sysdeps->{'ppr-ppad'} " . "rip \"$config->{'queue'}\""; } }
# Execute the ppad commands to set up the new queue
if ((system $ppad_interface) || (system $ppad_options) || (system $ppad_rip)) { die "Could not set up/change the queue \"$config->{'queue'}\"!\n"; }
# Use manufacturer and model as description when no description is # provided my($comment, $olddesc); if (defined($config->{'desc'})) { $comment = $config->{'desc'}; } else { # Before we overwrite the description field with manufacturer # and model, check if there is some old contents if (($reconf) && ($entry->{'Comment'})) { $olddesc = $entry->{'Comment'}; } if (!$olddesc) { if (!$rawqueue) { $comment = "$makemodel"; } else { $comment = "Raw queue"; } } } if ($comment) { my $ppad_comment = "$sysdeps->{'ppr-ppad'} comment " . "\"$config->{'queue'}\" \"$comment\""; if (system $ppad_comment) { warn "Could not set description for the queue " . "\"$config->{'queue'}\"!\n"; } }
# Fill in the "location" field if something for it is provided. if (defined($config->{'loc'})) { my $ppad_location = "$sysdeps->{'ppr-ppad'} location " . "\"$config->{'queue'}\" \"$config->{'loc'}\""; if (system $ppad_location) { warn "Could not set location for the queue " . "\"$config->{'queue'}\"!\n"; } }
# Various file setup mkdir $sysdeps->{'foo-etc'}, 0755; mkdir $sysdeps->{'foo-etc'} . '/ppr', 0755;
# Generate/write the PPD file writeppdfile($config, $ppdfile, $rawqueue, $newfoomaticdata);
if ($rawqueue) { my $ppad_ppd = "$sysdeps->{'ppr-ppad'} ppd " . "\"$config->{'queue'}\" \"\" 2> /dev/null"; if (!system $ppad_ppd) { # Automatic input tray selection not activated by default, # because the feature requires manual choice of the paper types # in the trays and other spoolers than PPR do not have automatic # paper tray selection. In addition "ppop media <queue>" is # broken for printers with a high number of input trays in their # PPD files. #my $ppad_bins = "$sysdeps->{'ppr-ppad'} bins delete " . #"\"$config->{'queue'}\" \"" . #join ('" "', @{$entry->{'Bins'}}) . "\""; #if (system $ppad_bins) { #warn "Could not set paper input trays for the " . #"queue \"$config->{'queue'}\"!\n"; #} my $ppad_deffiltopts = "$sysdeps->{'ppr-ppad'} " . "deffiltopts \"$config->{'queue'}\" 2> /dev/null"; if (system $ppad_deffiltopts) { warn "Could not set \"DefFiltOpts\" entry for " . "the queue \"$config->{'queue'}\"!\n"; } } else { die "Could not set PPD for the queue \"$config->{'queue'}\"!\n"; } } else { my $ppad_ppd = "$sysdeps->{'ppr-ppad'} ppd " . "\"$config->{'queue'}\" \"$ppdfile\" 2> /dev/null"; if (!system $ppad_ppd) { # Automatic input tray selection not activated by default, # because the feature requires manual choice of the paper types # in the trays and other spoolers than PPR do not have automatic # paper tray selection. In addition "ppop media <queue>" is # broken for printers with a high number of input trays in their # PPD files. #my $ppad_bins = "$sysdeps->{'ppr-ppad'} bins ppd " . #"\"$config->{'queue'}\""; #if (system $ppad_bins) { #warn "Could not set paper input trays for the " . #"queue \"$config->{'queue'}\"!\n"; #} my $ppad_deffiltopts = "$sysdeps->{'ppr-ppad'} " . "deffiltopts \"$config->{'queue'}\" 2> /dev/null"; if (system $ppad_deffiltopts) { warn "Could not set \"DefFiltOpts\" entry for the " . "queue \"$config->{'queue'}\"!\n"; } } else { die "Could not set PPD for the queue \"$config->{'queue'}\"!\n"; } }
if ($rawqueue) {
# If we have a raw queue, delete the PPD file if there is still # one from a former queue.
unlink "$ppdfile" if (-f "$ppdfile"); } else {
# Clean up "Switchset" entry
my @switchset = split('|', $entry->{'Switchset'}); my @newswitchset = (); for my $option (@switchset) { if (!(($option =~ /^F\s*\*([^\*\s=:]+)\s+([^\*\s=:]+)\s*$/) || ($option =~ /^F\s*([^\*\s=:]+)\s*=\s*([^\*\s=:]+)\s*$/) || ($option =~ /^F\s*\*([^\*\s=:]+)\s*$/) || ($option =~ /^F\s*([^\*\s=:]+)\s*$/))) { # The option is not a PPD option, keep it. # PPD options are incorporated in the PPD file now and so # they can be dropped in the "Switchset". if ($option =~ /^\s*(\S)(.*)$/) { push (@newswitchset, "-$1 \"$2\""); } } } my $ppad_switchset = "$sysdeps->{'ppr-ppad'} switchset " . "\"$config->{'queue'}\" " . join (' ', @newswitchset); if (system $ppad_switchset) { warn "Could not set switchset for the queue " . "\"$config->{'queue'}\"!\n"; }
# Check, if there is a PJL option and set the "Jobbreak" to "none" # because otherwise there is a Ctrl+D between the PJL frame added # by foomatic-rip and the PostScript job. This breaks printing of # certain PS files as the CUPS test page.
my $pjloption = 0; for my $arg (@{$db->{'dat'}->{'args'}}) { if ($arg->{'style'} eq "J") { $pjloption = 1; last; } } if ($pjloption) { my $ppad_jobbreak = "$sysdeps->{'ppr-ppad'} jobbreak " . "\"$config->{'queue'}\" none"; if (system $ppad_jobbreak) { warn "Could not set \"Jobbreak\" entry for the " . "queue \"$config->{'queue'}\"!\n"; } } }
return 1; }
sub default_ppr { my ($config) = $_[0]; # The default printer under PPR is the printer named "default". To be # able to easily switch the default printer we set up a printer group # named "default" containing the chosen default printer as its only # member. If there is already a printer called "default", we rename it.
my $name = $config->{'queue'}; my $printrc = load_ppr_printers_conf(); my $printerfound = 0; for my $p (@{$printrc}) { if ($p->{'name'} eq $name) { $printerfound = 1; } # Rename a printer whose name is 'default' if ($p->{'name'} eq 'default') { # Search for a free name my $i = 0; my $namefound = 0; my $newname = ""; while(!$namefound) { my $pp; my $nameinuse = 0; for $pp (@{$printrc}) { if (defined($pp->{'name'})) { if ($pp->{'name'} eq "default$i") { $nameinuse = 1; $i++; last; } } } $namefound = 1 - $nameinuse; } $newname = "default$i"; # If the printer we want to use as default printer has the # name "default", we must use the new name as the member name # in the default group. if ($name eq "default") { $name = $newname; } # Do the renaming # Copy the queue ... if (system("foomatic-configure -s ppr -n $newname -C default")){ die "Could not copy the queue \"default\" into the " . "queue \"$newname\"!\n"; } # ... and remove the original one if (system("foomatic-configure -s ppr -n default -R")) { die "Could not remove the queue \"default\"!\n"; } warn "Renamed the printer\"default\" to \"$newname\"!\n"; } }
# The desired default printer exists? Then make it the default if ($printerfound) { # Create a group named "default" with only this printer as member my $ppad_group = "$sysdeps->{'ppr-ppad'} group members " . "default \"$name\""; if (system $ppad_group) { warn "Could not create a group to make the queue \"$name\" " . "the default!\n"; } }
}
sub delete_ppr { my ($config) = $_[0];
# This line deletes the old printer queue my $queuedeleteline = "$sysdeps->{'ppr-ppad'} delete " . "\"$config->{'queue'}\"";
# Do it! system $queuedeleteline and die "Unable to delete queue \"$config->{'queue'}\"!\n";
# Rename the PPD file
# PPD file name my $ppdfile = sprintf('%s/ppr/%s.ppd', $sysdeps->{'foo-etc'}, $config->{'queue'});
# Rename old $ppdfile, if any rename "$ppdfile", "$ppdfile.old" if (-f "$ppdfile");
return 1; }
sub query_ppr { my ($config) = @_;
# User requests data of a printer/driver combo to see the options before # installing a queue if (($opt_P) && ((($config->{'driver'}) && ($config->{'printer'}) && ($config->{'driver'} ne "raw")) || ($config->{'ppdfile'}))) { if ($opt_n) { my $olddatablob = load_ppr_datablob($opt_n); print_perl_combo_data($config, $olddatablob); } else { print_perl_combo_data($config); } return; }
my $i = $ARGV[0]; if (!defined($i)) {$i = 0;}
my $pconf = load_ppr_printers_conf(); if (defined($opt_r)) {$opt_r = undef;} my $p;
if (!$opt_P) { print "<queues>\n"; }
# Query the default printer my $default; if (!defined($config->{'queue'})) { for $p (@{$pconf}) { if ($p->{'default'}) { $default = $p->{'name'}; if (!$opt_P) { print "<defaultqueue>$p->{'name'}</defaultqueue>\n"; } last; } } }
for $p (@{$pconf}) { # were we invoked for only one queue? next if (defined($config->{'queue'}) and $config->{'queue'} ne $p->{'name'});
# load the queue data $db->{'dat'} = load_ppr_datablob($p->{'name'});
# extract the queue data block my $c = $db->{'dat'}{'queuedata'}; if ($opt_P) { if ($p->{'name'} eq $default) { $db->{'dat'}{'queuedata'}{'default'} = 1; } else { $db->{'dat'}{'queuedata'}{'default'} = 0; } $db->{'dat'}{'queuedata'}{'remote'} = 0; my $asciidata = $db->getascii(); $asciidata =~ s/\$VAR1/\$QUEUES[$i]/g; print $asciidata; $i ++; } else { # and get it to standard output dump_config($c); } }
if (!$opt_P) { print "</queues>\n"; } return; }
### Queue manipulation functions for direct, spooler-less printing
sub setup_direct { my ($config) = $_[0];
# Read the previous config file my $pconfig = load_direct_config();
my ($entry, $reconf, $p); for $p (@{$pconfig}) { if ($p->{'name'} eq $config->{'queue'}) { $entry = $p; $reconf = 1; last;
use Data::Dumper; print "Reconfigure of ", Dumper($p); } }
# PPD file name my $ppdfile = sprintf('%s/direct/%s.ppd', $sysdeps->{'foo-etc'}, $config->{'queue'});
# Get the data from the former queue if we reconfigure or copy a queue # do also some checking of the user-supplied parameters my ($rawqueue, $newfoomaticdata, $makemodel) = getoldqueuedata($config, $reconf);
# Set the printer queue name $entry->{'name'} = $config->{'queue'};
# Use manufacturer and model as description when no description is # provided if (defined($config->{'desc'})) { $entry->{'desc'} = $config->{'desc'}; } else { # Before we overwrite the description field with manufacturer # and model, check if there is some old contents my( $olddesc ); if (($reconf) && ($entry->{'desc'})) { $olddesc = $entry->{'desc'}; } if (!$olddesc) { $entry->{'desc'} = "$makemodel"; } }
# Fill in the "location" field if something for it is provided. if (defined($config->{'loc'})) { $entry->{'loc'} = $config->{'loc'}; }
# If the printing jobs should not be passed to standard output, put the # command line into $postpipe (for example for Socket, Samba, parallel # port ...) my $postpipe = "";
if ((!$reconf) or ($config->{'connect'})) { # Set up connection type
# All URIs ("-c" option) have the same syntax as URIs in CUPS ("-v" # option of "lpadmin"). if ($config->{'connect'} =~ m!^(file|usb|parallel|serial):(.*)!) { # Local printer or printing to a file my $file = $2; if ($config->{'connect'} =~ m!^usb://!) { # Queue with printer-bound USB URI transferred from CUPS, # as spooler-less printing does not support these URIs, # translate it back to a standard USB device URI $file = cups_usb_printer_uri_to_device_uri($file); } if (! -e $file) { warn "The device or file $file doesn't exist? " . "Working anyway.\n"; } if (($file =~ m!^$sysdeps->{'ptal-pipes'}/(.+)$!) || ($file =~ m!^/dev/ptal-printd/(.+)$!) || ($file =~ m!^/var/run/ptal-printd/(.+)$!)) { # Translate URI for ptal-printd to postpipe using the # "ptal-connect" command my $devname = $1; $devname =~ s/_/:/; $devname =~ s/_/:/; $postpipe = "$sysdeps->{'ptal-connect'} $devname -print"; } else { $postpipe = "$sysdeps->{'cat'} > $file"; } } elsif ($config->{'connect'} =~ m!^ptal://?([^/].*)$!) { # HPOJ MLC protocol my $devname = $1; $postpipe = "$sysdeps->{'ptal-connect'} $devname -print"; } elsif ($config->{'connect'} =~ m!^mtink:/(.+)$!) { # Printing through "mtinkd" $postpipe = "$sysdeps->{'cat'} > $sysdeps->{'mtink-pipes'}/$1"; } elsif ($config->{'connect'} =~ m!^lpd://([^/]+)/([^/]+)$!) { # Remote LPD my $remhost = $1; my $remqueue = $2; $postpipe = "$sysdeps->{'rlpr'} -q -h -P $remqueue\@$remhost"; } elsif ($config->{'connect'} =~ m!^socket://([^/:]+):([0-9]+)/?$!){ # Socket (AppSocket/HP JetDirect) my $remhost = $1; my $remport = $2; $postpipe = "$sysdeps->{'nc'} -w 1 $remhost $remport"; } elsif ($config->{'connect'} =~ m!^smb://(.*)$!) { # SMB (Printer on Windows server) my $parameters = $1; # Get the user's login and password from the URI my $smbuser = ""; my $smbpassword = ""; if ($parameters =~ m!([^@]*)@([^@]+)!) { my $login = $1; $parameters = $2; if ($login =~ m!([^:]*):([^:]*)!) { $smbuser = $1; $smbpassword = $2; } else { $smbuser = $login; $smbpassword = ""; } } else { $smbuser = "GUEST"; $smbpassword = ""; } # Get the workgroup, server, and share name my $workgroup = ""; my $smbserver = ""; my $smbshare = ""; if ($parameters =~ m!([^/]*)/([^/]+)/([^/]+)$!) { $workgroup = $1; $smbserver = $2; $smbshare = $3; } elsif ($parameters =~ m!([^/]+)/([^/]+)$!) { $workgroup = ""; $smbserver = $1; $smbshare = $2; } else { die "The \"smb://\" URI must at least contain the " . "server name and the share name!\n"; } # Set up the command line for printing on the SMB server $postpipe = "$sysdeps->{'smbclient'} \"//$smbserver/$smbshare\""; if ($smbpassword ne "") { warn("WARNING: smbclient password is visible in PPD file\n"); $postpipe .= " $smbpassword"; } if ($smbuser ne "") {$postpipe .= " -U $smbuser";} if ($workgroup ne "") {$postpipe .= " -W $workgroup";} $postpipe .= " -N -P -c 'print -' "; } elsif ($config->{'connect'} =~ m!^ncp://(.*)$!) { my $parameters = $1; # Get the user's login and password from the URI my $ncpuser = ""; my $ncppassword = ""; if ($parameters =~ m!([^@]*)@([^@]+)!) { my $login = $1; $parameters = $2; if ($login =~ m!([^:]*):([^:]*)!) { $ncpuser = $1; $ncppassword = $2; } else { $ncpuser = $login; $ncppassword = ""; } } else { $ncpuser = ""; $ncppassword = ""; } # Get the server and share name my $ncpserver = ""; my $ncpqueue = ""; if ($parameters =~ m!([^/]+)/([^/]+)$!) { $ncpserver = $1; $ncpqueue = $2; } else { die "The \"ncp://\" URI must at least contain the server " . "name and the queue name!\n"; } # Set up the command line for printing on the Netware server $postpipe = "$sysdeps->{'nprint'} -S $ncpserver"; if ($ncpuser ne "") { $postpipe .= " -U $ncpuser"; if ($ncppassword ne "") { warn("WARNING: ncp password is visible in PPD file\n"); $postpipe .= " -P $ncppassword"; } else { $postpipe .= " -n"; } } $postpipe .= " -q $ncpqueue -N - 2>/dev/null"; } elsif ($config->{'connect'} =~ m!^postpipe:(.*)$!) { # Pipe output into a command $postpipe = $1; } elsif ($config->{'connect'} =~ m!^stdout!) { $postpipe = ""; } elsif ($config->{'connect'}) { die ("The URI \"$config->{'connect'}\" is not supported for " . "spooler-less printing or you have\nmistyped.\n"); } else { die "You must specify a connection with -c.\n"; } # Put $postpipe into the data structure, so that it will be # inserted into the PPD file if ($postpipe ne "") { $postpipe = "| $postpipe"; $db->{'dat'}{'postpipe'} = $postpipe; } else { undef $db->{'dat'}{'postpipe'}; } } else { # Keep previous connection type # Use previous $postpipe if (defined($db->{'dat'}{'postpipe'})) { $postpipe = $db->{'dat'}{'postpipe'}; } }
# Various file setup mkdir $sysdeps->{'foo-etc'}, 0755; mkdir $sysdeps->{'foo-etc'} . "/direct", 0755;
# Add to the config file if a new entry if (!$reconf) { push(@{$pconfig}, $entry); }
# Generate/write the PPD file writeppdfile($config, $ppdfile, $rawqueue, $newfoomaticdata);
# Write back /etc/foomatic/direct/.config my $pconfigname = $sysdeps->{'direct-config'}; rename $pconfigname, "$pconfigname.old"; open PCONFIG, "> $pconfigname" or die "Cannot write $pconfigname!\n"; print PCONFIG dump_direct_config($pconfig); close PCONFIG; chmod 0644, $pconfigname;
return 1; }
sub default_direct { my ($config) = $_[0];
my $name = $config->{'queue'};
my $pconfig = load_direct_config();
# Modify the "default" fields of the printers appropriately
for (@{$pconfig}) { $_->{'default'} = ($_->{'name'} eq $name); }
my @newpconfig = dump_direct_config($pconfig);
my $pconfigname = $sysdeps->{'direct-config'}; rename $pconfigname, "$pconfigname.old"; open PCONFIG, "> $pconfigname" or die "Cannot write $pconfigname!\n"; print PCONFIG @newpconfig; close PCONFIG; chmod 0644, $pconfigname;
return 1; }
sub delete_direct { my ($config) = $_[0];
my $name = $config->{'queue'};
my $pconfig = load_direct_config();
# Overtake all entries except the one of the deleted printer to the # new config file
my @newconf; for (@{$pconfig}) { push (@newconf, $_) unless ($_->{'name'} eq $name); }
my @newpconfig = dump_direct_config(\@newconf);
my $pconfigname = $sysdeps->{'direct-config'}; rename $pconfigname, "$pconfigname.old"; open PCONFIG, "> $pconfigname" or die "Cannot write $pconfigname!\n"; print PCONFIG @newpconfig; close PCONFIG; chmod 0644, $pconfigname;
# PPD file name my $ppdfile = sprintf('%s/direct/%s.ppd', $sysdeps->{'foo-etc'}, $config->{'queue'});
# Rename old $ppdfile, if any rename $ppdfile, "$ppdfile.old" if (-f $ppdfile);
return 1; }
sub query_direct { my ($config) = @_;
# User requests data of a printer/driver combo to see the options before # installing a queue if (($opt_P) && ((($config->{'driver'}) && ($config->{'printer'}) && ($config->{'driver'} ne "raw")) || ($config->{'ppdfile'}))) { if ($opt_n) { my $olddatablob = load_direct_datablob($opt_n); print_perl_combo_data($config, $olddatablob); } else { print_perl_combo_data($config); } return; }
my $i = $ARGV[0]; if (!defined($i)) {$i = 0;}
my $pconf = load_direct_config(); if (defined($opt_r)) {$opt_r = undef;} my $p;
if (!$opt_P) { print "<queues>\n"; }
# Query the default printer my $default; if (!defined($config->{'queue'})) { for $p (@{$pconf}) { if ($p->{'default'}) { $default = $p->{'name'}; if (!$opt_P) { print "<defaultqueue>$p->{'name'}</defaultqueue>\n"; } last; } } }
for $p (@{$pconf}) { # were we invoked for only one queue? next if (defined($config->{'queue'}) and $config->{'queue'} ne $p->{'name'});
# load the queue data $db->{'dat'} = load_direct_datablob($p->{'name'});
# extract the queue data block my $c = $db->{'dat'}{'queuedata'}; if ($opt_P) { if ($p->{'name'} eq $default) { $db->{'dat'}{'queuedata'}{'default'} = 1; } else { $db->{'dat'}{'queuedata'}{'default'} = 0; } $db->{'dat'}{'queuedata'}{'remote'} = 0; my $asciidata = $db->getascii(); $asciidata =~ s/\$VAR1/\$QUEUES[$i]/g; print $asciidata; $i ++; } else { # and get it to standard output dump_config($c); } }
if (!$opt_P) { print "</queues>\n"; } return; }
### Functions used by the queue manipulation functions from above
sub dump_config { my $c = $_[0];
print sprintf("<queue foomatic=\"%d\" spooler=\"%s\">\n", ($c->{'foomatic'} ? 1 : 0), $c->{'spooler'}),
_tag('name',$c->{'queue'}), _tag('printer',$c->{'printer'}), _tag('driver',$c->{'driver'}), _tag('connect',$c->{'connect'}), _tag('location',$c->{'loc'}), _tag('description',$c->{'desc'}), ($c->{'spooler'} eq "cups" ? (_tag('dontdisable',$c->{'dd'}), _tag('attempts',$c->{'att'}), _tag('delay',$c->{'delay'}), (defined($c->{'quotaperiod'}) ? _tag('quotaperiod',$c->{'quotaperiod'}) : ()), (defined($c->{'pagelimit'}) ? _tag('pagelimit',$c->{'pagelimit'}) : ()), (defined($c->{'klimit'}) ? _tag('klimit',$c->{'klimit'}) : ()), (defined($c->{'laststatechange'}) ? _tag('laststatechange',$c->{'laststatechange'}) : ()), (defined($c->{'shared'}) ? _tag('shared',$c->{'shared'}) : ()), (defined($c->{'operationpolicy'}) ? _tag('operationpolicy',$c->{'operationpolicy'}) : ()), (defined($c->{'errorpolicy'}) ? _tag('errorpolicy',$c->{'errorpolicy'}) : ())) : ()), "</queue>\n"; return; }
sub _tag { my ($t, $v) = @_;
return '' if !defined($v);
$v =~ s!\&!\&\;!g; $v =~ s!\<!\<\;!g;
return " <$t>$v</$t>\n"; }
sub dump_lpd_printcap { my ($config, $pcap )= @_;
my @retval;
my $item; my $backslash = "\\"; $backslash = "" if $config->{'spooler'} eq 'lprng'; for $item (@{$pcap}) { for (@{$item->{'comments'}}) { push (@retval, "$_\n"); } if (defined($item->{'names'})) { map { $_ = '' if not defined $_; } @{$item->{'names'}}; push (@retval, (join('|', @{$item->{'names'}}) . ":${backslash}\n")); } for (keys(%{$item->{'str'}})) { # special case of 'tc' items, as there can be more than one if ($_ =~ /^tc\d+$/) { push (@retval, sprintf(" :tc=%s:${backslash}\n", $item->{'str'}{$_})); } else { push (@retval, sprintf(" :$_=%s:${backslash}\n", $item->{'str'}{$_})); } } for (keys(%{$item->{'bool'}})) { if ($item->{'bool'}{$_}) { push (@retval, " :$_:${backslash}\n"); } } for (keys(%{$item->{'num'}})) { push (@retval, sprintf(" :$_#%s:${backslash}\n", $item->{'num'}{$_})); } if( $backslash ){ my $lastline = pop(@retval); $lastline =~ s!:\\!:!; push (@retval, $lastline); } } print "PRINTCAP (spooler '" . $config->{'spooler'} . "') " . Dumper(\@retval) . "\n" if $debug;
return @retval; }
sub load_lpd_printcap {
# list-o-printers, each with comments
open PCAP, $sysdeps->{'lpd-pcap'} or die "Cannot read printcap file!\n"; my $pcap = join('', <PCAP>); close PCAP; print "PC '$pcap'\n" if $debug;
# die( "Cannot currently parse lprng style printcaps created by " . # "lprngtool!\n" . # "See the BUGS section in the manpage for details.\n") # if $pcap =~ m/\n\s*(:.*[^\\]\n\s*:)/m;
# watch out for comments with \ at end of line - ignore \ $pcap =~ s!^(\s*\#.*\\)$!${1}MEMEMEM!gm; # now we join lines with \ at end $pcap =~ s!\\\n!!gms; # remove \ in comment lines $pcap =~ s!\\MEMEMEM!\\!g; print "AFTER '$pcap'\n" if $debug;
my (@comment, @items, @comments_in_pc_entry);
my ($pline, $pcentry); $pcentry = ""; for $pline (split('\n',$pcap)) { $pline =~ s/^\s+//; print "LINE '$pline', pcentry '$pcentry'\n" if $debug; next if $pline eq ""; if ($pline =~ m!^\#!) { if( $pcentry ){ push (@comments_in_pc_entry, $pline); } else { push (@comment, $pline); } } elsif ($pline =~ m!^:!) { push( @comment, @comments_in_pc_entry ); @comments_in_pc_entry = (); if( $pcentry ne "" ){ $pcentry .= $pline; } else { die( "bad printcap entry at '$pline'" ); } } elsif( $pcentry ne "" ){ push (@items, { 'itemstr' => $pcentry, 'comments' => [ @comment ] }); @comment = @comments_in_pc_entry; @comments_in_pc_entry = (); $pcentry = $pline; } else { $pcentry = $pline; } } if( $pcentry ){ push( @comment, @comments_in_pc_entry ); @comments_in_pc_entry = (); push (@items, { 'itemstr' => $pcentry, 'comments' => [ @comment ] }); @comment = (); } # Trailing comments get stuck on as empty item later... print "Printcap:\n" . Dumper(\@items ) if $debug;
my $p; for $p (@items) { my $item; my $first = 1; my $tci = 0; for $item (split(/:\s*/, $p->{'itemstr'})) { next if $item =~ m!^\s*$!; if ($first) { my $name; for $name (split('\|',$item)) { $name =~ s!\s*(.+)\s*!$1!; push (@{$p->{'names'}}, $name); } $first = 0; } else { if ($item =~ m!^([^=]*)=(.+)!) { # special case of 'tc' items, as there can be more # than one if ($1 eq 'tc') { $p->{'str'}{"tc$tci"} = $2; $tci++; } else { $p->{'str'}{$1} = $2; } } elsif ($item =~ m!^([^\#]*)\#(.+)!) { $p->{'num'}{$1} = $2; } elsif ($item =~ m!^([^\@]*)\@?!) { $p->{'bool'}{$1} = 1; } } } }
# Trailing comments from way above... if (scalar(@comment)) { push (@items, {'comments' => [ @comment ]}); }
return \@items; }
sub load_cups_printersconf {
# list-o-printers my @items = (); my $itemshash = {}; if ($< == 0) { # Get info from /etc/cups/printers.conf, works only as "root" and # with locally defined printers my @pconf = (); if (open PCONF, $sysdeps->{'cups-pconf'}) { @pconf = <PCONF>; close PCONF; } my $line; my $p = {}; my $linecount = 0; for $line (@pconf) { $linecount ++; if (!($line =~ m!^\s*\#!) && (!($line =~ m!^\s*$!))) { if ($line =~ m!^\s*<(.*)Printer\s+([^\s>]+)>\s*$!) { # Beginning of new <Printer ...> block $p->{'name'} = $2; $p->{'default'} = ($1 eq "Default"); } elsif ($line =~ m!^\s*</Printer>\s*$!) { # End of <Printer ...> block push (@items, $p); $itemshash->{$p->{name}} = $#items; $p = {}; } elsif (defined($p->{'name'})) { # Inside <Printer ...> block if (($line =~ m!^\s*(\S+)\s+(\S.*)$!) and ($1 ne '')) {$p->{$1} = $2}; } else { # Outside <Printer ...> block die "Line $linecount in $sysdeps->{'cups-pconf'} " . "invalid!\n"; } } } } if (($< != 0) || (($opt_r) && (($opt_Q) || ($opt_P)))) { # Get info with the "lpstat" command, works for normal users and for # remote printers. open LPSTAT, "$sysdeps->{'cups-lpstat'} -l -d -p -v |" or die "Cannot execute \"lpstat\".\n"; my @lpstat = <LPSTAT>; close LPSTAT; my $line; my $linecount = 0; my $defaultprinter = ''; my $currentitem = -1; for $line (@lpstat) { chomp ($line); $linecount ++; if (!($line =~ m!^\s*$!)) { if ($line =~ m!^\s*system\s+default\s+destination:\s+(\S+)\s*$!) { # Default printer $defaultprinter = $1; } elsif ($line =~ m!^printer\s+(\S+)\s+(\S.*)$!) { # Beginning of new printer's entry my $name = $1; my $state = $2; $state =~ s/\s+-$//; if (!defined($itemshash->{$name})) { push(@items, {}); $itemshash->{$name} = $#items; # If we are root and didn't see this entry # in /etc/cups/printers.conf, this printer # is remotely defined if ($< == 0) { $items[$itemshash->{$name}]{'remote'} = 1; } } $currentitem = $itemshash->{$name}; $items[$currentitem]{'name'} ||= $name; $items[$currentitem]{'State'} ||= $state; $items[$currentitem]{'default'} = ($name eq $defaultprinter); } elsif ($line =~ m!^\s+Description:\s+(\S.*)$!) { # Description field if ($currentitem != -1) { $items[$currentitem]{'Info'} ||= $1; } } elsif ($line =~ m!^\s+Location:\s+(\S.*)$!) { # Location field if ($currentitem != -1) { $items[$currentitem]{'Location'} ||= $1; } } elsif ($line =~ m!^\s+Connection:\s+remote!) { # Remote printer, only keep it when the "-r" option is # given if (!$opt_r) { # "delete" does not work on arrays with Perl 5.0.x # Thanks to Olaf Till (i7tiol@t-online.de) who # contributed this fix splice(@items, $currentitem, 1); #delete($items[$currentitem]); $currentitem = -1; } else { if ($currentitem != -1) { $items[$currentitem]{'remote'} = 1; } } } elsif ($line =~ m!^device\s+for\s+(\S+):\s+(\S.*)$!) { # "device for ..." line, extract URI my $name = $1; my $uri = $2; if (defined($itemshash->{$name})) { if ($uri !~ /:/) {$uri = "file:" . $uri}; $currentitem = $itemshash->{$name}; if (($currentitem <= $#items) && ($items[$currentitem]{'name'} eq $name)) { $items[$currentitem]{'DeviceURI'} ||= $uri; } } } } } }
return \@items; }
sub dump_pdq_printrc { my $printrc = $_[0];
my @retval;
my $item; for $item (@{$printrc}) { if (defined($item->{'name'})) { # $item is a "printer" block push (@retval, "printer \"$item->{'name'}\" \{\n"); for my $key (keys(%{$item})) { if (($key ne 'name') && ($key ne 'others')) { push (@retval, "\t$key $item->{$key}\n"); } } push (@retval, "\}\n"); } elsif (defined($item->{'others'})) { # $item is not a "printer" block push (@retval, $item->{'others'}); } }
# Check whether there is a already a 'try_include "/etc/foomatic/pdq/*"' # line in the config file if (!(join("", @retval) =~ m!^\s*try_include\s*\"$sysdeps->{'foo-etc'}/pdq/driverdescr/\*\"\s*$!m)) { splice(@retval,0,0,"# Line inserted by $progname\ntry_include " . "\"$sysdeps->{'foo-etc'}/pdq/driverdescr/*\"\n\n"); }
# De-activate old line from Foomatic 2.0.x ($_ =~ s!^\s*try_include\s*\"$sysdeps->{'foo-etc'}/pdq/\*\"\s*$!\#$&!m) foreach @retval;
return @retval; }
sub load_pdq_printrc {
# list-o-printers, with storage of non-printer-specific lines
open PRINTRC, $sysdeps->{'pdq-printrc'} or die "Cannot read printrc file!\n"; my @printrc = <PRINTRC>; close PRINTRC;
my @items; my @others; my $line; my $p; my $linecount = 0; my $inprinterblock = 0; my $nonprinterlines = 0; for $line (@printrc) { $linecount ++; if ($line =~ m!^\s*printer\s+\"(.+)\"\s*{\s*$!) { if ($inprinterblock == 1) { die "New printer block started without previous one " . "being closed!\nLine $linecount in " . "$sysdeps->{'pdq-printrc'}.\n"; } # Beginning of new "printer" block # Store all non-printer-block stuff at first if ($nonprinterlines == 1) { push (@items, {'others' => join ("", @others )}); $nonprinterlines = 0; @others = (); } # Read printer block name $inprinterblock = 1; $p->{'name'} = $1; } elsif ($inprinterblock == 1) { # Inside "printer" block if ($line =~ m!^\s*}\s*$!) { # End of "printer" block $inprinterblock = 0; push (@items, $p); $p = {}; } elsif ($line =~ m!^\s*(\S+)\s*(\S+.*)$!) { $p->{$1} = $2; } elsif ((!($line =~ m!^\s*\#!)) && (!($line =~ m!^\s*$!))) { die "Line $linecount in $sysdeps->{'pdq-printrc'} " . "invalid!\n"; } } else { # Outside "printer" block push(@others, $line); $nonprinterlines = 1; } } # Trailing non-printer lines get stuck on as empty item if ($nonprinterlines == 1) { my $lines = join ("", @others); # Make sure that the last line line ends with a newline character if (!($lines =~ m!\n$!s)) {$lines .= "\n";} push (@items, {'others' => $lines}); }
return \@items; }
sub load_ppr_printers_conf {
# Check whether there is a group named "default" to see what is the # default printer. my $defaultfromgroup = " "; if (open SHOWDEFAULTGROUP, "$sysdeps->{'ppr-ppad'} group show default 2>/dev/null |"){ for my $line (<SHOWDEFAULTGROUP>) { chomp $line; if ($line =~ /\s*Members:\s*([^\s,]+)\s*$/) { $defaultfromgroup = $1; last; } } close SHOWDEFAULTGROUP; }
# list-o-printers my @items = (); my $itemshash = {}; if ($< == 0) { # Get info from /etc/ppr/printers/<queue name>, works only as # "root" opendir PCONFDIR, "$sysdeps->{'ppr-etc'}/printers" or die "Cannot read $sysdeps->{'ppr-etc'}/printers directory!\n"; my $name; while ($name = readdir(PCONFDIR)) { # Do not consider "." and ".." as a printer queue next if ($name =~ /^\./); my $line; my $p = {}; $p->{'name'} = $name; $p->{'default'} = (($name eq "default") || ($name eq $defaultfromgroup)); @{$p->{'Bins'}} = (); my $linecount = 0; open PCONFFILE, "$sysdeps->{'ppr-etc'}/printers/$name" or die "Cannot read $sysdeps->{'ppr-etc'}/printers/$name!\n"; for my $line (<PCONFFILE>) { chomp $line; $linecount ++; if (!($line =~ m!^\s*\#!) && (!($line =~ m!^\s*$!))) { if (($line =~ m!^\s*([^\s:]+)\s*:\s*(\S.*)$!) || ($line =~ m!^\s*([^\s:]+)\s*:\s*()$!)) { # <keyword>: <value1> <value2> ... my $keyword = $1; my $values = $2; if (($values) && ($values ne "")) { # If the value is enclosed in double quotes, # remove the quotes $values =~ s/^\"(.*)\"$/$1/; if ($keyword eq "Bin") { push (@{$p->{'Bins'}}, $values); } else { $p->{$keyword} = $values; } } } else { warn "Line $linecount in " . "$sysdeps->{'ppr-etc'}/printers/$name " . "corrupted:\n $line\n"; } } } close PCONFFILE; push (@items, $p); $itemshash->{$p->{'name'}} = $#items; } } if ($< != 0) { # Get info with the "ppop"/"ppad" commands, works for normal users, # but needs installed and running PPR printing system open PPOP_DEST, "$sysdeps->{'ppr-ppop'} destination all |" or die "Cannot execute \"ppop\".\n"; my @ppop_dest = <PPOP_DEST>; close PPOP_DEST; my $line; my $linecount = 0; my $currentitem = -1; for $line (@ppop_dest) { chomp ($line); $linecount ++; if (($line !~ m!^\s*-+\s*$!) && ($line !~ m!^\s*Destination\s+Type\s+Status\s+Charge\s*$!)){ if ($line =~ m!^\s*(\S+)\s+printer!) { my $name = $1; open PPAD_SHOW,"$sysdeps->{'ppr-ppad'} show $name |" or die "Cannot execute \"ppad\".\n"; my $lcount = 0; if (!defined($itemshash->{$name})) { push(@items, {}); $itemshash->{$name} = $#items; #print Dumper($itemshash); } $currentitem = $itemshash->{$name}; $items[$currentitem]{'name'} ||= $name; $items[$currentitem]{'default'} = (($name eq "default") || ($name eq $defaultfromgroup)); for my $line (<PPAD_SHOW>) { chomp $line; $lcount ++; if ((!($line =~ m!^\s*\#!)) && (!($line =~ m!^\s*$!))) { if ($line =~ m!^\s*([^\s:][^:]*)\s*:\s*(.*)$!) { # <keyword>: <value1> <value2> ... my $keyword = $1; my $values = $2; if (($values) && ($values ne "")) { # If the value is enclosed in double # quotes, remove the quotes $values =~ s/^\"(.*)\"$/$1/; if ($keyword eq "Bins") { @{$items[$currentitem]{'Bins'}} = split(", ", $values); } else { if ($keyword eq "Switchset") { $values =~ s/ -(\S) /\|$1/g; $values =~ s/-(\S) /$1/g; $values =~ s/\'//g; $values =~ s/^|//g; } $items[$currentitem]{$keyword} = $values; } } } else { warn "Line $lcount in \"ppad show " . "$name\" corrupted:\n $line\n"; } } } close PPAD_SHOW; } } } }
return \@items; }
sub dump_direct_config { my $config = $_[0];
my @retval;
my $defaultprinter = undef; my $item; for $item (@{$config}) { if (defined($item->{'name'})) { if (defined($item->{'desc'})) { push (@retval, "$item->{'name'} desc:$item->{'desc'}\n"); } if (defined($item->{'loc'})) { push (@retval, "$item->{'name'} loc:$item->{'loc'}\n"); } if ($item->{'default'}) { $defaultprinter = $item->{'name'}; } } } if (defined($defaultprinter)) { unshift(@retval, "default: $defaultprinter\n"); } return @retval; }
sub load_direct_config {
# list-o-printers my @items = (); my $itemshash = {}; # Configured printers are represented by PPD files in /etc/foomatic/ opendir PCONFDIR, "$sysdeps->{'foo-etc'}/direct" or die "Cannot read $sysdeps->{'foo-etc'}/direct directory!\n"; my $name; while ($name = readdir(PCONFDIR)) { # Files beginning with a dot or ending with a tilde are never # printers next if (($name =~ /^\./) || ($name =~ /~$/)); # Only ".ppd" files are printer descriptions. next unless ($name =~ /\.ppd$/i); $name =~ s/\.ppd$//i; # Do not make two entries when there is both a ".ppd" AND ".PPD" # file for the same printer name. next if (defined($itemshash->{$name})); my $p = {}; $p->{'name'} = $name; push (@items, $p); $itemshash->{$p->{'name'}} = $#items; }
# Get additional info from /etc/foomatic/direct/.config (default # printer, description, location if (open CONFIG, "< $sysdeps->{'direct-config'}") { while (my $line = <CONFIG>) { chomp $line; if ($line =~ /^default\s*:\s*([^:\s]+)\s*$/) { my $currentitem = $itemshash->{$1}; $items[$currentitem]{'default'} = 1; } elsif ($line =~ /^\s*([^:\s]+)\s+([^:\s]+)\s*:(.*)$/) { my $currentitem = $itemshash->{$1}; $items[$currentitem]{$2} = $3; } } close CONFIG; }
return \@items; }
sub cups_correct_ptal_uri {
# HPOJ 0.9 uses "ptal:..." URIs with one slash # ("ptal:/mlc:usb:dj450") and the current CVS of HPOJ uses two # slashes ("ptal://mlc:usb:dj450"). Correct the user-supplied URI # according to what "lpinfo -v" reports.
my ($uri) = @_; $uri =~ m!^ptal://?([^/].*)$!; my $device = $1;
# PTAL URIs listed by "lpinfo -v" open F, "$sysdeps->{'cups-lpinfo'} -v |" or return (@_); while (my $line = <F>) { chomp($line); my $d = quotemeta($device); if ($line =~ m!(ptal://?$d)$!) { my $realdevice = $1; close F; return $realdevice; } } close F;
# Nothing found, do not correct the input return @_; }
sub cups_generate_usb_device_lists { # Generate two lists: One of the actual USB device files in the # file system, another of the USB URIs listed by CUPS' "lpinfo -v"
# Actual devices my @usbdevices; for my $pattern ("/dev/usb/lp*", "/dev/usb/usblp*") { open F, "ls -1 $pattern 2>/dev/null |" or next; @usbdevices = sort { Foomatic::DB::normalizename($a) cmp Foomatic::DB::normalizename($b) } grep { chomp } <F>; close F; last if $#usbdevices >= 0; } return ([], []) if $#usbdevices < 0;
# USB URIs listed by "lpinfo -v" open F, "$sysdeps->{'cups-lpinfo'} -v |" or return ([], []); my @usburis = grep { s!^direct usb:!! and chomp } <F>; close F;
return ([], []) if $#usburis < 0;
# Results return (\@usbdevices, \@usburis); }
sub cups_usb_device_uri_to_printer_uri {
# Transfer a device file name into a printer-bound CUPS URI for # the printer currently connected my ($device) = @_; return $device if $device =~ m!^//!; my @devicelists = cups_generate_usb_device_lists(); return $device if (($#{$devicelists[0]} < 0) || ($#{$devicelists[1]} < 0)); for (my $i = 0; $i <= $#{$devicelists[0]}; $i ++) { last if !$devicelists[1][$i]; if ($device eq $devicelists[0][$i]) { return $devicelists[1][$i]; } } return $device; }
sub cups_usb_printer_uri_to_device_uri {
# Transfer a device file name into a printer-bound CUPS URI for # the printer currently connected my ($device) = @_; return $device if $device =~ m!^/[^/]!; $device =~ s/ /\%20/g; my @devicelists = cups_generate_usb_device_lists(); return $device if (($#{$devicelists[0]} < 0) || ($#{$devicelists[1]} < 0)); for (my $i = 0; $i <= $#{$devicelists[1]}; $i ++) { last if !$devicelists[0][$i]; if ($device eq $devicelists[1][$i]) { return $devicelists[0][$i]; } } return $device; }
sub load_datablob {
my ($spooler, $queue) = @_;
my $spoolersubdir; my $datablob; if (($spooler eq "lpd") || ($spooler eq "lprng")) { $datablob = load_lpd_datablob($queue); $spoolersubdir = 'lpd'; } elsif ($spooler eq "cups") { $datablob = load_cups_datablob($queue); $spoolersubdir = 'cups'; } elsif ($spooler eq "pdq") { $datablob = load_pdq_datablob($queue); $spoolersubdir = 'pdq'; } elsif ($spooler eq "ppr") { $datablob = load_ppr_datablob($queue); $spoolersubdir = 'ppr'; } elsif ($spooler eq "direct") { $datablob = load_direct_datablob($queue); $spoolersubdir = 'direct'; } else { die "Unsupported spooler: $spooler\n"; } # Is the given queue a valid queue? if (!$datablob) { return undef; } return ($datablob); }
sub load_lpd_datablob { my ($queue) = $_[0]; # Load the PPD file my $ppdfile = sprintf('%s/lpd/%s.ppd', $sysdeps->{'foo-etc'}, $queue); my $dat = ppdtoperl($ppdfile); if (defined($dat)) { $dat->{'ppdfile'} = $ppdfile; } my $postpipe = (defined($dat) ? $dat->{'postpipe'} : ""); # Get additional info from /etc/printcap my $pcap = load_lpd_printcap(); my $p; for $p (@{$pcap}) { # enpty end entry for trailing comments next if !defined($p->{'names'}); # Search for the correct queue next if ($queue ne $p->{'names'}[0]); # Collect values my $c = {}; my $name = $c->{'queue'} = $p->{'names'}[0]; $c->{'desc'} = $p->{'names'}[1] if $p->{'names'}[1]; $c->{'loc'} = $p->{'names'}[3] if $p->{'names'}[3]; $c->{'foomatic'} = 0; my $if = ($p->{'str'}{'if'} || ""); if ($if =~ m!foomatic-rip$!) { $c->{'foomatic'} = 1; $c->{'printer'} = $dat->{'id'}; $c->{'driver'} = $dat->{'driver'}; } if (!$p->{'bool'}{'force_localhost'}) { # LPD $c->{'spooler'} = 'lpd'; } else { # LPRng $c->{'spooler'} = 'lprng'; } # TODO Raw queue for LPD # if (0 and $p->{'str'}{'if'} eq $file) { # Raw queue with $postpipe # if (open FILE, "$file") { # # The first line is #!/bin/sh # $line = <FILE>; # # The second line is a comment # $line = <FILE>; # # The remaining line(s) are the $postpipe # $line = join('', <FILE>); # chomp $line; # $postpipe = "| $line"; # close FILE; # } # } if (defined($postpipe)) { if ($postpipe =~ m!^\s*\|\s*($sysdeps->{'cat'}|cat)\s+-?\s*>\s*([^\s]+)\s*$!) { my $file = $2; if (($file =~ m!^$sysdeps->{'ptal-pipes'}/(.+)$!) || ($file =~ m!^/dev/ptal-printd/(.+)$!) || ($file =~ m!^/var/run/ptal-printd/(.+)$!)) { # Translate device for ptal-printd to ptal URI my $devname = $1; $devname =~ s/_/:/; $devname =~ s/_/:/; $c->{'connect'} = "ptal:/$devname"; } elsif (($file =~ m!^$sysdeps->{'mtink-pipes'}/(.+)$!) || ($file =~ m!^/var/mtink/(.+)$!)) { # Translate device for mtinkd to mtink URI $c->{'connect'} = "mtink:/$1"; } elsif ($file =~ m!usb!i) { $c->{'connect'} = "usb:$file"; } elsif ($file =~ m!(tty|serial)!i) { $c->{'connect'} = "serial:$file"; } elsif ($file =~ m!(lp[0-9]|parallel)!i) { $c->{'connect'} = "parallel:$file"; } else { $c->{'connect'} = "file:$file"; } } elsif ($postpipe =~ m!^\s*\|\s*($sysdeps->{'ptal-connect'}|ptal-connect|ptal-print)\s+(-print\s+|)([^\s]+)(\s+-print|)\s*$!){ $c->{'connect'} = "ptal:/$3"; } elsif ($postpipe =~ m!^\s*\|\s*($sysdeps->{'nc'}|netcat|nc)\s+(-w\s*1\s+|)([^\s]+)\s+([^\s]+)\s*$!){ $c->{'connect'} = "socket://$3:$4"; } elsif ($postpipe =~ m!^\s*\|\s*$sysdeps->{'rlpr'}\s.*-P\s*([^\s\\\@]+)\@([^\s\\\@]+)\s*$!) { $c->{'connect'} = "lpd://$2/$1"; } elsif ($postpipe =~ m!^.*\|\s*$sysdeps->{'smbclient'}\s+\"//([^/\s]+)/([^/\s]+)\"\s+(\S.*)$!s) { my $servershare = "$1/$2"; my $parameters = $3; my $password = ""; if ($parameters =~ m!^([^-]\S*)\s+(\S.*)$!) { $password = $1; $parameters = $2; } my $username = ""; if ($parameters =~ m!^-U\s+(\S*)\s+(\S.*)$!) { $username = $1; $parameters = $2; } my $workgroup = ""; if ($parameters =~ m!^-W\s+(\S*)\s+(\S.*)$!) { $workgroup = "$1/"; } my $identity = ""; if (($username eq "GUEST") && ($password eq "")) { $identity = ""; } elsif (($username eq "") && ($password eq "")) { $identity = ""; } elsif (($username ne "") && ($password eq "")) { $identity = "$username\@"; } elsif (($username eq "") && ($password ne "")) { $identity = ":$password\@"; } else { $identity = "$username:$password\@"; } $c->{'connect'} = "smb://$identity$workgroup$servershare"; } elsif ($postpipe =~ m!^\s*\|\s*$sysdeps->{'nprint'}\s+(\S.*)$!s) { my $parameters = $1; my $server = ""; if ($parameters =~ m!^-S\s+(\S*)\s+(\S.*)$!) { $server = $1; $parameters = $2; } my $username = ""; if ($parameters =~ m!^-U\s+(\S*)\s+(\S.*)$!) { $username = $1; $parameters = $2; } my $password = ""; if ($parameters =~ m!^-P\s+(\S*)\s+(\S.*)$!) { $password = $1; $parameters = $2; } if ($parameters =~ m!^-n\s+(\S.*)$!) { $parameters = $1; } my $queue = ""; if ($parameters =~ m!^-q\s+(\S*)\s+(\S.*)$!) { $queue = $1; } my $identity = ""; if (($username eq "") && ($password eq "")) { $identity = ""; } elsif (($username ne "") && ($password eq "")) { $identity = "$username\@"; } elsif (($username eq "") && ($password ne "")) { $identity = ":$password\@"; } else { $identity = "$username:$password\@"; } $c->{'connect'} = "ncp://$identity$server/$queue"; } elsif( $postpipe ){ $postpipe =~ m!\s*\|\s*(\S.*)$!; $c->{'connect'} = "postpipe:\"$1\""; } } else { my $lp = $p->{'str'}{'lp'}; if (defined($lp) and $lp and $lp ne '/dev/null') { if (($lp =~ m!^$sysdeps->{'ptal-pipes'}/(.+)$!) || ($lp =~ m!^/dev/ptal-printd/(.+)$!) || ($lp =~ m!^/var/run/ptal-printd/(.+)$!)) { # Translate device for ptal-printd to ptal URI my $devname = $1; $devname =~ s/_/:/; $devname =~ s/_/:/; $c->{'connect'} = "ptal:/$devname"; } elsif (($lp =~ m!^$sysdeps->{'mtink-pipes'}/(.+)$!) || ($lp =~ m!^/var/mtink/(.+)$!)) { # Translate device for mtinkd to mtink URI $c->{'connect'} = "mtink:/$1"; } elsif ($lp =~ m!^\w+:!i) { $c->{'connect'} = $lp; } else { $c->{'connect'} = "file:$lp"; } } my ($rm, $rp) = ($p->{'str'}{'rm'}, $p->{'str'}{'rp'}); if (defined($rm) and defined($rp)) { $c->{'connect'} = "lpd://$rm/$rp"; } } $dat->{'queuedata'} = $c; } if (!defined($dat->{'queuedata'})) {$dat = undef}; return $dat; }
sub load_cups_datablob { my ($queue) = $_[0]; # Load the PPD file my $ppdfile = sprintf('%s/ppd/%s.ppd', $sysdeps->{'cups-etc'}, $queue); #my $ppdfile = sprintf('%s/%s.ppd', # $sysdeps->{'foo-etc'}, # $queue); my $dat = ppdtoperl($ppdfile); if (defined($dat)) { $dat->{'ppdfile'} = $ppdfile; } # Get additional info from /etc/cups/printers.conf my $pconf = load_cups_printersconf(); my $p; for $p (@{$pconf}) { # were we invoked for only one queue? next if ($queue ne $p->{'name'});
# Collect values my $c = {}; $c->{'spooler'} = 'cups'; $c->{'queue'} = $p->{'name'}; $c->{'foomatic'} = 0; if (defined($dat->{'id'}) and defined($dat->{'driver'})) { $c->{'foomatic'} = 1; $c->{'printer'} = $dat->{'id'}; $c->{'driver'} = $dat->{'driver'}; } $c->{'desc'} = $p->{'Info'}; $c->{'loc'} = $p->{'Location'}; my $uri = $p->{'DeviceURI'}; # Is the beh (Backend Error Handler) wrapper backend in use? # If yes, read out its parameters and isolate the original URI. if ($uri =~ m!^beh:/(\d+)/(\d+)/(\d+)/(\S+)$!) { $c->{'dd'} = $1; $c->{'att'} = $2; $c->{'delay'} = $3; $uri = $4; } else { $c->{'dd'} = 0; $c->{'att'} = 1; $c->{'delay'} = 30; } if (($uri =~ m!^file:$sysdeps->{'ptal-pipes'}/(.+)$!) || ($uri =~ m!^file:/dev/ptal-printd/(.+)$!) || ($uri =~ m!^file:/var/run/ptal-printd/(.+)$!)) { # Translate URI for ptal-printd to ptal URI my $devname = $1; $devname =~ s/_/:/; $devname =~ s/_/:/; $uri = "ptal:/$devname"; } elsif (($uri =~ m!^file:$sysdeps->{'mtink-pipes'}/(.+)$!) || ($uri =~ m!^file:/var/mtink/(.+)$!)) { # Translate URI for mtinkd to mtink URI $uri = "mtink:/$1"; } $c->{'connect'} = $uri; # CUPS-specific extra info $c->{'quotaperiod'} = $p->{'QuotaPeriod'} if defined($p->{'QuotaPeriod'}); $c->{'pagelimit'} = $p->{'PageLimit'} if defined($p->{'PageLimit'}); $c->{'klimit'} = $p->{'KLimit'} if defined($p->{'KLimit'}); # CUPS 1.2-specific settings $c->{'laststatechange'} = $p->{'StateTime'} if defined($p->{'StateTime'}); $c->{'shared'} = $p->{'Shared'} if defined($p->{'Shared'}); $c->{'operationpolicy'} = $p->{'OpPolicy'} if defined($p->{'OpPolicy'}); $c->{'errorpolicy'} = $p->{'ErrorPolicy'} if defined($p->{'ErrorPolicy'}); $dat->{'queuedata'} = $c; } if (!defined($dat->{'queuedata'})) {$dat = undef}; return $dat; }
sub load_pdq_datablob { my ($queue) = $_[0]; # Load the PPD file my $ppdfile = sprintf('%s/pdq/%s.ppd', $sysdeps->{'foo-etc'}, $queue); my $dat = ppdtoperl($ppdfile); if (defined($dat)) { $dat->{'ppdfile'} = $ppdfile; } if (defined($dat)) { my $printrc = load_pdq_printrc(); my $p; my $pdqopts; my $pdqargs; for $p (@{$printrc}) { # Omit non-printer-block items next if (!(defined($p->{'name'}))); # Search the current queue next if ($queue ne $p->{'name'}); $pdqopts = $p->{'driver_opts'}; $pdqargs = $p->{'driver_args'}; } my @printrcdefaults = split(",", $pdqopts); push (@printrcdefaults, split(",", $pdqargs)); my $c; @{$c->{'options'}} = (); for my $option (@printrcdefaults) { if ($option =~ m!^\s*\{?\s*\"(OPT_|)(.+?)\"\s*=\s*\"(.*)\"\s*\}?\s*$!) { push (@{$c->{'options'}}, "$2=$3"); } elsif ($option =~ m!^\s*\{?\s*\"(OPT_|)([^_]+?)_(.+?)\"\s*\}?\s*$!) { push (@{$c->{'options'}}, "$2=$3"); } elsif ($option =~ m!^\s*\{?\s*\"(OPT_|)(.+?)\"\s*\}?\s*$!) { push (@{$c->{'options'}}, "$2"); } } set_default_options($c, $dat); } # Get additional info from printrc my $printrc = load_pdq_printrc(); my $p; for $p (@{$printrc}) { # Omit non-printer-block items next if (!(defined($p->{'name'}))); # Search for the appropriate queue next if ($queue ne $p->{'name'}); my $c = {}; $c->{'spooler'} = 'pdq'; $c->{'queue'} = $p->{'name'}; $c->{'foomatic'} = 0; if (defined($dat->{'id'}) and defined($dat->{'driver'})) { $c->{'foomatic'} = 1; $c->{'printer'} = $dat->{'id'}; $c->{'driver'} = $dat->{'driver'}; } if (defined($p->{'model'})) { my $desc = $p->{'model'}; $desc =~ s!^\"!!; $desc =~ s!\"$!!; if ($desc ne '') {$c->{'desc'} = $desc;} } if (defined($p->{'location'})) { my $loc = $p->{'location'}; $loc =~ s!^\"!!; $loc =~ s!\"$!!; if ($loc ne '') {$c->{'loc'} = $loc;} } if ($p->{'interface'} =~ m!local-port!) { # Local printer $p->{'interface_args'} =~ m!\"?PORT\"?\s*=\s*\"?([^\"\s]+)\"?!; my $file = $1; if (($file =~ m!^$sysdeps->{'ptal-pipes'}/(.+)$!) || ($file =~ m!^/dev/ptal-printd/(.+)$!) || ($file =~ m!^/var/run/ptal-printd/(.+)$!)) { # Translate device for ptal-printd to ptal URI my $devname = $1; $devname =~ s/_/:/; $devname =~ s/_/:/; $c->{'connect'} = "ptal:/$devname"; } elsif (($file =~ m!^$sysdeps->{'mtink-pipes'}/(.+)$!) || ($file =~ m!^/var/mtink/(.+)$!)) { # Translate device for mtinkd to mtink URI $c->{'connect'} = "mtink:/$1"; } elsif ($file =~ m!usb!i) { $c->{'connect'} = "usb:$file"; } elsif ($file =~ m!(tty|serial)!i) { $c->{'connect'} = "serial:$file"; } elsif ($file =~ m!(lp[0-9]|parallel)!i) { $c->{'connect'} = "parallel:$file"; } else { $c->{'connect'} = "file:$file"; } } elsif ($p->{'interface'} =~ m!bsd-lpd!) { # Remote LPD $p->{'interface_args'} =~ m!\"?REMOTE_HOST\"?\s*=\s*\"?([^\"\s]+)\"?!; my $remhost = $1; $p->{'interface_args'} =~ m!\"?QUEUE\"?\s*=\s*\"?([^\"\s]+)\"?!; my $remqueue = $1; $c->{'connect'} = "lpd://$remhost/$remqueue"; } elsif ($p->{'interface'} =~ m!tcp-port!) { # Socket $p->{'interface_args'} =~ m!\"?REMOTE_HOST\"?\s*=\s*\"?([^\"\s]+)\"?!; my $remhost = $1; $p->{'interface_args'} =~ m!\"?REMOTE_PORT\"?\s*=\s*\"?([^\"\s]+)\"?!; my $remport = $1; $c->{'connect'} = "socket://$remhost:$remport"; } $dat->{'queuedata'} = $c; } if (!defined($dat->{'queuedata'})) {$dat = undef}; return $dat; }
sub load_ppr_datablob { my ($queue) = $_[0]; # Load the PPD file my $ppdfile = sprintf('%s/ppr/%s.ppd', $sysdeps->{'foo-etc'}, $queue); my $dat = ppdtoperl($ppdfile); if (defined($dat)) { $dat->{'ppdfile'} = $ppdfile; } # Get additional info from /etc/ppr/* my $pconf = load_ppr_printers_conf(); my $p; for $p (@{$pconf}) {
# were we invoked for only one queue? next if ($queue ne $p->{'name'});
# Collect values my $c = {}; $c->{'spooler'} = 'ppr'; $c->{'queue'} = $p->{'name'}; $c->{'foomatic'} = 0; if (defined($dat->{'id'}) and defined($dat->{'driver'})) { $c->{'foomatic'} = 1; $c->{'printer'} = $dat->{'id'}; $c->{'driver'} = $dat->{'driver'}; } $c->{'desc'} = $p->{'Comment'}; $c->{'loc'} = $p->{'Location'}; if (defined($dat)) { my @printerdefaults = split('|', $p->{'Switchset'}); my $o; @{$o->{'options'}} = (); for my $option (@printerdefaults) { if (($option =~ /^F\s*\*([^\*\s=:]+)\s+([^\*\s=:]+)\s*$/) || ($option =~ /^F\s*([^\*\s=:]+)\s*=\s*([^\*\s=:]+)\s*$/)) { push (@{$o->{'options'}}, "$1=$2"); } elsif (($option =~ /^F\s*\*([^\*\s=:]+)\s*$/) || ($option =~ /^F\s*([^\*\s=:]+)\s*$/)) { push (@{$o->{'options'}}, "$1"); } } set_default_options($o, $dat); } my $address = $p->{'Address'}; my $interface = $p->{'Interface'}; my $interface_options = $p->{'Options'}; if (($interface eq "foomatic-rip") || ($interface eq "ppromatic")) { if ($interface_options =~ /backend=(\S+)/) { $interface = $1; $interface_options =~ s/backend=(\S+)//; if ($interface_options =~ /^\s*$/) { $interface_options = ""; } } else { $interface = ""; } } my $uri = ""; if (($interface eq "simple") || ($interface eq "parallel") || ($interface eq "serial") || ($interface eq "dummy")) { # local printer if (($address =~ m!^$sysdeps->{'ptal-pipes'}/(.+)$!) || ($address =~ m!^/dev/ptal-printd/(.+)$!) || ($address =~ m!^/var/run/ptal-printd/(.+)$!)) { # Translate device for ptal-printd to ptal URI my $devname = $1; $devname =~ s/_/:/; $devname =~ s/_/:/; $uri = "ptal:/$devname"; } elsif (($address =~ m!^$sysdeps->{'mtink-pipes'}/(.+)$!) || ($address =~ m!^/var/mtink/(.+)$!)) { # Translate device for mtinkd to mtink URI $uri = "mtink:/$1"; } elsif ($address =~ m!^\w+:!i) { $c->{'connect'} = $address; } else { $uri = "file:$address"; } } elsif ($interface eq "lpr") { # Remote LPD if ($address =~ /^([^\@]+)\@([^\@]+)$/) { my $remhost = $2; my $remqueue = $1; $uri = "lpd://$remhost/$remqueue"; } else { die "Remote LPD configuration of the queue $p->{'name'} " . "broken!\n"; } } elsif ($interface eq "tcpip") { # Socket (AppSocket/HP JetDirect) $uri = "socket://$address"; } elsif ($interface eq "smb") { # SMB (Printer on Windows server) if ($address =~ m!^//([^/]+)/([^/]+)$!) { my $smbserver = $1; my $smbshare = $2; my $smbuser = ""; if ($interface_options =~ /smbuser=(\S+)/) { $smbuser = $1; } else { # The PPR interface for SMB uses the user name "ppr" # when no user name is given. $smbuser = "ppr"; } my $smbpassword = ""; if ($interface_options =~ /smbpassword=(\S+)/) { $smbpassword = $1; } if (($smbpassword ne "") && ($smbuser eq "")) { $smbuser = "GUEST"; } $uri = "$smbserver/$smbshare"; if ($smbuser ne "") { if ($smbpassword ne "") { $smbuser .= ":$smbpassword"; } $uri = "$smbuser\@$uri"; } $uri = "smb://$uri"; } else { die "SMB configuration of the queue $p->{'name'} broken!\n"; } } else { # Interface not supported by Foomatic $uri = "$interface:$address"; } $c->{'connect'} = $uri; $dat->{'queuedata'} = $c; } if (!defined($dat->{'queuedata'})) {$dat = undef}; return $dat; }
sub load_direct_datablob { my ($queue) = $_[0]; # Load the PPD file my $ppdfile = sprintf('%s/direct/%s.ppd', $sysdeps->{'foo-etc'}, $queue); my $dat = ppdtoperl($ppdfile); if (defined($dat)) { $dat->{'ppdfile'} = $ppdfile; } my $postpipe = (defined($dat) ? $dat->{'postpipe'} : ""); # Get additional info from /etc/foomatic/direct/.config my $config = load_direct_config(); my $p; for $p (@{$config}) { # invalid entry next if !defined($p->{'name'}); # Search for the correct queue next if ($queue ne $p->{'name'}); # Collect values my $c = {}; my $name = $c->{'queue'} = $p->{'name'}; $c->{'desc'} = $p->{'desc'}; $c->{'loc'} = $p->{'loc'}; $c->{'foomatic'} = 0; if (defined($dat->{'id'}) and defined($dat->{'driver'})) { $c->{'foomatic'} = 1; $c->{'printer'} = $dat->{'id'}; $c->{'driver'} = $dat->{'driver'}; } $c->{'spooler'} = 'direct'; if (defined($postpipe)) { if ($postpipe =~ m!^\s*\|\s*($sysdeps->{'cat'}|cat)\s+-?\s*>\s*([^\s]+)\s*$!) { my $file = $2; if (($file =~ m!^$sysdeps->{'ptal-pipes'}/(.+)$!) || ($file =~ m!^/dev/ptal-printd/(.+)$!) || ($file =~ m!^/var/run/ptal-printd/(.+)$!)) { # Translate device for ptal-printd to ptal URI my $devname = $1; $devname =~ s/_/:/; $devname =~ s/_/:/; $c->{'connect'} = "ptal:/$devname"; } elsif (($file =~ m!^$sysdeps->{'mtink-pipes'}/(.+)$!) || ($file =~ m!^/var/mtink/(.+)$!)) { # Translate device for mtinkd to mtink URI $c->{'connect'} = "mtink:/$1"; } elsif ($file =~ m!usb!i) { $c->{'connect'} = "usb:$file"; } elsif ($file =~ m!(tty|serial)!i) { $c->{'connect'} = "serial:$file"; } elsif ($file =~ m!(lp[0-9]|parallel)!i) { $c->{'connect'} = "parallel:$file"; } else { $c->{'connect'} = "file:$file"; } } elsif ($postpipe =~ m!^\s*\|\s*($sysdeps->{'ptal-connect'}|ptal-connect|ptal-print)\s+(-print\s+|)([^\s]+)(\s+-print|)\s*$!){ $c->{'connect'} = "ptal:/$3"; } elsif ($postpipe =~ m!^\s*\|\s*($sysdeps->{'nc'}|netcat|nc)\s+(-w\s*1\s+|)([^\s]+)\s+([^\s]+)\s*$!){ $c->{'connect'} = "socket://$3:$4"; } elsif ($postpipe =~ m!^\s*\|\s*$sysdeps->{'rlpr'}\s.*-P\s*([^\s\\\@]+)\@([^\s\\\@]+)\s*$!) { $c->{'connect'} = "lpd://$2/$1"; } elsif ($postpipe =~ m!^.*\|\s*$sysdeps->{'smbclient'}\s+\"//([^/\s]+)/([^/\s]+)\"\s+(\S.*)$!s) { my $servershare = "$1/$2"; my $parameters = $3; my $password = ""; if ($parameters =~ m!^([^-]\S*)\s+(\S.*)$!) { $password = $1; $parameters = $2; } my $username = ""; if ($parameters =~ m!^-U\s+(\S*)\s+(\S.*)$!) { $username = $1; $parameters = $2; } my $workgroup = ""; if ($parameters =~ m!^-W\s+(\S*)\s+(\S.*)$!) { $workgroup = "$1/"; } my $identity = ""; if (($username eq "GUEST") && ($password eq "")) { $identity = ""; } elsif (($username eq "") && ($password eq "")) { $identity = ""; } elsif (($username ne "") && ($password eq "")) { $identity = "$username\@"; } elsif (($username eq "") && ($password ne "")) { $identity = ":$password\@"; } else { $identity = "$username:$password\@"; } $c->{'connect'} = "smb://$identity$workgroup$servershare"; } elsif ($postpipe =~ m!^\s*\|\s*$sysdeps->{'nprint'}\s+(\S.*)$!s) { my $parameters = $1; my $server = ""; if ($parameters =~ m!^-S\s+(\S*)\s+(\S.*)$!) { $server = $1; $parameters = $2; } my $username = ""; if ($parameters =~ m!^-U\s+(\S*)\s+(\S.*)$!) { $username = $1; $parameters = $2; } my $password = ""; if ($parameters =~ m!^-P\s+(\S*)\s+(\S.*)$!) { $password = $1; $parameters = $2; } if ($parameters =~ m!^-n\s+(\S.*)$!) { $parameters = $1; } my $queue = ""; if ($parameters =~ m!^-q\s+(\S*)\s+(\S.*)$!) { $queue = $1; } my $identity = ""; if (($username eq "") && ($password eq "")) { $identity = ""; } elsif (($username ne "") && ($password eq "")) { $identity = "$username\@"; } elsif (($username eq "") && ($password ne "")) { $identity = ":$password\@"; } else { $identity = "$username:$password\@"; } $c->{'connect'} = "ncp://$identity$server/$queue"; } else { $postpipe =~ m!\s*\|\s*(\S.*)$!; $c->{'connect'} = "postpipe:\"$1\""; } } else { $c->{'connect'} = "stdout"; } $dat->{'queuedata'} = $c; } if (!defined($dat->{'queuedata'})) {$dat = undef}; return $dat; }
sub overtake_defaults { # overtake the option default settings from $olddatablob my ($olddatablob) = $_[0]; my $c; @{$c->{'options'}} = (); for my $opt (@{$olddatablob->{'args'}}) { push (@{$c->{'options'}}, "$opt->{'name'}=$opt->{'default'}"); } set_default_options($c, $db->{'dat'}); }
sub set_default_options {
# Set the default printing options by doing changes on the Perl # structure produced by "getdat", before the spooler-specific # datafile is generated
my ($config) = $_[0]; my ($dest) = $_[1];
if ($#{$config->{'options'}} >= 0) { for (@{$config->{'options'}}) { my $option = $_; if ($option =~ m!^\s*([^=]+)=([^=]*)\s*$!) { # evaluated or numerical option, boolean option with # value "True", "False", "Yes", "No", "On", "Off", "1", "0" # given my $optname = $1; my $optvalue = $2; if (defined($dest->{'args_byname'}{$optname})) { if ($dest->{'args_byname'}{$optname}{'type'} eq 'bool') { if ((lc($optvalue) eq 'true') || (lc($optvalue) eq 'on') || (lc($optvalue) eq 'yes')) { $optvalue = '1'; } elsif ((lc($optvalue) eq 'false') || (lc($optvalue) eq 'off') || (lc($optvalue) eq 'no')) { $optvalue = '0'; } if (($optvalue eq '1') || ($optvalue eq '0')) { $dest->{'args_byname'}{$optname}{'default'} = $optvalue; } } elsif (($dest->{'args_byname'}{$optname}{'type'} eq 'int') || ($dest->{'args_byname'}{$optname}{'type'} eq 'float')) { if (($optvalue =~ m!^\s*[\+\-]?\s*[0-9]*\.?[0-9]*\s*$!) && ($optvalue >= $dest->{'args_byname'}{$optname}{'min'}) && ($optvalue <= $dest->{'args_byname'}{$optname}{'max'})) { $dest->{'args_byname'}{$optname}{'default'} = $optvalue; } } elsif (($dest->{'args_byname'}{$optname}{'type'} eq 'string') || ($dest->{'args_byname'}{$optname}{'type'} eq 'password')) { $optvalue = Foomatic::DB::checkoptionvalue ($dest, $optname, $optvalue, 0); $dest->{'args_byname'}{$optname}{'default'} = $optvalue if defined($optvalue); } else { if (defined($dest->{'args_byname'}{$optname}{'vals_byname'}{$optvalue})) { $dest->{'args_byname'}{$optname}{'default'} = $optvalue; } } } } else { if (($option =~ /^no(.+?)$/) && (defined($dest->{'args_byname'}{$1})) && ($dest->{'args_byname'}{$1}{'type'} eq 'bool')) { $dest->{'args_byname'}{$1}{'default'} = '0'; } elsif ((defined($dest->{'args_byname'}{$option})) && ($dest->{'args_byname'}{$option}{'type'} eq 'bool')) { $dest->{'args_byname'}{$option}{'default'} = '1'; } } } } }
sub print_perl_combo_data { my ($config, $olddatablob) = @_;
# Get the data if ($config->{'ppdfile'}) { # From PPD file my $dat = ppdtoperl($config->{'ppdfile'}); if (!defined($dat)) { die ("Unable to open PPD file \'$config->{'ppdfile'}\'\n"); } $db->{'dat'} = $dat; } else { # From Foomatic XML database my $possible = $db->getdat($config->{'driver'}, $config->{'printer'}); die "That printer and driver combination is not possible.\n" if (!$possible); die "There is neither a custom PPD file nor the driver database entry contains sufficient data to build a PPD file.\n" if (!$db->{'dat'}{'cmd'}) && (!$db->{'dat'}{'ppdfile'}); # Generate the PPD and extract it to Perl again (to get in the # composite options) my $ppd = $db->getppd($config->{'shortgui'}); delete ($db->{'dat'}); $db->{'dat'} = ppdfromvartoperl([split(/\n/, $ppd)]); }
# The data can be viewed with the option defaults of an existing # queue set if ($olddatablob) { my $c; @{$c->{'options'}} = (); for my $opt (@{$olddatablob->{'args'}}) { push (@{$c->{'options'}}, "$opt->{'name'}=$opt->{'default'}"); } set_default_options($c, $db->{'dat'}); }
# User can view the data of the combo also with options given on the # command line set_default_options($config, $db->{'dat'});
# Put it out my $asciidata = $db->getascii(); $asciidata =~ s/\$VAR1/\$COMBODATA/g; print $asciidata; return; }
sub detect_spooler {
# If tcp/localhost:631 opens, cups CUPS is the most sophisticated # spooler, if it is running, it is usually the primary printing # system my $page = ($db->getpage('http://localhost:631/', 1) || ""); if ($page =~ m!Common UNIX Printing System!) { return 'cups'; }
# PPR is also very sophisticated so check for this spooler if there is # no CUPS running. if (-x $sysdeps->{'ppr-ppr'}) { # There's a /usr/bin/ppr return 'ppr'; } # Else if /etc/printcap, some sort of lpd thing if (-f $sysdeps->{'lpd-pcap'}) { # If -f /etc/lpd.conf, lprng if (-f $sysdeps->{'lprng-conf'}) { return 'lprng'; } elsif (-x $sysdeps->{'lpd-bin'}) { # There's a /usr/sbin/lpd return 'lpd'; } }
# pdq executable in our path somewhere? for (split(':', $ENV{'PATH'})) { if (-x "$_/pdq") { return 'pdq'; } }
# If there is no known spooler, set up printers for direct, spooler-less # printing. return "direct"; }
sub unimp { die "Sorry, $action for your spooler is unimplemented...\n"; }
sub overview { print $db->get_overview_xml($opt_f); exit(0); }
sub get_xml { my $x = undef; if (($opt_p) and ($opt_d)) { $x = $db->get_combo_data_xml($opt_d,$opt_p); } elsif ($opt_p) { $x = $db->get_printer_xml($opt_p); } elsif ($opt_d) { $x = $db->get_driver_xml($opt_d); } else { die "You must specify a -p printer and/or -d driver.\n"; }
if (defined($x)) { print $x; } else { die "Unable to find object.\n"; }
exit(0); }
sub help { print STDERR <<EOH; Usage: $progname [ -s spooler ] -n queuename \\ [ -N 'Name/Descr.' ] [ -L 'Location Info' ] \\ [ -c connect ] \\ [ -d driver ] [ -p printer ] [ -f ] [ -w ] \\ [ --ppd ppdfile ] \\ [ -o option1=value1 -o option2 ... ] \\ [ --backend-dont-disable=value ] \\ [ --backend-attempts=value ] \\ [ --backend-delay=value ] \\ [ -q ] or $progname -C [ -s spooler ] -n queuename \\ [ sourcespooler ] sourcequeue \\ [ -N 'Name/Descr.' ] [ -L 'Location Info' ] \\ [ -c connect ] \\ [ -d driver ] [ -p printer ] [ -f ] [ -w ] \\ [ --ppd ppdfile ] \\ [ -o option1=value1 -o option2 ... ] \\ [ --backend-dont-disable=value ] \\ [ --backend-attempts=value ] \\ [ --backend-delay=value ] \\ [ -q ] or $progname -D [ -s spooler ] -n queuename [ -q ] or $progname -R [ -s spooler ] -n queuename [ -q ] or $progname -Q [ -s spooler ] [ -n queuename ] [ -q ] [ -r ] or $progname -P [ -s spooler ] [ -n queuename ] [ -q ] [ N ] or $progname -P [ -s spooler ] [ -n queuename ] \\ [ --ppd ppdfile ] [ -d driver -p printer ] \\ [ -o option1=value1 -o option2 ... ] [ -q ] or $progname -O or $progname -X [ -p printer ] [ -d driver ]
-n queuename Configure/create/delete/query this print queue -N Name/Descr. Long name/Short Description. An empty string ("") deletes the description. -L Location Short phrase describing this printer's location. An empty string ("") deletes the location. -c connection Printer is connected thusly (ex file:/dev/lp0), must be given when a new queue is created --ppd ppdfile Set up the queue using the PPD file ppdfile (can be a manufacturer-supplied PPD file for a PostScript printer). gzip-compressed PPD files are allowed, they must have the extension ".gz". -d driver Foomatic database name for desired printer driver or "raw" for a raw queue. When a non-raw queue is created, the printer must be specified in addition ("-p" option) -p printer Foomatic id for printer. When a non-raw queue is created, the driver must be specified in addition ("-d" option) -s spooler Explicit spooler type (cups, lpd, lprng, pdq, ppr, direct) -o option=value Use value as the default for option in this queue -o option Set the switch option by default in this queue --backend-dont-disable=value 1: Do not disable CUPS queue when backend fails, 0: Original CUPS behaviour, queue gets disabled when backend fails. Default: 0 (CUPS only) --backend-attempts=value Try that often when backend fails, for infinite retries set the value to zero, for standard CUPS behaviour to 1. Default: 1 (CUPS only) --backend-delay=value Delay in seconds between retries of failed backend. Default: 30 (CUPS only) -C [sourcespooler] sourcequeue Create a copy of a queue. All characteristics including default option settings are overtaken. Additional arguments modify the copy. This facility allows to overtake one's configured queues when one changes the spooler. -D Set this queue as the queue used by default. -R Remove this whole queue entirely (just give -n queuename) -Q Query existing configuration (gives XML summary). Supplying no queue name gives info about all installed queues for the current/selected spooler, including the default queue. -r list also remote queues (CUPS only). -P Query existing configuration (gives Perl data structure of the complete information about the queue, including options, possible choices, default settings, ..., for use by frontends, the output is done as a Perl array, one element per queue), With printer ID and driver name instead of queue name supplied the Perl data structure of the appropriate printer/driver combo is generated, supplied options are entered as default settings then, from a supplied queue the option default settings are used. Supplying no queue, printer, and driver gives info about all installed queues for the current/selected spooler. N The first index of the Perl array, default: 0 -O Print XML Overview of all known printer/drivers -X Print XML data for -p printer and/or -d driver object -f Force rebuild of PPD file from database -w Generate PPD which is compatible to the CUPS PostScript driver for Windows (GUI strings are limited to 39 characters). This applies only to PPDs built from the Foomatic database, it has no influence on PPDs supplied with the "--ppd" option. -q Run quietly and non-interactive -h --help Show this help message
EOH
#'# Fix emacs syntax highlighting
exit 0; }
|