#!/usr/bin/perl

#
# Guillaume Cottenceau (gc@mandrakesoft.com)
#
# Copyright 2000 MandrakeSoft
#
# This software may be freely redistributed under the terms of the GNU
# 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.
#

use lib qw(/usr/lib/libDrakX);

use common qw(:system :file);
use interactive;
use standalone;
use log;
use c;
use netconnect;
use detect_devices;

$::isEmbedded = ($::XID, $::CCPID) = "@ARGV" =~ /--embedded (\w+) (\w+)/;
$::isWizard = "@ARGV" =~ /--wizard/;

local $_ = join '', @ARGV;

/-h/ and die "usage: drakgw [--version --verbose]\n";
/-version/ and die 'version: $Id$ '."\n";

$::verbose = /-verbose/;
$::direct = /-direct/;

my $in = vnew interactive('su');


my $sysconf_network = "/etc/sysconfig/network";
my $conf_linuxconf = "/etc/conf.linuxconf";
my $rc_firewall = "/etc/rc.d/rc.firewall.inet_sharing";
my $dhcpd_conf = "/etc/dhcpd.conf";
my $cups_conf = "/etc/cups/cupsd.conf";


my $drakgw_setup = "/etc/sysconfig/inet_sharing";

sub start_daemons()
{
    my $cups_used = 0;
    log::l("[drakgw] Starting daemons\n");
    if (-f "/etc/rc.d/init.d/cups")
    {
        if (grep(/is running/, `/etc/rc.d/init.d/cups status`))
        {
           $cups_used = 1;
           (system("/etc/rc.d/init.d/cups stop") == 0) or die "Could not stop the CUPS daemon";
        }
    }
    grep(/is running/, `/etc/rc.d/init.d/dhcpd status`) and ((system("/etc/rc.d/init.d/dhcpd stop") == 0) or die "Could not stop the dhcp server");
    grep(/error: cannot connect/, `/etc/rc.d/init.d/named status 2>&1`) or ((system("/etc/rc.d/init.d/named stop") == 0) or die "Could not stop the named server");
    (system("/etc/rc.d/init.d/network restart") == 0) or die "Could not restart the network";
    (system("sh /etc/rc.d/rc.firewall") == 0) or die "Could not start the firewall script";
    (system("/etc/rc.d/init.d/named start") == 0) or die "Could not start the caching nameserver";
    (system("/sbin/chkconfig --level 345 named on") == 0) or die "Could not chkconfig named";
    (system("/etc/rc.d/init.d/dhcpd start") == 0) or die "Could not start the dhcp server";
    (system("/sbin/chkconfig --level 345 dhcpd on") == 0) or die "Could not chkconfig dhcpd";
    if ($cups_used == 1)
    {
        (system("/etc/rc.d/init.d/cups start") == 0) or die "Could not start the CUPS daemon";
    }
    local *DRAKGW_SETUP; open DRAKGW_SETUP, ">$drakgw_setup" or die "Can't open $drakgw_setup";
    print DRAKGW_SETUP "INET_SHARING=enabled\n";
    close DRAKGW_SETUP;
}

sub stop_daemons()
{
    log::l("[drakgw] Stopping daemons\n");
    grep(/is running/, `/etc/rc.d/init.d/dhcpd status`) and ((system("/etc/rc.d/init.d/dhcpd stop") == 0) or die "Could not stop the dhcp server");
    grep(/Connection refused/, `/etc/rc.d/init.d/named status 2>&1`) or ((system("/etc/rc.d/init.d/named stop") == 0) or die "Could not stop the named server");
    (system("/etc/rc.d/init.d/ipchains stop") == 0) or die "Could not stop ipchains";
    (system("/sbin/chkconfig --level 345 named off") == 0) or die "Could not chkconfig named";
    (system("/sbin/chkconfig --level 345 dhcpd off") == 0) or die "Could not chkconfig dhcpd";

    local *DRAKGW_SETUP; open DRAKGW_SETUP, ">$drakgw_setup" or die "Can't open $drakgw_setup";
    print DRAKGW_SETUP "INET_SHARING=disabled\n";
    close DRAKGW_SETUP;
}

