#!/usr/bin/env perl
#-*- Mode: perl; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-

# Network configurator. Designed to be architecture and distribution independent.
#
# Copyright (C) 2000-2001 Ximian, Inc.
#
# Authors: Hans Petter Jansson <hpj@ximian.com>
#          Michael Vogt <mvo@debian.org> (Debian Support)
#          Arturo Espinosa <arturo@ximian.com>
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU Library General Public License as published
# by the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU Library General Public License for more details.
#
# You should have received a copy of the GNU Library General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.

# Best viewed with 100 columns of width.

# Configuration files affected:
#
# /etc/resolv.conf
# /etc/host.conf
# /etc/hosts
# /etc/sysconfig/network
# /etc/rc.config
# /etc/smb.conf

# Running programs affected:
#
# smbd
# nmbd
# ifconfig: check current interfaces and activate/deactivate.


require "/usr/share/ximian-setup-tools/scripts/general.pl";
require "/usr/share/ximian-setup-tools/scripts/platform.pl";
require "/usr/share/ximian-setup-tools/scripts/util.pl";
require "/usr/share/ximian-setup-tools/scripts/file.pl";
require "/usr/share/ximian-setup-tools/scripts/xml.pl";
require "/usr/share/ximian-setup-tools/scripts/network.pl";


# Debug stuff
#$xst_prefix = "/tmp/y";
#open STDERR, ">/tmp/err";

# --- Tool information --- #

$name = "network";
$version = "0.1.0";
@platforms = ("redhat-5.2", "redhat-6.0", "redhat-6.1", "redhat-6.2", "redhat-7.0");

$description =<<"end_of_description;";
       Configures all network parameters and interfaces.
end_of_description;

$progress_max = 10;


# --- XML parsing ---

# Scan XML from standard input to an internal tree.

sub xml_parse
{
  my $tree, %hash;
  # Scan XML to tree.

  $tree = &xst_xml_scan;

  # Walk the tree recursively and extract configuration parameters.
  # This is the top level - find and enter the "network" tag.

  while (@$tree)
  {
    if ($$tree[0] eq "network") { &xml_parse_network ($$tree[1], \%hash); }

    shift @$tree;
    shift @$tree;
  }

  return(\%hash);
}

# <network>...</network>

sub xml_parse_network
{
  my $tree = $_[0];
  my $hash = $_[1];
  my (@searchdomain, @nameserver, @order, %statichost, %interface, %dialing);

  shift @$tree;  # Skip attributes.

  while (@$tree)
  {
       if ($$tree[0] eq "auto")            { $$hash{"auto"} = &xst_xml_get_pcdata ($$tree[1]); }
    elsif ($$tree[0] eq "forward")         { $$hash{"forward"} = &xst_xml_get_pcdata ($$tree[1]); }
    elsif ($$tree[0] eq "hostname")        { $$hash{"hostname"} = &xst_xml_get_pcdata ($$tree[1]); }
    elsif ($$tree[0] eq "gateway")         { $$hash{"gateway"} = &xst_xml_get_pcdata ($$tree[1]); }
    elsif ($$tree[0] eq "gateway_dev")     { $$hash{"gateway_dev"} = &xst_xml_get_pcdata ($$tree[1]); }
    elsif ($$tree[0] eq "domain")          { $$hash{"domain"} = &xst_xml_get_pcdata ($$tree[1]); }
    elsif ($$tree[0] eq "nameserver")      { push (@nameserver, &xst_xml_get_pcdata ($$tree[1])); }
    elsif ($$tree[0] eq "searchdomain")    { push (@searchdomain, &xst_xml_get_pcdata ($$tree[1])); }
    elsif ($$tree[0] eq "domainname")      { $$hash{"domainname"} = &xst_xml_get_pcdata ($$tree[1]); }
    elsif ($$tree[0] eq "order")           { push (@order, &xst_xml_get_pcdata ($$tree[1])); }
    elsif ($$tree[0] eq "hostmatch")       { $$hash{"hostmatch"} = &xst_xml_get_pcdata ($$tree[1]); }
    elsif ($$tree[0] eq "statichost")      { &xml_parse_statichost ($$tree[1], \%statichost); }
    elsif ($$tree[0] eq "workgroup")       { $$hash{"workgroup"} = &xst_xml_get_pcdata ($$tree[1]); }
    elsif ($$tree[0] eq "description")     { $$hash{"description"} = &xst_xml_get_pcdata ($$tree[1]); }
    elsif ($$tree[0] eq "winsserver")      { $$hash{"winsserver"} = &xst_xml_get_pcdata ($$tree[1]); }
    elsif ($$tree[0] eq "winsuse")         { $$hash{"winsuse"} = &xst_xml_get_pcdata ($$tree[1]); }
    elsif ($$tree[0] eq "interface")       { &xml_parse_interface ($$tree[1], \%interface); }
    elsif ($$tree[0] eq "dialing")         { &xml_parse_dialing ($$tree[1], \%dialing); }

    shift @$tree;
    shift @$tree;
  }

  $$hash{"order"} = \@order unless $#order < 0;
  $$hash{"searchdomain"} = \@searchdomain unless $#searchdomain < 0;
  $$hash{"nameserver"} = \@nameserver unless $#nameserver < 0;
  $$hash{"statichost"} = \%statichost unless scalar keys %statichost == 0;
  $$hash{"interface"} = \%interface unless scalar keys %interface == 0;
  $$hash{"dialing"} = \%dialing unless scalar keys %dialing == 0;
}

