package printer; # $Id$ #use diagnostics; #use strict; use common; use run_program; #-if we are in an DrakX config my $prefix = ""; #-location of the printer database in an installed system my $PRINTER_DB_FILE = "/usr/share/foomatic/db/compiled/overview.xml"; #-configuration directory of Foomatic my $FOOMATICCONFDIR = "/etc/foomatic"; #-location of the file containing the default spooler's name my $FOOMATIC_DEFAULT_SPOOLER = "$FOOMATICCONFDIR/defaultspooler"; #-Did we already read the subroutines of /usr/sbin/ptal-init? my $ptalinitread = 0; our %spoolers = ('ppq' => { 'help' => "/usr/bin/lphelp %s |", 'print_command' => 'lpr-pdq', 'long_name' =>N("PDQ - Print, Don't Queue"), 'short_name' => N("PDQ") }, 'lpd' => { 'help' => "/usr/bin/pdq -h -P %s 2>&1 |", 'print_command' => 'lpr-cups', 'long_name' => N("LPD - Line Printer Daemon"), 'short_name' => N("LPD") }, 'lprng' => { 'print_command' => 'lpr-lpd', 'long_name' => N("LPRng - LPR New Generation"), 'short_name' => N("LPRng") }, 'cups' => { 'print_command' => 'lpr-cups', 'long_name' => N("CUPS - Common Unix Printing System"), 'short_name' => N("CUPS") } ); our %spooler_inv = map { $spooler{$_}{long_name} => $_ } keys %spoolers; our %shortspooler_inv = map { $spooler{$_}{short_name} => $_ } keys %spoolers; %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 NetWare server") => "NCP", N("Enter a printer device URI") => "URI", N("Pipe job into a command") => "POSTPIPE" ); our %printer_type_inv = reverse %printer_type; #------------------------------------------------------------------------------ sub set_prefix($) { $prefix = $_[0] } sub default_printer_type($) { "LOCAL" } 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 Mandrake 9.0, so # show it only in the spooler menu when it was manually installed. my @res; if (files_exist((qw(/usr/lib/filters/lpf /usr/sbin/lpd)))) { foreach (qw(cups lprng pdq)) { push @res, $spooler_inv{$_}{long_name} }; # {qw(cups lprng pdq)}{long_name}; } else { foreach (qw(cups pdq)) { push @res, $spooler_inv{$_}{long_name} }; # return spooler_inv{qw(cups pdq)}{long_name}; } return @res; } sub printer_type($) { my ($printer) = @_; foreach ($printer->{SPOOLER}) { /cups/ && return @printer_type_inv{qw(LOCAL), qw(LPD SOCKET SMB), $::expert ? qw(URI) : ()}; /lpd/ && return @printer_type_inv{qw(LOCAL LPD SOCKET SMB NCP), $::expert ? qw(POSTPIPE URI) : ()}; /lprng/ && return @printer_type_inv{qw(LOCAL LPD SOCKET SMB NCP), $::expert ? qw(POSTPIPE URI) : ()}; /pdq/ && return @printer_type_inv{qw(LOCAL LPD SOCKET), $::expert ? qw(URI) : ()}; } } sub get_default_spooler () { if (-f "$prefix$FOOMATIC_DEFAULT_SPOOLER") { my $spool = cat_("$prefix$FOOMATIC_DEFAULT_SPOOLER"); chomp $spool; return $spool if $spool =~ /cups|lpd|lprng|pdq/; } } sub set_default_spooler ($) { my ($printer) = @_; # Mark the default driver in a file output_p("$prefix$FOOMATIC_DEFAULT_SPOOLER", $printer->{SPOOLER}); } sub set_permissions { my ($file, $perms, $owner, $group) = @_; # We only need to set the permissions during installation to be able to # print test pages. After installation the devfsd daemon does the business # automatically. if (!$::isInstall) { return 1 } if ($owner && $group) { run_program::rooted($prefix, "/bin/chown", "$owner.$group", $file) or die "Could not start chown!"; } elsif ($owner) { run_program::rooted($prefix, "/bin/chown", $owner, $file) or die "Could not start chown!"; } elsif ($group) { run_program::rooted($prefix, "/bin/chgrp", $group, $file) or die "Could not start chgrp!"; } run_program::rooted($prefix, "/bin/chmod", $perms, $file) or die "Could not start chmod!"; } sub restart_service ($) { my ($service) = @_; # Exit silently if the service is not installed return 1 if !(-x "$prefix/etc/rc.d/init.d/$service"); run_program::rooted($prefix, "/etc/rc.d/init.d/$service", "restart"); if (($? >> 8) != 0) { return 0; } else { # CUPS needs some time to come up. wait_for_cups() if $service eq "cups"; return 1; } } sub start_service ($) { my ($service) = @_; # Exit silently if the service is not installed return 1 if !(-x "$prefix/etc/rc.d/init.d/$service"); run_program::rooted($prefix, "/etc/rc.d/init.d/$service", "start"); if (($? >> 8) != 0) { return 0; } else { # CUPS needs some time to come up. wait_for_cups() if $service eq "cups"; return 1; } } sub start_not_running_service ($) { my ($service) = @_; # Exit silently if the service is not installed return 1 if !(-x "$prefix/etc/rc.d/init.d/$service"); run_program::rooted($prefix, "/etc/rc.d/init.d/$service", "status"); # The exit status is not zero when the service is not running if (($? >> 8) != 0) { run_program::rooted($prefix, "/etc/rc.d/init.d/$service", "start"); if (($? >> 8) != 0) { return 0; } else { # CUPS needs some time to come up. wait_for_cups() if $service eq "cups"; return 1; } } else { return 1; } } sub stop_service ($) { my ($service) = @_; # Exit silently if the service is not installed return 1 if !(-x "$prefix/etc/rc.d/init.d/$service"); run_program::rooted($prefix, "/etc/rc.d/init.d/$service", "stop"); if (($? >> 8) != 0) { return 0 } else { return 1 } } sub service_running ($) { my ($service) = @_; # Exit silently if the service is not installed return 0 if !(-x "$prefix/etc/rc.d/init.d/$service"); run_program::rooted($prefix, "/etc/rc.d/init.d/$service", "status"); # The exit status is not zero when the service is not running if (($? >> 8) != 0) { return 0; } else { return 1; } } sub service_starts_on_boot ($) { my ($service) = @_; local *F; open F, ($::testing ? $prefix : "chroot $prefix/ ") . "/bin/sh -c \"export LC_ALL=C; /sbin/chkconfig --list $service 2>&1\" |" or return 0; while (my $line = <F>) { chomp $line; if ($line =~ /:on/) { close F; return 1; } } close F; return 0; } sub start_service_on_boot ($) { my ($service) = @_; run_program::rooted($prefix, "/sbin/chkconfig", "--add", $service) or return 0; return 1; } sub SIGHUP_daemon { my ($service) = @_; if ($service eq "cupsd") { $service = "cups" }; # PDQ has no daemon, exit. if ($service eq "pdq") { 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 if ! defined $daemon; # if ($service eq "cups") { # # The current CUPS (1.1.13) dies on SIGHUP, do the normal restart. # restart_service($service); # # CUPS needs some time to come up. # 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. wait_for_cups(); } return 1; } sub wait_for_cups { # CUPS needs some time to come up. Wait up to 30 seconds, checking # whether CUPS is ready. my $cupsready = 0; my $i; for ($i = 0; $i < 30; $i++) { run_program::rooted($prefix, "/usr/bin/lpstat", "-r"); if (($? >> 8) != 0) { # CUPS is not ready, continue sleep 1; } else { # CUPS is ready, quit $cupsready = 1; last; } } return $cupsready; } 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 $result; for ($i = 0; $i < 3; $i++) { local *F; open 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 =~ /$device/) { # Found a line containing the device # name, so CUPS knows it. close F; return 1; } } close F; $result = SIGHUP_daemon("cups"); } return $result; } sub network_running { # If the network is not running return 0, otherwise 1. local *F; open F, ($::testing ? $prefix : "chroot $prefix/ ") . "/bin/sh -c \"export LC_ALL=C; /sbin/ifconfig\" |" or die "Could not run \"ifconfig\"!"; while (my $line = <F>) { if (($line !~ /^lo\s+/) && # The loopback device can have been # started by the spooler's startup script ($line =~ /^(\S+)\s+/)) { # In this line starts an entry for a # running network close F; return 1; } } close F; return 0; } sub getSNMPModel { my ($host) = @_; my $manufacturer = ""; my $model = ""; my $description = ""; my $serialnumber = ""; # SNMP request to auto-detect model local *F; open F, ($::testing ? $prefix : "chroot $prefix/ ") . "/bin/sh -c \"scli -1 -c 'show printer info' $host\" |" or return { CLASS => 'PRINTER', MODEL => N("Unknown Model"), MANUFACTURER => "", DESCRIPTION => "", SERIALNUMBER => "" }; while (my $l = <F>) { chomp $l; if (($l =~ /^\s*Manufacturer:\s*(\S.*)$/i) && ($l =~ /^\s*Vendor:\s*(\S.*)$/i)) { $manufacturer = $1; $manufacturer =~ s/Hewlett[-\s_]Packard/HP/; $manufacturer =~ s/HEWLETT[-\s_]PACKARD/HP/; } elsif ($l =~ /^\s*Model:\s*(\S.*)$/i) { $model = $1; } elsif ($l =~ /^\s*Description:\s*(\S.*)$/i) { $description = $1; $description =~ s/Hewlett[-\s_]Packard/HP/; $description =~ s/HEWLETT[-\s_]PACKARD/HP/; } elsif ($l =~ /^\s*Serial\s*Number:\s*(\S.*)$/i) { $serialnumber = $1; } } close F; # Was there a manufacturer and a model in the output? # If not, get them from the description if (($manufacturer eq "") || ($model eq "")) { if ($description =~ /^\s*(\S*)\s+(\S.*)$/) { if ($manufacturer eq "") { $manufacturer = $1; } if ($model eq "") { $model = $2; } } # No description field? Make one out of manufacturer and model. } elsif ($description eq "") { $description = "$manufacturer $model"; } # We couldn't determine a model if ($model eq "") { $model = N("Unknown Model"); } # Remove trailing spaces $manufacturer =~ s/(\S+)\s+$/$1/; $model =~ s/(\S+)\s+$/$1/; $description =~ s/(\S+)\s+$/$1/; $serialnumber =~ s/(\S+)\s+$/$1/; # Now we have all info for one printer # Store this auto-detection result in the data structure return { CLASS => 'PRINTER', MODEL => $model, MANUFACTURER => $manufacturer, DESCRIPTION => $description, SERIALNUMBER => $serialnumber }; } sub getSMBPrinterShares { my ($host) = @_; # SMB request to auto-detect shares local *F; open F, ($::testing ? "" : "chroot $prefix/ ") . "/bin/sh -c \"export LC_ALL=C; smbclient -N -L $host\" |" or return (); my $insharelist = 0; my @shares; while (my $l = <F>) { chomp $l; if ($l =~ /^\s*Sharename\s+Type\s+Comment\s*$/i) { $insharelist = 1; } elsif ($l =~ /^\s*Server\s+Comment\s*$/i) { $insharelist = 0; } elsif (($l =~ /^\s*(\S+)\s+Printer\s*(.*)$/i) && ($insharelist)) { my $name = $1; my $description = $2; $description =~ s/^(\s*)//; push (@shares, { name => $name, description => $description }); } } close F; return @shares; } sub getIPsInLocalNetworks { # subroutine determines the list of all hosts reachable in the local # networks by means of pinging the broadcast addresses. # Return an empty list if no network is running if (!network_running()) { return (); } # Read the output of "ifconfig" to determine the broadcast addresses of # the local networks my $dev_is_localnet = 0; my @local_bcasts; my $current_bcast = ""; local *IFCONFIG_OUT; open IFCONFIG_OUT, ($::testing ? "" : "chroot $prefix/ ") . "/bin/sh -c \"export LC_ALL=C; ifconfig\" |" or return (); while (my $readline = <IFCONFIG_OUT>) { # New entry ... if ($readline =~ /^(\S+)\s/) { my $dev = $1; # ... for a local network (eth = ethernet, # vmnet = VMWare, # ethernet card connected to ISP excluded)? $dev_is_localnet = (($dev =~ /^eth/) || ($dev =~ /^vmnet/)); # delete previous address $current_bcast = ""; } # Are we in the important line now? if ($readline =~ /\sBcast:([0-9]+\.[0-9]+\.[0-9]+\.[0-9]+)\s/) { # Rip out the broadcast IP address $current_bcast = $1; # Are we in an entry for a local network? if ($dev_is_localnet == 1) { # Store current IP address push @local_bcasts, $current_bcast; } } } close(IFCONFIG_OUT); my @addresses; # Now ping all broadcast addresses and additionally "nmblookup" the # networks (to find Windows servers which do not answer to ping) foreach my $bcast (@local_bcasts) { local *F; open F, ($::testing ? "" : "chroot $prefix/ ") . "/bin/sh -c \"export LC_ALL=C; ping -w 1 -b -n $bcast | cut -f 4 -d ' ' | sed s/:// | egrep '^[0-9]+\\.[0-9]+\\.[0-9]+\\.[0-9]+' | uniq | sort\" |" or next; local $_; while (<F>) { chomp; push @addresses, $_ } close F; if (-x "/usr/bin/nmblookup") { local *F; open F, ($::testing ? "" : "chroot $prefix/ ") . "/bin/sh -c \"export LC_ALL=C; nmblookup -B $bcast \\* | cut -f 1 -d ' ' | egrep '^[0-9]+\\.[0-9]+\\.[0-9]+\\.[0-9]+' | uniq | sort\" |" or next; local $_; while (<F>) { chomp; push @addresses, $_ if !(member($_,@addresses)) } } } @addresses; } sub whatNetPrinter { my ($network, $smb) = @_; my $i; my @res; # Which ports should be scanned? my @portstoscan; push @portstoscan, "139" if $smb; push @portstoscan, "4010", "4020", "4030", "5503", "9100-9104" if $network; return () if $#portstoscan < 0; my $portlist = join (",", @portstoscan); # Which hosts should be scanned? # (Applying nmap to a whole network is very time-consuming, because nmap # waits for a certain timeout period on non-existing hosts, so we get a # lists of existing hosts by pinging the broadcast addresses for existing # hosts and then scanning only them, which is much faster) my @hostips = getIPsInLocalNetworks(); return () if $#hostips < 0; my $hostlist = join (" ", @hostips); # Scan network for printers, the timeout settings are there to avoid # delays caused by machines blocking their ports with a firewall local *F; open F, ($::testing ? "" : "chroot $prefix/ ") . "/bin/sh -c \"export LC_ALL=C; nmap -r -P0 --host_timeout 400 --initial_rtt_timeout 200 -p $portlist $hostlist\" |" or return @res; my $host = ""; my $ip = ""; my $port = ""; my $modelinfo = ""; while (my $line = <F>) { chomp $line; # head line of the report of a host with the ports in question open #if ($line =~ m/^\s*Interesting\s+ports\s+on\s+(\S*)\s*\(([0-9]+\.[0-9]+\.[0-9]+\.[0-9]+)\)\s*:\s*$/i) { if ($line =~ m/^\s*Interesting\s+ports\s+on\s+(\S*)\s*\((\S+)\)\s*:\s*$/i) { $host = $1; $ip = $2; if ($host eq "") { $host = $ip; } $port = ""; undef $modelinfo; } elsif ($line =~ m/^\s*(\d+)\/\S+\s+open\s+/i) { next if $ip eq ""; $port = $1; # Now we have all info for one printer # Store this auto-detection result in the data structure # Determine the protocol by the port number # SMB/Windows if ($port eq "139") { my @shares = getSMBPrinterShares($ip); foreach my $share (@shares) { push @res, { port => "smb://$host/$share->{name}", val => { CLASS => 'PRINTER', MODEL => N("Unknown Model"), MANUFACTURER => "", DESCRIPTION => "$share->{description}", SERIALNUMBER => "" } }; } } else { if (!defined($modelinfo)) { # SNMP request to auto-detect model $modelinfo = getSNMPModel ($ip); } if (defined($modelinfo)) { push @res, { port => "socket://$host:$port", val => $modelinfo }; } } } } close F; @res; } 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; $file = "$prefix/etc/security/msec/server.$level"; if (-f $file) { local *F; open 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; $file = "$prefix/etc/security/msec/server.$level"; if (-f $file) { local *F; open F, ">> $file" or return 0; print F "$sp\n"; close F; } return 1; } sub files_exist { my @files = @_; foreach my $file (@files) { return 0 if (! -f "$prefix$file"), } return 1; } sub set_alternative { my ($command, $executable) = @_; local *F; # Read the list of executables for the given command to find the number # of the desired executable open F, ($::testing ? $prefix : "chroot $prefix/ ") . "/bin/sh -c \"export LC_ALL=C; /bin/echo | update-alternatives --config $command \" |" or die "Could not run \"update-alternatives\"!"; my $choice = 0; while (my $line = <F>) { chomp $line; if ($line =~ m/^[\* ][\+ ]\s*([0-9]+)\s+(\S+)\s*$/) { # list entry? if ($2 eq $executable) { $choice = $1; last; } } } close F; # If the executable was found, assign the command to it if ($choice > 0) { system(($::testing ? $prefix : "chroot $prefix/ ") . "/bin/sh -c \"/bin/echo $choice | update-alternatives --config $command > /dev/null 2>&1\""); } 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 = {}; my @QUEUES; set_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} ||= get_default_spooler(); if (!$printer->{SPOOLER}) { #- Find the first spooler where there are queues foreach my $spooler (qw(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_running($service)) { next; } # 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; 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; } } else { #- For other spoolers we have already found a running #- daemon when we have arrived here last; } } } else { #- Poll the queues of the current default spooler local *F; open F, ($::testing ? $prefix : "chroot $prefix/ ") . "foomatic-configure -P -q -s $printer->{SPOOLER} |" or die "Could not run foomatic-configure"; eval (join('',(<F>))); close F; } $printer->{configured} = {}; my $i; my $N = $#QUEUES + 1; for ($i = 0; $i < $N; $i++) { $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); $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 local *F; if (open 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'; } $printer->{configured}{$QUEUES[$i]{queuedata}{queue}}{queuedata}{driver} = 'CUPS/PPD'; $printer->{OLD_QUEUE} = ""; # Read out the printer's options $printer->{configured}{$QUEUES[$i]{queuedata}{queue}}{args} = read_cups_options($QUEUES[$i]{queuedata}{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 = $shortspooler_inv{$printer->{SPOOLER}}{short_name}; my $connect = $printer->{configured}{$queue}{queuedata}{connect}; my $localremote; if (($connect =~ m!^file:!) || ($connect =~ m!^ptal:/mlc:!)) { $localremote = N("Local Printers"); } else { $localremote = N("Remote Printers"); } my $make = $printer->{configured}{$queue}{queuedata}{make}; my $model = $printer->{configured}{$queue}{queuedata}{model}; my $connection; if ($connect =~ m!^file:/dev/lp(\d+)$!) { my $number = $1; $connection = N(" on parallel port \#%s", $number); } elsif ($connect =~ m!^file:/dev/usb/lp(\d+)$!) { my $number = $1; $connection = N(", USB printer \#%s", $number); } 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: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:(.+)$!) { $connection = N(", printing to %s", $1); } elsif ($connect =~ m!^lpd://([^/]+)/([^/]+)/?$!) { $connection = N(" on LPD server \"%s\", printer \"%s\"", $2, $1); } elsif ($connect =~ m!^socket://([^/:]+):([^/:]+)/?$!) { $connection = N(", TCP/IP host \"%s\", port %s", $1, $2); } elsif (($connect =~ m!^smb://([^/\@]+)/([^/\@]+)/?$!) || ($connect =~ m!^smb://.*/([^/\@]+)/([^/\@]+)/?$!) || ($connect =~ m!^smb://.*\@([^/\@]+)/([^/\@]+)/?$!)) { $connection = N(" on SMB/Windows server \"%s\", share \"%s\"", $1, $2); } elsif (($connect =~ m!^ncp://([^/\@]+)/([^/\@]+)/?$!) || ($connect =~ m!^ncp://.*/([^/\@]+)/([^/\@]+)/?$!) || ($connect =~ m!^ncp://.*\@([^/\@]+)/([^/\@]+)/?$!)) { $connection = N(" on Novell server \"%s\", printer \"%s\"", $1, $2); } elsif ($connect =~ m!^postpipe:(.+)$!) { $connection = N(", using command %s", $1); } else { $connection = ($::expert ? ", URI: $connect" : ""); } my $sep = "!"; $printer->{configured}{$queue}{queuedata}{menuentry} = ($::expert ? "$spooler$sep" : "") . "$localremote$sep$queue: $make $model$connection"; } sub read_printer_db(;$) { my $spooler = $_[0]; my $dbpath = $prefix . $PRINTER_DB_FILE; 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 if (!(-f $dbpath)) { open DBPATH, ($::testing ? $prefix : "chroot $prefix/ ") . #-# "foomatic-configure -O -q |" or die "Could not run foomatic-configure"; } else { open DBPATH, $dbpath or die "An error occurred on $dbpath : $!"; #-# } my $entry = {}; my $inentry = 0; my $indrivers = 0; my $inautodetect = 0; 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 ($inautodetect) { # We are inside the autodetect block of a printers entry # All entries inside this block will be ignored if (m!^.*</autodetect>\s*$!) { # End of autodetect block $inautodetect = 0; } } 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 ($::expert) { foreach my $driver (@{$entry->{drivers}}) { my $driverstr; if ($driver eq "Postscript") { $driverstr = "PostScript"; } else { $driverstr = "GhostScript + $driver"; } if ($driver eq $entry->{defaultdriver}) { $driverstr .= " (recommended)"; } $entry->{ENTRY} = "$entry->{make}|$entry->{model}|$driverstr"; $entry->{driver} = $driver; # 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}"; if ($entry->{defaultdriver}) { $entry->{driver} = $entry->{defaultdriver}; map { $thedb{$entry->{ENTRY}}{$_} = $entry->{$_} } keys %$entry; } } $entry = {}; } 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; @{$entry->{drivers}} = (); } elsif (m!^\s*<autodetect>\s*$!) { # Autodetect block $inautodetect = 1; } } } else { if (m!^\s*<printer>\s*$!) { # new entry $inentry = 1; } } } close DBPATH; # Add raw queue if ($spooler ne "pdq") { $entry->{ENTRY} = N("Raw printer (No driver)"); $entry->{driver} = "raw"; $entry->{make} = ""; $entry->{model} = N("Unknown model"); map { $thedb{$entry->{ENTRY}}{$_} = $entry->{$_} } keys %$entry; } #- Load CUPS driver database if CUPS is used as spooler if (($spooler) && ($spooler eq "cups") && ($::expert)) { #&$install('cups-drivers') unless $::testing; #my $w; #if ($in) { # $w = $in->wait_message(N("CUPS starting"), # N("Reading CUPS drivers database...")); #} poll_ppd_base(); } @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; } 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/ ") . "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_cups_options ($) { my ($queue_or_file) = @_; # Generate the option data from a CUPS PPD file/a CUPS queue # Use the same Perl data structure as Foomatic uses to be able to # reuse the dialog local *F; if ($queue_or_file =~ /.ppd.gz$/) { # compressed PPD file open F, ($::testing ? $prefix : "chroot $prefix/ ") . #-# "gunzip -cd $queue_or_file | lphelp - |" or return 0; } else { # PPD file not compressed or queue open F, ($::testing ? $prefix : "chroot $prefix/ ") . #-# "lphelp $queue_or_file |" or return 0; } my $i; my $j; my @args; my $line; my $inoption = 0; my $inchoices = 0; # my $innumerical = 0; while ($line = <F>) { chomp $line; if ($inoption) { if ($inchoices) { if ($line =~ /^\s*(\S+)\s+(\S.*)$/) { push(@{$args[$i]{vals}}, {}); $j = $#{$args[$i]{vals}}; $args[$i]{vals}[$j]{value} = $1; my $comment = $2; # Did we find the default setting? if ($comment =~ /default\)\s*$/) { $args[$i]{default} = $args[$i]{vals}[$j]{value}; $comment =~ s/,\s*default\)\s*$//; } else { $comment =~ s/\)\s*$//; } # Remove opening paranthese $comment =~ s/^\(//; # Remove page size info $comment =~ s/,\s*size:\s*[0-9\.]+x[0-9\.]+in$//; $args[$i]{vals}[$j]{comment} = $comment; } elsif (($line =~ /^\s*$/) && ($#{$args[$i]{vals}} > -1)) { $inchoices = 0; $inoption = 0; } # } elsif ($innumerical == 1) { # if ($line =~ /^\s*The default value is ([0-9\.]+)\s*$/) { # $args[$i]{default} = $1; # $innumerical = 0; # $inoption = 0; # } } else { if ($line =~ /^\s*<choice>/) { $inchoices = 1; # } elsif ($line =~ /^\s*<value> must be a(.*) number in the range ([0-9\.]+)\.\.([0-9\.]+)\s*$/) { # delete($args[$i]{vals}); # $args[$i]{min} = $2; # $args[$i]{max} = $3; # my $type = $1; # if ($type =~ /integer/) { # $args[$i]{type} = 'int'; # } else { # $args[$i]{type} = 'float'; # } # $innumerical = 1; } } } else { if ($line =~ /^\s*([^\s:][^:]*):\s+-o\s+([^\s=]+)=<choice>\s*$/) { # if ($line =~ /^\s*([^\s:][^:]*):\s+-o\s+([^\s=]+)=<.*>\s*$/) { $inoption = 1; push(@args, {}); $i = $#args; $args[$i]{comment} = $1; $args[$i]{name} = $2; $args[$i]{type} = 'enum'; @{$args[$i]{vals}} = (); } } } close F; # Return the arguments field return \@args; } sub set_cups_special_options { my ($queue) = $_[0]; # 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 (!grep { /$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"); } # Let images fill the whole page by default if (!grep { /$queue.*\s(scaling|natural-scaling|ppi)=/ } @lpoptions) { run_program::rooted($prefix, "lpoptions", "-p", $queue, "-o", "scaling=100"); } return 1; } #------------------------------------------------------------------------------ sub read_cups_printer_list { my ($printer) = $_[0]; # This function reads in a list of all printers which the local CUPS # daemon currently knows, including remote ones. local *F; open F, ($::testing ? $prefix : "chroot $prefix/ ") . "lpstat -v |" or return (); my @printerlist; my $line; while ($line = <F>) { if ($line =~ m/^\s*device\s+for\s+([^:\s]+):\s*(\S+)\s*$/) { my $queuename = $1; my $comment = ""; if (($2 =~ m!^ipp://([^/:]+)[:/]!) && (!$printer->{configured}{$queuename})) { $comment = N("(on %s)", $1); } else { $comment = N("(on this machine)"); } push (@printerlist, "$queuename $comment"); } } close F; return @printerlist; } sub get_cups_remote_queues { my ($printer) = $_[0]; # This function reads in a list of all remote printers which the local # CUPS daemon knows due to broadcasting of remote servers or # "BrowsePoll" entries in the local /etc/cups/cupsd.conf/ local *F; open F, ($::testing ? $prefix : "chroot $prefix/ ") . "lpstat -v |" or return (); my @printerlist; my $line; while ($line = <F>) { if ($line =~ m/^\s*device\s+for\s+([^:\s]+):\s*(\S+)\s*$/) { my $queuename = $1; my $comment = ""; if (($2 =~ m!^ipp://([^/:]+)[:/]!) && (!$printer->{configured}{$queuename})) { $comment = N("On CUPS server \"%s\"", $1); my $sep = "!"; push (@printerlist, ($::expert ? N("CUPS") . $sep : "") . N("Remote Printers") . "$sep$queuename: $comment" . ($queuename eq $printer->{DEFAULT} ? N(" (Default)") : (""))); } } } close F; return @printerlist; } sub set_cups_autoconf { my $autoconf = $_[0]; # Read config file my $file = "$prefix/etc/sysconfig/printing"; @file_content = cat_($file); # Remove all valid "CUPS_CONFIG" lines (/^\s*CUPS_CONFIG/ and $_ = "") foreach @file_content; # Insert the new "CUPS_CONFIG" line if ($autoconf) { push @file_content, "CUPS_CONFIG=automatic\n"; } else { push @file_content, "CUPS_CONFIG=manual\n"; } output($file, @file_content); # Restart CUPS restart_service("cups"); return 1; } sub get_cups_autoconf { local *F; open F, "< $prefix/etc/sysconfig/printing" or return 1; my $line; while ($line = <F>) { if ($line =~ m!^[^\#]*CUPS_CONFIG=manual!) { return 0; } } return 1; } sub set_usermode { my $usermode = $_[0]; $::expert = $usermode; # Read config file my $file = "$prefix/etc/sysconfig/printing"; @file_content = cat_($file); # Remove all valid "USER_MODE" lines (/^\s*USER_MODE/ and $_ = "") foreach @file_content; # Insert the new "USER_MODE" line if ($usermode) { push @file_content, "USER_MODE=expert\n"; } else { push @file_content, "USER_MODE=recommended\n"; } output($file, @file_content); return 1; } sub get_usermode { local *F; open F, "< $prefix/etc/sysconfig/printing" or return 0; my $line; while ($line = <F>) { if ($line =~ m!^[^\#]*USER_MODE=expert!) { $::expert = 1; return 1; } } $::expert = 0; return 0; } sub set_default_printer { my ($printer) = $_[0]; run_program::rooted($prefix, "foomatic-configure", "-D", "-q", "-s", $printer->{SPOOLER}, "-n", $printer->{DEFAULT}) or return 0; return 1; } sub get_default_printer { my $printer = $_[0]; local *F; open F, ($::testing ? $prefix : "chroot $prefix/ ") . "foomatic-configure -Q -q -s $printer->{SPOOLER} |" or return undef; my $line; while ($line = <F>) { if ($line =~ m!^\s*<defaultqueue>(.*)</defaultqueue>\s*$!) { return $1; } } return undef; } sub read_cupsd_conf { cat_("$prefix/etc/cups/cupsd.conf"); } sub write_cupsd_conf { my (@cupsd_conf) = @_; output("$prefix/etc/cups/cupsd.conf", @cupsd_conf); #- restart cups after updating configuration. restart_service("cups"); } 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 local *PRINTERS; open 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 (/\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; local *F; open 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 get_descr_from_ppd { my ($printer) = @_; my %ppd; #- if there is no ppd, this means this is a raw queue. local *F; open F, "$prefix/etc/cups/ppd/$printer->{OLD_QUEUE}.ppd" or return "|" . N("Unknown model"); # "OTHERS|Generic PostScript printer|PostScript (en)"; local $_; while (<F>) { /^\*([^\s:]*)\s*:\s*\"([^\"]*)\"/ and do { $ppd{$1} = $2; next }; /^\*([^\s:]*)\s*:\s*([^\s\"]*)/ and do { $ppd{$1} = $2; next }; } close F; my $descr = ($ppd{NickName} || $ppd{ShortNickName} || $ppd{ModelName}); # Apply the beautifying rules of poll_ppd_base if ($descr =~ /Foomatic \+ Postscript/) { $descr =~ s/Foomatic \+ Postscript/PostScript/; } elsif ($descr =~ /Foomatic/) { $descr =~ s/Foomatic/GhostScript/; } elsif ($descr =~ /CUPS\+GIMP-print/) { $descr =~ s/CUPS\+GIMP-print/CUPS \+ GIMP-Print/; } elsif ($descr =~ /Series CUPS/) { $descr =~ s/Series CUPS/Series, CUPS/; } elsif (!(uc($descr) =~ /POSTSCRIPT/)) { $descr .= ", PostScript"; } # Split the $descr into model and driver my $model; my $driver; if ($descr =~ /^([^,]+), (.*)$/) { $model = $1; $driver = $2; } else { # Some PPDs do not have the ", <driver>" part. $model = $descr; $driver = "PostScript"; } my $make = $ppd{Manufacturer}; my $lang = $ppd{LanguageVersion}; # Remove manufacturer's name from the beginning of the model name if (($make) && ($model =~ /^$make[\s\-]+([^\s\-].*)$/)) { $model = $1; } # Put out the resulting description string uc($make) . '|' . $model . '|' . $driver . ($lang && (" (" . lc(substr($lang, 0, 2)) . ")")); } sub poll_ppd_base { #- 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 :-) run_program::rooted($prefix, "ifconfig lo 127.0.0.1"); #- else cups will not be happy! and ifup lo don't run ? start_not_running_service("cups"); my $driversthere = scalar(keys %thedb); foreach (1..60) { local *PPDS; open 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 } my ($model, $driver); if ($descr) { if ($descr =~ /^([^,]+), (.*)$/) { $model = $1; $driver = $2; } else { # Some PPDs do not have the ", <driver>" part. $model = $descr; $driver = "PostScript"; } } # 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-/; } $ppd && $mf && $descr and do { my $key = "$mf|$model|$driver" . ($lang && " ($lang)"); $thedb{$key}{ppd} = $ppd; $thedb{$key}{driver} = $driver; $thedb{$key}{make} = $mf; $thedb{$key}{model} = $model; } } 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"; } #-****************************************************************************** #- write functions #-****************************************************************************** sub configure_queue($) { my ($printer) = @_; if ($printer->{currentqueue}{foomatic}) { #- Create the queue with "foomatic-configure", in case of queue #- renaming copy the old queue 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", $printer->{currentqueue}{connect}, "-p", $printer->{currentqueue}{printer}, "-d", $printer->{currentqueue}{driver}, "-N", $printer->{currentqueue}{desc}, "-L", $printer->{currentqueue}{loc}, @{$printer->{currentqueue}{options}} ) or die "foomatic-configure failed"; } elsif ($printer->{currentqueue}{ppd}) { #- If the chosen driver is a PPD file from /usr/share/cups/model, #- we use lpadmin to set up the queue run_program::rooted($prefix, "lpadmin", "-p", $printer->{currentqueue}{queue}, # $printer->{State} eq 'Idle' && # $printer->{Accepting} eq 'Yes' ? ("-E") : (), "-E", "-v", $printer->{currentqueue}{connect}, ($printer->{currentqueue}{ppd} ne '1') ? ("-m", $printer->{currentqueue}{ppd}) : (), $printer->{currentqueue}{desc} ? ("-D", $printer->{currentqueue}{desc}) : (), $printer->{currentqueue}{loc} ? ("-L", $printer->{currentqueue}{loc}) : (), @{$printer->{currentqueue}{options}} ) or die "lpadmin failed"; # 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') { local *F; open F, ">> $prefix/etc/cups/ppd/$printer->{currentqueue}{queue}.ppd"; print F "*%MDKMODELCHOICE:$printer->{currentqueue}{ppd}\n"; } # Copy the old queue's PPD file to the new queue when it is renamed, # to conserve the option settings if (($printer->{currentqueue}{queue} ne $printer->{OLD_QUEUE}) && ($printer->{configured}{$printer->{OLD_QUEUE}})) { system("cp -f $prefix/etc/cups/ppd/$printer->{OLD_QUEUE}.ppd $prefix/etc/cups/ppd/$printer->{currentqueue}{queue}.ppd"); } } else { # Raw queue run_program::rooted($prefix, "foomatic-configure", "-q", "-s", $printer->{currentqueue}{spooler}, "-n", $printer->{currentqueue}{queue}, "-c", $printer->{currentqueue}{connect}, "-d", $printer->{currentqueue}{driver}, "-N", $printer->{currentqueue}{desc}, "-L", $printer->{currentqueue}{loc} ) or die "foomatic-configure failed"; } # Make sure that queue is active if ($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}}) { $useUSB ||= $_->{queuedata}{connect} =~ /usb/ || $_->{DeviceURI} =~ /usb/; } $useUSB ||= ($printer->{currentqueue}{queue}{queuedata}{connect} =~ /usb/); 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:(\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} = {}; if ($printer->{currentqueue}{foomatic}) { my $tmp = $printer->{OLD_QUEUE}; $printer->{OLD_QUEUE} = $printer->{currentqueue}{queue}; $printer->{configured}{$printer->{currentqueue}{queue}}{args} = read_foomatic_options($printer); $printer->{OLD_QUEUE} = $tmp; } elsif ($printer->{currentqueue}{ppd}) { $printer->{configured}{$printer->{currentqueue}{queue}}{args} = read_cups_options($printer->{currentqueue}{queue}); } # Clean up delete($printer->{ARGS}); $printer->{OLD_CHOICE} = ""; $printer->{ARGS} = {}; $printer->{DBENTRY} = ""; $printer->{currentqueue} = {}; } sub remove_queue($$) { my ($printer) = $_[0]; my ($queue) = $_[1]; 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} = {}; removeprinterfromapplications($printer, $queue); } sub restart_queue($) { my ($printer) = @_; my $queue = $printer->{QUEUE}; # Restart the daemon(s) foreach ($printer->{SPOOLER}) { /cups/ && do { #- restart cups. restart_service("cups"); last }; /lpr|lprng/ && 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$_"; } restart_service("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"; # 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 ($printer->{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"); } 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}, "-P", $queue, "-o", "scaling=90", $page); } } else { run_program::rooted($prefix, $lpr, "-s", $printer->{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 |"; 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}; local *F; open F, ($::testing ? $prefix : "chroot $prefix/ ") . sprintf($spoolers{$spooler}{help}, $queue); $helptext = join("", <F>); close F; $helptext = "Option list not available!\n" if $spooler eq 'lpq' && (!$helptext || ($helptext eq "")); 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) = @_; 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 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:/) || ($entry->{connect} =~ /^ptal:/) || ($entry->{connect} =~ /^lpd:/) || ($entry->{connect} =~ /^socket:/) || ($entry->{connect} =~ /^smb:/) || ($entry->{connect} =~ /^ipp:/))) || ((($newspooler eq "lpd") || ($newspooler eq "lprng")) && (($entry->{connect} =~ /^file:/) || ($entry->{connect} =~ /^ptal:/) || ($entry->{connect} =~ /^lpd:/) || ($entry->{connect} =~ /^socket:/) || ($entry->{connect} =~ /^smb:/) || ($entry->{connect} =~ /^ncp:/) || ($entry->{connect} =~ /^postpipe:/))) || (($newspooler eq "pdq") && (($entry->{connect} =~ /^file:/) || ($entry->{connect} =~ /^ptal:/) || ($entry->{connect} =~ /^lpd:/) || ($entry->{connect} =~ /^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($newqueue); } } # ------------------------------------------------------------------ # # Configuration of HP multi-function devices # # ------------------------------------------------------------------ sub configure_hpoj { my ($device, @autodetected) = @_; # 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!^[^\#]!) { # 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; } push (@ptalinitfunctions, $_); } } close PTALINIT; eval "@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 setupVariables (); 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 = ""; my $serialnumber = ""; my $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 =~ /\/dev\/lp/) || ($device =~ /printers/)) { $bus = "par"; $address_arg = parport_addr($device); $address_arg =~ /^\s*-base\s+(\S+)/; eval ("$base_address = $1"); } elsif ($device =~ /socket/) { $bus = "hpjd"; $hostname = $model; return "" if $port && ($port < 9100 || $port > 9103); if ($port && $port != 9100) { $port -= 9100; $hostname .= ":$port"; } } else { return ""; } 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}; # Check if the device is really an HP multi-function device if ($bus ne "hpjd") { # Start ptal-mlcd daemon for locally connected devices stop_service("hpoj"); run_program::rooted($prefix, "ptal-mlcd", "$bus:probe", "-device", $device, split(' ',$address_arg)); } $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; } } } 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; } if (cardReaderDetected ($ptalprobedevice)) { $cardreader = 1; } } } if ($bus ne "hpjd") { # Stop ptal-mlcd daemon for locally connected devices local *F; if (open F, ($::testing ? $prefix : "chroot $prefix/ ") . "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; } start_service("hpoj"); } last; } # No, it is not an HP multi-function device. return "" if !$device_ok; # Determine the ptal device name from already existing config files my $ptalprefix = ($bus eq "hpjd" ? "hpjd:" : "mlc:$bus:"); my $ptaldevice = 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 deleteDevice ($ptaldevice); if ($bus eq "par") { while (1) { my $oldDevname = lookupDevname ("mlc:par:",undef,undef, $base_address); if (!defined($oldDevname)) { last; } deleteDevice ($oldDevname); } } # Configure the device # Open configuration file local *CONFIG; open(CONFIG,"> $prefix/etc/ptal/$ptaldevice") or die "Could not open /etc/ptal/$ptaldevice for writing!\n"; # Write file header. $_ = `date`; chomp; print CONFIG "# Added $_ by \"printerdrake\".\n". "\n". "# The basic format for this file is \"key[+]=value\".\n". "# If you say \"+=\" instead of \"=\", then the value is appended to any\n". "# value already defined for this key, rather than replacing it.\n". "\n". "# Comments must start at the beginning of the line. Otherwise, they may\n". "# be interpreted as being part of the value.\n". "\n". "# If you have multiple devices and want to define options that apply to\n". "# all of them, then put them in the file /etc/ptal/defaults, which is read\n". "# in before this file.\n". "\n". "# The format version of this file:\n". "# ptal-init ignores devices with incorrect/missing versions.\n". "init.version=1\n"; # Write model string. if ($model_long !~ /\S/) { print CONFIG "\n". "# \"printerdrake\" couldn't read the model but added this device anyway:\n". "# "; } else { print CONFIG "\n". "# The device model that was originally detected on this port:\n". "# 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"; } } print CONFIG "init.mlcd.append+=-devidmatch \"$model_long\"\n"; # 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"; } } print CONFIG "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/lp[0-9]*"; } elsif ($bus eq "par") { print CONFIG "$address_arg -device $device"; } 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". "# 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". "# then change the line below to use the \"-portoffset <n>\" option.\n". "init.photod.append+=-maxaltports 26\n"; } close(CONFIG); readOneDevice ($ptaldevice); # Restart HPOJ restart_service("hpoj"); # Return HPOJ device name to form the URI return $ptaldevice; } sub parport_addr { # auto-detect the parallel port addresses my ($device) = @_; $device =~ m!^/dev/lp(\d+)$! or $device =~ m!^/dev/printers/(\d+)$!; my $portnumber = $1; my $parport_addresses = `cat /proc/sys/dev/parport/parport$portnumber/base-addr`; my $address_arg; if ($parport_addresses =~ /^\s*(\d+)\s+(\d+)\s*$/) { $address_arg = sprintf(" -base 0x%x -basehigh 0x%x", $1, $2); } elsif ($parport_addresses =~ /^\s*(\d+)\s*$/) { $address_arg = sprintf(" -base 0x%x", $1); } else { $address_arg = ""; } return $address_arg; } 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"))); local *F; open F, ">> $prefix/etc/sane.d/dll.conf" or die "can't write SANE config in /etc/sane.d/dll.conf: $!"; print F "hpoj\n"; close F; } sub config_photocard { # Add definitions for the drives p:. q:, r:, and s: to /etc/mtools.conf my $mtoolsconf = join("", cat_("$prefix/etc/mtools.conf")); return if $mtoolsconf =~ m/^\s*drive\s+p:/m; my $mtoolsconf_append = " # 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 "; local *F; open F, ">> $prefix/etc/mtools.conf" or die "can't write mtools config in /etc/mtools.conf: $!"; print F $mtoolsconf_append; close F; # 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: $!"; $mtoolsfmconf =~ m/^\s*DRIVES\s*=\s*\"([A-Za-z ]*)\"/m; my $alloweddrives = lc($1); foreach my $letter ("p", "q", "r", "s") { if ($alloweddrives !~ /$letter/) { $alloweddrives .= $letter; } } $mtoolsfmconf =~ s/^\s*DRIVES\s*=\s*\"[A-Za-z ]*\"/DRIVES=\"$alloweddrives\"/m; $mtoolsfmconf =~ s/^\s*LEFTDRIVE\s*=\s*\"[^\"]*\"/LEFTDRIVE=\"p\"/m; } else { $mtoolsfmconf = "\# 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=\" \" "; } output("$prefix/etc/mtoolsfm.conf", $mtoolsfmconf); } # ------------------------------------------------------------------ # # Configuration of printers in applications # # ------------------------------------------------------------------ sub configureapplications { my ($printer) = @_; setcupslink ($printer); configurestaroffice($printer); configureopenoffice($printer); configuregimp($printer); } sub addcupsremotetoapplications { my ($printer, $queue) = @_; setcupslink ($printer); return (addcupsremotetostaroffice($printer, $queue) && addcupsremotetoopenoffice($printer, $queue) && addcupsremotetogimp($printer, $queue)); } sub removeprinterfromapplications { my ($printer, $queue) = @_; setcupslink ($printer); return (removeprinterfromstaroffice($printer, $queue) && removeprinterfromopenoffice($printer, $queue) && removeprinterfromgimp($printer, $queue)); } sub removelocalprintersfromapplications { my ($printer) = @_; setcupslink ($printer); removelocalprintersfromstaroffice($printer); removelocalprintersfromopenoffice($printer); removelocalprintersfromgimp($printer); } sub setcupslink { my ($printer) = @_; return 1 if !$::isInstall; return 1 if $printer->{SPOOLER} ne "cups"; return 1 if -d "/etc/cups/ppd"; system("ln -sf $prefix/etc/cups /etc/cups"); return 1; } sub getcupsremotequeues { # The following code reads in a list of all remote printers which the # local CUPS daemon knows due to broadcasting of remote servers or # "BrowsePoll" entries in the local /etc/cups/cupsd.conf local *F; open F, ($::testing ? $prefix : "chroot $prefix/ ") . "lpstat -v |" or return (); my @printerlist; my $line; while ($line = <F>) { if ($line =~ m/^\s*device\s+for\s+([^:\s]+):\s*(\S+)\s*$/) { my $queuename = $1; if (($2 =~ m!^ipp://([^/:]+)[:/]!) && (!$printer->{configured}{$queuename})) { my $server = $1; push (@printerlist, "$queuename|$server"); } } } close F; return @printerlist; } # ------------------------------------------------------------------ # Star Offica/OpenOffice.org # ------------------------------------------------------------------ sub configurestaroffice { my ($printer) = @_; # Do we have Star Office installed? my $configfilename = findsofficeconfigfile(); return 1 if !$configfilename; $configfilename =~ m!^(.*)/share/xp3/Xpdefaults$!; my $configprefix = $1; # Load Star Office printer config file my $configfilecontent = readsofficeconfigfile($configfilename); # Update remote CUPS queues if (0 && ($printer->{SPOOLER} eq "cups") && ((-x "$prefix/usr/bin/curl") || (-x "$prefix/usr/bin/wget"))) { my @printerlist = getcupsremotequeues(); foreach my $listentry (@printerlist) { next if !($listentry =~ /^([^\|]+)\|([^\|]+)$/); my $queue = $1; my $server = $2; if (-x "$prefix/usr/bin/wget") { eval(run_program::rooted ($prefix, "/usr/bin/wget", "-O", "/etc/foomatic/$queue.ppd", "http://$server:631/printers/$queue.ppd")); } else { eval(run_program::rooted ($prefix, "/usr/bin/curl", "-o", "/etc/foomatic/$queue.ppd", "http://$server:631/printers/$queue.ppd")); } if (-r "$prefix/etc/foomatic/$queue.ppd") { $configfilecontent = makestarofficeprinterentry($printer, $queue, $configprefix, $configfilecontent); } } } # Update local printer queues foreach my $queue (keys(%{$printer->{configured}})) { # Check if we have a PPD file if (! -r "$prefix/etc/foomatic/$queue.ppd") { if (-r "$prefix/etc/cups/ppd/$queue.ppd") { # If we have a PPD file in the CUPS config dir, link to it run_program::rooted($prefix, "ln", "-sf", "/etc/cups/ppd/$queue.ppd", "/etc/foomatic/$queue.ppd"); } elsif (-r "$prefix/usr/share/postscript/ppd/$queue.ppd") { # Check PPD directory of GPR, too run_program::rooted($prefix, "ln", "-sf", "/usr/share/postscript/ppd/$queue.ppd", "/etc/foomatic/$queue.ppd"); } else { # No PPD file at all? We cannot set up this printer next; } } $configfilecontent = makestarofficeprinterentry($printer, $queue, $configprefix, $configfilecontent); } # Patch PostScript output to print Euro symbol correctly also for # the "Generic Printer" $configfilecontent = removeentry ("ports", "default_queue=", $configfilecontent); $configfilecontent = addentry ("ports", "default_queue=/usr/bin/perl -p -e \"s=16#80 /euro=16#80 /Euro=\" | /usr/bin/$lprcommand{$printer->{SPOOLER}{print_command}}", $configfilecontent); # Write back Star Office configuration file return writesofficeconfigfile($configfilename, $configfilecontent); } sub configureopenoffice { my ($printer) = @_; # Do we have OpenOffice.org installed? my $configfilename = findopenofficeconfigfile(); return 1 if !$configfilename; $configfilename =~ m!^(.*)/share/psprint/psprint.conf$!; my $configprefix = $1; # Load OpenOffice.org printer config file my $configfilecontent = readsofficeconfigfile($configfilename); # Update remote CUPS queues if (0 && ($printer->{SPOOLER} eq "cups") && ((-x "$prefix/usr/bin/curl") || (-x "$prefix/usr/bin/wget"))) { my @printerlist = getcupsremotequeues(); foreach my $listentry (@printerlist) { next if !($listentry =~ /^([^\|]+)\|([^\|]+)$/); my $queue = $1; my $server = $2; if (-x "$prefix/usr/bin/wget") { eval(run_program::rooted ($prefix, "/usr/bin/wget", "-O", "/etc/foomatic/$queue.ppd", "http://$server:631/printers/$queue.ppd")); } else { eval(run_program::rooted ($prefix, "/usr/bin/curl", "-o", "/etc/foomatic/$queue.ppd", "http://$server:631/printers/$queue.ppd")); } if (-r "$prefix/etc/foomatic/$queue.ppd") { $configfilecontent = makeopenofficeprinterentry($printer, $queue, $configprefix, $configfilecontent); } } } # Update local printer queues foreach my $queue (keys(%{$printer->{configured}})) { # Check if we have a PPD file if (! -r "$prefix/etc/foomatic/$queue.ppd") { if (-r "$prefix/etc/cups/ppd/$queue.ppd") { # If we have a PPD file in the CUPS config dir, link to it run_program::rooted($prefix, "ln", "-sf", "/etc/cups/ppd/$queue.ppd", "/etc/foomatic/$queue.ppd"); } elsif (-r "$prefix/usr/share/postscript/ppd/$queue.ppd") { # Check PPD directory of GPR, too run_program::rooted($prefix, "ln", "-sf", "/usr/share/postscript/ppd/$queue.ppd", "/etc/foomatic/$queue.ppd"); } else { # No PPD file at all? We cannot set up this printer next; } } $configfilecontent = makeopenofficeprinterentry($printer, $queue, $configprefix, $configfilecontent); } # Patch PostScript output to print Euro symbol correctly also for # the "Generic Printer" $configfilecontent = removeentry ("Generic Printer", "Command=", $configfilecontent); $configfilecontent = addentry ("Generic Printer", "Command=/usr/bin/perl -p -e \"s=/euro /unused=/Euro /unused=\" | /usr/bin/$lprcommand{$printer->{SPOOLER}{print_command}}", $configfilecontent); # Write back OpenOffice.org configuration file return writesofficeconfigfile($configfilename, $configfilecontent); } sub addcupsremotetostaroffice { my ($printer, $queue) = @_; # Do we have Star Office installed? my $configfilename = findsofficeconfigfile(); return 1 if !$configfilename; $configfilename =~ m!^(.*)/share/xp3/Xpdefaults$!; my $configprefix = $1; # Load Star Office printer config file my $configfilecontent = readsofficeconfigfile($configfilename); # Update remote CUPS queues if (($printer->{SPOOLER} eq "cups") && ((-x "$prefix/usr/bin/curl") || (-x "$prefix/usr/bin/wget"))) { my @printerlist = getcupsremotequeues(); foreach my $listentry (@printerlist) { next if !($listentry =~ /^([^\|]+)\|([^\|]+)$/); my $q = $1; next if $q ne $queue; my $server = $2; # Remove server name from queue name $q =~ s/^([^@]*)@.*$/$1/; if (-x "$prefix/usr/bin/wget") { eval(run_program::rooted ($prefix, "/usr/bin/wget", "-O", "/etc/foomatic/$queue.ppd", "http://$server:631/printers/$q.ppd")); } else { eval(run_program::rooted ($prefix, "/usr/bin/curl", "-o", "/etc/foomatic/$queue.ppd", "http://$server:631/printers/$q.ppd")); } # Does the file exist and is it not an error message? if ((-r "$prefix/etc/foomatic/$queue.ppd") && (cat_("$prefix/etc/foomatic/$queue.ppd") =~ /^\*PPD-Adobe/)) { $configfilecontent = makestarofficeprinterentry($printer, $queue, $configprefix, $configfilecontent); } else { unlink ("$prefix/etc/foomatic/$queue.ppd"); return 0; } last; } } # Write back Star Office configuration file return writesofficeconfigfile($configfilename, $configfilecontent); } sub addcupsremotetoopenoffice { my ($printer, $queue) = @_; # Do we have OpenOffice.org installed? my $configfilename = findopenofficeconfigfile(); return 1 if !$configfilename; $configfilename =~ m!^(.*)/share/psprint/psprint.conf$!; my $configprefix = $1; # Load OpenOffice.org printer config file my $configfilecontent = readsofficeconfigfile($configfilename); # Update remote CUPS queues if (($printer->{SPOOLER} eq "cups") && ((-x "$prefix/usr/bin/curl") || (-x "$prefix/usr/bin/wget"))) { my @printerlist = getcupsremotequeues(); foreach my $listentry (@printerlist) { next if !($listentry =~ /^([^\|]+)\|([^\|]+)$/); my $q = $1; next if $q ne $queue; my $server = $2; # Remove server name from queue name $q =~ s/^([^@]*)@.*$/$1/; if (-x "$prefix/usr/bin/wget") { eval(run_program::rooted ($prefix, "/usr/bin/wget", "-O", "/etc/foomatic/$queue.ppd", "http://$server:631/printers/$q.ppd")); } else { eval(run_program::rooted ($prefix, "/usr/bin/curl", "-o", "/etc/foomatic/$queue.ppd", "http://$server:631/printers/$q.ppd")); } # Does the file exist and is it not an error message? if ((-r "$prefix/etc/foomatic/$queue.ppd") && (cat_("$prefix/etc/foomatic/$queue.ppd") =~ /^\*PPD-Adobe/)) { $configfilecontent = makeopenofficeprinterentry($printer, $queue, $configprefix, $configfilecontent); } else { unlink ("$prefix/etc/foomatic/$queue.ppd"); return 0; } } } # Write back OpenOffice.org configuration file return writesofficeconfigfile($configfilename, $configfilecontent); } sub removeprinterfromstaroffice { my ($printer, $queue) = @_; # Do we have Star Office installed? my $configfilename = findsofficeconfigfile(); return 1 if !$configfilename; $configfilename =~ m!^(.*)/share/xp3/Xpdefaults$!; my $configprefix = $1; # Load Star Office printer config file my $configfilecontent = readsofficeconfigfile($configfilename); # Remove the printer entry $configfilecontent = removestarofficeprinterentry($printer, $queue, $configprefix, $configfilecontent); # Write back Star Office configuration file return writesofficeconfigfile($configfilename, $configfilecontent); } sub removeprinterfromopenoffice { my ($printer, $queue) = @_; # Do we have OpenOffice.org installed? my $configfilename = findopenofficeconfigfile(); return 1 if !$configfilename; $configfilename =~ m!^(.*)/share/psprint/psprint.conf$!; my $configprefix = $1; # Load OpenOffice.org printer config file my $configfilecontent = readsofficeconfigfile($configfilename); # Remove the printer entry $configfilecontent = removeopenofficeprinterentry($printer, $queue, $configprefix, $configfilecontent); # Write back OpenOffice.org configuration file return writesofficeconfigfile($configfilename, $configfilecontent); } sub removelocalprintersfromstaroffice { my ($printer) = @_; # Do we have Star Office installed? my $configfilename = findsofficeconfigfile(); return 1 if !$configfilename; $configfilename =~ m!^(.*)/share/xp3/Xpdefaults$!; my $configprefix = $1; # Load Star Office printer config file my $configfilecontent = readsofficeconfigfile($configfilename); # Remove the printer entries foreach my $queue (keys(%{$printer->{configured}})) { $configfilecontent = removestarofficeprinterentry($printer, $queue, $configprefix, $configfilecontent); } # Write back Star Office configuration file return writesofficeconfigfile($configfilename, $configfilecontent); } sub removelocalprintersfromopenoffice { my ($printer) = @_; # Do we have OpenOffice.org installed? my $configfilename = findopenofficeconfigfile(); return 1 if !$configfilename; $configfilename =~ m!^(.*)/share/psprint/psprint.conf$!; my $configprefix = $1; # Load OpenOffice.org printer config file my $configfilecontent = readsofficeconfigfile($configfilename); # Remove the printer entries foreach my $queue (keys(%{$printer->{configured}})) { $configfilecontent = removeopenofficeprinterentry($printer, $queue, $configprefix, $configfilecontent); } # Write back OpenOffice.org configuration file return writesofficeconfigfile($configfilename, $configfilecontent); } sub makestarofficeprinterentry { my ($printer, $queue, $configprefix, $configfile) = @_; # Set default printer if ($queue eq $printer->{DEFAULT}) { $configfile = removeentry("windows", "device=", $configfile); $configfile = addentry("windows", "device=$queue,$queue PostScript,$queue", $configfile); } # Make an entry in the "[devices]" section $configfile = removeentry("devices", "$queue=", $configfile); $configfile = addentry("devices", "$queue=$queue PostScript,$queue", $configfile); # Make an entry in the "[ports]" section # The "perl" command patches the PostScript output to print the Euro # symbol correctly. $configfile = removeentry("ports", "$queue=", $configfile); $configfile = addentry("ports", "$queue=/usr/bin/perl -p -e \"s=16#80 /euro=16#80 /Euro=\" | /usr/bin/$lprcommand{$printer->{SPOOLER}{print_command}} -P $queue", $configfile); # Make printer's section $configfile = addsection("$queue,PostScript,$queue", $configfile); # Load PPD file my $ppd = cat_("$prefix/etc/foomatic/$queue.ppd"); # Set the PostScript level my $pslevel; if ($ppd =~ /^\s*\*LanguageLevel:\s*\"?([^\s\"]+)\"?\s*$/m) { $pslevel = $1; $pslevel = "2" if $pslevel eq "3"; } else { $pslevel = "2"; } $configfile = removeentry("$queue.PostScript.$queue", "Level=", $configfile); $configfile = addentry("$queue.PostScript.$queue", "Level=$pslevel", $configfile); # Set Color/BW my $color; if ($ppd =~ /^\s*\*ColorDevice:\s*\"?([Tt]rue)\"?\s*$/m) { $color = "1"; } else { $color = "0"; } $configfile = removeentry("$queue.PostScript.$queue", "BitmapColor=", $configfile); $configfile = addentry("$queue.PostScript.$queue", "BitmapColor=$color", $configfile); # Set the default paper size if ($ppd =~ /^\s*\*DefaultPageSize:\s*(\S+)\s*$/m) { my $papersize = $1; $configfile = removeentry("$queue.PostScript.$queue", "PageSize=", $configfile); $configfile = removeentry("$queue.PostScript.$queue", "PPD_PageSize=", $configfile); $configfile = addentry("$queue.PostScript.$queue", "PageSize=$papersize", $configfile); $configfile = addentry("$queue.PostScript.$queue", "PPD_PageSize=$papersize", $configfile); } # Link the PPD file run_program::rooted($prefix, "ln", "-sf", "/etc/foomatic/$queue.ppd", "$configprefix/share/xp3/ppds/$queue.PS"); return $configfile; } sub makeopenofficeprinterentry { my ($printer, $queue, $configprefix, $configfile) = @_; # Make printer's section $configfile = addsection($queue, $configfile); # Load PPD file my $ppd = cat_("$prefix/etc/foomatic/$queue.ppd"); # "PPD_PageSize" line if ($ppd =~ /^\s*\*DefaultPageSize:\s*(\S+)\s*$/m) { my $papersize = $1; $configfile = removeentry($queue, "PPD_PageSize=", $configfile); $configfile = addentry($queue, "PPD_PageSize=$papersize", $configfile); } # "Command" line # The "perl" command patches the PostScript output to print the Euro # symbol correctly. $configfile = removeentry($queue, "Command=", $configfile); $configfile = addentry($queue, "Command=/usr/bin/perl -p -e \"s=/euro /unused=/Euro /unused=\" | /usr/bin/$lprcommand{$printer->{SPOOLER}{print_command}} -P $queue", $configfile); # "Comment" line $configfile = removeentry($queue, "Comment=", $configfile); if (($printer->{configured}{$queue}) && ($printer->{configured}{$queue}{queuedata}{desc})) { $configfile = addentry ($queue, "Comment=$printer->{configured}{$queue}{queuedata}{desc}", $configfile); } else { $configfile = addentry($queue, "Comment=", $configfile); } # "Location" line $configfile = removeentry($queue, "Location=", $configfile); if (($printer->{configured}{$queue}) && ($printer->{configured}{$queue}{queuedata}{loc})) { $configfile = addentry ($queue, "Location=$printer->{configured}{$queue}{queuedata}{loc}", $configfile); } else { $configfile = addentry($queue, "Location=", $configfile); } # "DefaultPrinter" line $configfile = removeentry($queue, "DefaultPrinter=", $configfile); my $default = "0"; if ($queue eq $printer->{DEFAULT}) { $default = "1"; # "DefaultPrinter=0" for the "Generic Printer" $configfile = removeentry("Generic Printer", "DefaultPrinter=", $configfile); $configfile = addentry("Generic Printer", "DefaultPrinter=0", $configfile); } $configfile = addentry($queue, "DefaultPrinter=$default", $configfile); # "Printer" line $configfile = removeentry($queue, "Printer=", $configfile); $configfile = addentry($queue, "Printer=$queue/$queue", $configfile); # Link the PPD file run_program::rooted($prefix, "ln", "-sf", "/etc/foomatic/$queue.ppd", "$configprefix/share/psprint/driver/$queue.PS"); return $configfile; } sub removestarofficeprinterentry { my ($printer, $queue, $configprefix, $configfile) = @_; # Remove default printer entry $configfile = removeentry("windows", "device=$queue,", $configfile); # Remove entry in the "[devices]" section $configfile = removeentry("devices", "$queue=", $configfile); # Remove entry in the "[ports]" section $configfile = removeentry("ports", "$queue=", $configfile); # Remove "[$queue,PostScript,$queue]" section $configfile = removesection("$queue,PostScript,$queue", $configfile); # Remove Link of PPD file run_program::rooted($prefix, "rm", "-f", "$configprefix/share/xp3/ppds/$queue.PS"); return $configfile; } sub removeopenofficeprinterentry { my ($printer, $queue, $configprefix, $configfile) = @_; # Remove printer's section $configfile = removesection($queue, $configfile); # Remove Link of PPD file run_program::rooted($prefix, "rm", "-f", "$configprefix/share/psprint/driver/$queue.PS"); return $configfile; } sub findsofficeconfigfile { my @configfilenames = ("/usr/lib/*/share/xp3/Xpdefaults", "/usr/local/lib/*/share/xp3/Xpdefaults", "/usr/local/*/share/xp3/Xpdefaults", "/opt/*/share/xp3/Xpdefaults"); foreach my $configfilename (@configfilenames) { local *F; if (open F, "ls -r $prefix$configfilename 2> /dev/null |") { my $filename = <F>; close F; if ($filename) { if ($prefix ne "") { $filename =~ s/^$prefix//; } # Work around a bug in the "ls" of "busybox". During # installation it outputs the mask given on the command line # instead of nothing when the mask does not match any file next if $filename =~ /\*/; return $filename; } } } return ""; } sub findopenofficeconfigfile { my @configfilenames = ("/usr/lib/*/share/psprint/psprint.conf", "/usr/local/lib/*/share/psprint/psprint.conf", "/usr/local/*/share/psprint/psprint.conf", "/opt/*/share/psprint/psprint.conf"); foreach my $configfilename (@configfilenames) { local *F; if (open F, "ls -r $prefix$configfilename 2> /dev/null |") { my $filename = <F>; close F; if ($filename) { if ($prefix ne "") { $filename =~ s/^$prefix//; } # Work around a bug in the "ls" of "busybox". During # installation it outputs the mask given on the command line # instead of nothing when the mask does not match any file next if $filename =~ /\*/; return $filename; } } } return ""; } sub readsofficeconfigfile { my ($file) = @_; local *F; open F, "< $prefix$file" or return ""; my $filecontent = join("", <F>); close F; return $filecontent; } sub writesofficeconfigfile { my ($file, $filecontent) = @_; local *F; open F, "> $prefix$file" or return 0; print F $filecontent; close F; return 1; } sub addentry { my ($section, $entry, $filecontent) = @_; my $sectionfound = 0; my $entryinserted = 0; my @lines = split("\n", $filecontent); foreach (@lines) { if (!$sectionfound) { if (/^\s*\[\s*$section\s*\]\s*$/) { $sectionfound = 1; } } else { if (!/^\s*$/ && !/^\s*;/) { #-# $_ = "$entry\n$_"; $entryinserted = 1; last; } } } if ($sectionfound && !$entryinserted) { push(@lines, $entry); } return join ("\n", @lines); } sub addsection { my ($section, $filecontent) = @_; my $entryinserted = 0; my @lines = split("\n", $filecontent); foreach (@lines) { if (/^\s*\[\s*$section\s*\]\s*$/) { # section already there, nothing to be done return $filecontent; } } return $filecontent . "\n[$section]"; } sub removeentry { my ($section, $entry, $filecontent) = @_; my $sectionfound = 0; my $done = 0; my @lines = split("\n", $filecontent); foreach (@lines) { $_ = "$_\n"; next if $done; if (!$sectionfound) { if (/^\s*\[\s*$section\s*\]\s*$/) { $sectionfound = 1; } } else { if (/^\s*\[.*\]\s*$/) { # Next section $done = 1; } elsif (/^\s*$entry/) { $_ = ""; $done = 1; } } } return join ("", @lines); } sub removesection { my ($section, $filecontent) = @_; my $sectionfound = 0; my $done = 0; my @lines = split("\n", $filecontent); foreach (@lines) { $_ = "$_\n"; next if $done; if (!$sectionfound) { if (/^\s*\[\s*$section\s*\]\s*$/) { $_ = ""; $sectionfound = 1; } } else { if (/^\s*\[.*\]\s*$/) { # Next section $done = 1; } else { $_ = ""; } } } return join ("", @lines); } # ------------------------------------------------------------------ # GIMP # ------------------------------------------------------------------ sub configuregimp { my ($printer, $queue) = @_; # Do we have files to treat? my @configfilenames = findgimpconfigfiles(); return 1 if $#configfilenames < 0; # There is no system-wide config file, treat every user's config file foreach my $configfilename (@configfilenames) { # Load GIMP's printer config file my $configfilecontent = readgimpconfigfile($configfilename); # Update local printer queues foreach my $queue (keys(%{$printer->{configured}})) { # Check if we have a PPD file if (! -r "$prefix/etc/foomatic/$queue.ppd") { if (-r "$prefix/etc/cups/ppd/$queue.ppd") { # If we have a PPD file in the CUPS config dir, link to it run_program::rooted($prefix, "ln", "-sf", "/etc/cups/ppd/$queue.ppd", "/etc/foomatic/$queue.ppd"); } elsif (-r "$prefix/usr/share/postscript/ppd/$queue.ppd") { # Check PPD directory of GPR, too run_program::rooted ($prefix, "ln", "-sf", "/usr/share/postscript/ppd/$queue.ppd", "/etc/foomatic/$queue.ppd"); } else { # No PPD file at all? We cannot set up this printer next; } } # Add the printer entry if (!isgimpprinterconfigured ($queue, $configfilecontent)) { # Remove the old printer entry $configfilecontent = removegimpprinter($queue, $configfilecontent); # Add the new printer entry $configfilecontent = makegimpprinterentry($printer, $queue, $configfilecontent); } } # Default printer if ($printer->{DEFAULT}) { if ($configfilecontent !~ /^\s*Current\-Printer\s*:/m) { $configfilecontent =~ s/\n/\nCurrent-Printer: $printer->{DEFAULT}\n/s; } else { $configfilecontent =~ /^\s*Current\-Printer\s*:\s*(\S+)\s*$/m; if (!isgimpprinterconfigured ($1, $configfilecontent)) { $configfilecontent =~ s/(Current\-Printer\s*:\s*)\S+/$1$printer->{DEFAULT}/; } } } # Write back GIMP's printer configuration file writegimpconfigfile($configfilename, $configfilecontent); } return 1; } sub addcupsremotetogimp { my ($printer, $queue) = @_; # Do we have files to treat? my @configfilenames = findgimpconfigfiles(); return 1 if $#configfilenames < 0; my @printerlist = getcupsremotequeues(); my $ppdfile = ""; if (($printer->{SPOOLER} eq "cups") && ((-x "$prefix/usr/bin/curl") || (-x "$prefix/usr/bin/wget"))) { foreach my $listentry (@printerlist) { next if !($listentry =~ /^([^\|]+)\|([^\|]+)$/); my $q = $1; next if $q ne $queue; my $server = $2; # Remove server name from queue name $q =~ s/^([^@]*)@.*$/$1/; if (-x "$prefix/usr/bin/wget") { eval(run_program::rooted ($prefix, "/usr/bin/wget", "-O", "/etc/foomatic/$queue.ppd", "http://$server:631/printers/$q.ppd")); } else { eval(run_program::rooted ($prefix, "/usr/bin/curl", "-o", "/etc/foomatic/$queue.ppd", "http://$server:631/printers/$q.ppd")); } # Does the file exist and is it not an error message? if ((-r "$prefix/etc/foomatic/$queue.ppd") && (cat_("$prefix/etc/foomatic/$queue.ppd") =~ /^\*PPD-Adobe/)) { $ppdfile = "/etc/foomatic/$queue.ppd"; } else { unlink ("$prefix/etc/foomatic/$queue.ppd"); return 0; } } } else { return 1; } # There is no system-wide config file, treat every user's config file foreach my $configfilename (@configfilenames) { # Load GIMP's printer config file my $configfilecontent = readgimpconfigfile($configfilename); # Add the printer entry if (!isgimpprinterconfigured ($queue, $configfilecontent)) { # Remove the old printer entry $configfilecontent = removegimpprinter($queue, $configfilecontent); # Add the new printer entry $configfilecontent = makegimpprinterentry($printer, $queue, $configfilecontent); } # Write back GIMP's printer configuration file writegimpconfigfile($configfilename, $configfilecontent); } return 1; } sub removeprinterfromgimp { my ($printer, $queue) = @_; # Do we have files to treat? my @configfilenames = findgimpconfigfiles(); return 1 if $#configfilenames < 0; # There is no system-wide config file, treat every user's config file foreach my $configfilename (@configfilenames) { # Load GIMP's printer config file my $configfilecontent = readgimpconfigfile($configfilename); # Remove the printer entry $configfilecontent = removegimpprinter($queue, $configfilecontent); # Write back GIMP's printer configuration file writegimpconfigfile($configfilename, $configfilecontent); } return 1; } sub removelocalprintersfromgimp { my ($printer) = @_; # Do we have files to treat? my @configfilenames = findgimpconfigfiles(); return 1 if $#configfilenames < 0; # There is no system-wide config file, treat every user's config file foreach my $configfilename (@configfilenames) { # Load GIMP's printer config file my $configfilecontent = readgimpconfigfile($configfilename); # Remove the printer entries foreach my $queue (keys(%{$printer->{configured}})) { $configfilecontent = removegimpprinter($queue, $configfilecontent); } # Write back GIMP's printer configuration file writegimpconfigfile($configfilename, $configfilecontent); } return 1; } sub makegimpprinterentry { my ($printer, $queue, $configfile) = @_; # Make printer's section $configfile = addgimpprinter($queue, $configfile); # Load PPD file my $ppd = cat_("$prefix/etc/foomatic/$queue.ppd"); # Is the printer configured with GIMP-Print? my $gimpprintqueue = 0; my $gimpprintdriver = "ps2"; if ($ppd =~ /CUPS\s*\+\s*GIMP\s*\-\s*Print/im) { # Native CUPS driver $gimpprintqueue = 1; $ppd =~ /\s*\*ModelName:\s*\"(\S+)\"\s*$/im; $gimpprintdriver = $1; } elsif ($ppd =~ /Foomatic\s*\+\s*gimp\s*\-\s*print/im) { # GhostScript + Foomatic driver $gimpprintqueue = 1; $ppd =~ /'idx'\s*=>\s*'ev\/gimp-print-((escp2|pcl|bjc|lexmark)\-\S*)'/im; $gimpprintdriver = $1; } if ($gimpprintqueue) { # Get the paper size from the PPD file if ($ppd =~ /^\s*\*DefaultPageSize:\s*(\S+)\s*$/m) { my $papersize = $1; $configfile = removegimpentry($queue, "Media-Size", $configfile); $configfile = addgimpentry($queue, "Media-Size: $papersize", $configfile); } $configfile = removegimpentry($queue, "PPD-File:", $configfile); $configfile = addgimpentry($queue, "PPD-File:", $configfile); $configfile = removegimpentry($queue, "Driver:", $configfile); $configfile = addgimpentry($queue, "Driver: $gimpprintdriver", $configfile); $configfile = removegimpentry($queue, "Destination:", $configfile); $configfile = addgimpentry($queue, "Destination: /usr/bin/$lprcommand{$printer->{SPOOLER}{print_command}} -P $queue -o raw", $configfile); } else { $configfile = removegimpentry($queue, "PPD-File:", $configfile); $configfile = addgimpentry($queue, "PPD-File: /etc/foomatic/$queue.ppd", $configfile); $configfile = removegimpentry($queue, "Driver:", $configfile); $configfile = addgimpentry($queue, "Driver: ps2", $configfile); $configfile = removegimpentry($queue, "Destination:", $configfile); $configfile = addgimpentry($queue, "Destination: /usr/bin/$lprcommand{$printer->{SPOOLER}{print_command}} -P $queue", $configfile); } return $configfile; } sub findgimpconfigfiles { my @configfilenames; if (-d "$prefix/usr/lib/gimp/1.2") { push (@configfilenames, ".gimp-1.2/printrc"); } if (-d "$prefix/usr/lib/gimp/1.3") { push (@configfilenames, ".gimp-1.3/printrc"); } my @filestotreat; local *PASSWD; open PASSWD, "< $prefix/etc/passwd" or die "Cannot read /etc/passwd!\n"; local $_; while (<PASSWD>) { chomp; if (/^([^:]+):[^:]*:([^:]+):([^:]+):[^:]*:([^:]+):[^:]*$/) { my ($username, $uid, $gid, $homedir) = ($1, $2, $3, $4); if ((($uid == 0) || ($uid >= 500)) && ($username ne "nobody")) { foreach my $file (@configfilenames) { my $dir = "$homedir/$file"; $dir =~ s,/[^/]*$,,; next if (-f $dir) && (! -d $dir); if (! -d "$prefix$dir") { run_program::rooted($prefix, "/bin/mkdir", $dir) or next; run_program::rooted($prefix, "/bin/chown", "$uid.$gid", $dir) or next; } if (! -f "$prefix$homedir/$file") { local *F; open F, "> $prefix$homedir/$file" or next; print F "#PRINTRCv1 written by GIMP-PRINT 4.2.2 - 13 Sep 2002\n"; close F; run_program::rooted($prefix, "/bin/chown", "$uid.$gid", "$homedir/$file") or next; } push (@filestotreat, "$homedir/$file"); } } } } @filestotreat; } sub readgimpconfigfile { my ($file) = @_; local *F; open F, "< $prefix$file" or return ""; my $filecontent = join("", <F>); close F; return $filecontent; } sub writegimpconfigfile { my ($file, $filecontent) = @_; local *F; open F, "> $prefix$file" or return 0; print F $filecontent; close F; return 1; } sub addgimpentry { my ($section, $entry, $filecontent) = @_; my $sectionfound = 0; my $entryinserted = 0; my @lines = split("\n", $filecontent); foreach (@lines) { if (!$sectionfound) { if (/^\s*Printer\s*:\s*($section)\s*$/) { $sectionfound = 1; } } else { if (!/^\s*$/ && !/^\s*;/) { #-# $_ = "$entry\n$_"; $entryinserted = 1; last; } } } if ($sectionfound && !$entryinserted) { push(@lines, $entry); } return join ("\n", @lines); } sub addgimpprinter { my ($section, $filecontent) = @_; my $entryinserted = 0; my @lines = split("\n", $filecontent); foreach (@lines) { if (/^\s*Printer\s*:\s*($section)\s*$/) { # section already there, nothing to be done return $filecontent; } } return $filecontent . "\nPrinter: $section"; } sub removegimpentry { my ($section, $entry, $filecontent) = @_; my $sectionfound = 0; my $done = 0; my @lines = split("\n", $filecontent); foreach (@lines) { $_ = "$_\n"; next if $done; if (!$sectionfound) { if (/^\s*Printer\s*:\s*($section)\s*$/) { $sectionfound = 1; } } else { if (/^\s*Printer\s*:\s*.*\s*$/) { # Next section $done = 1; } elsif (/^\s*$entry/) { $_ = ""; $done = 1; } } } return join ("", @lines); } sub removegimpprinter { my ($section, $filecontent) = @_; my $sectionfound = 0; my $done = 0; my @lines = split("\n", $filecontent); foreach (@lines) { $_ = "$_\n"; next if $done; if (!$sectionfound) { if (/^\s*Printer\s*:\s*($section)\s*$/) { $_ = ""; $sectionfound = 1; } } else { if (/^\s*Printer\s*:\s*.*\s*$/) { # Next section $done = 1; } else { $_ = ""; } } } return join ("", @lines); } sub isgimpprinterconfigured { my ($queue, $filecontent) = @_; my $sectionfound = 0; my $done = 0; my $drivernotps2 = 0; my $ppdfileset = 0; my $nonrawprinting = 0; my @lines = split("\n", $filecontent); foreach (@lines) { last if $done; if (!$sectionfound) { if (/^\s*Printer\s*:\s*($queue)\s*$/) { $sectionfound = 1; } } else { if (/^\s*Printer\s*:\s*.*\s*$/) { # Next section $done = 1; } elsif (/^\s*Driver:\s*(\S+)\s*$/) { $drivernotps2 = ($1 ne "ps2"); } elsif (/^\s*PPD\-File:\s*(\S+)\s*$/) { $ppdfileset = 1; } elsif (/^\s*Destination:\s*(\S+.*)$/) { $nonrawprinting = ($1 !~ /\-o\s*raw/); } } } return 0 if $done && !$sectionfound; return 1 if $ppdfileset || $drivernotps2 || $nonrawprinting; return 0; } # ------------------------------------------------------------------ 1;