diff options
Diffstat (limited to 'perl-install/printer/main.pm')
-rw-r--r-- | perl-install/printer/main.pm | 3010 |
1 files changed, 0 insertions, 3010 deletions
diff --git a/perl-install/printer/main.pm b/perl-install/printer/main.pm deleted file mode 100644 index 37e2952bb..000000000 --- a/perl-install/printer/main.pm +++ /dev/null @@ -1,3010 +0,0 @@ -package printer::main; - -# $Id$ - -use strict; - -use common; -use run_program; -use printer::data; -use printer::services; -use printer::default; -use printer::cups; -use printer::detect; -use handle_configs; -use services; -use lang; - -use vars qw(@ISA @EXPORT); - -@ISA = qw(Exporter); -@EXPORT = qw(%printer_type %printer_type_inv); - -our %printer_type = ( - N("Local printer") => "LOCAL", - N("Remote printer") => "REMOTE", - N("Printer on remote CUPS server") => "CUPS", - N("Printer on remote lpd server") => "LPD", - N("Network printer (TCP/Socket)") => "SOCKET", - N("Printer on SMB/Windows server") => "SMB", - N("Printer on NetWare server") => "NCP", - N("Enter a printer device URI") => "URI", - N("Pipe job into a command") => "POSTPIPE" -); - -our %printer_type_inv = reverse %printer_type; - -our %thedb; -our %linkedppds; - -our $hplipdevicesdb; - -# Translation of the "(recommended)" in printer driver entries -our $recstr = N("recommended"); -our $precstr = "($recstr)"; -our $sprecstr = quotemeta($precstr); - -#------------------------------------------------------------------------------ - -sub spooler() { - # LPD is taken from the menu for the moment because the classic LPD is - # highly unsecure. Depending on how the GNU lpr development is going on - # LPD support can be reactivated by uncommenting the following line. - - #return @spooler_inv{qw(cups lpd lprng pdq)}; - - # LPRng is not officially supported any more since version 9.0 of - # this distribution, so show it only in the spooler menu when it - # was manually installed. - - # PDQ is not officially supported any more since version 9.1, so - # show it only in the spooler menu when it was manually installed. - - return map { $spoolers{$_}{long_name} } ('cups', 'rcups' , - if_(files_exist(qw(/usr/bin/pdq)), 'pdq'), - if_(files_exist("/usr/$lib/filters/lpf", "/usr/sbin/lpd"), 'lprng')); -} - -sub printer_type($) { - my ($printer) = @_; - for ($printer->{SPOOLER}) { - /cups/ and return @printer_type_inv{qw(LOCAL LPD SOCKET SMB), if_($printer->{expert}, qw(URI))}; - /lpd/ and return @printer_type_inv{qw(LOCAL LPD SOCKET SMB NCP), if_($printer->{expert}, qw(POSTPIPE URI))}; - /lprng/ and return @printer_type_inv{qw(LOCAL LPD SOCKET SMB NCP), if_($printer->{expert}, qw(POSTPIPE URI))}; - /pdq/ and return @printer_type_inv{qw(LOCAL LPD SOCKET), if_($printer->{expert}, qw(URI))}; - /rcups/ and return (); - } -} - -sub SIGHUP_daemon { - my ($service) = @_; - if ($service eq "cupsd") { $service = "cups" } - # PDQ and remote CUPS have no daemons, exit. - if (($service eq "pdq") || ($service eq "rcups")) { return 1 } - # CUPS needs auto-correction for its configuration - run_program::rooted($::prefix, "/usr/sbin/correctcupsconfig") if $service eq "cups"; - # Name of the daemon - my %daemons = ( - "lpr" => "lpd", - "lpd" => "lpd", - "lprng" => "lpd", - "cups" => "cupsd", - "devfs" => "devfsd", - ); - my $daemon = $daemons{$service}; - $daemon = $service unless defined $daemon; -# if ($service eq "cups") { -# # The current CUPS (1.1.13) dies on SIGHUP, do the normal restart. -# printer::services::restart($service); -# # CUPS needs some time to come up. -# printer::services::wait_for_cups(); -# } else { - - # Send the SIGHUP - run_program::rooted($::prefix, "/usr/bin/killall", "-HUP", $daemon); - if ($service eq "cups") { - # CUPS needs some time to come up. - printer::services::wait_for_cups(); - } - - return 1; -} - - -sub assure_device_is_available_for_cups { - # Checks whether CUPS already "knows" a certain port, it does not - # know it usually when the appropriate kernel module is loaded - # after CUPS was started or when the printer is turned on after - # CUPS was started. CUPS 1.1.12 and newer refuses to set up queues - # on devices which it does not know, it points these queues to - # file:/dev/null instead. Restart CUPS if necessary to assure that - # CUPS knows the device. - my ($device) = @_; - my $sdevice = handle_configs::searchstr($device); - my ($result, $i); - # USB printers get special model-dependent URLs in "lpinfo -v" here - # checking is complicated, so we simply restart CUPS then and ready. - if ($device =~ /usb/) { - $result = printer::services::restart("cups"); - return 1; - } - my $maxattempts = 3; - for ($i = 0; $i < $maxattempts; $i++) { - open(my $F, ($::testing ? $::prefix : "chroot $::prefix/ ") . - '/bin/sh -c "export LC_ALL=C; /usr/sbin/lpinfo -v" |') or - die 'Could not run "lpinfo"!'; - while (my $line = <$F>) { - if ($line =~ /$sdevice/) { # Found a line containing the device - # name, so CUPS knows it. - close $F; - return 1; - } - } - close $F; - $result = printer::services::restart("cups"); - } - return $result; -} - - -sub spooler_in_security_level { - # Was the current spooler already added to the current security level? - my ($spooler, $level) = @_; - my $sp; - $sp = $spooler eq "lpr" || $spooler eq "lprng" ? "lpd" : $spooler; - my $file = "$::prefix/etc/security/msec/server.$level"; - if (-f $file) { - open(my $F, "< $file") or return 0; - while (my $line = <$F>) { - if ($line =~ /^\s*$sp\s*$/) { - close $F; - return 1; - } - } - close $F; - } - return 0; -} - -sub add_spooler_to_security_level { - my ($spooler, $level) = @_; - my $sp; - $sp = $spooler eq "lpr" || $spooler eq "lprng" ? "lpd" : $spooler; - my $file = "$::prefix/etc/security/msec/server.$level"; - if (-f $file) { - eval { append_to_file($file, "$sp\n") } or return 0; - } - return 1; -} - -sub pdq_panic_button { - my $setting = $_[0]; - if (-f "$::prefix/usr/sbin/pdqpanicbutton") { - run_program::rooted($::prefix, "/usr/sbin/pdqpanicbutton", "--$setting") - or die "Could not $setting PDQ panic buttons!"; - } -} - -sub copy_printer_params($$) { - my ($from, $to) = @_; - map { $to->{$_} = $from->{$_} } grep { $_ ne 'configured' } keys %$from; - #- avoid cycles-----------------^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -} - -sub getinfo($) { - my ($prefix) = @_; - my $printer = {}; - - $::prefix = $prefix; - - # Initialize $printer data structure - resetinfo($printer); - - return $printer; -} - -#------------------------------------------------------------------------------ -sub resetinfo($) { - my ($printer) = @_; - $printer->{QUEUE} = ""; - $printer->{OLD_QUEUE} = ""; - $printer->{OLD_CHOICE} = ""; - $printer->{ARGS} = {}; - $printer->{DBENTRY} = ""; - $printer->{DEFAULT} = ""; - $printer->{currentqueue} = {}; - # -check which printing system was used previously and load the information - # -about its queues - read_configured_queues($printer); -} - -sub read_configured_queues($) { - my ($printer) = @_; - my @QUEUES; - # Get the default spooler choice from the config file - $printer->{SPOOLER} ||= printer::default::get_spooler(); - if (!$printer->{SPOOLER}) { - #- Find the first spooler where there are queues - foreach my $spooler (qw(rcups cups pdq lprng lpd)) { - #- Is the spooler's daemon running? - my $service = $spooler; - if ($service eq "lprng") { - $service = "lpd"; - } - if (($service ne "pdq") && ($service ne "rcups")) { - next unless services::is_service_running($service); - # daemon is running, spooler found - $printer->{SPOOLER} = $spooler; - } - #- poll queue info - if ($service ne "rcups") { - open(my $F, ($::testing ? - $::prefix : "chroot $::prefix/ ") . - "foomatic-configure -P -q -s $spooler |") or - die "Could not run foomatic-configure"; - eval join('', <$F>); - close $F; - } - if ($service eq "pdq") { - #- Have we found queues? PDQ has no damon, so we consider - #- it in use when there are defined printer queues - if ($#QUEUES != -1) { - $printer->{SPOOLER} = $spooler; - last; - } - } elsif ($service eq "rcups") { - #- In daemon-less CUPS mode there are no local queues, - #- we can only recognize it by a server entry in - #- /etc/cups/client.conf - my ($daemonless_cups, $remote_cups_server) = - printer::main::read_client_conf(); - if ($daemonless_cups) { - $printer->{SPOOLER} = $spooler; - $printer->{remote_cups_server} = $remote_cups_server; - last; - } - } else { - #- For other spoolers we have already found a running - #- daemon when we have arrived here - last; - } - } - } else { - if ($printer->{SPOOLER} ne "rcups") { - #- Poll the queues of the current default spooler - open(my $F, ($::testing ? $::prefix : "chroot $::prefix/ ") . - "foomatic-configure -P -q -s $printer->{SPOOLER} -r |") or - die "Could not run foomatic-configure"; - eval join('', <$F>); - close $F; - } else { - my ($_daemonless_cups, $remote_cups_server) = - printer::main::read_client_conf(); - $printer->{remote_cups_server} = $remote_cups_server; - } - } - $printer->{configured} = {}; - my $i; - my $N = $#QUEUES + 1; - for ($i = 0; $i < $N; $i++) { - # Set the default printer - $printer->{DEFAULT} = $QUEUES[$i]{queuedata}{queue} if - $QUEUES[$i]{queuedata}{default}; - # Advance to the next entry if the current is a remotely defined - # printer - next if $QUEUES[$i]{queuedata}{remote}; - # Add an entry for a locally defined queue - $printer->{configured}{$QUEUES[$i]{queuedata}{queue}} = - $QUEUES[$i]; - if (!$QUEUES[$i]{make} || !$QUEUES[$i]{model}) { - if ($printer->{SPOOLER} eq "cups") { - $printer->{OLD_QUEUE} = $QUEUES[$i]{queuedata}{queue}; - my $descr = get_descr_from_ppd($printer); - if ($descr =~ m/^([^\|]*)\|([^\|]*)(\|.*|)$/) { - $printer->{configured}{$QUEUES[$i]{queuedata}{queue}}{queuedata}{make} ||= $1; - $printer->{configured}{$QUEUES[$i]{queuedata}{queue}}{queuedata}{model} ||= $2; - } - # Read out which PPD file was originally used to set up this - # queue - if (open(my $F, "< $::prefix/etc/cups/ppd/$QUEUES[$i]{queuedata}{queue}.ppd")) { - while (my $line = <$F>) { - if ($line =~ /^\*%MDKMODELCHOICE:(.+)$/) { - $printer->{configured}{$QUEUES[$i]{queuedata}{queue}}{queuedata}{ppd} = $1; - } - } - close $F; - # Mark that we have a CUPS queue but do not know the - # name the PPD file in /usr/share/cups/model - $printer->{configured}{$QUEUES[$i]{queuedata}{queue}}{queuedata}{ppd} ||= '1'; - # Mark that our PPD file is not a Foomatic one - $printer->{configured}{$QUEUES[$i]{queuedata}{queue}}{queuedata}{driver} = "PPD"; - } else { - # We do not have a PPD file for this queue - $printer->{configured}{$QUEUES[$i]{queuedata}{queue}}{queuedata}{ppd} = undef; - # No PPD found? Then we have a raw queue - $printer->{configured}{$QUEUES[$i]{queuedata}{queue}}{queuedata}{driver} = "raw"; - } - $printer->{OLD_QUEUE} = ""; - } - $printer->{configured}{$QUEUES[$i]{queuedata}{queue}}{queuedata}{make} ||= ""; - $printer->{configured}{$QUEUES[$i]{queuedata}{queue}}{queuedata}{model} ||= N("Unknown model"); - } else { - $printer->{configured}{$QUEUES[$i]{queuedata}{queue}}{queuedata}{make} = $QUEUES[$i]{make}; - $printer->{configured}{$QUEUES[$i]{queuedata}{queue}}{queuedata}{model} = $QUEUES[$i]{model}; - } - # Fill in "options" field - if (my $args = $printer->{configured}{$QUEUES[$i]{queuedata}{queue}}{args}) { - my @options; - foreach my $arg (@$args) { - push(@options, "-o"); - my $optstr = $arg->{name} . "=" . $arg->{default}; - push(@options, $optstr); - } - @{$printer->{configured}{$QUEUES[$i]{queuedata}{queue}}{queuedata}{options}} = @options; - } - # Construct an entry line for tree view in main window of - # printerdrake - make_menuentry($printer, $QUEUES[$i]{queuedata}{queue}); - } -} - -sub make_menuentry { - my ($printer, $queue) = @_; - my $spooler = $spoolers{$printer->{SPOOLER}}{short_name}; - my $connect = $printer->{configured}{$queue}{queuedata}{connect}; - my $localremote = N("Configured on this machine"); - my $make = $printer->{configured}{$queue}{queuedata}{make}; - my $model = $printer->{configured}{$queue}{queuedata}{model}; - my $connection; - if ($connect =~ m!^(file|parallel):/dev/lp(\d+)$!) { - my $number = $2; - $connection = N(" on parallel port #%s", $number); - } elsif ($connect =~ m!^(file|usb):/dev/usb/lp(\d+)$!) { - my $number = $2; - $connection = N(", USB printer #%s", $number); - } elsif ($connect =~ m!^usb://!) { - $connection = N(", USB printer"); - } elsif ($connect =~ m!^hp:/(.+?)$!) { - my $hplipdevice = $1; - if ($hplipdevice =~ m!^par/!) { - $connection = N(", HP printer on a parallel port"); - } elsif ($hplipdevice =~ m!^usb/!) { - $connection = N(", HP printer on USB"); - } elsif ($hplipdevice =~ m!^net/!) { - $connection = N(", HP printer on HP JetDirect"); - } else { - $connection = N(", HP printer"); - } - } elsif ($connect =~ m!^ptal://?(.+?)$!) { - my $ptaldevice = $1; - if ($ptaldevice =~ /^mlc:par:(\d+)$/) { - my $number = $1; - $connection = N(", multi-function device on parallel port #%s", - $number); - } elsif ($ptaldevice =~ /^mlc:par:/) { - $connection = N(", multi-function device on a parallel port"); - } elsif ($ptaldevice =~ /^mlc:usb:/) { - $connection = N(", multi-function device on USB"); - } elsif ($ptaldevice =~ /^hpjd:/) { - $connection = N(", multi-function device on HP JetDirect"); - } else { - $connection = N(", multi-function device"); - } - } elsif ($connect =~ m!^file:(.+)$!) { - my $file = $1; - $connection = N(", printing to %s", $file); - } elsif ($connect =~ m!^lpd://([^/]+)/([^/]+)/?$!) { - my ($server, $printer) = ($1, $2); - $connection = N(" on LPD server \"%s\", printer \"%s\"", $server, $printer); - } elsif ($connect =~ m!^socket://([^/:]+):([^/:]+)/?$!) { - my ($host, $port) = ($1, $2); - $connection = N(", TCP/IP host \"%s\", port %s", $host, $port); - } elsif ($connect =~ m!^smb://([^/\@]+)/([^/\@]+)/?$! || - $connect =~ m!^smb://.*/([^/\@]+)/([^/\@]+)/?$! || - $connect =~ m!^smb://.*\@([^/\@]+)/([^/\@]+)/?$!) { - my ($server, $share) = ($1, $2); - $connection = N(" on SMB/Windows server \"%s\", share \"%s\"", $server, $share); - } elsif ($connect =~ m!^ncp://([^/\@]+)/([^/\@]+)/?$! || - $connect =~ m!^ncp://.*/([^/\@]+)/([^/\@]+)/?$! || - $connect =~ m!^ncp://.*\@([^/\@]+)/([^/\@]+)/?$!) { - my ($server, $printer) = ($1, $2); - $connection = N(" on Novell server \"%s\", printer \"%s\"", $server, $printer); - } elsif ($connect =~ m!^postpipe:(.+)$!) { - my $command = $1; - $connection = N(", using command %s", $command); - } else { - $connection = ($printer->{expert} ? ", URI: $connect" : ""); - } - my $sep = "!"; - $printer->{configured}{$queue}{queuedata}{menuentry} = - ($printer->{expert} ? "$spooler$sep" : "") . - "$localremote$sep$queue: $make $model$connection"; -} - -sub connectionstr { - my ($connect) = @_; - my $connection; - if ($connect =~ m!^(file|parallel):/dev/lp(\d+)$!) { - my $number = $2; - $connection = N("Parallel port #%s", $number); - } elsif ($connect =~ m!^(file|usb):/dev/usb/lp(\d+)$!) { - my $number = $2; - $connection = N("USB printer #%s", $number); - } elsif ($connect =~ m!^usb://!) { - $connection = N("USB printer"); - } elsif ($connect =~ m!^hp:/(.+?)$!) { - my $hplipdevice = $1; - if ($hplipdevice =~ m!^par/!) { - $connection = N("HP printer on a parallel port"); - } elsif ($hplipdevice =~ m!^usb/!) { - $connection = N("HP printer on USB"); - } elsif ($hplipdevice =~ m!^net/!) { - $connection = N("HP printer on HP JetDirect"); - } else { - $connection = N("HP printer"); - } - } elsif ($connect =~ m!^ptal://?(.+?)$!) { - my $ptaldevice = $1; - if ($ptaldevice =~ /^mlc:par:(\d+)$/) { - my $number = $1; - $connection = N("Multi-function device on parallel port #%s", - $number); - } elsif ($ptaldevice =~ /^mlc:par:/) { - $connection = N("Multi-function device on a parallel port"); - } elsif ($ptaldevice =~ /^mlc:usb:/) { - $connection = N("Multi-function device on USB"); - } elsif ($ptaldevice =~ /^hpjd:/) { - $connection = N("Multi-function device on HP JetDirect"); - } else { - $connection = N("Multi-function device"); - } - } elsif ($connect =~ m!^file:(.+)$!) { - my $file = $1; - $connection = N("Prints into %s", $file); - } elsif ($connect =~ m!^lpd://([^/]+)/([^/]+)/?$!) { - my ($server, $port) = ($1, $2); - $connection = N("LPD server \"%s\", printer \"%s\"", $server, $port); - } elsif ($connect =~ m!^socket://([^/:]+):([^/:]+)/?$!) { - my ($host, $port) = ($1, $2); - $connection = N("TCP/IP host \"%s\", port %s", $host, $port); - } elsif ($connect =~ m!^smb://([^/\@]+)/([^/\@]+)/?$! || - $connect =~ m!^smb://.*/([^/\@]+)/([^/\@]+)/?$! || - $connect =~ m!^smb://.*\@([^/\@]+)/([^/\@]+)/?$!) { - my ($server, $share) = ($1, $2); - $connection = N("SMB/Windows server \"%s\", share \"%s\"", $server, $share); - } elsif ($connect =~ m!^ncp://([^/\@]+)/([^/\@]+)/?$! || - $connect =~ m!^ncp://.*/([^/\@]+)/([^/\@]+)/?$! || - $connect =~ m!^ncp://.*\@([^/\@]+)/([^/\@]+)/?$!) { - my ($server, $share) = ($1, $2); - $connection = N("Novell server \"%s\", printer \"%s\"", $server, $share); - } elsif ($connect =~ m!^postpipe:(.+)$!) { - my $command = $1; - $connection = N("Uses command %s", $command); - } else { - $connection = N("URI: %s", $connect); - } - return $connection; -} - -sub read_printer_db { - - my ($printer, $spooler, $newppd) = @_; - - # If a $newppd is supplied, we return the key of the DB entry which - # is for this file. This way we can pre-select a freshly added PPD in - # the model/driver list. - - # No local queues available in daemon-less CUPS mode - return 1 if $spooler eq "rcups"; - - my $DBPATH; #- do not have to do close ... and do not modify globals at least - # Generate the Foomatic printer/driver overview - open($DBPATH, ($::testing ? $::prefix : "chroot $::prefix/ ") . #-# - "foomatic-configure -O -q |") or - die "Could not run foomatic-configure"; - - %linkedppds = (); - my $entry = {}; - @{$entry->{drivers}} = (); - my $inentry = 0; - my $indrivers = 0; - my $inppds = 0; - my $inppd = 0; - my $inautodetect = 0; - my $autodetecttype = ""; - my $ppds = {}; - my $ppddriver = ""; - my $ppdfile = ""; - my $ppdentry = ""; - local $_; - while (<$DBPATH>) { - chomp; - if ($inentry) { - # We are inside a printer entry - if ($indrivers) { - # We are inside the drivers block of a printers entry - if (m!^\s*</drivers>\s*$!) { - # End of drivers block - $indrivers = 0; - } elsif (m!^\s*<driver>(.+)</driver>\s*$!) { - push @{$entry->{drivers}}, $1; - } - } elsif ($inppds) { - # We are inside the ppds block of a printers entry - if ($inppd) { - # We are inside a PPD entry in the ppds block - if (m!^\s*</ppd>\s*$!) { - # End of ppds block - $inppd = 0; - if ($ppddriver && $ppdfile) { - $ppds->{$ppddriver} = $ppdfile; - } - $ppddriver = ""; - $ppdfile = ""; - } elsif (m!^\s*<driver>(.+)</driver>\s*$!) { - $ppddriver = $1; - } elsif (m!^\s*<ppdfile>(.+)</ppdfile>\s*$!) { - $ppdfile = $1; - } - } else { - if (m!^\s*</ppds>\s*$!) { - # End of ppds block - $inppds = 0; - } elsif (m!^\s*<ppd>\s*$!) { - $inppd = 1; - } - } - } elsif ($inautodetect) { - # We are inside the autodetect block of a printers entry - # All entries inside this block will be ignored - if ($autodetecttype) { - if (m!^.*</$autodetecttype>\s*$!) { - # End of general, parallel, USB, or SNMP section - $autodetecttype = ""; - } elsif (m!^\s*<manufacturer>\s*([^<>]+)\s*</manufacturer>\s*$!) { - # Manufacturer - $entry->{devidmake} = $1; - } elsif (m!^\s*<model>\s*([^<>]+)\s*</model>\s*$!) { - # Model - $entry->{devidmodel} = $1; - } elsif (m!^\s*<description>\s*([^<>]+)\s*</description>\s*$!) { - # Description - $entry->{deviddesc} = $1; - } elsif (m!^\s*<commandset>\s*([^<>]+)\s*</commandset>\s*$!) { - # Command set - $entry->{devidcmdset} = $1; - } elsif (m!^\s*<ieee1284>\s*([^<>]+)\s*</ieee1284>\s*$!) { - # Full ID string - my $idstr = $1; - $idstr =~ m!(MFG|MANUFACTURER):([^;]+);!i - and $entry->{devidmake} = $2; - $idstr =~ m!(MDL|MODEL):([^;]+);!i - and $entry->{devidmodel} = $2; - $idstr =~ m!(DES|DESCRIPTION):([^;]+);!i - and $entry->{deviddesc} = $2; - $idstr =~ m!(CMD|COMMAND\s*SET):([^;]+);!i - and $entry->{devidcmdset} = $2; - } - } else { - if (m!^.*</autodetect>\s*$!) { - # End of autodetect block - $inautodetect = 0; - } elsif (m!^\s*<(general|parallel|usb|snmp)>\s*$!) { - # Beginning of parallel, USB, or SNMP section - $autodetecttype = $1; - } - } - } else { - if (m!^\s*</printer>\s*$!) { - # entry completed - $inentry = 0; - # Expert mode: - # Make one database entry per driver with the entry name - # manufacturer|model|driver - if ($printer->{expert}) { - foreach my $driver (@{$entry->{drivers}}) { - my $driverstr; - if ($driver eq "Postscript") { - $driverstr = "PostScript"; - } else { - $driverstr = "GhostScript + $driver"; - } - if ($driver eq $entry->{defaultdriver}) { - $driverstr .= " $precstr"; - } - $entry->{ENTRY} = "$entry->{make}|$entry->{model}|$driverstr"; - $entry->{ENTRY} =~ s/^CITOH/C.ITOH/i; - $entry->{ENTRY} =~ - s/^KYOCERA[\s\-]*MITA/KYOCERA/i; - $entry->{driver} = $driver; - if (defined($ppds->{$driver})) { - $entry->{ppd} = $ppds->{$driver}; - $ppds->{$driver} =~ m!([^/]+)$!; - push(@{$linkedppds{$1}}, $entry->{ENTRY}); - } else { - undef $entry->{ppd}; - } - # Duplicate contents of $entry because it is multiply entered to the database - map { $thedb{$entry->{ENTRY}}{$_} = $entry->{$_} } keys %$entry; - } - } else { - # Recommended mode - # Make one entry per printer, with the recommended - # driver (manufacturerer|model) - $entry->{ENTRY} = "$entry->{make}|$entry->{model}"; - $entry->{ENTRY} =~ s/^CITOH/C.ITOH/i; - $entry->{ENTRY} =~ - s/^KYOCERA[\s\-]*MITA/KYOCERA/i; - if (($entry->{defaultdriver}) && - (member($entry->{defaultdriver}, - @{$entry->{drivers}}))) { - my $driver = $entry->{defaultdriver}; - $entry->{driver} = $driver; - if (defined($ppds->{$driver})) { - $entry->{ppd} = $ppds->{$driver}; - $ppds->{$driver} =~ m!([^/]+)$!; - push(@{$linkedppds{$1}}, $entry->{ENTRY}); - } else { - undef $entry->{ppd}; - } - map { $thedb{$entry->{ENTRY}}{$_} = $entry->{$_} } keys %$entry; - } - } - $entry = {}; - @{$entry->{drivers}} = (); - $ppds = {}; - } elsif (m!^\s*<id>\s*([^\s<>]+)\s*</id>\s*$!) { - # Foomatic printer ID - $entry->{printer} = $1; - } elsif (m!^\s*<make>(.+)</make>\s*$!) { - # Printer manufacturer - $entry->{make} = uc($1); - } elsif (m!^\s*<model>(.+)</model>\s*$!) { - # Printer model - $entry->{model} = $1; - } elsif (m!<driver>(.+)</driver>!) { - # Printer default driver - $entry->{defaultdriver} = $1; - } elsif (m!^\s*<drivers>\s*$!) { - # Drivers block - $indrivers = 1; - } elsif (m!^\s*<ppds>\s*$!) { - # PPDs block - $inppds = 1; - } elsif (m!^\s*<autodetect>\s*$!) { - # Autodetect block - $inautodetect = 1; - } - } - } else { - if (m!^\s*<printer>\s*$!) { - # new entry - $inentry = 1; - } - } - } - close $DBPATH; - - # Add raw queue - $entry->{ENTRY} = N("Raw printer (No driver)"); - $entry->{driver} = "raw"; - $entry->{make} = ""; - $entry->{model} = N("Unknown model"); - $thedb{$entry->{ENTRY}}{$_} = $entry->{$_} foreach keys %$entry; - - #- Load CUPS driver database if CUPS is used as spooler - if ($spooler && $spooler eq "cups") { - $ppdentry = poll_ppd_base($printer, $newppd); - } - - #my @entries_db_short = sort keys %printer::thedb; - #%descr_to_db = map { $printer::thedb{$_}{DESCR}, $_ } @entries_db_short; - #%descr_to_help = map { $printer::thedb{$_}{DESCR}, $printer::thedb{$_}{ABOUT} } @entries_db_short; - #@entry_db_description = keys %descr_to_db; - #db_to_descr = reverse %descr_to_db; - - return $ppdentry if $newppd; - -} - -sub read_foomatic_options ($) { - my ($printer) = @_; - # Generate the option data for the chosen printer/driver combo - my $COMBODATA; - open(my $F, ($::testing ? $::prefix : "chroot $::prefix/ ") . - "foomatic-configure -P -q -p $printer->{currentqueue}{printer}" . - " -d $printer->{currentqueue}{driver}" . - ($printer->{OLD_QUEUE} ? - " -s $printer->{SPOOLER} -n $printer->{OLD_QUEUE}" : "") . - ($printer->{SPECIAL_OPTIONS} ? - " $printer->{SPECIAL_OPTIONS}" : "") - . " |") or - die "Could not run foomatic-configure"; - eval join('', (<$F>)); - close $F; - # Return the arguments field - return $COMBODATA->{args}; -} - -sub read_ppd_options ($) { - my ($printer) = @_; - # Generate the option data for a given PPD file - my $COMBODATA; - open(my $F, ($::testing ? $::prefix : "chroot $::prefix/ ") . - "foomatic-configure -P -q" . - if_($printer->{currentqueue}{ppd} && - ($printer->{currentqueue}{ppd} ne '1'), - " --ppd \'" . ($printer->{currentqueue}{ppd} !~ m!^/! ? - "/usr/share/cups/model/" : "") . - $printer->{currentqueue}{ppd} . "\'") . - ($printer->{OLD_QUEUE} ? - " -s $printer->{SPOOLER} -n $printer->{OLD_QUEUE}" : "") . - ($printer->{SPECIAL_OPTIONS} ? - " $printer->{SPECIAL_OPTIONS}" : "") - . " |") or - die "Could not run foomatic-configure"; - eval join('', (<$F>)); - close $F; - # Return the arguments field - return $COMBODATA->{args}; -} - -sub set_cups_special_options { - my ($printer, $queue) = @_; - # Set some special CUPS options - my @lpoptions = cat_("$::prefix/etc/cups/lpoptions"); - # If nothing is already configured, set text file borders of half - # an inch so nothing of the text gets cut off by unprintable - # borders. Do this only when the driver is not Gutenprint or HPIJS, as - # both drivers decent border settings are already done and with - # Gutenprint this will even break PostScript printing - if ((($queue eq $printer->{currentqueue}{$queue}) && - (($printer->{currentqueue}{driver} =~ - /(guten.*print|hpijs|hplip)/i) || - ($printer->{currentqueue}{ppd} =~ - /(guten.*print|hpijs|hplip)/i))) || - ((defined($printer->{configured}{$queue})) && - (($printer->{configured}{$queue}{queuedata}{driver} =~ - /(guten.*print|hpijs|hplip)/i) || - ($printer->{configured}{$queue}{queuedata}{ppd} =~ - /(guten.*print|hpijs|hplip)/i))) || - (($printer->{SPOOLER} eq "cups") && - (-r "$::prefix/etc/cups/ppd/$queue.ppd") && - (`egrep -ic '(gutenprint|hpijs|hplip)' $::prefix/etc/cups/ppd/$queue.ppd` > 2))) { - # Remove page margin settings - foreach (@lpoptions) { - s/\s*page-(top|bottom|left|right)=\S+//g if /$queue/; - } - output("$::prefix/etc/cups/lpoptions", @lpoptions); - } else { - if (!any { /$queue.*\spage-(top|bottom|left|right)=/ } @lpoptions) { - run_program::rooted($::prefix, "lpoptions", - "-p", $queue, - "-o", "page-top=36", "-o", "page-bottom=36", - "-o", "page-left=36", "-o page-right=36"); - } - } - # Let images fill the whole page by default and let text be word-wrapped - # and printed in a slightly smaller font - if (!any { /$queue.*\s(scaling|natural-scaling|ppi)=/ } @lpoptions) { - run_program::rooted($::prefix, "lpoptions", - "-p", $queue, - "-o", "scaling=100"); - } - if (!any { /$queue.*\s(cpi|lpi)=/ } @lpoptions) { - run_program::rooted($::prefix, "lpoptions", - "-p", $queue, - "-o", "cpi=12", "-o", "lpi=7", "-o", "wrap"); - } - return 1; -} - -my %sysconfig = getVarsFromSh("$::prefix/etc/sysconfig/printing"); - -sub set_cups_autoconf { - my ($autoconf) = @_; - $sysconfig{CUPS_CONFIG} = $autoconf ? "automatic" : "manual"; - setVarsInSh("$::prefix/etc/sysconfig/printing", \%sysconfig); - # Restart CUPS - printer::services::restart("cups") if $autoconf; - return 1; -} - -sub get_cups_autoconf() { $sysconfig{CUPS_CONFIG} ne 'manual' ? 1 : 0 } - -sub set_usermode { - my ($usermode) = @_; - $sysconfig{USER_MODE} = $usermode ? "expert" : "recommended"; - setVarsInSh("$::prefix/etc/sysconfig/printing", \%sysconfig) if !$::testing; - return $usermode; -} - -sub get_usermode() { $sysconfig{USER_MODE} eq 'expert' ? 1 : 0 } - -sub set_auto_admin { - my ($printer) = @_; - $sysconfig{ENABLE_QUEUES_ON_PRINTER_CONNECTED} = - $printer->{enablequeuesonnewprinter} ? "yes" : "no"; - $sysconfig{AUTO_SETUP_QUEUES_ON_PRINTER_CONNECTED} = - $printer->{autoqueuesetuponnewprinter} ? "yes" : "no"; - $sysconfig{ENABLE_QUEUES_ON_SPOOLER_START} = - $printer->{enablequeuesonspoolerstart} ? "yes" : "no"; - $sysconfig{AUTO_SETUP_QUEUES_ON_PRINTERDRAKE_START} = - $printer->{autoqueuesetuponstart} ? "yes" : "no"; - $sysconfig{AUTO_SETUP_QUEUES_MODE} = - $printer->{autoqueuesetupgui} ? "waitforgui" : "nogui"; - setVarsInSh("$::prefix/etc/sysconfig/printing", \%sysconfig); - return 1; -} - -sub get_auto_admin { - my ($printer) = @_; - $printer->{enablequeuesonnewprinter} = - (!defined($sysconfig{ENABLE_QUEUES_ON_PRINTER_CONNECTED}) || - ($sysconfig{ENABLE_QUEUES_ON_PRINTER_CONNECTED} =~ /no/i) ? - 0 : 1); - $printer->{autoqueuesetuponnewprinter} = - (!defined($sysconfig{AUTO_SETUP_QUEUES_ON_PRINTER_CONNECTED}) || - ($sysconfig{AUTO_SETUP_QUEUES_ON_PRINTER_CONNECTED} =~ /yes/i) ? - 1 : 0); - $printer->{enablequeuesonspoolerstart} = - (!defined($sysconfig{ENABLE_QUEUES_ON_SPOOLER_START}) || - ($sysconfig{ENABLE_QUEUES_ON_SPOOLER_START} =~ /no/i) ? - 0 : 1); - $printer->{autoqueuesetuponstart} = - (!defined($sysconfig{AUTO_SETUP_QUEUES_ON_PRINTERDRAKE_START}) || - ($sysconfig{AUTO_SETUP_QUEUES_ON_PRINTERDRAKE_START} =~ /yes/i) ? - 1 : 0); - $printer->{autoqueuesetupgui} = - (!defined($sysconfig{AUTO_SETUP_QUEUES_MODE}) || - ($sysconfig{AUTO_SETUP_QUEUES_MODE} =~ /waitforgui/i) ? - 1 : 0); -} - -sub set_jap_textmode { - my $textmode = ($_[0] ? 'cjk' : ''); - # Do not write mime.convs if the file does not exist, as then - # CUPS is not installed and the created mime.convs will be broken. - # When installing CUPS later it will not work. - return 1 if (! -r "$::prefix/etc/cups/mime.convs"); - substInFile { - s!^(\s*text/plain\s+\S+\s+\d+\s+)\S+(\s*$)!$1${textmode}texttops$2!; - } "$::prefix/etc/cups/mime.convs"; - return 1; -} - -sub get_jap_textmode() { - my @mimeconvs = cat_("$::prefix/etc/cups/mime.convs"); - (m!^\s*text/plain\s+\S+\s+\d+\s+(\S+)\s*$!m and - $1 eq 'cjktexttops' and return 1) foreach @mimeconvs; - return 0; -} - -#---------------------------------------------------------------------- -# Handling of /etc/cups/cupsd.conf - -sub read_cupsd_conf() { - # If /etc/cups/cupsd.conf does not exist a default cupsd.conf will be - # put out to avoid writing of a broken cupsd.conf file when we write - # it back later. - my @cupsd_conf = cat_("$::prefix/etc/cups/cupsd.conf"); - if (!@cupsd_conf) { - @cupsd_conf = map { /\n$/s or "$_\n" } split('\n', -'LogLevel info -TempDir /var/spool/cups/tmp -Port 631 -Browsing On -BrowseAddress @LOCAL -BrowseDeny All -BrowseAllow 127.0.0.1 -BrowseAllow @LOCAL -BrowseOrder deny,allow -<Location /> -Order Deny,Allow -Deny From All -Allow From 127.0.0.1 -Allow From @LOCAL -</Location> -<Location /admin> -AuthType Basic -AuthClass System -Order Deny,Allow -Deny From All -Allow From 127.0.0.1 -</Location> -'); - } - return @cupsd_conf; -} - -sub write_cupsd_conf { - my (@cupsd_conf) = @_; - # Do not write cupsd.conf if the file does not exist, as then - # CUPS is not installed and the created cupsd.conf will be broken. - # When installing CUPS later it will not start. - return 1 if (! -r "$::prefix/etc/cups/cupsd.conf"); - output("$::prefix/etc/cups/cupsd.conf", @cupsd_conf); -} - -sub read_location { - - # Return the lines inside the [path] location block - # - # <Location [path]> - # ... - # </Location> - - my ($cupsd_conf_ptr, $path) = @_; - - my @result; - if (any { m!^\s*<Location\s+$path\s*>! } @$cupsd_conf_ptr) { - my $location_start = -1; - my $location_end = -1; - # Go through all the lines, bail out when start and end line found - for (my $i = 0; - $i <= $#{$cupsd_conf_ptr} && $location_end == -1; - $i++) { - if ($cupsd_conf_ptr->[$i] =~ m!^\s*<\s*Location\s+$path\s*>!) { - # Start line of block - $location_start = $i; - } elsif ($cupsd_conf_ptr->[$i] =~ - m!^\s*<\s*/Location\s*>! && - $location_start != -1) { - # End line of block - $location_end = $i; - last; - } elsif ($location_start >= 0 && $location_end < 0) { - # Inside the location block - push(@result, $cupsd_conf_ptr->[$i]); - } - } - } else { - # If there is no root location block, set the result array to - # "undef" - @result = undef; - } - return @result; -} - -sub rip_location { - - # Cut out the [path] location block - # - # <Location [path]> - # ... - # </Location> - # - # so that it can be treated seperately without affecting the - # rest of the file - - my ($cupsd_conf_ptr, $path) = @_; - - my @location; - my $location_start = -1; - my $location_end = -1; - if (any { m!^\s*<Location\s+$path\s*>! } @$cupsd_conf_ptr) { - # Go through all the lines, bail out when start and end line found - for (my $i = 0; - $i <= $#{$cupsd_conf_ptr} && $location_end == -1; - $i++) { - if ($cupsd_conf_ptr->[$i] =~ m!^\s*<\s*Location\s+$path\s*>!) { - # Start line of block - $location_start = $i; - } elsif ($cupsd_conf_ptr->[$i] =~ - m!^\s*<\s*/Location\s*>! && - $location_start != -1) { - # End line of block - $location_end = $i; - last; - } - } - # Rip out the block and store it seperately - @location = - splice(@$cupsd_conf_ptr, $location_start, - $location_end - $location_start + 1); - } else { - # If there is no location block, create one - $location_start = $#{$cupsd_conf_ptr} + 1; - @location = ("<Location $path>\n", "</Location>\n"); - } - - return $location_start, @location; -} - -sub insert_location { - - # Re-insert a location block ripped with "rip_location" - - my ($cupsd_conf_ptr, $location_start, @location) = @_; - - splice(@$cupsd_conf_ptr, $location_start,0,@location); -} - -sub add_to_location { - - # Add a directive to a given location (only if it is not already there) - - my ($cupsd_conf_ptr, $path, $directive) = @_; - - my ($location_start, @location) = rip_location($cupsd_conf_ptr, $path); - my $success = handle_configs::insert_directive(\@location, $directive); - insert_location($cupsd_conf_ptr, $location_start, @location); - return $success; -} - -sub remove_from_location { - - # Remove a directive from a given location - - my ($cupsd_conf_ptr, $path, $directive) = @_; - - my ($location_start, @location) = rip_location($cupsd_conf_ptr, $path); - my $success = handle_configs::remove_directive(\@location, $directive); - insert_location($cupsd_conf_ptr, $location_start, @location); - return $success; -} - -sub replace_in_location { - - # Replace a directive in a given location - - my ($cupsd_conf_ptr, $path, $olddirective, $newdirective) = @_; - - my ($location_start, @location) = rip_location($cupsd_conf_ptr, $path); - my $success = handle_configs::replace_directive(\@location, - $olddirective, - $newdirective); - insert_location($cupsd_conf_ptr, $location_start, @location); - return $success; -} - -sub add_allowed_host { - - # Add a host or network which should get access to the local printer(s) - my ($cupsd_conf_ptr, $host) = @_; - - return (handle_configs::insert_directive($cupsd_conf_ptr, - "BrowseAddress $host") and - add_to_location($cupsd_conf_ptr, "/", "Allow From $host")); -} - -sub remove_allowed_host { - - # Remove a host or network which should get access to the local - # printer(s) - my ($cupsd_conf_ptr, $host) = @_; - - return (handle_configs::remove_directive($cupsd_conf_ptr, "BrowseAddress $host") and - remove_from_location($cupsd_conf_ptr, "/", - "Allow From $host")); -} - -sub replace_allowed_host { - - # Remove a host or network which should get access to the local - # printer(s) - my ($cupsd_conf_ptr, $oldhost, $newhost) = @_; - - return (handle_configs::replace_directive($cupsd_conf_ptr, - "BrowseAddress $oldhost", - "BrowseAddress $newhost") and - replace_in_location($cupsd_conf_ptr, "/", "Allow From $newhost", - "Allow From $newhost")); -} - -sub broadcastaddress { - - # Determines the broadcast address (for "BrowseAddress" line) for - # a given network IP - - my ($address) = @_; - - if ($address =~ /^\d+\.\*$/) { - $address =~ s/\*$/255.255.255/; - } elsif ($address =~ /^\d+\.\d+\.\*$/) { - $address =~ s/\*$/255.255/; - } elsif ($address =~ /^\d+\.\d+\.\d+\.\*$/) { - $address =~ s/\*$/255/; - } elsif ($address =~ m!^(\d+)\.(\d+)\.(\d+)\.(\d+)/(\d+)$!) { - my $numadr = ($1 << 24) + ($2 << 16) + ($3 << 8) + $4; - my $mask = ((1 << $5) - 1) << (32 - $5); - my $broadcast = $numadr | (~$mask); - $address = - (($broadcast & (255 << 24)) >> 24) . '.' . - (($broadcast & (255 << 16)) >> 16) . '.' . - (($broadcast & (255 << 8)) >> 8) . '.' . - ($broadcast & 255); - } elsif ($address =~ - m!^(\d+)\.(\d+)\.(\d+)\.(\d+)/(\d+)\.(\d+)\.(\d+)\.(\d+)$!) { - my $numadr = ($1 << 24) + ($2 << 16) + ($3 << 8) + $4; - my $mask = ($5 << 24) + ($6 << 16) + ($7 << 8) + $8; - my $broadcast = $numadr | (~$mask); - $address = - (($broadcast & (255 << 24)) >> 24) . '.' . - (($broadcast & (255 << 16)) >> 16) . '.' . - (($broadcast & (255 << 8)) >> 8) . '.' . - ($broadcast & 255); - } - - return $address; -} - -sub networkaddress { - - # Guesses a network address for a given broadcast address - - my ($address) = @_; - - if ($address =~ /\.255$/) { - while ($address =~ s/\.255$//) {} - $address .= ".*"; - } - - return $address; -} - -sub localprintersshared { - - # Do we broadcast our local printers - - my ($printer) = @_; - - return ($printer->{cupsconfig}{keys}{Browsing} !~ /off/i && - $printer->{cupsconfig}{keys}{BrowseInterval} != 0 && - $#{$printer->{cupsconfig}{keys}{BrowseAddress}} >= 0); -} - -sub remotebroadcastsaccepted { - - # Do we accept broadcasts from remote CUPS servers? - - my ($printer) = @_; - - # Is browsing not turned on at all? - if ($printer->{cupsconfig}{keys}{Browsing} =~ /off/i) { - return 0; - } - - # No "BrowseDeny" lines at all - if ($#{$printer->{cupsconfig}{keys}{BrowseDeny}} < 0) { - return 1; - } - - my $havedenyall = - join('', @{$printer->{cupsconfig}{keys}{BrowseDeny}}) =~ - /All/im; - my $havedenylocal = - join('', @{$printer->{cupsconfig}{keys}{BrowseDeny}}) =~ - /\@LOCAL/im; - my $orderallowdeny = - $printer->{cupsconfig}{keys}{BrowseOrder} =~ - /allow\s*,\s*deny/i; - my $haveallowremote = 0; - foreach my $allowline (@{$printer->{cupsconfig}{keys}{BrowseAllow}}) { - next if - $allowline =~ /^\s*(localhost|0*127\.0+\.0+\.0*1|none)\s*$/i; - $haveallowremote = 1; - } - - # A line denying all (or at least the all LANs) together with the order - # "allow,deny" or without "BrowseAllow" lines (which allow the - # broadcasts of at least one remote resource). - if (($havedenyall || $havedenylocal) && - ($orderallowdeny || !$haveallowremote)) { - return 0; - } - - return 1; -} - -sub clientnetworks { - - # Determine the client networks to which the printers will be - # shared If the configuration is supported by our simplified - # interface ("Deny From All", "Order Deny,Allow", "Allow From ..." - # lines in "<location /> ... </location>", a "BrowseAddress ..." - # line for each "Allow From ..." line), return the list of allowed - # client networks ("Allow"/"BrowseAddress" lines), if not, return - # the list of all items which are at least one of the - # "BrowseAddresse"s or one of the "Allow From" addresses together - # with a flag that the setup is not supported. - - my ($printer) = @_; - - # Check for a "Deny From All" line - my $havedenyfromall = - (join('', @{$printer->{cupsconfig}{root}{DenyFrom}}) =~ - /All/im ? 1 : 0); - - # Check for "Deny From XXX" with XXX != All - my $havedenyfromnotall = - ($#{$printer->{cupsconfig}{root}{DenyFrom}} - $havedenyfromall < 0 ? - 0 : 1); - - # Check for a "BrowseDeny All" line - my $havebrowsedenyall = - (join('', @{$printer->{cupsconfig}{keys}{BrowseDeny}}) =~ - /All/im ? 1 : 0); - - # Check for "BrowseDeny XXX" with XXX != All - my $havebrowsedenynotall = - ($#{$printer->{cupsconfig}{keys}{BrowseDeny}} - - $havebrowsedenyall < 0 ? 0 : 1); - - my @sharehosts; - my $haveallowfromlocalhost = 0; - my $haveallowedhostwithoutbrowseaddress = 0; - my $haveallowedhostwithoutbrowseallow = 0; - # Go through all "Allow From" lines - foreach my $line (@{$printer->{cupsconfig}{root}{AllowFrom}}) { - if ($line =~ /^\s*(localhost|0*127\.0+\.0+\.0*1)\s*$/i) { - # Line pointing to localhost - $haveallowfromlocalhost = 1; - } elsif ($line =~ /^\s*(none)\s*$/i) { - # Skip "Allow From None" lines - } elsif (!member($line, @sharehosts)) { - # Line pointing to remote server - push(@sharehosts, $line); - if (!member(broadcastaddress($line), - @{$printer->{cupsconfig}{keys}{BrowseAddress}})) { - $haveallowedhostwithoutbrowseaddress = 1; - } - if (!member($line, - @{$printer->{cupsconfig}{keys}{BrowseAllow}})) { - $haveallowedhostwithoutbrowseallow = 1; - } - } - } - my $havebrowseaddresswithoutallowedhost = 0; - # Go through all "BrowseAdress" lines - foreach my $line (@{$printer->{cupsconfig}{keys}{BrowseAddress}}) { - if ($line =~ /^\s*(localhost|0*127\.0+\.0+\.0*1)\s*$/i) { - # Skip lines pointing to localhost - } elsif ($line =~ /^\s*(none)\s*$/i) { - # Skip "Allow From None" lines - } elsif (!member($line, map { broadcastaddress($_) } @sharehosts)) { - # Line pointing to remote server - push(@sharehosts, networkaddress($line)); - if ($printer->{cupsconfig}{localprintersshared}) { - $havebrowseaddresswithoutallowedhost = 1; - } - } - } - my $havebrowseallowwithoutallowedhost = 0; - # Go through all "BrowseAllow" lines - foreach my $line (@{$printer->{cupsconfig}{keys}{BrowseAllow}}) { - if ($line =~ /^\s*(localhost|0*127\.0+\.0+\.0*1)\s*$/i) { - # Skip lines pointing to localhost - } elsif ($line =~ /^\s*(none)\s*$/i) { - # Skip "BrowseAllow None" lines - } elsif (!member($line, @sharehosts)) { - # Line pointing to remote server - push(@sharehosts, $line); - #$havebrowseallowwithoutallowedhost = 1; - } - } - - my $configunsupported = (!$havedenyfromall || $havedenyfromnotall || - !$havebrowsedenyall || $havebrowsedenynotall || - !$haveallowfromlocalhost || - $haveallowedhostwithoutbrowseaddress || - $havebrowseaddresswithoutallowedhost || - $haveallowedhostwithoutbrowseallow || - $havebrowseallowwithoutallowedhost); - - return $configunsupported, @sharehosts; -} - -sub makesharehostlist { - - # Human-readable strings for hosts onto which the local printers - # are shared - - my ($printer) = @_; - - my @sharehostlist; - my %sharehosthash; - foreach my $host (@{$printer->{cupsconfig}{clientnetworks}}) { - if ($host =~ /\@LOCAL/i) { - $sharehosthash{$host} = N("Local network(s)"); - } elsif ($host =~ /\@IF\((.*)\)/i) { - $sharehosthash{$host} = N("Interface \"%s\"", $1); - } elsif ($host =~ m!(/|^\*|\*$|^\.)!) { - $sharehosthash{$host} = N("Network %s", $host); - } else { - $sharehosthash{$host} = N("Host %s", $host); - } - push(@sharehostlist, $sharehosthash{$host}); - } - my %sharehosthash_inv = reverse %sharehosthash; - - return { list => \@sharehostlist, - hash => \%sharehosthash, - invhash => \%sharehosthash_inv }; -} - -sub makebrowsepolllist { - - # Human-readable strings for hosts from which the print queues are - # polled - - my ($printer) = @_; - - my @browsepolllist; - my %browsepollhash; - foreach my $host (@{$printer->{cupsconfig}{BrowsePoll}}) { - my ($ip, $port); - if ($host =~ /^([^:]+):([^:]+)$/) { - $ip = $1; - $port = $2; - } else { - $ip = $host; - $port = '631'; - } - $browsepollhash{$host} = N("%s (Port %s)", $ip, $port); - push(@browsepolllist, $browsepollhash{$host}); - } - my %browsepollhash_inv = reverse %browsepollhash; - - return { list => \@browsepolllist, - hash => \%browsepollhash, - invhash => \%browsepollhash_inv }; -} - -sub is_network_ip { - - # Determine whwther the given string is a valid network IP - - my ($address) = @_; - - $address =~ /^(\d+)\.(\d+)\.(\d+)\.(\d+)$/ || - $address =~ /^(\d+\.){1,3}\*$/ || - $address =~ m!^(\d+)\.(\d+)\.(\d+)\.(\d+)/(\d+)$! || - $address =~ - m!^(\d+)\.(\d+)\.(\d+)\.(\d+)/(\d+)\.(\d+)\.(\d+)\.(\d+)$!; - -} - -sub read_cups_config { - - # Read the information relevant to the printer sharing dialog from - # the CUPS configuration - - my ($printer) = @_; - - # From /etc/cups/cupsd.conf - - # Keyword "Browsing" - $printer->{cupsconfig}{keys}{Browsing} = - handle_configs::read_unique_directive($printer->{cupsconfig}{cupsd_conf}, - 'Browsing', 'On'); - - # Keyword "BrowseInterval" - $printer->{cupsconfig}{keys}{BrowseInterval} = - handle_configs::read_unique_directive($printer->{cupsconfig}{cupsd_conf}, - 'BrowseInterval', '30'); - - # Keyword "BrowseAddress" - @{$printer->{cupsconfig}{keys}{BrowseAddress}} = - handle_configs::read_directives($printer->{cupsconfig}{cupsd_conf}, - 'BrowseAddress'); - - # Keyword "BrowseAllow" - @{$printer->{cupsconfig}{keys}{BrowseAllow}} = - handle_configs::read_directives($printer->{cupsconfig}{cupsd_conf}, - 'BrowseAllow'); - - # Keyword "BrowseDeny" - @{$printer->{cupsconfig}{keys}{BrowseDeny}} = - handle_configs::read_directives($printer->{cupsconfig}{cupsd_conf}, - 'BrowseDeny'); - - # Keyword "BrowseOrder" - $printer->{cupsconfig}{keys}{BrowseOrder} = - handle_configs::read_unique_directive($printer->{cupsconfig}{cupsd_conf}, - 'BrowseOrder', 'deny,allow'); - - # Keyword "BrowsePoll" (needs "Browsing On") - if ($printer->{cupsconfig}{keys}{Browsing} !~ /off/i) { - @{$printer->{cupsconfig}{BrowsePoll}} = - handle_configs::read_directives($printer->{cupsconfig}{cupsd_conf}, - 'BrowsePoll'); - } - - # Root location - @{$printer->{cupsconfig}{rootlocation}} = - read_location($printer->{cupsconfig}{cupsd_conf}, '/'); - - # Keyword "Allow from" - @{$printer->{cupsconfig}{root}{AllowFrom}} = - handle_configs::read_directives($printer->{cupsconfig}{rootlocation}, - 'Allow From'); - # Remove the IPs pointing to the local machine - my @localips = printer::detect::getIPsOfLocalMachine(); - @{$printer->{cupsconfig}{root}{AllowFrom}} = - grep { - !member($_, @localips); - } @{$printer->{cupsconfig}{root}{AllowFrom}}; - - # Keyword "Deny from" - @{$printer->{cupsconfig}{root}{DenyFrom}} = - handle_configs::read_directives($printer->{cupsconfig}{rootlocation}, - 'Deny From'); - - # Keyword "Order" - $printer->{cupsconfig}{root}{Order} = - handle_configs::read_unique_directive($printer->{cupsconfig}{rootlocation}, - 'Order', 'Deny,Allow'); - - # Widget settings - - # Local printers available to other machines? - $printer->{cupsconfig}{localprintersshared} = - localprintersshared($printer); - - # This machine is accepting printers shared by remote machines? - $printer->{cupsconfig}{remotebroadcastsaccepted} = - remotebroadcastsaccepted($printer); - - # To which machines are the local printers available? - ($printer->{cupsconfig}{customsharingsetup}, - @{$printer->{cupsconfig}{clientnetworks}}) = - clientnetworks($printer); - -} - -sub write_cups_config { - - # Write the information edited via the printer sharing dialog into - # the CUPS configuration - - my ($printer) = @_; - - # Local printers available to other machines? - if ($printer->{cupsconfig}{localprintersshared}) { - handle_configs::set_directive($printer->{cupsconfig}{cupsd_conf}, - 'Browsing On'); - if ($printer->{cupsconfig}{keys}{BrowseInterval} == 0) { - handle_configs::set_directive($printer->{cupsconfig}{cupsd_conf}, - 'BrowseInterval 30'); - } - } else { - handle_configs::set_directive($printer->{cupsconfig}{cupsd_conf}, - 'BrowseInterval 0'); - } - - # This machine is accepting printers shared by remote machines? - if ($printer->{cupsconfig}{remotebroadcastsaccepted}) { - handle_configs::set_directive($printer->{cupsconfig}{cupsd_conf}, - 'Browsing On'); - if (!$printer->{cupsconfig}{customsharingsetup}) { - # If we broadcast our printers, let's accept the broadcasts - # from the machines to which we broadcast - handle_configs::set_directive($printer->{cupsconfig}{cupsd_conf}, - 'BrowseDeny All'); - handle_configs::set_directive($printer->{cupsconfig}{cupsd_conf}, - 'BrowseOrder Deny,Allow'); - } - } else { - if ($printer->{cupsconfig}{localprintersshared} || - $#{$printer->{cupsconfig}{BrowsePoll}} >= 0) { - # Deny all broadcasts, but leave all "BrowseAllow" lines - # untouched - handle_configs::set_directive($printer->{cupsconfig}{cupsd_conf}, - 'BrowseDeny All'); - handle_configs::set_directive($printer->{cupsconfig}{cupsd_conf}, - 'BrowseOrder Allow,Deny'); - } else { - # We also do not share printers, if we also do not - # "BrowsePoll", we turn browsing off to do not need to deal - # with any addresses - handle_configs::set_directive($printer->{cupsconfig}{cupsd_conf}, - 'Browsing Off'); - } - } - - # To which machines are the local printers available? - if (!$printer->{cupsconfig}{customsharingsetup}) { - my @localips = printer::detect::getIPsOfLocalMachine(); - # root location block - @{$printer->{cupsconfig}{rootlocation}} = - "<Location />\n" . - "Order Deny,Allow\n" . - "Deny From All\n" . - "Allow From 127.0.0.1\n" . - (@localips ? - "Allow From " . - join("\nAllow From ", @localips) . - "\n" : "") . - ($printer->{cupsconfig}{localprintersshared} && - $#{$printer->{cupsconfig}{clientnetworks}} >= 0 ? - "Allow From " . - join("\nAllow From ", - grep { - !member($_, @localips); - } @{$printer->{cupsconfig}{clientnetworks}}) . - "\n" : "") . - "</Location>\n"; - my ($location_start, @_location) = - rip_location($printer->{cupsconfig}{cupsd_conf}, "/"); - insert_location($printer->{cupsconfig}{cupsd_conf}, $location_start, - @{$printer->{cupsconfig}{rootlocation}}); - # "BrowseAddress" lines - if ($#{$printer->{cupsconfig}{clientnetworks}} >= 0) { - handle_configs::set_directive($printer->{cupsconfig}{cupsd_conf}, - 'BrowseAddress ' . - join("\nBrowseAddress ", - map { broadcastaddress($_) } - @{$printer->{cupsconfig}{clientnetworks}})); - } else { - handle_configs::comment_directive($printer->{cupsconfig}{cupsd_conf}, - 'BrowseAddress'); - } - # Set "BrowseAllow" lines - if ($#{$printer->{cupsconfig}{clientnetworks}} >= 0) { - handle_configs::set_directive($printer->{cupsconfig}{cupsd_conf}, - 'BrowseAllow ' . - join("\nBrowseAllow ", - @{$printer->{cupsconfig}{clientnetworks}})); - } else { - handle_configs::comment_directive($printer->{cupsconfig}{cupsd_conf}, - 'BrowseAllow'); - } - } - - # Set "BrowsePoll" lines - if ($#{$printer->{cupsconfig}{BrowsePoll}} >= 0) { - handle_configs::set_directive($printer->{cupsconfig}{cupsd_conf}, - 'BrowsePoll ' . - join("\nBrowsePoll ", - @{$printer->{cupsconfig}{BrowsePoll}})); - # "Browsing" must be on for "BrowsePoll" to work - handle_configs::set_directive($printer->{cupsconfig}{cupsd_conf}, - 'Browsing On'); - } else { - handle_configs::comment_directive($printer->{cupsconfig}{cupsd_conf}, - 'BrowsePoll'); - } - -} - -sub clean_cups_config { - - # Clean $printer data structure from all settings not related to - # the CUPS printer sharing dialog - - my ($printer) = @_; - - delete $printer->{cupsconfig}{keys}; - delete $printer->{cupsconfig}{root}; - delete $printer->{cupsconfig}{cupsd_conf}; - delete $printer->{cupsconfig}{rootlocation}; -} - -#---------------------------------------------------------------------- -# Handling of /etc/cups/client.conf - -sub read_client_conf() { - return (0, undef) if (! -r "$::prefix/etc/cups/client.conf"); - my @client_conf = cat_("$::prefix/etc/cups/client.conf"); - my @servers = handle_configs::read_directives(\@client_conf, - "ServerName"); - return (@servers > 0, - $servers[0]); # If there is more than one entry in client.conf, - # the first one counts. -} - -sub write_client_conf { - my ($daemonless_cups, $remote_cups_server) = @_; - # Create the directory for client.conf if needed - (-d "$::prefix/etc/cups/") || mkdir("$::prefix/etc/cups/") || return 1; - my (@client_conf) = cat_("$::prefix/etc/cups/client.conf"); - if ($daemonless_cups) { - handle_configs::set_directive(\@client_conf, - "ServerName $remote_cups_server"); - } else { - handle_configs::comment_directive(\@client_conf, "ServerName"); - } - output("$::prefix/etc/cups/client.conf", @client_conf); -} - - - -#---------------------------------------------------------------------- -sub read_printers_conf { - my ($printer) = @_; - my $current; - - #- read /etc/cups/printers.conf file. - #- according to this code, we are now using the following keys for each queues. - #- DeviceURI > lpd://printer6/lp - #- Info > Info Text - #- Location > Location Text - #- State > Idle|Stopped - #- Accepting > Yes|No - open(my $PRINTERS, "$::prefix/etc/cups/printers.conf") or return; - local $_; - while (<$PRINTERS>) { - chomp; - /^\s*#/ and next; - if (/^\s*<(?:DefaultPrinter|Printer)\s+([^>]*)>/) { $current = { mode => 'cups', QUEUE => $1, } } - elsif (m!\s*</Printer>!) { $current->{QUEUE} && $current->{DeviceURI} or next; #- minimal check of synthax. - add2hash($printer->{configured}{$current->{QUEUE}} ||= {}, $current); $current = undef } - elsif (/\s*(\S*)\s+(.*)/) { $current->{$1} = $2 } - } - close $PRINTERS; - - #- assume this printing system. - $printer->{SPOOLER} ||= 'cups'; -} - -sub get_direct_uri() { - #- get the local printer to access via a Device URI. - my @direct_uri; - open(my $F, ($::testing ? $::prefix : "chroot $::prefix/ ") . "/usr/sbin/lpinfo -v |"); - local $_; - while (<$F>) { - /^(direct|usb|serial)\s+(\S*)/ and push @direct_uri, $2; - } - close $F; - @direct_uri; -} - -sub checkppd { - # Check whether the PPD file is valid - my ($printer, $ppdfile) = @_; - return 1 if $printer->{SPOOLER} ne "cups"; - return run_program::rooted($::prefix, "cupstestppd", "-q", - $ppdfile); -} - -sub installppd { - # Install the PPD file in /usr/share/cups/model/printerdrake/ - my ($printer, $ppdfile) = @_; - return "" if !$ppdfile; - # Install PPD file - mkdir_p("$::prefix/usr/share/cups/model/printerdrake"); - # "cp_f()" is broken, it hangs infinitely - # cp_f($ppdfile, "$::prefix/usr/share/cups/model/printerdrake"); - run_program::rooted($::prefix, "cp", "-f", $ppdfile, - "$::prefix/usr/share/cups/model/printerdrake"); - $ppdfile =~ s!^(.*)(/[^/]+)$!/usr/share/cups/model/printerdrake$2!; - chmod 0644, "$::prefix$ppdfile"; - # Restart CUPS to register new PPD file - printer::services::restart("cups") if $printer->{SPOOLER} eq "cups"; - # Re-read printer database - %thedb = (); - # Supplying $ppdfile returns us the key for this PPD file in the - # so that we can point to it in the printer/driver list - return read_printer_db($printer, $printer->{SPOOLER}, $ppdfile); -} - -sub clean_manufacturer_name { - my ($make) = @_; - # Clean some manufacturer's names so that every manufacturer has only - # one entry in the tree list - $make =~ s/^CANON\W.*$/CANON/i; - $make =~ s/^LEXMARK.*$/LEXMARK/i; - $make =~ s/^HEWLETT?[\s\-]*PACKARD/HP/i; - $make =~ s/^SEIKO[\s\-]*EPSON/EPSON/i; - $make =~ s/^KYOCERA[\s\-]*MITA/KYOCERA/i; - $make =~ s/^CITOH/C.ITOH/i; - $make =~ s/^OKI(|[\s\-]*DATA)\s*$/OKIDATA/i; - $make =~ s/^(SILENTWRITER2?|COLORMATE)/NEC/i; - $make =~ s/^(XPRINT|MAJESTIX)/XEROX/i; - $make =~ s/^QMS-PS/QMS/i; - $make =~ s/^(PERSONAL|LASERWRITER)/APPLE/i; - $make =~ s/^DIGITAL/DEC/i; - $make =~ s/\s+Inc\.//i; - $make =~ s/\s+Corp\.//i; - $make =~ s/\s+SA\.//i; - $make =~ s/\s+S\.\s*A\.//i; - $make =~ s/\s+Ltd\.//i; - $make =~ s/\s+International//i; - $make =~ s/\s+Int\.//i; - return uc($make); -} - -sub ppd_entry_str { - my ($mf, $descr, $lang) = @_; - my ($model, $driver); - if ($descr) { - # Apply the beautifying rules of poll_ppd_base - if ($descr =~ /Foomatic \+ Postscript/) { - $descr =~ s/Foomatic \+ Postscript/PostScript/; - } elsif ($descr =~ /Foomatic/i) { - $descr =~ s/Foomatic/GhostScript/i; - } elsif ($descr =~ /CUPS\+Gimp-Print/i) { - $descr =~ s/CUPS\+Gimp-Print/CUPS + Gimp-Print/i; - } elsif ($descr =~ /CUPS\+Gutenprint/i) { - $descr =~ s/CUPS\+Gutenprint/CUPS + Gutenprint/i; - } elsif ($descr =~ /Series CUPS/i) { - $descr =~ s/Series CUPS/Series, CUPS/i; - } elsif ($descr !~ /(PostScript|GhostScript|CUPS|Foomatic|PCL|PXL)/i) { - $descr .= ", PostScript"; - } - # Avoid duplicate entries produced by ready-made Foomatic PPDs vs. - # the Foomatic XML database - $descr =~ s!(Foomatic|GhostScript)\s*/\s*(\S+)!$1 + $2!i; - # Split model and driver - $descr =~ s/\s*Series//i; - $descr =~ s/\((.*?(PostScript|PS.*).*?)\)/$1/i; - if ($descr =~ - /^\s*(Generic\s*PostScript\s*Printer)\s*,?\s*(.*)$/i || - $descr =~ - /^\s*(PostScript\s*Printer)\s*,?\s*(.*)$/i || - $descr =~ /^([^,]+?)\s*,?\s*(Foomatic.*)$/i || - $descr =~ /^([^,]+?)\s*,?\s*(GhostScript.*)$/i || - $descr =~ /^([^,]+?)\s*,?\s*(CUPS.*)$/i || - $descr =~ /^([^,]+?)\s*,\s+(PS.*)$/i || - $descr =~ /^([^,]+?)\s*,\s+(PXL.*)$/i || - $descr =~ /^([^,]+?)\s*,\s+(PCL.*)$/i || - $descr =~ - /^([^,]+?)\s*,?\s*(\(v?\.?\s*\d\d\d\d\.\d\d\d\).*)$/i || - $descr =~ /^([^,]+?)\s*,?\s*(v?\.?\s*\d+\.\d+.*)$/i || - $descr =~ /^([^,]+?)\s*,?\s*(PostScript.*)$/i || - $descr =~ /^([^,]+?)\s*,\s*(.+?)$/) { - $model = $1; - $driver = $2; - $model =~ s/[\-\s,]+$//; - $driver =~ s/\b(PS|PostScript\b)/PostScript/gi; - $driver =~ s/(PostScript)(.*)(PostScript)/$1$2/i; - $driver =~ s/\b(PXL|PCL[\s\-]*(6|XL)\b)/PCL-XL/gi; - $driver =~ s/(PCL-XL)(.*)(PCL-XL)/$1$2/i; - $driver =~ s/\b(PCL[\s\-]*(|4|5|5c|5e)\b)/PCL/gi; - $driver =~ s/(PCL)(.*)(PCL)/$1$2/i; - $driver =~ - s/^\s*(\(?v?\.?\s*\d\d\d\d\.\d\d\d\)?|v\d+\.\d+)([,\s]*)(.*?)\s*$/$3$2$1/i; - $driver =~ s/,\s*\(/ (/g; - $driver =~ s/[\-\s,]+$//; - $driver =~ s/^[\-\s,]+//; - $driver =~ s/\s+/ /g; - if ($driver !~ /[a-z]/i) { - $driver = "PostScript " . $driver; - $driver =~ s/ $//; - } - } else { - # Some PPDs do not have the ", <driver>" part. - $model = $descr; - if ($model =~ /\b(PXL|PCL[\s\-]*(6|XL))\b/i) { - $driver = "PCL-XL"; - } elsif ($model =~ /\b(PCL[\s\-]*(|4|5|5c|5e)\b)/i) { - $driver = "PCL"; - } else { - $driver = "PostScript"; - } - } - } - # Remove manufacturer's name from the beginning of the model - # name (do not do this with manufacturer names which contain - # odd characters) - $model =~ s/^$mf[\s\-]+//i - if $mf && $mf !~ m![\\/\(\)\[\]\|\.\$\@\%\*\?]!; - # Clean some manufacturer's names - $mf = clean_manufacturer_name($mf); - # Rename Canon "BJC XXXX" models into "BJC-XXXX" so that the - # models do not appear twice - if ($mf eq "CANON") { - $model =~ s/BJC\s+/BJC-/; - } - # New MF devices from Epson have mis-spelled name in PPD files for - # native CUPS drivers of Gimp-Print - if ($mf eq "EPSON") { - $model =~ s/Stylus CX\-/Stylus CX/; - } - # Remove the "Oki" from the beginning of the model names of Okidata - # printers - if ($mf eq "OKIDATA") { - $model =~ s/Oki\s+//i; - } - # Try again to remove manufacturer's name from the beginning of the - # model name, this time with the cleaned manufacturer name - $model =~ s/^$mf[\s\-]+//i - if $mf && $mf !~ m![\\/\(\)\[\]\|\.\$\@\%\*\?]!; - # Translate "(recommended)" in the driver string - $driver =~ s/\(recommended\)/$precstr/gi; - # Put out the resulting description string - uc($mf) . '|' . $model . '|' . $driver . - ($lang && " (" . lang::locale_to_main_locale($lang) . ")"); -} - -sub get_descr_from_ppd { - my ($printer) = @_; - #- if there is no ppd, this means this is a raw queue. - if (! -r "$::prefix/etc/cups/ppd/$printer->{OLD_QUEUE}.ppd") { - return "|" . N("Unknown model"); - } - return get_descr_from_ppdfile($printer, "/etc/cups/ppd/$printer->{OLD_QUEUE}.ppd"); -} - -sub get_descr_from_ppdfile { - my ($printer, $ppdfile) = @_; - my %ppd; - - # Remove ".gz" from end of file name, so that "catMaybeCompressed" works - $ppdfile =~ s/\.gz$//; - - eval { - local $_; - foreach (catMaybeCompressed("$::prefix$ppdfile")) { - # "OTHERS|Generic PostScript printer|PostScript (en)"; - /^\*([^\s:]*)\s*:\s*"([^"]*)"/ and - do { $ppd{$1} = $2; next }; - /^\*([^\s:]*)\s*:\s*([^\s"]*)/ and - do { $ppd{$1} = $2; next }; - } - }; - my $descr = ($ppd{NickName} || $ppd{ShortNickName} || $ppd{ModelName}); - my $make = $ppd{Manufacturer}; - my $lang = $ppd{LanguageVersion}; - my $entry = ppd_entry_str($make, $descr, $lang); - if (!$printer->{expert}) { - # Remove driver from printer list entry when in recommended mode - $entry =~ s/^([^\|]+\|[^\|]+)\|.*$/$1/; - } - return $entry; -} - -sub ppd_devid_data { - my ($ppd) = @_; - $ppd = "$::prefix/usr/share/cups/model/$ppd"; - my @content = eval { catMaybeCompressed($ppd) } or return "", ""; - - my ($devidmake, $devidmodel); - /^\*Manufacturer:\s*"(.*)"\s*$/ and $devidmake = $1 - foreach @content; - /^\*Product:\s*"\(?(.*?)\)?"\s*$/ and $devidmodel = $1 - foreach @content; - return $devidmake, $devidmodel; -} - -sub poll_ppd_base { - my ($printer, $ppdfile) = @_; - - # If a $ppdfile is supplied, we return the key of the DB entry which - # is for this file. This way we can pre-select a freshly added PPD in - # the model/driver list. - - #- Before trying to poll the ppd database available to cups, we have - #- to make sure the file /etc/cups/ppds.dat is no more modified. - #- If cups continue to modify it (because it reads the ppd files - #- available), the poll_ppd_base program simply cores :-) - # else cups will not be happy! and ifup lo do not run ? - run_program::rooted($::prefix, 'ifconfig', 'lo', '127.0.0.1'); - printer::services::start_not_running_service("cups"); - my $driversthere = scalar(keys %thedb); - my $ppdentry = ""; - $ppdfile = "" if !defined($ppdfile); - foreach (1..60) { - open(my $PPDS, ($::testing ? $::prefix : - "chroot $::prefix/ ") . - "/usr/bin/poll_ppd_base -a |"); - local $_; - while (<$PPDS>) { - chomp; - my ($ppd, $mf, $descr, $lang) = split /\|/; - if ($ppd eq "raw") { next } - $ppd && $mf && $descr and do { - my $key = ppd_entry_str($mf, $descr, $lang); - my ($model, $driver) = ($1, $2) if $key =~ /^[^\|]+\|([^\|]+)\|(.*)$/; - # Clean some manufacturer's names - $mf = clean_manufacturer_name($mf); - # Remove language tag - $driver =~ s/\s*\([a-z]{2}(|_[A-Z]{2})\)\s*$//; - # Recommended Foomatic PPD? Extract "(recommended)" - my $isrecommended = - $driver =~ s/\s+$sprecstr\s*$//i; - # Remove trailing white space - $driver =~ s/\s+$//; - # For Foomatic: Driver with "GhostScript + " - my $fullfoomaticdriver = $driver; - # Foomatic PPD? Extract driver name - my $isfoomatic = - $driver =~ s!^\s*(GhostScript|Foomatic)(\s*\+\s*|/)!!i; - # Foomatic PostScript driver? - $isfoomatic ||= $descr =~ /Foomatic/i; - # Native CUPS? - my $isnativecups = $driver =~ /CUPS/i; - # Native PostScript - my $isnativeps = !$isfoomatic && !$isnativecups; - # Key without language tag (key as it was produced for the - # entries from the Foomatic XML database) - my $keynolang = $key; - $keynolang =~ s/\s*\([a-z]{2}(|_[A-Z]{2})\)\s*$//; - if (!$isfoomatic) { - # Driver is PPD when the PPD is a non-Foomatic one - $driver = "PPD"; - } else { - # Remove language tag in menu entry when PPD is from - # Foomatic - $key = $keynolang; - } - my ($devidmake, $devidmodel, $deviddesc, $devidcmdset); - # Replace an existing printer entry if it has linked - # to the current PPD file. - my ($filename, $ppdkey); - $ppd =~ m!([^/]+\.ppd)(\.gz|\.bz2|)$!; - if (($filename = $1) && - ($#{$linkedppds{$filename}} >= 0)) { - foreach $ppdkey (@{$linkedppds{$filename}}) { - next if !defined($thedb{$ppdkey}); - # Save the autodetection data - $devidmake = $thedb{$ppdkey}{devidmake}; - $devidmodel = $thedb{$ppdkey}{devidmodel}; - $deviddesc = $thedb{$ppdkey}{deviddesc}; - $devidcmdset = $thedb{$ppdkey}{devidcmdset}; - # We must preserve make and model if we have one - # PPD for multiple printers - my $oldmake = $thedb{$ppdkey}{make}; - my $oldmodel = $thedb{$ppdkey}{model}; - # Remove the old entry - delete $thedb{$ppdkey}; - my $newkey = $key; - # User-added PPD file, mark it - if ($ppd =~ m!printerdrake/!i) { - $newkey .= ", " . N("user-supplied"); - } - # Newly added PPD file, mark it - if ($ppdfile eq "/usr/share/cups/model/$ppd") { - $newkey .= ", " . N("NEW"); - } - if (!$printer->{expert}) { - # Remove driver part in recommended mode - $newkey =~ s/^([^\|]+\|[^\|]+)\|.*$/$1/; - } else { - # If the Foomatic entry is "recommended" let - # the new PPD entry be "recommended" and - # otherwise not - $newkey =~ s/\s*$sprecstr//g; - $newkey .= " $precstr" - if $ppdkey =~ m!$sprecstr!; - # Remove duplicate "recommended" tags and have - # the "recommended" tag at the end - $newkey =~ - s/(\s*$sprecstr)(.*?)(\s*$sprecstr)/$2$3/; - $newkey =~ s/(\s*$sprecstr)(.+)$/$2$1/; - } - # Conserve the make and model of the original entry - $newkey =~ - s/^([^\|]+)(\|[^\|]+)(\|.*|)$/$oldmake$2$3/; - $newkey =~ - s/^([^\|]+\|)([^\|]+)(\|.*|)$/$1$oldmodel$3/; - # Do not overwrite another entry which has the - # same key, consider different PPD files with - # the same identity as a bug and drop them - if ($thedb{$newkey}) { - next; - } - # Create the new entry - $thedb{$newkey}{ppd} = $ppd; - $thedb{$newkey}{make} = $oldmake; - $thedb{$newkey}{model} = $oldmodel; - $thedb{$newkey}{driver} = $driver; - # Recover saved autodetection data - $thedb{$newkey}{devidmake} = $devidmake - if $devidmake; - $thedb{$newkey}{devidmodel} = $devidmodel - if $devidmodel; - $thedb{$newkey}{deviddesc} = $deviddesc - if $deviddesc; - $thedb{$newkey}{devidcmdset} = $devidcmdset - if $devidcmdset; - # Rememeber which entry is the freshly added - # PPD file - $ppdentry = $newkey if - $ppdfile eq "/usr/share/cups/model/$ppd"; - } - next; - } elsif (!$printer->{expert}) { - # Remove driver from printer list entry when in - # recommended mode - $key =~ s/^([^\|]+\|[^\|]+)\|.*$/$1/; - # Only replace an existing printer entry if - # - its driver is not the same as the driver of the - # new one - # AND if one of the following items is true - # - The existing entry uses a "Foomatic + Postscript" - # driver and the new one is native PostScript - # - The existing entry is a Foomatic entry and the new - # one is "recommended" - # - The existing entry is a native PostScript entry - # and the new entry is a "recommended" driver other - # then "Foomatic + Postscript" - if (defined($thedb{$key})) { - next if lc($thedb{$key}{driver}) eq - lc($driver); - if ($isnativeps && - $thedb{$key}{driver} =~ /^PostScript$/i || - $thedb{$key}{driver} ne "PPD" && $isrecommended || - $thedb{$key}{driver} eq "PPD" && $isrecommended && $driver ne "PostScript") { - # Save the autodetection data - $devidmake = $thedb{$key}{devidmake}; - $devidmodel = $thedb{$key}{devidmodel}; - $deviddesc = $thedb{$key}{deviddesc}; - $devidcmdset = $thedb{$key}{devidcmdset}; - # Remove the old entry - delete $thedb{$key}; - } else { - next; - } - } - } elsif ((defined - $thedb{"$mf|$model|$fullfoomaticdriver"} || - defined - $thedb{"$mf|$model|$fullfoomaticdriver $precstr"}) && - $isfoomatic) { - # Expert mode: There is already an entry for the - # same printer/driver combo produced by the - # Foomatic XML database, so do not make a second - # entry - next; - } elsif (defined - $thedb{"$mf|$model|PostScript $precstr"} && - $isnativeps) { - # Expert mode: "Foomatic + Postscript" driver is - # recommended and this is a PostScript PPD? Make - # this PPD the recommended one - foreach (keys - %{$thedb{"$mf|$model|PostScript $precstr"}}) { - $thedb{"$mf|$model|PostScript"}{$_} = - $thedb{"$mf|$model|PostScript $precstr"}{$_}; - } - delete - $thedb{"$mf|$model|PostScript $precstr"}; - if (!$isrecommended) { - $key .= " $precstr"; - } - } elsif ($driver =~ /PostScript/i && - $isrecommended && $isfoomatic && - (my @foundkeys = grep { - /^$mf\|$model\|/ && !/CUPS/i && - $thedb{$_}{driver} eq "PPD"; - } keys %thedb)) { - # Expert mode: "Foomatic + Postscript" driver is - # recommended and there was a PostScript PPD? Make - # the PostScript PPD the recommended one - my $firstfound = $foundkeys[0]; - if (!(any { /$sprecstr/ } @foundkeys)) { - # Do it only if none of the native PostScript - # PPDs for this printer is already "recommended" - foreach (keys %{$thedb{$firstfound}}) { - $thedb{"$firstfound $precstr"}{$_} = - $thedb{$firstfound}{$_}; - } - delete $thedb{$firstfound}; - } - $key =~ s/\s*$sprecstr//; - } elsif ($driver !~ /PostScript/i && - $isrecommended && $isfoomatic && - (@foundkeys = grep { - /^$mf\|$model\|.*$sprecstr/ && - !/CUPS/i && $thedb{$_}{driver} eq "PPD"; - } keys %thedb)) { - # Expert mode: Foomatic driver other than "Foomatic + - # Postscript" is recommended and there was a PostScript - # PPD which was recommended? Make the Foomatic driver - # the recommended one - foreach my $sourcekey (@foundkeys) { - # Remove the "recommended" tag - my $destkey = $sourcekey; - $destkey =~ s/\s+$sprecstr\s*$//i; - foreach (keys %{$thedb{$sourcekey}}) { - $thedb{$destkey}{$_} = $thedb{$sourcekey}{$_}; - } - delete $thedb{$sourcekey}; - } - } - - # User-added PPD file, mark it - if ($ppd =~ m!printerdrake/!i) { - $key .= ", " . N("user-supplied"); - } - # Newly added PPD file, mark it - if ($ppdfile eq "/usr/share/cups/model/$ppd") { - $key .= ", " . N("NEW"); - } - # Remove duplicate "recommended" tags and have the - # "recommended" tag at the end - $key =~ s/(\s*$sprecstr)(.*?)(\s*$sprecstr)/$2$3/; - $key =~ s/(\s*$sprecstr)(.+)$/$2$1/; - # Do not overwrite another entry which has the same - # key, consider different PPD files with the same identity - # as a bug and drop them - if ($thedb{$key}) { - next; - } - # Create the new entry - $thedb{$key}{ppd} = $ppd; - $thedb{$key}{make} = $mf; - $thedb{$key}{model} = $model; - $thedb{$key}{driver} = $driver; - # Recover saved autodetection data - $thedb{$key}{devidmake} = $devidmake if $devidmake; - $thedb{$key}{devidmodel} = $devidmodel if $devidmodel; - $thedb{$key}{deviddesc} = $deviddesc if $deviddesc; - $thedb{$key}{devidcmdset} = $devidcmdset if $devidcmdset; - # Get autodetection data - #my ($devidmake, $devidmodel) = ppd_devid_data($ppd); - #$thedb{$key}{devidmake} = $devidmake; - #$thedb{$key}{devidmodel} = $devidmodel; - # Rememeber which entry is the freshly added PPD file - $ppdentry = $key if - $ppdfile eq "/usr/share/cups/model/$ppd"; - }; - } - close $PPDS; - scalar(keys %thedb) - $driversthere > 5 and last; - #- we have to try again running the program, wait here a little - #- before. - sleep 1; - } - #scalar(keys %descr_to_ppd) > 5 or - # die "unable to connect to cups server"; - - return $ppdentry; -} - - - -#-****************************************************************************** -#- write functions -#-****************************************************************************** - -sub configure_queue($) { - my ($printer) = @_; - - #- Create the queue with "foomatic-configure", in case of queue - #- renaming copy the old queue - my $quotedconnect = $printer->{currentqueue}{connect}; - $quotedconnect =~ s/\$/\\\$/g; # Quote '$' in URI - run_program::rooted($::prefix, "foomatic-configure", "-q", - "-s", $printer->{currentqueue}{spooler}, - "-n", $printer->{currentqueue}{queue}, - ($printer->{currentqueue}{queue} ne - $printer->{OLD_QUEUE} && - $printer->{configured}{$printer->{OLD_QUEUE}} ? - ("-C", $printer->{OLD_QUEUE}) : ()), - "-c", $quotedconnect, - ($printer->{currentqueue}{ppd} ? - ($printer->{currentqueue}{ppd} ne '1' ? - ("--ppd", - ($printer->{currentqueue}{ppd} !~ m!^/! ? - "/usr/share/cups/model/" : "") . - $printer->{currentqueue}{ppd}) : ()) : - ($printer->{currentqueue}{foomatic} ? - ("-p", $printer->{currentqueue}{printer}, - "-d",($printer->{currentqueue}{driver} ne "PPD" ? - $printer->{currentqueue}{driver} : - "Postscript")) : - ("-d", "raw"))), - "-N", $printer->{currentqueue}{desc}, - "-L", $printer->{currentqueue}{loc}, - if_($printer->{SPOOLER} eq "cups", - "--backend-dont-disable=" . - $printer->{currentqueue}{dd}, - "--backend-attempts=" . - $printer->{currentqueue}{att}, - "--backend-delay=" . - $printer->{currentqueue}{delay}), - @{$printer->{currentqueue}{options}} - ) or return 0; - if ($printer->{currentqueue}{ppd} && - ($printer->{currentqueue}{ppd} ne '1')) { - # Add a comment line containing the path of the used PPD file to the - # end of the PPD file - if ($printer->{currentqueue}{ppd} ne '1') { - append_to_file("$::prefix/etc/cups/ppd/$printer->{currentqueue}{queue}.ppd", "*%MDKMODELCHOICE:$printer->{currentqueue}{ppd}\n"); - } - } - - # Make sure that queue is active - if ($printer->{NEW} && ($printer->{SPOOLER} ne "pdq")) { - run_program::rooted($::prefix, "foomatic-printjob", - "-s", $printer->{currentqueue}{spooler}, - "-C", "up", $printer->{currentqueue}{queue}); - } - - # Check whether a USB printer is configured and activate USB printing if so - my $useUSB = 0; - foreach (values %{$printer->{configured}}) { - $useUSB ||= $_->{queuedata}{connect} =~ /usb/i || - $_->{DeviceURI} =~ /usb/i; - } - $useUSB ||= $printer->{currentqueue}{connect} =~ /usb/i; - if ($useUSB) { - my $f = "$::prefix/etc/sysconfig/usb"; - my %usb = getVarsFromSh($f); - $usb{PRINTER} = "yes"; - setVarsInSh($f, \%usb); - } - - # Open permissions for device file when PDQ is chosen as spooler - # so normal users can print. - if ($printer->{SPOOLER} eq 'pdq') { - if ($printer->{currentqueue}{connect} =~ - m!^\s*(file|parallel|usb|serial):(\S*)\s*$!) { - set_permissions($1, "666"); - } - } - - # Make a new printer entry in the $printer structure - $printer->{configured}{$printer->{currentqueue}{queue}}{queuedata} = - {}; - copy_printer_params($printer->{currentqueue}, - $printer->{configured}{$printer->{currentqueue}{queue}}{queuedata}); - # Construct an entry line for tree view in main window of - # printerdrake - make_menuentry($printer, $printer->{currentqueue}{queue}); - - # Store the default option settings - $printer->{configured}{$printer->{currentqueue}{queue}}{args} = {}; - $printer->{configured}{$printer->{currentqueue}{queue}}{args} = - $printer->{ARGS}; - - # In case of CUPS set some more useful defaults for text and image - # printing - if ($printer->{SPOOLER} eq "cups") { - set_cups_special_options($printer, - $printer->{currentqueue}{queue}); - } - - # Clean up - delete($printer->{ARGS}); - $printer->{OLD_CHOICE} = ""; - $printer->{ARGS} = {}; - $printer->{DBENTRY} = ""; - $printer->{currentqueue} = {}; - - return 1; -} - -sub enable_disable_queue { - my ($printer, $queue, $state) = @_; - - if (($printer->{SPOOLER} ne "pdq") && - ($printer->{SPOOLER} ne "rcups")) { - run_program::rooted($::prefix, "foomatic-printjob", - "-s", $printer->{SPOOLER}, - "-C", ($state ? "start" : "stop"), $queue); - } -} - -sub remove_queue($$) { - my ($printer, $queue) = @_; - run_program::rooted($::prefix, "foomatic-configure", "-R", "-q", - "-s", $printer->{SPOOLER}, - "-n", $queue); - # Delete old stuff from data structure - delete $printer->{configured}{$queue}; - delete($printer->{currentqueue}); - delete($printer->{ARGS}); - $printer->{OLD_CHOICE} = ""; - $printer->{ARGS} = {}; - $printer->{DBENTRY} = ""; - $printer->{currentqueue} = {}; -} - -sub restart_queue($) { - my ($printer) = @_; - my $queue = $printer->{QUEUE}; - - # Restart the daemon(s) - for ($printer->{SPOOLER}) { - /cups/ and do { - #- restart cups. - printer::services::restart("cups"); - last }; - /lpr|lprng/ and do { - #- restart lpd. - foreach ("/var/spool/lpd/$queue/lock", "/var/spool/lpd/lpd.lock") { - my $pidlpd = (cat_("$::prefix$_"))[0]; - kill 'TERM', $pidlpd if $pidlpd; - unlink "$::prefix$_"; - } - printer::services::restart("lpd"); sleep 1; - last }; - } - # Kill the jobs - run_program::rooted($::prefix, "foomatic-printjob", "-R", - "-s", $printer->{SPOOLER}, - "-P", $queue, "-"); - -} - -sub print_pages($@) { - my ($printer, @pages) = @_; - my $queue = $printer->{QUEUE}; - my $lpr = "/usr/bin/foomatic-printjob"; - my $lpq = "$lpr -Q"; - my $spooler = $printer->{SPOOLER}; - $spooler = "cups" if $spooler eq "rcups"; - - # Print the pages - foreach (@pages) { - my $page = $_; - # Only text and PostScript can be printed directly with all - # spoolers, images must be treated seperately - if ($page =~ /\.jpg$/) { - if ($spooler ne "cups") { - # Use "convert" from ImageMagick for non-CUPS spoolers - system(($::testing ? $::prefix : "chroot $::prefix/ ") . - "/usr/bin/convert $page -page 427x654+100+65 PS:- | " . - ($::testing ? $::prefix : "chroot $::prefix/ ") . - "$lpr -s $spooler -P $queue"); - } else { - # Use CUPS's internal image converter with CUPS, tell it - # to let the image occupy 90% of the page size (so nothing - # gets cut off by unprintable borders) - run_program::rooted($::prefix, $lpr, "-s", $spooler, - "-P", $queue, "-o", "scaling=90", $page); - } - } else { - run_program::rooted($::prefix, $lpr, "-s", $spooler, - "-P", $queue, $page); - } - } - sleep 5; #- allow lpr to send pages. - # Check whether the job is queued - open(my $F, ($::testing ? $::prefix : "chroot $::prefix/ ") . "$lpq -s $spooler -P $queue |"); - my @lpq_output = - grep { !/^no entries/ && !(/^Rank\s+Owner/ .. /^\s*$/) } <$F>; - close $F; - @lpq_output; -} - -sub help_output { - my ($printer, $spooler) = @_; - my $queue = $printer->{QUEUE}; - - open(my $F, ($::testing ? $::prefix : "chroot $::prefix/ ") . sprintf($spoolers{$spooler}{help}, $queue)); - my $helptext = join("", <$F>); - close $F; - $helptext ||= "Option list not available!\n"; - return $helptext; -} - -sub print_optionlist { - my ($printer) = @_; - my $queue = $printer->{QUEUE}; - my $lpr = "/usr/bin/foomatic-printjob"; - - # Print the option list pages - if ($printer->{configured}{$queue}{queuedata}{foomatic}) { - run_program::rooted($::prefix, $lpr, "-s", $printer->{SPOOLER}, - "-P", $queue, "-o", "docs", - "/etc/bashrc"); - } elsif ($printer->{configured}{$queue}{queuedata}{ppd}) { - system(($::testing ? $::prefix : "chroot $::prefix/ ") . - "/usr/bin/lphelp $queue | " . - ($::testing ? $::prefix : "chroot $::prefix/ ") . - "$lpr -s $printer->{SPOOLER} -P $queue"); - } -} - -# --------------------------------------------------------------- -# -# Spooler config stuff -# -# --------------------------------------------------------------- - -sub get_copiable_queues { - my ($oldspooler, $newspooler) = @_; - - # No local queues available in daemon-less CUPS mode - return () if ($oldspooler eq "rcups") or ($newspooler eq "rcups"); - - my @queuelist; #- here we will list all Foomatic-generated queues - # Get queue list with foomatic-configure - open(my $QUEUEOUTPUT, ($::testing ? $::prefix : "chroot $::prefix/ ") . - "foomatic-configure -Q -q -s $oldspooler |") or - die "Could not run foomatic-configure"; - - my $entry = {}; - my $inentry = 0; - local $_; - while (<$QUEUEOUTPUT>) { - chomp; - if ($inentry) { - # We are inside a queue entry - if (m!^\s*</queue>\s*$!) { - # entry completed - $inentry = 0; - if ($entry->{foomatic} && $entry->{spooler} eq $oldspooler) { - # Is the connection type supported by the new - # spooler? - if ($newspooler eq "cups" && $entry->{connect} =~ /^(file|hp|ptal|lpd|socket|smb|ipp):/ || - $newspooler =~ /^(lpd|lprng)$/ && $entry->{connect} =~ /^(file|ptal|lpd|socket|smb|ncp|postpipe):/ || - $newspooler eq "pdq" && $entry->{connect} =~ /^(file|ptal|lpd|socket):/) { - push(@queuelist, $entry->{name}); - } - } - $entry = {}; - } elsif (m!^\s*<name>(.+)</name>\s*$!) { - # queue name - $entry->{name} = $1; - } elsif (m!^\s*<connect>(.+)</connect>\s*$!) { - # connection type (URI) - $entry->{connect} = $1; - } - } else { - if (m!^\s*<queue\s+foomatic\s*=\s*"?(\d+)"?\s*spooler\s*=\s*"?(\w+)"?\s*>\s*$!) { - # new entry - $inentry = 1; - $entry->{foomatic} = $1; - $entry->{spooler} = $2; - } - } - } - close $QUEUEOUTPUT; - - return @queuelist; -} - -sub copy_foomatic_queue { - my ($printer, $oldqueue, $oldspooler, $newqueue) = @_; - run_program::rooted($::prefix, "foomatic-configure", "-q", - "-s", $printer->{SPOOLER}, - "-n", $newqueue, - "-C", $oldspooler, $oldqueue); - # In case of CUPS set some more useful defaults for text and image printing - if ($printer->{SPOOLER} eq "cups") { - set_cups_special_options($printer, $newqueue); - } -} - -# ------------------------------------------------------------------ -# -# Stuff for non-interactive printer configuration -# -# ------------------------------------------------------------------ - -# Check whether a given URI (for example of an existing queue matches -# one of the auto-detected printers - -sub autodetectionentry_for_uri { - my ($uri, @autodetected) = @_; - - if ($uri =~ m!^usb://([^/]+)/([^\?]+)(|\?serial=(\S+))$!) { - # USB device with URI referring to printer model - my $make = $1; - my $model = $2; - my $serial = $4; - if ($make && $model) { - $make =~ s/\%20/ /g; - $model =~ s/\%20/ /g; - $serial =~ s/\%20/ /g; - $make =~ s/Hewlett[-\s_]Packard/HP/; - $make =~ s/HEWLETT[-\s_]PACKARD/HP/; - my $smake = handle_configs::searchstr($make); - my $smodel = handle_configs::searchstr($model); - foreach my $p (@autodetected) { - next if $p->{port} !~ /usb/i; - next if ((!$p->{val}{MANUFACTURER} || - $p->{val}{MANUFACTURER} ne $make) && - (!$p->{val}{DESCRIPTION} || - $p->{val}{DESCRIPTION} !~ /^\s*$smake\s+/)); - next if ((!$p->{val}{MODEL} || - $p->{val}{MODEL} ne $model) && - (!$p->{val}{DESCRIPTION} || - $p->{val}{DESCRIPTION} !~ /\s+$smodel\s*$/)); - next if ($serial && - (!$p->{val}{SERIALNUMBER} || - $p->{val}{SERIALNUMBER} ne $serial)); - return $p; - } - } - } elsif ($uri =~ m!^hp:/(usb|par|net)/!) { - # HP printer (controlled by HPLIP) - my $hplipdevice = $uri; - $hplipdevice =~ m!^hp:/(usb|par|net)/(\S+?)(\?(serial|device)=(\S+)|)$!; - my $bus = $1; - my $model = $2; - my $serial = undef; - my $device = undef; - if ($4 eq 'serial') { - $serial = $5; - } elsif ($4 eq 'device') { - $device = $5; - } - $model =~ s/_/ /g; - foreach my $p (@autodetected) { - next if (!$p->{port}) || - (($p->{port} =~ m!/usb!) && ($bus ne "usb")) || - (($p->{port} =~ m!/dev/(lp|par.*|printer.*)\d+!) && - ($bus ne "par")); - next if !$p->{val}{MODEL}; - if (uc($p->{val}{MODEL}) ne uc($model)) { - my $entry = hplip_device_entry($p->{port}, @autodetected); - next if !$entry; - my $m = $entry->{model}; - $m =~ s/_/ /g; - next if uc($m) ne uc($model); - } - next if ($serial && !$p->{val}{SERIALNUMBER}) || - (!$serial && $p->{val}{SERIALNUMBER}) || - (uc($serial) ne uc($p->{val}{SERIALNUMBER})); - if ($device) { - if ($bus eq "par") { - $device =~ m!/dev/(lp|parport|printer/)(\d+)!; - my $parporthplip = $1; - $p->{port} =~ m!/dev/(lp|parport|printer/)(\d+)!; - my $parportauto = $1; - next if $parporthplip != $parportauto; - } else { - next if $device ne $p->{port}; - } - } - return $p; - } - } elsif ($uri =~ m!^ptal://?mlc:!) { - # HP multi-function device (controlled by HPOJ) - my $ptaldevice = $uri; - $ptaldevice =~ s!^ptal://?mlc:!!; - if ($ptaldevice =~ /^par:(\d+)$/) { - my $device = "/dev/lp$1"; - foreach my $p (@autodetected) { - next if !$p->{port} || - $p->{port} ne $device; - return $p; - } - } else { - my $model = $2 if $ptaldevice =~ /^(usb|par):(.*)$/; - $model =~ s/_/ /g; - foreach my $p (@autodetected) { - next if !$p->{val}{MODEL} || - $p->{val}{MODEL} ne $model; - return $p; - } - } - } elsif ($uri =~ m!^(socket|smb|file|parallel|usb|serial):/!) { - # Local print-only device, Ethernet-(TCP/Socket)-connected printer, - # or printer on Windows server - my $device = $uri; - $device =~ s/^(file|parallel|usb|serial)://; - foreach my $p (@autodetected) { - next if !$p->{port} || - $p->{port} ne $device; - return $p; - } - } - return undef; -} - -# ------------------------------------------------------------------ -# -# Configuration of HP printers and multi-function devices with HPLIP -# -# ------------------------------------------------------------------ - -sub read_hplip_db { - - # Read the device database XML file which comes with the HPLIP - # package - open(my $F, "< $::prefix/usr/share/hplip/data/xml/models.xml") or - warn "Could not read /usr/share/hplip/data/xml/models.xml\n"; - - my $entry = {}; - my $inentry = 0; - my $inrX = 0; - my $incomment = 0; - my %hplipdevices; - local $_; - while (<$F>) { - chomp; - if ($incomment) { - # In a comment block, skip all except the end of the comment - if (m!^(.*?)-->(.*)$!) { - # End of comment, keep rest of line - $_ = $2; - $incomment = 0; - } else { - # Skip line - $_ = ''; - } - } else { - while (m/^(.*?)<!--(.*?)-->(.*)$/) { - # Remove one-line comments - $_ = $1 . $3; - } - if (m/^(.*?)<!--(.*)$/) { - # Start of comment, keep the beginning of the line - $_ = $1; - $incomment = 1; - } - } - # Is there some non-comment part left in the line - if (m!\S!) { - if ($inentry) { - # We are inside a device entry - if ($inrX) { - # We are in one of the the device's <rX> sections, - # skip the section - if (m!^\s*</r\d+>\s*$!) { - # End of <rX> section - $inrX = 0; - } - } else { - if (m!^\s*<r\d+>\s*$!) { - # Start of <rX> section - $inrX = 1; - } elsif (m!^\s*</model>\s*$!) { - # End of device entry - $inentry = 0; - my $devidmodel; - if ($entry->{$devidmodel}) { - $devidmodel = $entry->{devidmodel}; - $devidmodel =~ s/ /_/g; - } else { - $devidmodel = $entry->{model}; - } - $hplipdevices{$devidmodel} = $entry; - $entry = {}; - } elsif (m!^\s*<id>\s*([^<>]+)\s*</id>\s*$!) { - # Full ID string - my $idstr = $1; - $idstr =~ m!(MFG|MANUFACTURER):([^;]+);!i - and $entry->{devidmake} = $2; - $idstr =~ m!(MDL|MODEL):([^;]+);!i - and $entry->{devidmodel} = $2; - $idstr =~ m!(DES|DESCRIPTION):([^;]+);!i - and $entry->{deviddesc} = $2; - $idstr =~ m!(CMD|COMMAND\s*SET):([^;]+);!i - and $entry->{devidcmdset} = $2; - } elsif (m!^\s*<io support="(\d+)".*/>\s*$!) { - # Input/Output ports explicitly supported by HPLIP - my $ports = $1; - $entry->{bus}{par} = 1 if ($ports & 1); - $entry->{bus}{usb} = 1 if ($ports & 2); - $entry->{bus}{net} = 1 if ($ports & 4); - } elsif (m!^\s*<tech type="(\d+)"/>\s*$!) { - # Printing technology - $entry->{tech} = $1; - } elsif (m!^\s*<align type="(\d+)"/>\s*$!) { - # Head alignment type - $entry->{align} = $1; - } elsif (m!^\s*<clean type="(\d+)"/>\s*$!) { - # Head cleaning type - $entry->{clean} = $1; - } elsif (m!^\s*<color-cal type="(\d+)"/>\s*$!) { - # Color calibration type - $entry->{colorcal} = $1; - } elsif (m!^\s*<status type="(\d+)"/>\s*$!) { - # Status request type - $entry->{status} = $1; - } elsif (m!^\s*<scan type="(\d+)"/>\s*$!) { - # Scanner access type - $entry->{scan} = $1; - } elsif (m!^\s*<fax type="(\d+)"/>\s*$!) { - # Fax access type - $entry->{fax} = $1; - } elsif (m!^\s*<pcard type="(\d+)"/>\s*$!) { - # Memory card access type - $entry->{card} = $1; - } elsif (m!^\s*<copy type="(\d+)"/>\s*$!) { - # Copier access type - $entry->{copy} = $1; - } - } - } else { - # We are not in a printer entry - if (m!^\s*<\s*model\s+name=\"(\S+)\"\a*>\s*$!) { - $inentry = 1; - # HPLIP model ID - $entry->{model} = $1; - } - } - } - } - close $F; - return \%hplipdevices; -} - -sub hplip_simple_model { - my ($model) = @_; - my $simplemodel = $model; - $simplemodel =~ s/[^A-Za-z0-9]//g; - $simplemodel =~ s/HewlettPackard/HP/gi; - $simplemodel =~ s/HP//gi; - $simplemodel =~ s/(DeskJet\d+C?)([a-z]*?)/$1/gi; - $simplemodel =~ s/((LaserJet|OfficeJet|PhotoSmart|PSC)\d+)([a-z]*?)/$1/gi; - $simplemodel =~ s/DeskJet/DJ/gi; - $simplemodel =~ s/PhotoSmartP/PhotoSmart/gi; - $simplemodel =~ s/LaserJet/LJ/gi; - $simplemodel =~ s/OfficeJet/OJ/gi; - $simplemodel =~ s/Series//gi; - $simplemodel = uc($simplemodel); - return $simplemodel; -} - -sub hplip_device_entry { - my ($device, @autodetected) = @_; - - # Currently, only local or TCP/Socket device work - return undef if ($device !~ /usb/i) && - ($device !~ m!/dev/(lp|par.*|printer.*)\d+!) && - ($device !~ m!^socket://!i); - - if (!$hplipdevicesdb) { - # Read the HPLIP device database if not done already - $hplipdevicesdb = read_hplip_db(); - } - - my $entry; - foreach my $a (@autodetected) { - $device eq $a->{port} or next; - # Only HP devices supported - return undef if $a->{val}{MANUFACTURER} !~ /^\s*HP\s*$/i; - my $modelstr = $a->{val}{MODEL}; - $modelstr =~ s/ /_/g; - if ($entry = $hplipdevicesdb->{$modelstr}) { - # Exact match - return $entry; - } - my $hpmodelstr = "HP_" . $modelstr; - if ($entry = $hplipdevicesdb->{$hpmodelstr}) { - # Exact match - return $entry; - } - my $hpmodelstr = "hp_" . $modelstr; - if ($entry = $hplipdevicesdb->{$hpmodelstr}) { - # Exact match - return $entry; - } - # More 'fuzzy' matching - my $simplemodel = hplip_simple_model($modelstr); - foreach my $key (keys %{$hplipdevicesdb}) { - my $simplekey = hplip_simple_model($key); - return $hplipdevicesdb->{$key} if $simplemodel eq $simplekey; - } - foreach my $key (keys %{$hplipdevicesdb}) { - my $simplekey = hplip_simple_model($key); - $simplekey =~ s/(\d\d)00(C?)$/$1\\d\\d$2/; - $simplekey =~ s/(\d\d\d)0(C?)$/$1\\d$2/; - $simplekey =~ s/(\d\d)0(\dC?)$/$1\\d$2/; - return $hplipdevicesdb->{$key} if - $simplemodel =~ m/^$simplekey$/i; - } - # Device not supported - return undef; - } - # $device not in @autodetected - return undef; -} - -sub hplip_device_entry_from_uri { - my ($deviceuri) = @_; - - return undef if $deviceuri !~ m!^hp:/!; - - if (!$hplipdevicesdb) { - # Read the HPLIP device database if not done already - $hplipdevicesdb = read_hplip_db(); - } - - $deviceuri =~ m!^hp:/(usb|par|net)/(\S+?)(\?\S+|)$!; - my $model = $2; - return undef if !$model; - - my $entry; - if ($entry = $hplipdevicesdb->{$model}) { - return $entry; - } - return undef; -} - -sub start_hplip { - my ($device, $hplipentry, @autodetected) = @_; - - # Determine connection type - my $bus; - if ($device =~ /usb/) { - $bus = "usb"; - } elsif ($device =~ m!/dev/(lp|par.*|printer.*)\d+!) { - $bus = "par"; - } elsif ($device =~ m!^socket://!) { - $bus = "net"; - } else { - return undef; - } - - # Start HPLIP daemons - printer::services::start_not_running_service("hplip"); - - # Determine HPLIP device URI for the CUPS queue - if ($bus eq "net") { - $device =~ m!^socket://([^:]+)(|:\d+)$!; - my $host = $1; - my $ip; - if ($host !~ m!^\d+\.\d+\.\d+\.\d+$!) { - my $addr = gethostbyname("$host"); - my ($a,$b,$c,$d) = unpack('C4',$addr); - $ip = sprintf("%d.%d.%d.%d", $a, $b, $c, $d); - } else { - $ip = $host; - } - open(my $F, ($::testing ? $::prefix : "chroot $::prefix/ ") . - "/bin/sh -c \"export LC_ALL=C; /usr/bin/hp-makeuri $ip\" |") or - die "Could not run \"/usr/bin/hp-makeuri $ip\"!"; - while (my $line = <$F>) { - if ($line =~ m!(hp:/net/\S+)!) { - my $uri = $1; - close $F; - return $uri; - } - } - close $F; - } else { - foreach my $a (@autodetected) { - $device eq $a->{port} or next; - open(my $F, ($::testing ? $::prefix : "chroot $::prefix/ ") . - "/bin/sh -c \"export LC_ALL=C; /usr/$lib/cups/backend/hp\" |") or - die "Could not run \"/usr/$lib/cups/backend/hp\"!"; - while (my $line = <$F>) { - if (($line =~ m!^direct\s+(hp:/$bus/(\S+?)\?serial=(\S+))\s+!) || - ($line =~ m!^direct\s+(hp:/$bus/(\S+?)\?device=()(\S+))\s+!) || - ($line =~ m!^direct\s+(hp:/$bus/(\S+))\s+!)) { - my $uri = $1; - my $modelstr = $2; - my $serial = $3; - my $devicestr = $4; - $devicestr =~ m!/dev/(lp|parport|printer/)(\d+)!; - my $parporthplip = $1; - $device =~ m!/dev/(lp|parport|printer/)(\d+)!; - my $parportdevice = $1; - if ((uc($modelstr) eq uc($hplipentry->{model})) && - (!$serial || - (uc($serial) eq uc($a->{val}{SERIALNUMBER}))) && - (!$devicestr || - ($devicestr eq $device) || - (($parporthplip ne "") && - ($parportdevice ne "") && - ($parporthplip == $parportdevice)))) { - close $F; - return $uri; - } - } - } - close $F; - last; - } - } - # HPLIP URI not found - return undef; -} - -sub start_hplip_manual { - - # Start HPLIP daemons - printer::services::start_not_running_service("hplip"); - - # Return all possible device URIs - open(my $F, ($::testing ? $::prefix : "chroot $::prefix/ ") . - "/bin/sh -c \"export LC_ALL=C; /usr/$lib/cups/backend/hp\" |") or - die "Could not run \"/usr/$lib/cups/backend/hp\"!"; - my @uris; - while (<$F>) { - m!^direct\s+(hp:\S+)\s+!; - push(@uris, $1); - } - return @uris; -} - -sub remove_hpoj_config { - my ($device, @autodetected) = @_; - - for my $d (@autodetected) { - $device eq $d->{port} or next; - my $bus; - if ($device =~ /usb/) { - $bus = "usb"; - } elsif ($device =~ m!/dev/(lp|par.*|printer.*)\d+!) { - $bus = "par"; - } elsif ($device =~ /socket/) { - $bus = "hpjd"; - } - my $path = "$::prefix/etc/ptal"; - opendir PTALDIR, "$path"; - while (my $file = readdir(PTALDIR)) { - next if $file !~ /^(mlc:|)$bus:/; - $file = "$path/$file"; - if ($bus eq "hpjd") { - $device =~ m!^socket://(\S+?)(:\d+|)$!; - my $host = $1; - if ($file =~ /$host/) { - closedir PTALDIR; - unlink($file) or return $file; - printer::services::restart("hpoj"); - return undef; - } - } else { - if ((grep { /$d->{val}{MODEL}/ } chomp_(cat_($file))) && - ((!$d->{val}{SERIALNUMBER}) || - (grep { /$d->{val}{SERIALNUMBER}/ } - chomp_(cat_($file))))) { - closedir PTALDIR; - unlink($file) or return $file; - printer::services::restart("hpoj"); - return undef; - } - } - } - last; - } - closedir PTALDIR; - return undef; -} - -sub devicefound { - my ($usbid, $model, $serial) = @_; - # Compare the output of "lsusb -vv" with the elements of the device - # ID string - if ($serial && $usbid->{SERIALNUMBER} eq $serial) { - # Match of serial number has absolute priority - return 1; - } elsif ($model && $usbid->{MODEL} eq $model) { - # Try to match the model name otherwise - return 1; - } - return 0; -} - -sub usbdevice { - my ($usbid) = @_; - # Run "lsusb -vv" and search the given device to get its USB bus and - # device numbers - open(my $F, ($::testing ? "" : "chroot $::prefix/ ") . - '/bin/sh -c "export LC_ALL=C; lsusb -vv 2> /dev/null" |') - or return undef; - my ($bus, $device, $model, $serial) = ("", "", "", ""); - my $found = 0; - while (my $line = <$F>) { - chomp $line; - if ($line =~ m/^\s*Bus\s+(\d+)\s+Device\s+(\d+)\s*:/i) { - # head line of a new device - my ($newbus, $newdevice) = ($1, $2); - last if (($model || $serial) && - ($found = devicefound($usbid, $model, $serial))); - ($bus, $device) = ($newbus, $newdevice); - } elsif ($line =~ m/^\s*iProduct\s+\d+\s+(.+)$/i) { - # model line - next if $device eq ""; - $model = $1; - } elsif ($line =~ m/^\s*iSerial\s+\d+\s+(.+)$/i) { - # model line - next if $device eq ""; - $serial = $1; - } - } - close $F; - # Check last entry - $found = devicefound($usbid, $model, $serial); - - return 0 if !$found; - return sprintf("%%%03d%%%03d", $bus, $device); -} - -sub config_sane { - my ($backend) = @_; - - # Add HPOJ/HPLIP backend to /etc/sane.d/dll.conf if needed (no - # individual config file /etc/sane.d/hplip.conf or - # /etc/sane.d/hpoj.conf necessary, the HPLIP and HPOJ drivers find - # the scanner automatically) - - return if (! -f "$::prefix/etc/sane.d/dll.conf"); - return if member($backend, - chomp_(cat_("$::prefix/etc/sane.d/dll.conf"))); - eval { append_to_file("$::prefix/etc/sane.d/dll.conf", - "$backend\n") } or - die "can not write SANE config in /etc/sane.d/dll.conf: $!"; -} - -sub setcupslink { - my ($printer) = @_; - return 1 if !$::isInstall || $printer->{SPOOLER} ne "cups" || -d "/etc/cups/ppd"; - system("ln -sf $::prefix/etc/cups /etc/cups"); - return 1; -} - - -1; |