# <network><statichost>...</statichost></network>

sub xml_parse_statichost
{
  my $tree = $_[0];
  my $statichost = $_[1];
  my $ip;
  my @alias;

  shift @$tree;

  while (@$tree)
  {
    if    ($$tree[0] eq "ip")    { $ip = &xst_xml_get_pcdata($$tree[1]); }
    elsif ($$tree[0] eq "alias") { push(@alias, &xst_xml_get_pcdata($$tree[1])); }

    shift @$tree;
    shift @$tree;
  }

  if ($ip =~ /[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+/)
  {
    $$statichost{$ip} = \@alias;
  }
}

# <interface>...</interface>

sub xml_parse_interface
{
  my $tree = $_[0];
  my $interface = $_[1];
  my %hash;
  my $dev;

  shift @$tree;
  
  while (@$tree)
  {
    $hash{$$tree[0]} = &xst_xml_get_pcdata ($$tree[1]);

    shift @$tree;
    shift @$tree;
  }

  $hash{"file"} = &xst_network_get_file ($hash{"dev"}) if !exists $hash{"file"};
  $dev = $hash{"file"};
  $$interface{$dev} = \%hash;
}

sub xml_parse_dialing
{
  my $tree = $_[0];
  my $dialing = $_[1];
  my %hash;
  my $name;

  shift @$tree;
  
  while (@$tree)
  {
    $hash{$$tree[0]} = &xst_xml_get_pcdata ($$tree[1]);

    shift @$tree;
    shift @$tree;
  }

  $name = $hash{"name"};
  $$dialing{$name} = \%hash;
}

# --- XML printing --- #

sub xml_print_scalars
{
  my ($h, @scalar_keys) = @_;
  my $i, $val;

  while ($i = shift @scalar_keys)
  {
    $val = &xst_xml_quote ($$h{$i});
    &xst_xml_print ("<$i>$val</$i>\n") if exists $$h{$i};
  }
}

sub xml_print_arrays
{
  my ($h, @array_keys) = @_;
  my $i, $j, $val;
  
  foreach $i (@array_keys)
  {
    if (exists $$h{$i})
    {
      &xst_xml_vspace ();
      foreach $j (@{$$h{$i}})
      {
        $val = &xst_xml_quote ($j);
        &xst_xml_print ("<$i>$val</$i>\n");
      }
    }
  }
}

sub xml_print_statichost
{
  my ($h) = $_[0];
  my $statichost, $i, $j, $val;
  
  &xst_xml_vspace ();
  foreach $i (keys %{$$h{"statichost"}})
  {
    $statichost = $ {$$h{"statichost"}}{$i};
    &xst_xml_print ("<statichost>\n");
    &xst_xml_enter ();
    $val = &xst_xml_quote ($i);
    &xst_xml_print ("<ip>$val</ip>\n");
    foreach $j (@$statichost)
    {
      $val = &xst_xml_quote ($j);
      &xst_xml_print ("<alias>$val</alias>\n");
    }
    &xst_xml_leave ();
    &xst_xml_print ("</statichost>\n");
  }
}

sub xml_print_hash
{
  my ($h, $tag) = @_;
  my $hash, $i, $j, $val;
  
  foreach $i (keys %$h)
  { 
    $hash = $$h{$i};
    &xst_xml_vspace ();
    &xst_xml_print ("<$tag>\n");
    &xst_xml_enter ();
    
    foreach $j (keys (%$hash))
    {
      $val = &xst_xml_quote ($$hash{$j});
      &xst_xml_print ("<$j>$val</$j>\n");
    }
    
    &xst_xml_leave ();
    &xst_xml_print ("</$tag>\n");
  } 
}

sub xml_print
{
  my $h = $_[0];
  my @scalar_keys =
	 (auto, forward, hostname, gateway, gateway_dev, domain, domainname,
	  hostmatch, workgroup, description, winsserver, winsuse);
  my @array_keys =
	 (nameserver, searchdomain, order);

  print "<?xml version='1.0' encoding='ISO-8859-1' standalone='yes'?>\n";
  print "<!DOCTYPE network []>\n\n";
  print "<network>\n";
  &xst_xml_vspace ();
  &xst_xml_enter ();

  # Hostname, domain, search domains, nameservers.

  &xml_print_scalars ($h, @scalar_keys);
  &xml_print_arrays ($h, @array_keys);
  &xml_print_statichost ($h);

  &xml_print_hash ($$h{"interface"}, "interface");
  &xml_print_hash ($$h{"dialing"}, "dialing");
  
  &xst_xml_leave ();
  &xst_xml_vspace ();
  &xst_xml_print ("</network>\n");
}


# Top-level actions.


sub get
{
  my $hash;

  &xst_begin ();
  
  # network interface stuff
  $hash = &xst_network_conf_get ();

  &xst_end();
  &xml_print ($hash);
}


# --- Set (write) config --- #

sub set_immediate
{
  # Set hostname via utility, in case the config files aren't enough.

  if ($cf_hostname ne "")
  {
    if (&xst_run("hostname $cf_hostname >/dev/null 2>/dev/null"))
    {
      &xst_report_warning(3, "Failed to set runtime hostname");
    }
    else
    {
      &xst_report_info(1, "Runtime hostname set");
    }
  }
  else
  {
    &xst_report_warning(4, "No hostname specified; runtime hostname not set");
  }

  # Reload SMB configuration.
  
  &xst_service_restart(80, "-D", "samba", "smb", "smbd");

  # Activate or deactivate network interfaces

  &set_active_interfaces ();
}


sub set
{
  my $hash;
  
  &xst_begin ();
  $hash = &xml_parse ();

  # check the success result of this.
  &xst_network_conf_set ($hash);
#  &be_ensure_local_host_entry ($$hash{"hostname"});

  &xst_end ();
}


# --- Filter config: XML in, XML out --- #


sub filter
{
  my $hash;
  
  &xst_begin ();
  $hash = &xml_parse ();
  &xst_end ();
  &xml_print ($hash);
}


# --- Main --- #

&xst_init ($name, $version, $description, @ARGV);
&xst_platform_ensure_supported (@platforms);

# Do our thing.

if    ($xst_operation eq "get")    { &get; }
elsif ($xst_operation eq "set")    { &set; }
elsif ($xst_operation eq "filter") { &filter; }
