#!/usr/bin/perl -w

#
# Script to automatically restrict the printer information broadcasting the
# accepting of external broadcasts, and the access to the printers to local
# (eth?) networks 
#

#
# Till Kamppeter (till@mandrakesoft.com)
#
# Copyright 2001
#
# This software may be freely redistributed under the terms of the GNU
# General Public License.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
#

# Some great piece of code taken from
# /usr/lib/perl5/site_perl/5.6.1/MDK/Common/DataStructure.pm
# member( $a, @b ) returns 1 if $a is in @b, 0 otherwise.

sub member { my $e = shift; foreach (@_) { $e eq $_ and return 1 } 0 };

# Do not do any changes when the user chose manual configuration in
# printerdrake

my $manual = 0;
my $manualconffile = "/etc/sysconfig/printing";
if (open MANUALCONFFILE, "< $manualconffile") {
    @manualconf_content = <MANUALCONFFILE>;
    close MANUALCONFFILE;
    ($_ =~ /^\s*CUPS_CONFIG\s*=\s*manual\s*$/ and $manual = 1) foreach @manualconf_content;
}
if ($manual) {exit;}

# Read CUPS config file or create a new one if necessary.

my $cups_conf = "/etc/cups/cupsd.conf";
my @cups_conf_content;
my $config_modified = 0;

