diff options
Diffstat (limited to 'perl-install/printer/main.pm')
-rw-r--r-- | perl-install/printer/main.pm | 1674 |
1 files changed, 991 insertions, 683 deletions
diff --git a/perl-install/printer/main.pm b/perl-install/printer/main.pm index d9f06ddde..31d8981ba 100644 --- a/perl-install/printer/main.pm +++ b/perl-install/printer/main.pm @@ -9,28 +9,24 @@ use run_program; use printer::data; use printer::services; use printer::default; -use printer::gimp; use printer::cups; -use printer::office; 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); -#-Did we already read the subroutines of /usr/sbin/ptal-init? -my $ptalinitread = 0; - 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 95/98/NT server") => "SMB", + 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" @@ -39,6 +35,14 @@ our %printer_type = ( 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); #------------------------------------------------------------------------------ @@ -56,9 +60,9 @@ sub spooler() { # 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} } qw(cups), + return map { $spoolers{$_}{long_name} } ('cups', 'rcups' , if_(files_exist(qw(/usr/bin/pdq)), 'pdq'), - if_(files_exist(qw(/usr/lib/filters/lpf /usr/sbin/lpd)), 'lprng'); + if_(files_exist("/usr/$lib/filters/lpf", "/usr/sbin/lpd"), 'lprng')); } sub printer_type($) { @@ -68,14 +72,15 @@ sub printer_type($) { /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 has no daemon, exit. - if ($service eq "pdq") { return 1 }; + 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 @@ -125,18 +130,17 @@ sub assure_device_is_available_for_cups { } my $maxattempts = 3; for ($i = 0; $i < $maxattempts; $i++) { - local *F; - open F, ($::testing ? $::prefix : "chroot $::prefix/ ") . - '/bin/sh -c "export LC_ALL=C; /usr/sbin/lpinfo -v" |' or + 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>) { + while (my $line = <$F>) { if ($line =~ /$sdevice/) { # Found a line containing the device # name, so CUPS knows it. - close F; + close $F; return 1; } } - close F; + close $F; $result = printer::services::restart("cups"); } return $result; @@ -150,15 +154,14 @@ sub spooler_in_security_level { $sp = $spooler eq "lpr" || $spooler eq "lprng" ? "lpd" : $spooler; my $file = "$::prefix/etc/security/msec/server.$level"; if (-f $file) { - local *F; - open F, "< $file" or return 0; - while (my $line = <F>) { + open(my $F, "< $file") or return 0; + while (my $line = <$F>) { if ($line =~ /^\s*$sp\s*$/) { - close F; + close $F; return 1; } } - close F; + close $F; } return 0; } @@ -206,7 +209,7 @@ sub resetinfo($) { $printer->{QUEUE} = ""; $printer->{OLD_QUEUE} = ""; $printer->{OLD_CHOICE} = ""; - $printer->{ARGS} = ""; + $printer->{ARGS} = {}; $printer->{DBENTRY} = ""; $printer->{DEFAULT} = ""; $printer->{currentqueue} = {}; @@ -222,24 +225,26 @@ sub read_configured_queues($) { $printer->{SPOOLER} ||= printer::default::get_spooler(); if (!$printer->{SPOOLER}) { #- Find the first spooler where there are queues - foreach my $spooler (qw(cups pdq lprng lpd)) { + 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") { + 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 - local *F; - open F, ($::testing ? $::prefix : "chroot $::prefix/ ") . - "foomatic-configure -P -q -s $spooler |" or - die "Could not run foomatic-configure"; - eval join('', <F>); - close F; + #- 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 @@ -247,6 +252,17 @@ sub read_configured_queues($) { $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 @@ -254,13 +270,18 @@ sub read_configured_queues($) { } } } else { - #- Poll the queues of the current default spooler - local *F; - open 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; + 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; @@ -285,21 +306,24 @@ sub read_configured_queues($) { } # Read out which PPD file was originally used to set up this # queue - local *F; - if (open F, "< $::prefix/etc/cups/ppd/$QUEUES[$i]{queuedata}{queue}.ppd") { - while (my $line = <F>) { + 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 - if (!$printer->{configured}{$QUEUES[$i]{queuedata}{queue}}{queuedata}{ppd}) { - $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->{configured}{$QUEUES[$i]{queuedata}{queue}}{queuedata}{driver} = 'PPD'; $printer->{OLD_QUEUE} = ""; } $printer->{configured}{$QUEUES[$i]{queuedata}{queue}}{queuedata}{make} ||= ""; @@ -340,6 +364,17 @@ sub make_menuentry { $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+)$/) { @@ -356,21 +391,27 @@ sub make_menuentry { $connection = N(", multi-function device"); } } elsif ($connect =~ m!^file:(.+)$!) { - $connection = N(", printing to %s", $1); + my $file = $1; + $connection = N(", printing to %s", $file); } elsif ($connect =~ m!^lpd://([^/]+)/([^/]+)/?$!) { - $connection = N(" on LPD server \"%s\", printer \"%s\"", $1, $2); + my ($server, $printer) = ($1, $2); + $connection = N(" on LPD server \"%s\", printer \"%s\"", $server, $printer); } elsif ($connect =~ m!^socket://([^/:]+):([^/:]+)/?$!) { - $connection = N(", TCP/IP host \"%s\", port %s", $1, $2); + 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://.*\@([^/\@]+)/([^/\@]+)/?$!) { - $connection = N(" on SMB/Windows server \"%s\", share \"%s\"", $1, $2); + 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://.*\@([^/\@]+)/([^/\@]+)/?$!) { - $connection = N(" on Novell server \"%s\", printer \"%s\"", $1, $2); + my ($server, $printer) = ($1, $2); + $connection = N(" on Novell server \"%s\", printer \"%s\"", $server, $printer); } elsif ($connect =~ m!^postpipe:(.+)$!) { - $connection = N(", using command %s", $1); + my $command = $1; + $connection = N(", using command %s", $command); } else { $connection = ($printer->{expert} ? ", URI: $connect" : ""); } @@ -391,6 +432,17 @@ sub connectionstr { $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+)$/) { @@ -407,21 +459,27 @@ sub connectionstr { $connection = N("Multi-function device"); } } elsif ($connect =~ m!^file:(.+)$!) { - $connection = N("Prints into %s", $1); + my $file = $1; + $connection = N("Prints into %s", $file); } elsif ($connect =~ m!^lpd://([^/]+)/([^/]+)/?$!) { - $connection = N("LPD server \"%s\", printer \"%s\"", $1, $2); + my ($server, $port) = ($1, $2); + $connection = N("LPD server \"%s\", printer \"%s\"", $server, $port); } elsif ($connect =~ m!^socket://([^/:]+):([^/:]+)/?$!) { - $connection = N("TCP/IP host \"%s\", port %s", $1, $2); + 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://.*\@([^/\@]+)/([^/\@]+)/?$!) { - $connection = N("SMB/Windows server \"%s\", share \"%s\"", $1, $2); + 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://.*\@([^/\@]+)/([^/\@]+)/?$!) { - $connection = N("Novell server \"%s\", printer \"%s\"", $1, $2); + my ($server, $share) = ($1, $2); + $connection = N("Novell server \"%s\", printer \"%s\"", $server, $share); } elsif ($connect =~ m!^postpipe:(.+)$!) { - $connection = N("Uses command %s", $1); + my $command = $1; + $connection = N("Uses command %s", $command); } else { $connection = N("URI: %s", $connect); } @@ -430,22 +488,36 @@ sub connectionstr { sub read_printer_db { - my ($printer, $spooler) = @_; + 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. - local *DBPATH; #- don't have to do close ... and don't modify globals at least - # Generate the Foomatic printer/driver overview, read it from the - # appropriate file when it is already generated - open DBPATH, ($::testing ? $::prefix : "chroot $::prefix/ ") . #-# - "foomatic-configure -O -q |" or + # 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>) { + while (<$DBPATH>) { chomp; if ($inentry) { # We are inside a printer entry @@ -457,6 +529,31 @@ sub read_printer_db { } 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 @@ -513,13 +610,20 @@ sub read_printer_db { $driverstr = "GhostScript + $driver"; } if ($driver eq $entry->{defaultdriver}) { - $driverstr .= " (recommended)"; + $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; } @@ -532,11 +636,21 @@ sub read_printer_db { $entry->{ENTRY} =~ s/^KYOCERA[\s\-]*MITA/KYOCERA/i; if ($entry->{defaultdriver}) { - $entry->{driver} = $entry->{defaultdriver}; + 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; @@ -552,7 +666,9 @@ sub read_printer_db { } elsif (m!^\s*<drivers>\s*$!) { # Drivers block $indrivers = 1; - @{$entry->{drivers}} = (); + } elsif (m!^\s*<ppds>\s*$!) { + # PPDs block + $inppds = 1; } elsif (m!^\s*<autodetect>\s*$!) { # Autodetect block $inautodetect = 1; @@ -565,7 +681,7 @@ sub read_printer_db { } } } - close DBPATH; + close $DBPATH; # Add raw queue $entry->{ENTRY} = N("Raw printer (No driver)"); @@ -576,7 +692,7 @@ sub read_printer_db { #- Load CUPS driver database if CUPS is used as spooler if ($spooler && $spooler eq "cups") { - poll_ppd_base($printer); + $ppdentry = poll_ppd_base($printer, $newppd); } #my @entries_db_short = sort keys %printer::thedb; @@ -585,24 +701,25 @@ sub read_printer_db { #@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; - local *F; - open F, ($::testing ? $::prefix : "chroot $::prefix/ ") . + 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 + . " |") or die "Could not run foomatic-configure"; - eval join('', (<F>)); - close F; + eval join('', (<$F>)); + close $F; # Return the arguments field return $COMBODATA->{args}; } @@ -611,47 +728,77 @@ sub read_ppd_options ($) { my ($printer) = @_; # Generate the option data for a given PPD file my $COMBODATA; - local *F; - open F, ($::testing ? $::prefix : "chroot $::prefix/ ") . + open(my $F, ($::testing ? $::prefix : "chroot $::prefix/ ") . "foomatic-configure -P -q" . - " --ppd /usr/share/cups/model/$printer->{currentqueue}{ppd}" . + 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 + . " |") or die "Could not run foomatic-configure"; - eval join('', (<F>)); - close F; + eval join('', (<$F>)); + close $F; # Return the arguments field return $COMBODATA->{args}; } -my %sysconfig = getVarsFromSh("$::prefix/etc/sysconfig/printing"); - sub set_cups_special_options { - my ($queue) = $_[0]; + my ($printer, $queue) = @_; # Set some special CUPS options - my @lpoptions = chomp_(cat_("$::prefix/etc/cups/lpoptions")); - # If nothing is already configured, set text file borders of half an inch - # and decrease the font size a little bit, so nothing of the text gets - # cut off by unprintable borders. - if (!any { /$queue.*\s(page-(top|bottom|left|right)|lpi|cpi)=/ } @lpoptions) { - run_program::rooted($::prefix, "lpoptions", - "-p", $queue, - "-o", "page-top=36", "-o", "page-bottom=36", - "-o", "page-left=36", "-o page-right=36", - "-o", "cpi=12", "-o", "lpi=7", "-o", "wrap"); + 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 + # 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"; @@ -672,10 +819,54 @@ sub set_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! + s!^(\s*text/plain\s+\S+\s+\d+\s+)\S+(\s*$)!$1${textmode}texttops$2!; } "$::prefix/etc/cups/mime.convs"; return 1; } @@ -691,15 +882,46 @@ sub get_jap_textmode() { # Handling of /etc/cups/cupsd.conf sub read_cupsd_conf() { - cat_("$::prefix/etc/cups/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); - - #- restart cups after updating configuration. - printer::services::restart("cups"); } sub read_location { @@ -718,7 +940,7 @@ sub read_location { 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} and $location_end == -1; + $i <= $#{$cupsd_conf_ptr} && $location_end == -1; $i++) { if ($cupsd_conf_ptr->[$i] =~ m!^\s*<\s*Location\s+$path\s*>!) { # Start line of block @@ -739,7 +961,7 @@ sub read_location { # "undef" @result = undef; } - return (@result); + return @result; } sub rip_location { @@ -761,7 +983,7 @@ sub rip_location { 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} and $location_end == -1; + $i <= $#{$cupsd_conf_ptr} && $location_end == -1; $i++) { if ($cupsd_conf_ptr->[$i] =~ m!^\s*<\s*Location\s+$path\s*>!) { # Start line of block @@ -784,7 +1006,7 @@ sub rip_location { @location = ("<Location $path>\n", "</Location>\n"); } - return ($location_start, @location); + return $location_start, @location; } sub insert_location { @@ -912,7 +1134,7 @@ sub networkaddress { my ($address) = @_; if ($address =~ /\.255$/) { - while ($address =~ s/\.255$//) {}; + while ($address =~ s/\.255$//) {} $address .= ".*"; } @@ -1068,7 +1290,7 @@ sub clientnetworks { $haveallowedhostwithoutbrowseallow || $havebrowseallowwithoutallowedhost); - return ($configunsupported, @sharehosts); + return $configunsupported, @sharehosts; } sub makesharehostlist { @@ -1199,7 +1421,7 @@ sub read_cups_config { my @localips = printer::detect::getIPsOfLocalMachine(); @{$printer->{cupsconfig}{root}{AllowFrom}} = grep { - !member($_, @localips) + !member($_, @localips); } @{$printer->{cupsconfig}{root}{AllowFrom}}; # Keyword "Deny from" @@ -1297,7 +1519,7 @@ sub write_cups_config { "Allow From " . join("\nAllow From ", grep { - !member($_, @localips) + !member($_, @localips); } @{$printer->{cupsconfig}{clientnetworks}}) . "\n" : "") . "</Location>\n"; @@ -1314,7 +1536,7 @@ sub write_cups_config { @{$printer->{cupsconfig}{clientnetworks}})); } else { handle_configs::comment_directive($printer->{cupsconfig}{cupsd_conf}, - 'BrowseAddress') + 'BrowseAddress'); } # Set "BrowseAllow" lines if ($#{$printer->{cupsconfig}{clientnetworks}} >= 0) { @@ -1358,6 +1580,35 @@ sub clean_cups_config { } #---------------------------------------------------------------------- +# 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; @@ -1369,9 +1620,9 @@ sub read_printers_conf { #- Location > Location Text #- State > Idle|Stopped #- Accepting > Yes|No - local *PRINTERS; open PRINTERS, "$::prefix/etc/cups/printers.conf" or return; + open(my $PRINTERS, "$::prefix/etc/cups/printers.conf") or return; local $_; - while (<PRINTERS>) { + while (<$PRINTERS>) { chomp; /^\s*#/ and next; if (/^\s*<(?:DefaultPrinter|Printer)\s+([^>]*)>/) { $current = { mode => 'cups', QUEUE => $1, } } @@ -1379,7 +1630,7 @@ sub read_printers_conf { add2hash($printer->{configured}{$current->{QUEUE}} ||= {}, $current); $current = undef } elsif (/\s*(\S*)\s+(.*)/) { $current->{$1} = $2 } } - close PRINTERS; + close $PRINTERS; #- assume this printing system. $printer->{SPOOLER} ||= 'cups'; @@ -1388,12 +1639,12 @@ sub read_printers_conf { sub get_direct_uri() { #- get the local printer to access via a Device URI. my @direct_uri; - local *F; open F, ($::testing ? $::prefix : "chroot $::prefix/ ") . "/usr/sbin/lpinfo -v |"; + open(my $F, ($::testing ? $::prefix : "chroot $::prefix/ ") . "/usr/sbin/lpinfo -v |"); local $_; - while (<F>) { + while (<$F>) { /^(direct|usb|serial)\s+(\S*)/ and push @direct_uri, $2; } - close F; + close $F; @direct_uri; } @@ -1413,7 +1664,7 @@ sub installppd { 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", + 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"; @@ -1421,10 +1672,9 @@ sub installppd { printer::services::restart("cups") if $printer->{SPOOLER} eq "cups"; # Re-read printer database %thedb = (); - read_printer_db($printer, $printer->{SPOOLER}); - # Return description string of the PPD file - my $ppdentry = get_descr_from_ppdfile($printer, $ppdfile); - return $ppdentry; + # 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 { @@ -1462,11 +1712,11 @@ sub ppd_entry_str { $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\+Gimp-Print/i) { + $descr =~ s/CUPS\+Gimp-Print/CUPS + Gimp-Print/i; } elsif ($descr =~ /Series CUPS/i) { $descr =~ s/Series CUPS/Series, CUPS/i; - } elsif ($descr !~ /(PostScript|GhostScript|CUPS|Foomatic)/i) { + } elsif ($descr !~ /(PostScript|GhostScript|CUPS|Foomatic|PCL|PXL)/i) { $descr .= ", PostScript"; } # Split model and driver @@ -1476,20 +1726,26 @@ sub ppd_entry_str { /^\s*(Generic\s*PostScript\s*Printer)\s*,?\s*(.*)$/i || $descr =~ /^\s*(PostScript\s*Printer)\s*,?\s*(.*)$/i || - $descr =~ /^([^,]+[^,\s])\s*,?\s*(Foomatic.*)$/i || - $descr =~ /^([^,]+[^,\s])\s*,?\s*(GhostScript.*)$/i || - $descr =~ /^([^,]+[^,\s])\s*,?\s*(CUPS.*)$/i || - $descr =~ /^([^,]+[^,\s])\s*,?\s+(PS.*)$/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*,?\s*(\(v?\.?\s*\d\d\d\d\.\d\d\d\).*)$/i || - $descr =~ /^([^,]+[^,\s])\s*,?\s*(v\d+\.\d+.*)$/i || - $descr =~ /^([^,]+[^,\s])\s*,?\s*(PostScript.*)$/i || - $descr =~ /^([^,]+)\s*,?\s*(.+)$/) { + /^([^,]+?)\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; @@ -1503,7 +1759,13 @@ sub ppd_entry_str { } else { # Some PPDs do not have the ", <driver>" part. $model = $descr; - $driver = "PostScript"; + 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 @@ -1519,7 +1781,7 @@ sub ppd_entry_str { $model =~ s/BJC\s+/BJC-/; } # New MF devices from Epson have mis-spelled name in PPD files for - # native CUPS drivers of GIMP-Print + # native CUPS drivers of Gimp-Print if ($mf eq "EPSON") { $model =~ s/Stylus CX\-/Stylus CX/; } @@ -1529,9 +1791,11 @@ sub ppd_entry_str { $model =~ s/Oki\s+//i; } # Try again to remove manufacturer's name from the beginning of the - # model name, this with the cleaned manufacturer name + # 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 && " (" . lc(substr($lang, 0, 2)) . ")"); @@ -1557,9 +1821,9 @@ sub get_descr_from_ppdfile { local $_; foreach (catMaybeCompressed("$::prefix$ppdfile")) { # "OTHERS|Generic PostScript printer|PostScript (en)"; - /^\*([^\s:]*)\s*:\s*\"([^\"]*)\"/ and + /^\*([^\s:]*)\s*:\s*"([^"]*)"/ and do { $ppd{$1} = $2; next }; - /^\*([^\s:]*)\s*:\s*([^\s\"]*)/ and + /^\*([^\s:]*)\s*:\s*([^\s"]*)/ and do { $ppd{$1} = $2; next }; } }; @@ -1579,34 +1843,40 @@ sub ppd_devid_data { $ppd = "$::prefix/usr/share/cups/model/$ppd"; my @content; if ($ppd =~ /\.gz$/i) { - @content = cat_("$::prefix/bin/zcat $ppd |") or return ("", ""); + @content = cat_("$::prefix/bin/zcat $ppd |") or return "", ""; } else { - @content = cat_($ppd) or return ("", ""); + @content = cat_($ppd) or return "", ""; } my ($devidmake, $devidmodel); - /^\*Manufacturer:\s*\"(.*)\"\s*$/ and $devidmake = $1 + /^\*Manufacturer:\s*"(.*)"\s*$/ and $devidmake = $1 foreach @content; - /^\*Product:\s*\"\(?(.*?)\)?\"\s*$/ and $devidmodel = $1 + /^\*Product:\s*"\(?(.*?)\)?"\s*$/ and $devidmodel = $1 foreach @content; - return ($devidmake, $devidmodel); + return $devidmake, $devidmodel; } -sub poll_ppd_base() { - my ($printer) = @_; +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 don't run ? + # 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 = ""; foreach (1..60) { - local *PPDS; open PPDS, ($::testing ? $::prefix : - "chroot $::prefix/ ") . - "/usr/bin/poll_ppd_base -a |"; + open(my $PPDS, ($::testing ? $::prefix : + "chroot $::prefix/ ") . + "/usr/bin/poll_ppd_base -a |"); local $_; - while (<PPDS>) { + while (<$PPDS>) { chomp; my ($ppd, $mf, $descr, $lang) = split /\|/; if ($ppd eq "raw") { next } @@ -1619,20 +1889,20 @@ sub poll_ppd_base() { $driver =~ s/\s*\([a-z]{2}(|_[A-Z]{2})\)\s*$//; # Recommended Foomatic PPD? Extract "(recommended)" my $isrecommended = - $driver =~ s/\s+\(recommended\)\s*$//i; + $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; + $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 and !$isnativecups); + my $isnativeps = !$isfoomatic && !$isnativecups; # Key without language tag (key as it was produced for the # entries from the Foomatic XML database) my $keynolang = $key; @@ -1645,10 +1915,75 @@ sub poll_ppd_base() { # Foomatic $key = $keynolang; } - if (!$printer->{expert}) { + 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; + 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" + $newkey =~ s/\s*$sprecstr//g; + $newkey .= " $precstr" + if $ppdkey =~ m!$precstr!; + # 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/; + } + # If the PPD serves for multiple printers, conserve + # the make and model of the original entry + if (($#{$linkedppds{$filename}} > 0)) { + $newkey =~ + s/^([^\|]+)(\|[^\|]+)(\|.*|)$/$oldmake$2$3/; + $newkey =~ + s/^([^\|]+\|)([^\|]+)(\|.*|)$/$1$oldmodel$3/; + } + # Create the new entry + $thedb{$newkey}{ppd} = $ppd; + $thedb{$newkey}{make} = $mf; + $thedb{$newkey}{model} = $model; + $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 @@ -1663,18 +1998,25 @@ sub poll_ppd_base() { if (defined($thedb{$key})) { next if lc($thedb{$key}{driver}) eq lc($driver); - next unless $isnativeps && - $thedb{$key}{driver} =~ /^PostScript$/i || - $thedb{$key}{driver} ne "PPD" && $isrecommended || - $thedb{$key}{driver} eq "PPD" && $isrecommended && $driver ne "PostScript"; - - # Remove the old entry - delete $thedb{$key}; + 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 (recommended)"}) && + $thedb{"$mf|$model|$fullfoomaticdriver $precstr"}) && $isfoomatic) { # Expert mode: There is already an entry for the # same printer/driver combo produced by the @@ -1682,72 +2024,86 @@ sub poll_ppd_base() { # entry next; } elsif (defined - $thedb{"$mf|$model|PostScript (recommended)"} && + $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 (recommended)"}}) { + %{$thedb{"$mf|$model|PostScript $precstr"}}) { $thedb{"$mf|$model|PostScript"}{$_} = - $thedb{"$mf|$model|PostScript (recommended)"}{$_}; + $thedb{"$mf|$model|PostScript $precstr"}{$_}; } delete - $thedb{"$mf|$model|PostScript (recommended)"}; + $thedb{"$mf|$model|PostScript $precstr"}; if (!$isrecommended) { - $key .= " (recommended)"; + $key .= " $precstr"; } } elsif ($driver =~ /PostScript/i && $isrecommended && $isfoomatic && (my @foundkeys = grep { /^$mf\|$model\|/ && !/CUPS/i && - $thedb{$_}{driver} eq "PPD" + $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 { /\(recommended\)/ } @foundkeys)) { + 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 (recommended)"}{$_} = + $thedb{"$firstfound $precstr"}{$_} = $thedb{$firstfound}{$_}; } delete $thedb{$firstfound}; } - $key =~ s/\s*\(recommended\)//; + $key =~ s/\s*$sprecstr//; } elsif ($driver !~ /PostScript/i && $isrecommended && $isfoomatic && (@foundkeys = grep { - /^$mf\|$model\|.*\(recommended\)/ && - !/CUPS/i && $thedb{$_}{driver} eq "PPD" + /^$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 + # 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+\(recommended\)\s*$//i; + $destkey =~ s/\s+$sprecstr\s*$//i; foreach (keys %{$thedb{$sourcekey}}) { $thedb{$destkey}{$_} = $thedb{$sourcekey}{$_}; } delete $thedb{$sourcekey}; } } + + # 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/; + # Create the new entry $thedb{$key}{ppd} = $ppd; $thedb{$key}{make} = $mf; $thedb{$key}{model} = $model; $thedb{$key}{driver} = $driver; - # Get auto-detection data + # 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; + close $PPDS; scalar(keys %thedb) - $driversthere > 5 and last; #- we have to try again running the program, wait here a little #- before. @@ -1756,6 +2112,7 @@ sub poll_ppd_base() { #scalar(keys %descr_to_ppd) > 5 or # die "unable to connect to cups server"; + return $ppdentry; } @@ -1783,16 +2140,25 @@ sub configure_queue($) { ("-p", $printer->{currentqueue}{printer}, "-d", $printer->{currentqueue}{driver}) : ($printer->{currentqueue}{ppd} ? - ("--ppd", - ($printer->{currentqueue}{ppd} !~ m!^/! ? - "/usr/share/cups/model/" : "") . - $printer->{currentqueue}{ppd}) : + ($printer->{currentqueue}{ppd} ne '1' ? + ("--ppd", + ($printer->{currentqueue}{ppd} !~ m!^/! ? + "/usr/share/cups/model/" : "") . + $printer->{currentqueue}{ppd}) : ()) : ("-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}) { + ) 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') { @@ -1801,18 +2167,12 @@ sub configure_queue($) { } # Make sure that queue is active - if ($printer->{SPOOLER} ne "pdq") { + if ($printer->{NEW} && ($printer->{SPOOLER} ne "pdq")) { run_program::rooted($::prefix, "foomatic-printjob", "-s", $printer->{currentqueue}{spooler}, "-C", "up", $printer->{currentqueue}{queue}); } - # In case of CUPS set some more useful defaults for text and image - # printing - if ($printer->{SPOOLER} eq "cups") { - set_cups_special_options($printer->{currentqueue}{queue}); - } - # Check whether a USB printer is configured and activate USB printing if so my $useUSB = 0; foreach (values %{$printer->{configured}}) { @@ -1849,6 +2209,14 @@ sub configure_queue($) { $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} = ""; @@ -1859,9 +2227,19 @@ sub configure_queue($) { 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) = $_[0]; - my ($queue) = $_[1]; + my ($printer, $queue) = @_; run_program::rooted($::prefix, "foomatic-configure", "-R", "-q", "-s", $printer->{SPOOLER}, "-n", $queue); @@ -1873,7 +2251,6 @@ sub remove_queue($$) { $printer->{ARGS} = {}; $printer->{DBENTRY} = ""; $printer->{currentqueue} = {}; - removeprinterfromapplications($printer, $queue); } sub restart_queue($) { @@ -1908,38 +2285,39 @@ sub print_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 + # Only text and PostScript can be printed directly with all + # spoolers, images must be treated seperately if ($page =~ /\.jpg$/) { - if ($printer->{SPOOLER} ne "cups") { + 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 $printer->{SPOOLER} -P $queue"); + "$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", $printer->{SPOOLER}, + run_program::rooted($::prefix, $lpr, "-s", $spooler, "-P", $queue, "-o", "scaling=90", $page); } } else { - run_program::rooted($::prefix, $lpr, "-s", $printer->{SPOOLER}, + run_program::rooted($::prefix, $lpr, "-s", $spooler, "-P", $queue, $page); } } sleep 5; #- allow lpr to send pages. # Check whether the job is queued - local *F; - open F, ($::testing ? $::prefix : "chroot $::prefix/ ") . "$lpq -s $printer->{SPOOLER} -P $queue |"; + 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; + grep { !/^no entries/ && !(/^Rank\s+Owner/ .. /^\s*$/) } <$F>; + close $F; @lpq_output; } @@ -1947,10 +2325,9 @@ sub help_output { my ($printer, $spooler) = @_; my $queue = $printer->{QUEUE}; - local *F; - open F, ($::testing ? $::prefix : "chroot $::prefix/ ") . sprintf($spoolers{$spooler}{help}, $queue); - my $helptext = join("", <F>); - close F; + 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; } @@ -1982,17 +2359,19 @@ sub print_optionlist { 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 - local *QUEUEOUTPUT; - open QUEUEOUTPUT, ($::testing ? $::prefix : "chroot $::prefix/ ") . - "foomatic-configure -Q -q -s $oldspooler |" or + 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>) { + while (<$QUEUEOUTPUT>) { chomp; if ($inentry) { # We are inside a queue entry @@ -2002,7 +2381,7 @@ sub get_copiable_queues { if ($entry->{foomatic} && $entry->{spooler} eq $oldspooler) { # Is the connection type supported by the new # spooler? - if ($newspooler eq "cups" && $entry->{connect} =~ /^(file|ptal|lpd|socket|smb|ipp):/ || + 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}); @@ -2017,7 +2396,7 @@ sub get_copiable_queues { $entry->{connect} = $1; } } else { - if (m!^\s*<queue\s+foomatic\s*=\s*\"?(\d+)\"?\s*spooler\s*=\s*\"?(\w+)\"?\s*>\s*$!) { + if (m!^\s*<queue\s+foomatic\s*=\s*"?(\d+)"?\s*spooler\s*=\s*"?(\w+)"?\s*>\s*$!) { # new entry $inentry = 1; $entry->{foomatic} = $1; @@ -2025,8 +2404,8 @@ sub get_copiable_queues { } } } - close QUEUEOUTPUT; - + close $QUEUEOUTPUT; + return @queuelist; } @@ -2038,7 +2417,7 @@ sub copy_foomatic_queue { "-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($newqueue); + set_cups_special_options($printer, $newqueue); } } @@ -2054,7 +2433,7 @@ sub copy_foomatic_queue { sub autodetectionentry_for_uri { my ($uri, @autodetected) = @_; - if ($uri =~ m!^usb://([^/]+)/([^/\?]+)(|\?serial=(\S+))$!) { + if ($uri =~ m!^usb://([^/]+)/([^\?]+)(|\?serial=(\S+))$!) { # USB device with URI referring to printer model my $make = $1; my $model = $2; @@ -2083,6 +2462,49 @@ sub autodetectionentry_for_uri { 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; @@ -2119,424 +2541,392 @@ sub autodetectionentry_for_uri { # ------------------------------------------------------------------ # -# Configuration of HP multi-function devices +# Configuration of HP printers and multi-function devices with HPLIP # # ------------------------------------------------------------------ -sub configure_hpoj { - my ($device, @autodetected) = @_; +sub read_hplip_db { - # Make the subroutines of /usr/sbin/ptal-init available - # It's only necessary to read it at the first call of this subroutine, - # the subroutine definitions stay valid after leaving this subroutine. - if (!$ptalinitread) { - local *PTALINIT; - open PTALINIT, "$::prefix/usr/sbin/ptal-init" or do { - die "unable to open $::prefix/usr/sbin/ptal-init"; - }; - my @ptalinitfunctions; # subroutine definitions in /usr/sbin/ptal-init - local $_; - while (<PTALINIT>) { - if (m!sub main!) { - last; - } elsif ((m!^[^\#]!) && !(m!^\s*exec\b!)){ - # Comment lines and the "exec" line (probably obsolete - # Red Hat workaround) are skipped. - - # Make the subroutines also working during installation - if ($::isInstall) { - s!\$::prefix!\$hpoj_prefix!g; - s!prefix="/usr"!prefix="$::prefix/usr"!g; - s!etcPtal="/etc/ptal"!etcPtal="$::prefix/etc/ptal"!g; - s!varLock="/var/lock"!varLock="$::prefix/var/lock"!g; - s!varRunPrefix="/var/run"!varRunPrefix="$::prefix/var/run"!g; - s!/sbin/lsmod!/usr/bin/lsmod!g; - s!/sbin/modprobe!/usr/bin/modprobe!g; - s!/sbin/rmmod!/usr/bin/rmmod!g; - s!(my\s*\$osPlatform\s*=\s*).*?$!$1"Linux";!g; - s!chomp\s*\$osPlatform\s*;\s*$!!g; - s!(my\s*\$linuxVersion\s*=\s*).*?$!$1"$kernelversion";!g; - s!^\s*\$linuxVersion\s*=~\s*s.*$!!g; - s!chomp\s*\$linuxVersion\s*;\s*$!!g; - s!(my\s*\$usbprintermodule\s*=\s*).*?$!$1"$usbprintermodule";!g; - } - push @ptalinitfunctions, $_; - } - } - close PTALINIT; + # 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"; - eval "package printer::hpoj; - @ptalinitfunctions - sub getDevnames { - return (%devnames) - } - sub getConfigInfo { - return (%configInfo) - }"; - - if ($::isInstall) { - # Needed for photo card reader detection during installation - system("ln -s $::prefix/var/run/ptal-mlcd /var/run/ptal-mlcd"); - system("ln -s $::prefix/etc/ptal /etc/ptal"); - } - $ptalinitread = 1; - } - - # Read the HPOJ config file and check whether this device is already - # configured - printer::hpoj::setupVariables(); - printer::hpoj::readDeviceInfo(); - - $device =~ m!^/dev/\S*lp(\d+)$! or - $device =~ m!^/dev/printers/(\d+)$! or - $device =~ m!^socket://([^:]+)$! or - $device =~ m!^socket://([^:]+):(\d+)$!; - my $model = $1; - my ($model_long, $serialnumber, $serialnumber_long) = ("", "", ""); - my $cardreader = 0; - my $device_ok = 1; - my $bus; - my $address_arg = ""; - my $base_address = ""; - my $hostname = ""; - my $port = $2; - if ($device =~ /usb/) { - $bus = "usb"; - } elsif ($device =~ /par/ || - $device =~ m!/dev/lp! || - $device =~ /printers/) { - $bus = "par"; - $address_arg = printer::detect::parport_addr($device); - eval "$base_address = $1" if $address_arg =~ /^\s*-base\s+(\S+)/; - } elsif ($device =~ /socket/) { - $bus = "hpjd"; - $hostname = $model; - return "" if $port && ($port < 9100 || $port > 9103); - if ($port && $port != 9100) { - $port -= 9100; - $hostname .= ":$port"; - } - } else { - return ""; - } - if ($#autodetected < 0) { - # Make a pseudo structure for the auto-detected data if there is - # no auto-detected data (for example when configuring manually) - $autodetected[0] = { - 'port' => $device, - 'val' => { - 'MODEL' => N("Unknown model") + 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 + $_ = ''; } - }; - } - my $devdata; - foreach (@autodetected) { - $device eq $_->{port} or next; - $devdata = $_; - # $model is for the PTAL device name, so make sure that it is unique - # so in the case of the model name auto-detection having failed leave - # the port number or the host name as model name. - my $searchunknown = N("Unknown model"); - if ($_->{val}{MODEL} && - $_->{val}{MODEL} !~ /$searchunknown/i && - $_->{val}{MODEL} !~ /^\s*$/) { - $model = $_->{val}{MODEL}; - } - $serialnumber = $_->{val}{SERIALNUMBER}; - services::stop("hpoj") if ($bus ne "hpjd"); - # Check if the device is really an HP multi-function device - #my $libusb = 0; - foreach my $libusb (0, 1) { - # Do access via libusb/user mode only if we have a USB device - next if ($libusb && ($bus ne "usb")); - # Preliminary workaround to make the user-mode USB devices - # (LIDIL devices) installable as verification of the HPOJ - # settings of these devices does not work yet. The workaround - # will probably removed after version 9.2 of this distribution. - # Note: This workaround leaves out the checking for a photo - # memory card reader, but to my knowledge there are no LIDIL - # devices with card reader yet. - if ($libusb) { - $device_ok = 1; - next; + } else { + while (m/^(.*?)<!--(.*?)-->(.*)$/) { + # Remove one-line comments + $_ = $1 . $3; } - my $printermoduleunloaded = 0; - if ($bus ne "hpjd") { - if (!$libusb) { - # Start ptal-mlcd daemon for locally connected devices - # (kernel mode with "printer"/"usblp" module for USB). - run_program::rooted($::prefix, - "ptal-mlcd", "$bus:probe", - "-device", - $device, split(' ',$address_arg)); - } else { - # Start ptal-mlcd daemon for user-mode USB devices - # (all LIDIL MF devices as HP PSC 1xxx and OfficeJet - # 4xxx) - my $usbdev = usbdevice($_->{val}); - if (defined($usbdev)) { - # Unload kernel module "printer"/"usblp" - if (modules::get_probeall("usb-interface")) { - eval(modules::unload($usbprintermodule)); - $printermoduleunloaded = 1; - } - # Start ptal-mlcd - run_program::rooted($::prefix, - "ptal-mlcd", "$bus:probe", - "-device", $usbdev); - } else { - # We could not determine the USB device number, - # so we cannot check this device in user mode - next; - } - } + if (m/^(.*?)<!--(.*)$/) { + # Start of comment, keep the beginning of the line + $_ = $1; + $incomment = 1; } - $device_ok = 0; - my $ptalprobedevice = $bus eq "hpjd" ? "hpjd:$hostname" : "mlc:$bus:probe"; - local *F; - if (open F, ($::testing ? $::prefix : "chroot $::prefix/ ") . "/usr/bin/ptal-devid $ptalprobedevice |") { - my $devid = join("", <F>); - close F; - if ($devid) { - $device_ok = 1; - local *F; - if (open F, ($::testing ? $::prefix : "chroot $::prefix/ ") . "/usr/bin/ptal-devid $ptalprobedevice -long -mdl 2>/dev/null |") { - $model_long = join("", <F>); - close F; - chomp $model_long; - # If SNMP or local port auto-detection failed but - # HPOJ auto-detection succeeded, fill in model name - # here. - if (!$_->{val}{MODEL} || - $_->{val}{MODEL} =~ /$searchunknown/i || - $_->{val}{MODEL} =~ /^\s*$/) { - if ($model_long =~ /:([^:;]+);/) { - $_->{val}{MODEL} = $1; - $model = $_->{val}{MODEL}; - $model =~ s/ /_/g; - } - } + } + # 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; } - if (open F, ($::testing ? $::prefix : "chroot $::prefix/ ") . "/usr/bin/ptal-devid $ptalprobedevice -long -sern 2>/dev/null |") { #-# - $serialnumber_long = join("", <F>); - close F; - chomp $serialnumber_long; + } 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; } - $cardreader = 1 if printer::hpoj::cardReaderDetected($ptalprobedevice); } - } - if ($bus ne "hpjd") { - # Stop ptal-mlcd daemon for locally connected devices - local *F; - if (open F, ($::testing ? $::prefix : "chroot $::prefix/ ") . qq(ps auxwww | grep "ptal-mlcd $bus:probe" | grep -v grep | )) { - my $line = <F>; - if ($line =~ /^\s*\S+\s+(\d+)\s+/) { - my $pid = $1; - kill 15, $pid; - } - close F; + } 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; } - $printermoduleunloaded && - eval(modules::load($usbprintermodule)); } - last if $device_ok; } - printer::services::start("hpoj") if ($bus ne "hpjd"); - last; } - # No, it is not an HP multi-function device. - return "" if !$device_ok; - - # If $model_long and $serialnumber_long stay empty, fill them with - # $model and $serialnumber - $model_long ||= $model; - $serialnumber_long ||= $serialnumber; - - # Determine the ptal device name from already existing config files - my $ptalprefix = - ($bus eq "hpjd" ? "hpjd:" : "mlc:$bus:"); - my $ptaldevice = printer::hpoj::lookupDevname($ptalprefix, $model_long, - $serialnumber_long, $base_address); - - # It's all done for us, the device is already configured - return $ptaldevice if defined($ptaldevice); - - # Determine the ptal name for a new device - if ($bus eq "hpjd") { - $ptaldevice = "hpjd:$hostname"; - } else { - $ptaldevice = $model; - $ptaldevice =~ s![\s/]+!_!g; - $ptaldevice = "mlc:$bus:$ptaldevice"; - } - - # Delete any old/conflicting devices - printer::hpoj::deleteDevice($ptaldevice); - if ($bus eq "par") { - while (1) { - my $oldDevname = printer::hpoj::lookupDevname("mlc:par:",undef,undef,$base_address); - last unless defined($oldDevname); - printer::hpoj::deleteDevice($oldDevname); + 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; +} - # Configure the device +sub hplip_device_entry_from_uri { + my ($deviceuri) = @_; - # Open configuration file - local *CONFIG; - open(CONFIG, "> $::prefix/etc/ptal/$ptaldevice") or - die "Could not open /etc/ptal/$ptaldevice for writing!\n"; + return undef if $deviceuri !~ m!^hp:/!; + + if (!$hplipdevicesdb) { + # Read the HPLIP device database if not done already + $hplipdevicesdb = read_hplip_db(); + } - # Write file header. - my $date = chomp_(`date`); - print CONFIG -qq( -# Added $date by "printerdrake" + $deviceuri =~ m!^hp:/(usb|par|net)/(\S+?)(\?\S+|)$!; + my $model = $2; + return undef if !$model; -# The basic format for this file is "key[+]=value". -# If you say "+=" instead of "=", then the value is appended to any -# value already defined for this key, rather than replacing it. + my $entry; + if ($entry = $hplipdevicesdb->{$model}) { + return $entry; + } + return undef; +} -# Comments must start at the beginning of the line. Otherwise, they may -# be interpreted as being part of the value. +sub start_hplip { + my ($device, $hplipentry, @autodetected) = @_; -# If you have multiple devices and want to define options that apply to -# all of them, then put them in the file /etc/ptal/defaults, which is read -# in before this file. + # 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; + } -# The format version of this file: -# ptal-init ignores devices with incorrect/missing versions. -init.version=2 -); + # Start HPLIP daemons + printer::services::start_not_running_service("hplip"); - # Write model string. - if ($model_long !~ /\S/) { - print CONFIG - "\n" . - qq(# "printerdrake" couldn't read the model but added this device anyway:\n) . - "# "; + # 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 { - print CONFIG - "\n" . - "# The device model that was originally detected on this port:\n" . - qq(# If this ever changes, then you should re-run "printerdrake"\n) . - "# to delete and re-configure this device.\n"; - if ($bus eq "par") { - print CONFIG - "# Comment out if you don't care what model is really connected to this\n" . - "# parallel port.\n"; + 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; } } - print CONFIG - qq(init.mlcd.append+=-devidmatch "$model_long"\n); + # HPLIP URI not found + return undef; +} - # Write serial-number string. - if ($serialnumber_long !~ /\S/) { - print CONFIG - "\n" . - "# The device's serial number is unknown.\n" . - "# "; - } else { - print CONFIG - "\n" . - "# The serial number of the device that was originally detected on this port:\n"; - if ($bus =~ /^[pu]/) { - print CONFIG - "# Comment out if you want to disable serial-number matching.\n"; - } +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); } - print CONFIG - qq(init.mlcd.append+=-devidmatch "$serialnumber_long"\n); - - if ($bus =~ /^[pu]/) { - print CONFIG - "\n" . - "# Standard options passed to ptal-mlcd:\n" . - "init.mlcd.append+="; - if ($bus eq "usb") { - # Important: don't put more quotes around /dev/usb/lp[0-9]*, - # because ptal-mlcd currently does no globbing: - print CONFIG "-device /dev/usb/lp0 /dev/usb/lp1 /dev/usb/lp2 /dev/usb/lp3 /dev/usb/lp4 /dev/usb/lp5 /dev/usb/lp6 /dev/usb/lp7 /dev/usb/lp8 /dev/usb/lp9 /dev/usb/lp10 /dev/usb/lp11 /dev/usb/lp12 /dev/usb/lp13 /dev/usb/lp14 /dev/usb/lp15"; - } elsif ($bus eq "par") { - print CONFIG "$address_arg -device $device"; + 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"; } - print CONFIG "\n" . - "\n" . - "# ptal-mlcd's remote console can be useful for debugging, but may be a\n" . - "# security/DoS risk otherwise. In any case, it's accessible with the\n" . - qq(# command "ptal-connect mlc:<XXX>:<YYY> -service PTAL-MLCD-CONSOLE".\n) . - "# Uncomment the following line if you want to enable this feature for\n" . - "# this device:\n" . - "# init.mlcd.append+=-remconsole\n" . - "\n" . - "# If you need to pass any other command-line options to ptal-mlcd, then\n" . - "# add them to the following line and uncomment the line:\n" . - "# init.mlcd.append+=\n" . - "\n" . - "# By default ptal-printd is started for mlc: devices. If you use CUPS,\n" . - "# then you may not be able to use ptal-printd, and you can uncomment the\n" . - "# following line to disable ptal-printd for this device:\n" . - "# init.printd.start=0\n"; - } else { - print CONFIG - "\n" . - "# By default ptal-printd isn't started for hpjd: devices.\n" . - "# If for some reason you want to start it for this device, then\n" . - "# uncomment the following line:\n" . - "init.printd.start=1\n"; - } - - print CONFIG - "\n" . - "# If you need to pass any additional command-line options to ptal-printd,\n" . - "# then add them to the following line and uncomment the line:\n" . - "# init.printd.append+=\n"; - if ($cardreader) { - print CONFIG - "\n" . - "# Uncomment the following line to enable ptal-photod for this device:\n" . - "init.photod.start=1\n" . - "\n" . - "# If you have more than one photo-card-capable peripheral and you want to\n" . - "# assign particular TCP port numbers and mtools drive letters to each one,\n" . - qq(# then change the line below to use the "-portoffset <n>" option.\n) . - "init.photod.append+=-maxaltports 26\n"; - } - close(CONFIG); - printer::hpoj::readOneDevice($ptaldevice); - - # Restart HPOJ - printer::services::restart("hpoj"); - - # Return HPOJ device name to form the URI - return $ptaldevice; + 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)) { + if ($serial && $usbid->{SERIALNUMBER} eq $serial) { # Match of serial number has absolute priority return 1; - } elsif ($model && ($usbid->{'MODEL'} eq $model)) { + } elsif ($model && $usbid->{MODEL} eq $model) { # Try to match the model name otherwise return 1; } return 0; } -sub usbdevice() { +sub usbdevice { my ($usbid) = @_; - # Run "lsusb -vv¨ and search the given device to get its USB bus and + # Run "lsusb -vv" and search the given device to get its USB bus and # device numbers - local *F; - open F, ($::testing ? "" : "chroot $::prefix/ ") . - '/bin/sh -c "export LC_ALL=C; lsusb -vv 2> /dev/null" |' + 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>) { + 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 @@ -2554,7 +2944,7 @@ sub usbdevice() { $serial = $1; } } - close F; + close $F; # Check last entry $found = devicefound($usbid, $model, $serial); @@ -2562,102 +2952,20 @@ sub usbdevice() { return sprintf("%%%03d%%%03d", $bus, $device); } -sub config_sane() { - # Add HPOJ backend to /etc/sane.d/dll.conf if needed (no individual - # config file /etc/sane.d/hpoj.conf necessary, the HPOJ driver finds the - # scanner automatically) - return if member("hpoj", chomp_(cat_("$::prefix/etc/sane.d/dll.conf"))); - eval { append_to_file("$::prefix/etc/sane.d/dll.conf", "hpoj\n") } or - die "can't write SANE config in /etc/sane.d/dll.conf: $!"; -} - -sub config_photocard() { - - # Add definitions for the drives p:. q:, r:, and s: to /etc/mtools.conf - cat_("$::prefix/etc/mtools.conf") !~ m/^\s*drive\s+p:/m or return; - - append_to_file("$::prefix/etc/mtools.conf", <<'EOF'); -# Drive definitions added for the photo card readers in HP multi-function -# devices driven by HPOJ -drive p: file=":0" remote -drive q: file=":1" remote -drive r: file=":2" remote -drive s: file=":3" remote -# This turns off some file system integrity checks of mtools, it is needed -# for some photo cards. -mtools_skip_check=1 -EOF - - # Generate a config file for the graphical mtools frontend MToolsFM or - # modify the existing one - my $mtoolsfmconf; - if (-f "$::prefix/etc/mtoolsfm.conf") { - $mtoolsfmconf = cat_("$::prefix/etc/mtoolsfm.conf") or die "can't read MToolsFM config in $::prefix/etc/mtoolsfm.conf: $!"; - my $alloweddrives = lc($1) if $mtoolsfmconf =~ m/^\s*DRIVES\s*=\s*\"([A-Za-z ]*)\"/m; - foreach my $letter ("p", "q", "r", "s") { - $alloweddrives .= $letter if $alloweddrives !~ /$letter/; - } - $mtoolsfmconf =~ s/^\s*DRIVES\s*=\s*"[A-Za-z ]*"/DRIVES="$alloweddrives"/m; - $mtoolsfmconf =~ s/^\s*LEFTDRIVE\s*=\s*"[^"]*"/LEFTDRIVE="p"/m; - #"# Fix emacs syntax highlighting - } else { - $mtoolsfmconf = <<'EOF'; -# MToolsFM config file. comments start with a hash sign. -# -# This variable sets the allowed driveletters (all lowercase). Example: -# DRIVES="ab" -DRIVES="apqrs" -# -# This variable sets the driveletter upon startup in the left window. -# An empty string or space is for the hardisk. Example: -# LEFTDRIVE="a" -LEFTDRIVE="p" -# -# This variable sets the driveletter upon startup in the right window. -# An empty string or space is for the hardisk. Example: -# RIGHTDRIVE="a" -RIGHTDRIVE=" " -EOF - } - output("$::prefix/etc/mtoolsfm.conf", $mtoolsfmconf); -} +sub config_sane { + my ($backend) = @_; -# ------------------------------------------------------------------ -# -# Configuration of printers in applications -# -# ------------------------------------------------------------------ - -sub configureapplications { - my ($printer) = @_; - setcupslink($printer); - printer::office::configureoffice('Star Office', $printer); - printer::office::configureoffice('OpenOffice.Org', $printer); - printer::gimp::configure($printer); -} + # 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) -sub addcupsremotetoapplications { - my ($printer, $queue) = @_; - setcupslink($printer); - return printer::office::add_cups_remote_to_office('Star Office', $printer, $queue) && - printer::office::add_cups_remote_to_office('OpenOffice.Org', $printer, $queue) && - printer::gimp::addcupsremoteto($printer, $queue); -} - -sub removeprinterfromapplications { - my ($printer, $queue) = @_; - setcupslink($printer); - return printer::office::remove_printer_from_office('Star Office', $printer, $queue) && - printer::office::remove_printer_from_office('OpenOffice.Org', $printer, $queue) && - printer::gimp::removeprinterfrom($printer, $queue); -} - -sub removelocalprintersfromapplications { - my ($printer) = @_; - setcupslink($printer); - printer::office::remove_local_printers_from_office('Star Office', $printer); - printer::office::remove_local_printers_from_office('OpenOffice.Org', $printer); - printer::gimp::removelocalprintersfrom($printer); + 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 { |