sub fatal_quit($)
{
    log::l("[drakgw] FATAL: $_[0]\n");
    (defined $wait_configuring) and (undef $wait_configuring);
    $in->ask_warn('', $_[0]);
    $in->exit(-1);
}

begin:

#- **********************************
#- * 0th step: verify if we are already set up

if (-f $drakgw_setup)
{
    open DRAKGW_SETUP, "$drakgw_setup" or die "Can't open $drakgw_setup";
    my @drakgw_setup_content = <DRAKGW_SETUP>;
    close DRAKGW_SETUP;

    if (grep(/enabled/, @drakgw_setup_content))
    {
	my $r = $in->ask_from_list(_("Internet Connection Sharing currently enabled"),
				   _("The setup of Internet connection sharing has already been done.
It's currently enabled.\n
What would you like to do?"),
				   [ _("disable"), _("reconfigure"), _("dismiss") ]);
	if ($r eq _("disable"))
	{
	    stop_daemons();
	    -f "$dhcpd_conf.drakgwdisable" and (unlink("$dhcpd_conf.drakgwdisable") or die "Could not unlink $dhcpd_conf.drakgwdisable");
	    rename($dhcpd_conf, "$dhcpd_conf.drakgwdisable") or die "Could not rename $dhcpd_conf to $dhcpd_conf.drakgwdisable";
	    -f "$rc_firewall.drakgwdisable" and (unlink("$rc_firewall.drakgwdisable") or die "Could not unlink $rc_firewall.drakgwdisable");
	    rename($rc_firewall, "$rc_firewall.drakgwdisable") or die "Could not rename $rc_firewall to $rc_firewall.drakgwdisable";
            log::l("[drakgw] Disabled\n");
	    $in->exit(0);
	}
	($r eq _("dismiss")) and $in->exit(0);
    }
    elsif (grep(/disabled/, @drakgw_setup_content))
    {
	my $r = $in->ask_from_list(_("Internet Connection Sharing currently disabled"),
				   _("The setup of Internet connection sharing has already been done.
It's currently disabled.\n
What would you like to do?"),
				   [ _("enable"), _("reconfigure"), _("dismiss") ]);
	if ($r eq _("enable"))
	{
	    -f $dhcpd_conf and rename($dhcpd_conf, "$dhcpd_conf.old");
	    rename("$dhcpd_conf.drakgwdisable", $dhcpd_conf) or die "Could not find configuration. Please reconfigure.";
	    -f $rc_firewall and rename($rc_firewall, "$rc_firewall.old");
	    rename("$rc_firewall.drakgwdisable", $rc_firewall) or die "Could not find configuration. Please reconfigure.";
	    start_daemons();
            log::l("[drakgw] Enabled\n");
	    $in->exit(0);
	}
	($r eq _("dismiss")) and $in->exit(0);
    }
    else
    {
	$in->ask_warn("Unrecognized config file", _("Config file content could not be interpreted."));
	$in->exit(-1);
    }
}



#- **********************************
#- * 1st step: detect/setup

$::direct or $in->ask_okcancel(_("Internet Connection Sharing"),
			       _("Your computer can be configured to share its Internet connection.\n
Note: you need a dedicated Network Adapter to set up a Local Area Network (LAN).\n
Would you like to setup the Internet Connection Sharing?"), 1) or $in->exit(0);

#my @pci_ethernet_cards;
#OBSOLETE! require pci_probing::main;
#($_->[0] =~ /NETWORK_ETHERNET/) and (push @pci_ethernet_cards, $_) foreach (pci_probing::main::probe('.'));

#($#pci_ethernet_cards == -1) and $in->ask_warn('', _("No PCI network ethernet devices found!")) and $in->exit(0);
#
##  push @pci_ethernet_cards, [ "NETWORK_ETHERNET", "Fake ne2000", "ne2k" ];

my @configured_devices = map { /ifcfg-(\S+)/; $1 } `ls /etc/sysconfig/network-scripts/ifcfg*`;

#my @active_devices = `/sbin/ifconfig | grep ^[a-z] | awk '{print \$1}'`; chop @active_devices;

my %aliased_devices; (/^alias\s+(eth[0-9])\s+(\S+)/) and ($aliased_devices{$1} = $2) foreach cat_("/etc/modules.conf");
my $card_netconnect = netconnect::get_net_device("/");
(defined $card_netconnect) and log::l("[drakgw] Information from netconnect: ignore card $card_netconnect\n");
my @all_cards_getnet = detect_devices::getNet();
my @all_cards;
foreach my $card (@all_cards_getnet)
{
    log::l("[drakgw] Have network card: $card\n");
    next if ($card eq $card_netconnect);
    push @all_cards, exists $aliased_devices{$card} ? "Interface $card ("._("using module")." $aliased_devices{$card})" : "Interface $card";
}
log::l("[drakgw] Available network cards: ".join(" ; ", @all_cards)."\n");

#  print "pci_detection: "; print ">".$_->[2]."< " foreach (@pci_ethernet_cards);
#  print "\nconfigured: "; print ">".$_."< " foreach (@configured_devices);
#  print "\nactive: "; print ">".$_."< " foreach (@active_devices);
#  print "\naliased: "; print ">".$_." => ".$aliased_devices{$_}."< " foreach (keys %aliased_devices);
#  print "\n";
#  print "\nall_cards: "; print ">".$_."< " foreach (@all_cards);

#
#foreach $pci_card (@pci_ethernet_cards)
#{
#    my $this_card = $pci_card->[1];
#    foreach $aliased_dev (keys %aliased_devices)
#    {
#	 if ($pci_card->[2] eq $aliased_devices{$aliased_dev})
#	 {
#	     $this_card .= ", hardware-configured";
#	     grep(/$aliased_dev/, @configured_devices) and $this_card .= ", software-configured";
#	     grep(/$aliased_dev/, @active_devices) and $this_card .= ", active";
#	 }
#    }
#    push @all_cards, $this_card;
#}


#- setup the network interface we shall use

my $interface;
if ($#all_cards == -1)
{
    $in->ask_warn(_("No network adapter on your system!"), 
		  _("No ethernet network adapter has been detected on your system. Please run the hardware configuration tool."));
    quit_global($in);
}
elsif ($#all_cards == 0)
{
    $interface = $all_cards[0];
    $::verbose and ($in->ask_okcancel(_("Network interface"),
				      _("There is only one configured network adapter on your system:\n\n$interface\n\nWould you like to setup your Local Area Network with that adapter?"), 1) or quit_global($in));
}
else
{
    $interface = $in->ask_from_list(_("Choose the network interface"),
				    _("Please choose what network adapter will be connected to your Local Area Network."),
				    \@all_cards,
				    );
    defined $interface or quit_global($in);
}
$interface =~ /(eth[0-9]+)/ or die("Internal error");
my $device = $1;
log::l("[drakgw] Choosing network card: $device\n");

grep(/$device/, @configured_devices) and
    ($in->ask_okcancel('', _("Warning, the network adapter is already configured.\nWould you like to reconfigure?")) or quit_global($in));


#- setup the address for the LAN

my $full_lan_address = "192.168.0.0";
#$::expert and ($full_lan_address = $in->ask_from_entry(_("Local Area Network specification"),
#						       _("You may now decide which class C network to use.\n"),
#						       _("Network:"), $full_lan_address,
#						       ) or $in->exit(0));
($full_lan_address =~ /^([0-9]+\.[0-9]+\.[0-9]+)\.0$/) or die "Invalid network.\n";
my $lan_address = $1;


#- test for potential conflict with other networks

foreach (@configured_devices)
{
    if ($_ ne $device)
    {
	local *IFCFG;
	my $ifcfg = "/etc/sysconfig/network-scripts/ifcfg-$_";
	open IFCFG, "$ifcfg" or die "Can't open $ifcfg";
	my @ifcfg_content = <IFCFG>;
	grep(/$lan_address/, @ifcfg_content) and
	    $in->ask_warn('', _("Potential LAN address conflict found in current config of $_!\n")) and quit_global($in);
	close IFCFG;
    }
}


#- test for potential conflict with previous firewall config

my @chain_rules;
(-f "/etc/sysconfig/ipchains" or ((-x "/sbin/ipchains") and (@chain_rules = `/sbin/ipchains -L`) and ($#chain_rules > 2)))
    and ($in->ask_okcancel(_("Firewalling configuration detected!"),
			   _("Warning! An existing firewalling configuration has been detected. You may need some manual fix after installation. Proceed?"), 1) or quit_global($in));


#- ask for confirmation
#
#$in->ask_okcancel(_("Internet Connection Sharing - setup"),
#		  _("The local network is about to be configured.\n") .
#		    "You will then be able to connect other computers to this network, with automatic ".
#		    "DHCP configuration."), 1) or $in->exit(0);

#- **********************************
#- * 2nd step: configure

$wait_configuring = $in->wait_message(_("Configuring..."), _("Configuring scripts, installing software, starting servers..."));

#- setup the /etc/sysconfig/network-script/ script

my $network_scripts = "/etc/sysconfig/network-scripts";
-f "$network_scripts/ifcfg-$device" and rename("$network_scripts/ifcfg-$device", "$network_scripts/old.ifcfg-$device");
my $ifcfg = "$network_scripts/ifcfg-$device";
local *IFCFG; open IFCFG, ">$ifcfg" or die "Can't open $ifcfg";
print IFCFG <<EOF;
DEVICE=$device
BOOTPROTO=static
IPADDR=$lan_address.1
NETMASK=255.255.255.0
NETWORK=$lan_address.0
BROADCAST=$lan_address.255
ONBOOT=yes
EOF
close IFCFG;


#- install and setup the RPM packages

my $rpms_to_install;
my %bin2rpm = ( "/sbin/ipchains" => "ipchains",
		"/usr/sbin/dhcpd" => "dhcp",
		$conf_linuxconf => "linuxconf",
		"/usr/sbin/named" => "bind",
		"/var/named/named.local" => "caching-nameserver" );

-e $_ or $rpms_to_install .= "$bin2rpm{$_} " foreach (keys %bin2rpm);
standalone::pkgs_install($rpms_to_install);
-e $_ or fatal_quit(_("Problems installing package $bin2rpm{$_}")) foreach (keys %bin2rpm);


#- setup the masquerading configuration

if (-f "/etc/rc.d/rc.firewall")
{
    local *RCFIREWALL;
    open RCFIREWALL, "/etc/rc.d/rc.firewall" or die "Can't open /etc/rc.d/rc.firewall";
    my @rcfirewall_content = <RCFIREWALL>;
    close RCFIREWALL;
    grep(/\/etc\/rc\.d\/rc\.firewall\.inet_sharing/, @rcfirewall_content) or push @rcfirewall_content, "# Added by drakgw\n[ -x /etc/rc.d/rc.firewall.inet_sharing ] && /etc/rc.d/rc.firewall.inet_sharing\n";
    open RCFIREWALL, ">/etc/rc.d/rc.firewall" or die "Can't open /etc/rc.d/rc.firewall";
    print RCFIREWALL @rcfirewall_content;
    close RCFIREWALL;
}
else
{
    local *RCFIREWALL; open RCFIREWALL, ">/etc/rc.d/rc.firewall" or die "Can't open /etc/rc.d/rc.firewall";
    print RCFIREWALL <<EOF;
#!/bin/sh
#
# Automatically generated by drakgw

[ -x /etc/rc.d/rc.firewall.inet_sharing ] && /etc/rc.d/rc.firewall.inet_sharing
EOF
    chmod 0700, "/etc/rc.d/rc.firewall";
}

-f $rc_firewall and rename($rc_firewall, "$rc_firewall.old");
local *RCFIREWALL; open RCFIREWALL, ">$rc_firewall" or die "Can't open $rc_firewall";
print RCFIREWALL <<EOF;
#!/bin/sh
#
# rc.firewall - Initial SIMPLE IP Masquerade test for 2.1.x and 2.2.x kernels using IPCHAINS
#
# Load all required IP MASQ modules
#
#   NOTE:  Only load the IP MASQ modules you need.  All current IP MASQ modules
#          are shown below but are commented out from loading.

# Needed to initially load modules
#
/sbin/depmod -a

# Supports the proper masquerading of FTP file transfers using the PORT method
#
/sbin/modprobe ip_masq_ftp

# Supports the masquerading of RealAudio over UDP.  Without this module,
#       RealAudio WILL function but in TCP mode.  This can cause a reduction
#       in sound quality
#
/sbin/modprobe ip_masq_raudio

# Supports the masquerading of IRC DCC file transfers
#
/sbin/modprobe ip_masq_irc


# Supports the masquerading of Quake and QuakeWorld by default.  This modules is
#   for for multiple users behind the Linux MASQ server.  If you are going to play
#   Quake I, II, and III, use the second example.
#
#   NOTE:  If you get ERRORs loading the QUAKE module, you are running an old
#   -----  kernel that has bugs in it.  Please upgrade to the newest kernel.
#
#Quake I / QuakeWorld (ports 26000 and 27000)
#/sbin/modprobe ip_masq_quake
#
#Quake I/II/III / QuakeWorld (ports 26000, 27000, 27910, 27960)
/sbin/modprobe ip_masq_quake 26000,27000,27910,27960


# Supports the masquerading of the CuSeeme video conferencing software
#
/sbin/modprobe ip_masq_cuseeme

#Supports the masquerading of the VDO-live video conferencing software
#
/sbin/modprobe ip_masq_vdolive


#CRITICAL:  Enable IP forwarding since it is disabled by default since
#
#           Redhat Users:  you may try changing the options in /etc/sysconfig/network from:
#
#                       FORWARD_IPV4=false
#                             to
#                       FORWARD_IPV4=true
#
echo "1" > /proc/sys/net/ipv4/ip_forward


# Dynamic IP users:
#
#   If you get your IP address dynamically from SLIP, PPP, or DHCP, enable this following
#       option.  This enables dynamic-ip address hacking in IP MASQ, making the life 
#       with Diald and similar programs much easier.
#
#echo "1" > /proc/sys/net/ipv4/ip_dynaddr


# MASQ timeouts
#
#   2 hrs timeout for TCP session timeouts
#  10 sec timeout for traffic after the TCP/IP "FIN" packet is received
#  160 sec timeout for UDP traffic (Important for MASQ'ed ICQ users) 
#
/sbin/ipchains -M -S 7200 10 160


# DHCP:  For people who receive their external IP address from either DHCP or BOOTP
#        such as ADSL or Cablemodem users, it is necessary to use the following
#        before the deny command.  The "bootp_client_net_if_name" should be replaced
#        the name of the link that the DHCP/BOOTP server will put an address on to?
#        This will be something like "eth0", "eth1", etc.
#
#        This example is currently commented out.
#
#
#/sbin/ipchains -A input -j ACCEPT -i bootp_clients_net_if_name -s 0/0 67 -d 0/0 68 -p udp

# Enable simple IP forwarding and Masquerading
#
#  NOTE:  The following is an example for an internal LAN address in the 192.168.0.x
#         network with a 255.255.255.0 or a "24" bit subnet mask.
#
#         Please change this network number and subnet mask to match your internal LAN setup
#
/sbin/ipchains -P forward DENY
/sbin/ipchains -A forward -s $lan_address.0/24 -j MASQ
EOF
close RCFIREWALL;
chmod 0700, $rc_firewall;


#- be sure that FORWARD_IPV4 is enabled in /etc/sysconfig/network

open SYSCONF_NETWORK, "$sysconf_network" or die "Can't open $sysconf_network";
my @sysconf_network_content = <SYSCONF_NETWORK>;
close SYSCONF_NETWORK;
($_ =~ /^FORWARD_IPV4=/ and $_="FORWARD_IPV4=true\n") foreach @sysconf_network_content;
grep(/^FORWARD_IPV4=/, @sysconf_network_content) or push @sysconf_network_content, "FORWARD_IPV4=true\n";
open SYSCONF_NETWORK, ">$sysconf_network" or die "Can't open $sysconf_network";
print SYSCONF_NETWORK @sysconf_network_content;
close SYSCONF_NETWORK;


#- setup the DHCP server

-f $dhcpd_conf and rename($dhcpd_conf, "$dhcpd_conf.old");
local *DHCPDCONF; open DHCPDCONF, ">$dhcpd_conf" or die "Can't open $dhcpd_conf";
print DHCPDCONF <<EOF;
subnet $lan_address.0 netmask 255.255.255.0 {
	# default gateway
	option routers $lan_address.1;
	option subnet-mask 255.255.255.0;

	option domain-name "homelan.org";
	option domain-name-servers $lan_address.1;

	range dynamic-bootp $lan_address.16 $lan_address.253;
	default-lease-time 21600;
	max-lease-time 43200;
}
EOF
close DHCPDCONF;


#- put the interface for the dhcp server in linuxconf config, for the /etc script of dhcpd

open CONF_LINUXCONF, "$conf_linuxconf" or die "Can't open $conf_linuxconf";
my @conf_linuxconf_content = <CONF_LINUXCONF>;
close CONF_LINUXCONF;
($_ =~ /^DHCP.interface/ and $_="DHCP.interface $device\n") foreach @conf_linuxconf_content;
grep(/DHCP.interface/, @conf_linuxconf_content) or push @conf_linuxconf_content, "DHCP.interface $device\n";
open CONF_LINUXCONF, ">$conf_linuxconf" or die "Can't open $conf_linuxconf";
print CONF_LINUXCONF @conf_linuxconf_content;
close CONF_LINUXCONF;

#- Set up /etc/cups/cupsd.conf to make the broadcasting of the printer info
#- working correctly: 
#- 
#-  1. ServerName <server's IP address>  # because clients do necessarily 
#-                                       # know the server's name
#-
#-  2. BrowseAddress <server's Broadcast IP> # broadcast printer info into
#-                                           # the local network.
#-
#- These steps are only done when the CUPS package is installed.

if (-f $cups_conf)
{
    open CONF_CUPS, "$cups_conf" or die "Can't open $cups_conf";
    my @cups_conf_content = <CONF_CUPS>;
    close CONF_CUPS;
    ($_ =~ /ServerName[^:]/ and $_="ServerName $lan_address.1\n") foreach @cups_conf_content;
    grep(/ServerName[^:]/, @cups_conf_content) or push @cups_conf_content, "ServerName $lan_address.1\n";
    grep(/^BrowseAddress $lan_address.255/, @cups_conf_content) or push @cups_conf_content, "BrowseAddress $lan_address.255\n";
    open CONF_CUPS, ">$cups_conf" or die "Can't open $conf_linuxconf";
    print CONF_CUPS @cups_conf_content;
    close CONF_CUPS;
}

#- start the daemons

start_daemons();


#- bye-bye message

undef $wait_configuring;

$in->ask_warn(_("Congratulations!"), 
	      _("Everything has been configured.
You may now share Internet connection with other computers on your Local Area Network, using automatic network configuration (DHCP)."));


log::l("[drakgw] Installation complete, exiting\n");
quit_global($in);

sub quit_global {
    my ($in)=@_;
    $::isEmbedded ? kill(USR1, $::CCPID) : $in->exit(0);
    goto begin
}



#-------------------------------------------------
#- $Log$
#- Revision 1.21  2001/02/08 10:11:37  damien
#- implemented or updated embedded mode
#-
#- Revision 1.20  2001/02/08 07:00:41  damien
#- added embedded and (ugly) wizard mode.
#-
#- Revision 1.19  2001/01/10 00:32:42  prigaux
#- use standalone and standalone::pkgs_install
#-
#- Revision 1.18  2000/12/16 16:13:34  prigaux
#- use ldetect-lst
#-
#- Revision 1.17  2000/11/13 15:48:33  gc
#- Integrate Till's patches for better work with Cups.
#-
#- Revision 1.16  2000/10/10 15:31:50  gc
#- make only one call to urpmi in order to install all the needed rpm's
#-