if (!(-f $cups_conf)) {
    warn "No CUPS configuration file $cups_conf, creating one ...\n";
    @cups_conf_content = split('\n',
"LogLevel info
TempDir /var/spool/cups/tmp
Port 631
BrowseAddress \@LOCAL
BrowseDeny All
BrowseAllow 127.0.0.1
BrowseAllow \@LOCAL
BrowseOrder deny,allow
<Location />
Order Deny,Allow
Deny From All
Allow From 127.0.0.1
Allow From \@LOCAL
</Location>
<Location /admin>
AuthType Basic
AuthClass System
Order Deny,Allow
Deny From All
Allow From 127.0.0.1
</Location>
");
    ($_ =~ s/$/\n/) foreach @cups_conf_content;
    $config_modified = 1;
} else {
    open CONF_CUPS, "$cups_conf" or die "Can't open $cups_conf!";
    @cups_conf_content = <CONF_CUPS>;
    close CONF_CUPS;
}

# Check if we have "localhost" as hostname, broadcast printer info and
# have nothing defined as "ServerName". If so, we must set our IP as
# ServerName. Otherwise clients try to send the jobs to "localhost"
# which means, that they send them to themselves and this leads to an
# endless loop.
my $hostname = `hostname`;
chomp $hostname;

if ((($hostname eq "localhost") || ($hostname =~ m!^localhost\.!)) &&
    !(grep { /^\s*Browsing\s+[Oo]ff/ } @cups_conf_content) &&
    !(grep { /^\s*BrowseInterval\s+0+\s*$/ } @cups_conf_content)) {

    # We try to find out into which network we broadcast and due to
    # having valid host name, we insert the IP address for this
    # network as "ServerName" in /etc/cups/cupsd.conf

    # Check if there was an IP as the previous ServerName, this IP has to be
    # checked whether it is still valid (It can be a dynamic IP from DHCP or
    # similar)
    my $oldserverip = "";
    ($_ =~ /^\s*ServerName\s+(\S*)\s*$/ and $oldserverip = $1)
	foreach @cups_conf_content;

    # Only continue if there was no ServerName before or if there was
    # an IP as ServerName. Other types of ServerName are probably entered
    # manually and should be left untouched.
    if (($oldserverip eq "") ||
	($oldserverip =~ /^\s*[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+\s*$/)) {
	
	# Read the /etc/sysconfig/network-scripts/draknet_conf 
	# (if available)

	my $internetaccess = "";
	my $internetinterface = "";
	my $draknet_conf = "/etc/sysconfig/network-scripts/draknet_conf";
	if (open CONF_DRAKNET, "$draknet_conf") {
	    my @draknet_conf_content = <CONF_DRAKNET>;
	    close CONF_DRAKNET;

	    # Check the internet connection type
	    
	    ($_ =~ /^\s*InternetAccessType=(.*)$/ and $internetaccess = $1) foreach @draknet_conf_content;
	    
	    # Check the internet access interface
	    
	    ($_ =~ /^\s*InternetInterface=(.*)$/ and $internetinterface = $1) foreach @draknet_conf_content;
	    
	    if ($internetaccess eq "lan") {$internetinterface = ""};
	    
	}
	
	# Read the output of "ifconfig" to look for local networks
	
	my $dev_is_localnet = 0;
	my @local_ips = ();
	my @local_ifaces = ();
	my $current_ip = "";
	my $oldserveripok = 0;
	
	if (-x "/sbin/ifconfig") {
	    open IFCONFIG_OUT, "export LC_MESSAGES=C; /sbin/ifconfig|" or die "Couldn't run \"ifconfig\"!";

	    while (defined($readline = <IFCONFIG_OUT>)) {
		# New entry ...
		if ($readline =~ /^(\S+)\s/) {
		    $dev = $1;

		    # ... for a local network (eth = ethernet, 
		    #     vmnet = VMWare,
		    #     ethernet card connected to ISP excluded)?
		    if ((($dev =~ /^eth/) || ($dev =~ /^vmnet/)) &&
			(!($dev eq $internetinterface))) {
			$dev_is_localnet = 1 
		    } else {$dev_is_localnet = 0};
		    # delete previous network data
		    $current_ip = "";
		}
		# Are we in the important line now?
		if ($readline =~ /\sinet addr:[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+\s/) {
		    # Rip out the IP address
		    if ($readline =~ /\sinet addr:([0-9]+\.[0-9]+\.[0-9]+\.[0-9]+)\s/) {
			$current_ip = $1;
		    }

		    # Is this IP the same as the IP previously set as the
		    # ServerName? Then that IP is OK and we do not need to
		    # change anything
		    if (($current_ip ne "") &&
			($oldserverip eq $current_ip)) {
			$oldserveripok = 1;
		    }

		    # Are we in an entry for a local network?
		    if ($dev_is_localnet == 1) {
			# Store current IP address
			push @local_ips, $current_ip;
			# Store interface name
			push @local_ifaces, $dev;
		    }
		}
	    }
	    close(IFCONFIG_OUT);
	}

	# If the previously set server IP is OK, we do not need to change
	# anything
	if (!$oldserveripok) {

	    # Find principal IP (the one to set as "ServerName" and to
	    # use for broadcasting.

	    my $servername = "";
	    if ($#local_ips == -1) {
		# No local network at all => No broadcasting, nothing to do
	    } elsif ($#local_ips == 0) {
		# One local network => Broadcast to this network, 
		# ServerName = IP address
		$servername = $local_ips[0];
	    } else {
		# Two or more local networks
		# try to find out which local network leads to outside, do 
		# only broadcast to the others.
		my $togateway = "";
		if (-x "/sbin/route") {
		    open ROUTE_OUT, "export LC_MESSAGES=C; /sbin/route|" or die "Couldn't run \"route\"!\n";
		    while ($readline = <ROUTE_OUT>) {
			if ($readline =~ m!^default\s+.*\s+(\S+)$!) {
			    $togateway = $1;
			}
		    }
		    close ROUTE_OUT;
		}
	    
		# Mark network with the gateway for being removed from the
		# broadcast list, set network with gateway as the network from
		# where to browserelay.
		my $gatewaynet;
		my $i;
		for ($i = 0; $i <= $#local_ips; $i++) {
		    if ($local_ifaces[$i] eq $togateway) {
			$gatewaynet = $i;
			last;
		    }
		}
		
		if ((!defined($gatewaynet)) || ($gatewaynet != 0)) {
		    $servername = $local_ips[0];
		} else {
		    $servername = $local_ips[1];
		}
	    }
	    
	    if ($servername ne "") {
		print STDERR "WARNING: Inserted \"ServerName $servername\" in /etc/cups/cupsd.conf\n         (to make broadcasting of printer queue info working correctly)\n";
		
		# Remove all valid "ServerName" lines
		($_ =~ /^\s*ServerName[^:]/ and $_="") foreach @cups_conf_content;
		
		# Insert the new "ServerName" line
		push @cups_conf_content, "ServerName $servername\n";
		
		$config_modified = 1;
	    }
	}
    }
}

# Check whether LPD/LPRng is installed and turn off creation of an
# /etc/printcap file by CUPS.

if ((-x "/usr/sbin/lpd") &&
    !(grep { /^\s*Printcap\s*$/ } @cups_conf_content)) {

    my $oldprintcap = "";
    ($_ =~ /^\s*Printcap\s+(\S*)\s*$/ and $oldprintcap = $1)
	foreach @cups_conf_content;

    if (($oldprintcap eq "") || ($oldprintcap eq "/etc/printcap")) {

	print STDERR "WARNING: Inserted \"Printcap\" line in /etc/cups/cupsd.conf\n         (to avoid overwriting the /etc/printcap of the installed LPD/LPRng)\n";

	# Remove all valid "Printcap" lines
	($_ =~ /^\s*Printcap[^:]/ and $_="") foreach @cups_conf_content;
	
	# Insert the new "Printcap" line
	push @cups_conf_content, "Printcap\n";

	# Remove the /etc/printcap file which the CUPS daemon left during
	# shutdown and replace it by an empty file
	unlink "/etc/printcap";
	system "touch /etc/printcap";
		
	$config_modified = 1;
    }
}

# Write back the modified CUPS config file
if ($config_modified) {
    open CONF_CUPS, ">$cups_conf" or die "Can't open $cups_conf";
    print CONF_CUPS @cups_conf_content;
    close CONF_CUPS;
}
