#!/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.


BEGIN {
  $SCRIPTSDIR = "/usr/share/setup-tool-backends/scripts";
  if ($SCRIPTSDIR =~ /^___scriptsdir__[_]/)
  {
      $SCRIPTSDIR = ".";
      $DOTIN = ".in";
  }
  
  require "$SCRIPTSDIR/general.pl$DOTIN";
  require "$SCRIPTSDIR/platform.pl$DOTIN";
  require "$SCRIPTSDIR/util.pl$DOTIN";
  require "$SCRIPTSDIR/file.pl$DOTIN";
  require "$SCRIPTSDIR/xml.pl$DOTIN";
  require "$SCRIPTSDIR/network.pl$DOTIN";
}


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

# --- Tool information --- #

$name = "network";
$version = "0.11.0";
@platforms = ("redhat-5.2", "redhat-6.0", "redhat-6.1", "redhat-6.2", "redhat-7.0", "redhat-7.1",
              
              "mandrake-7.1", "mandrake-7.2",
              
              "debian-2.2", "debian-woody",
              
              "suse-7.0", "turbolinux-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, $elem);
  # 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 ($elem = shift @$tree)
  {
    if ($elem eq "network") { &xml_parse_network (shift @$tree, \%hash); }
    else { &xst_report ("xml_unexp_tag", $elem); shift @$tree; }
  }

  return(\%hash);
}

# <network>...</network>

sub push_unique
{
  my ($arr, $val) = @_;
  my $i;

  foreach $i (@$arr)
  {
    return if $i eq $val;
  }

  push @$arr, $val;
}

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

  shift @$tree;  # Skip attributes.

  while ($elem = shift @$tree)
  {
    if    ($elem eq "auto")          { $$hash{"auto"} =        &xst_xml_get_pcdata (shift @$tree); }
    elsif ($elem eq "hostname")      { $$hash{"hostname"} =    &xst_xml_get_pcdata (shift @$tree); }
    elsif ($elem eq "gateway")       { $$hash{"gateway"} =     &xst_xml_get_pcdata (shift @$tree); }
    elsif ($elem eq "gatewaydev")    { $$hash{"gatewaydev"} =  &xst_xml_get_pcdata (shift @$tree); }
    elsif ($elem eq "domain")        { $$hash{"domain"} =      &xst_xml_get_pcdata (shift @$tree); }
    elsif ($elem eq "workgroup")     { $$hash{"workgroup"} =   &xst_xml_get_pcdata (shift @$tree); }
    elsif ($elem eq "description")   { $$hash{"description"} = &xst_xml_get_pcdata (shift @$tree); }
    elsif ($elem eq "winsserver")    { $$hash{"winsserver"} =  &xst_xml_get_pcdata (shift @$tree); }
    elsif ($elem eq "winsuse")       { $$hash{"winsuse"} =     &xst_xml_get_pcdata (shift @$tree); }
    elsif ($elem eq "smbuse")        { $$hash{"smbuse"} =      &xst_xml_get_pcdata (shift @$tree); }
    elsif ($elem eq "hostmatch")     { $$hash{"hostmatch"} =   &xst_xml_get_pcdata (shift @$tree); }
    elsif ($elem eq "nameserver")    { &push_unique (\@nameserver, &xst_xml_get_pcdata (shift @$tree)); }
    elsif ($elem eq "searchdomain")  { &push_unique (\@searchdomain, &xst_xml_get_pcdata (shift @$tree)); }
    elsif ($elem eq "order")         { push (@order, &xst_xml_get_pcdata (shift @$tree)); }
    elsif ($elem eq "statichost")    { &xml_parse_statichost (shift @$tree, \%statichost); }
    elsif ($elem eq "interface")     { &xst_network_xml_parse_interface (shift @$tree, \%interface); }
    elsif ($elem eq "dialing")       { &xml_parse_dialing (shift @$tree, \%dialing); }
    elsif ($elem eq "dialinstalled") { shift @$tree; }
    elsif ($elem eq "smbinstalled")  { shift @$tree; }
    elsif ($elem eq "smartdhcpcd")   { shift @$tree; }
    else                             { &xst_report ("xml_unexp_tag", $elem); 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, $statichost) = @_;
  my ($ip, @alias, $elem);

  shift @$tree;

  while ($elem = shift @$tree)
  {
    if    ($elem eq "ip")    { $ip = &xst_xml_get_pcdata (shift @$tree); }
    elsif ($elem eq "alias") { push(@alias, &xst_xml_get_pcdata (shift @$tree)); }
    else                     { &xst_report ("xml_unexp_tag", $elem); shift @$tree; }
  }

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

sub xml_parse_dialing
{
  my ($tree, $dialing) = @_;
  my (%hash, $name, $elem);

  shift @$tree;
  
  while ($elem = shift @$tree)
  {
    $hash{$elem} = &xst_xml_get_pcdata (shift @$tree);
  }

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

# --- XML printing --- #

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

sub xml_print
{
  my ($h) = @_;
  my @scalar_keys =
      qw(auto hostname gateway gatewaydev gwdevunsup domain 
         hostmatch workgroup description winsserver winsuse smbuse
         smartdhcpcd smbinstalled dialinstalled);
  my @array_keys =
      qw(nameserver searchdomain order);

  &xst_xml_print_begin ();

  # Hostname, domain, search domains, nameservers.

  &xst_xml_print_scalars ($h, @scalar_keys);
  &xst_xml_print_arrays ($h, @array_keys);
  &xml_print_statichost ($h);

  &xst_xml_print_hash_hash ($$h{"interface"}, "interface");
  &xst_xml_print_hash_hash ($$h{"dialing"}, "dialing");

  &xst_xml_print_end ();
}


# Top-level actions.


sub get
{
  my $hash;

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

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


sub set
{
  my $hash;
  
  $hash = &xml_parse ();
  &xst_network_conf_set ($hash);
  &xst_report_end ();
}


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


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


sub enable_iface
{
  my ($tool, $iface, $enabled) = @_;
  my (%dist_attrib, $iface_set);
  my %hash = ("file" => $iface);
  
  %dist_attrib = &xst_network_get_interface_replace_table ();
  $iface_set = $dist_attrib{"iface_set"};
  &$iface_set (\%hash, undef, $enabled, 1);
  # Don't forget to do xst_end when the reports are over!
  &xst_report_end ();
  # XML output would come here, but this directive returns no XML.
}


sub list_ifaces
{
  my ($tool) = @_;
  my ($ifaces, $iface, @arr);

  $ifaces = &xst_network_interfaces_get_info ();
  
  foreach $iface (keys %$ifaces)
  {
    push @arr, $$ifaces{$iface};
  }
  
  &xst_report_end ();
  &xst_xml_print_begin ("network-ifaces");
  &xst_xml_print_structure (\@arr, "interface");
  &xst_xml_print_end ("network-ifaces");
}


# --- Main --- #

# get, set and filter are special cases that don't need more parameters than a ref to their function.
# Read general.pl.in:xst_run_directive to know about the format of this hash.

$directives = {
  "get"          => [ \&get,          [], "" ],
  "set"          => [ \&set,          [], "" ],
  "filter"       => [ \&filter,       [], "" ],
  "list_ifaces"  => [ \&list_ifaces,  [],
                      "List interfaces and active/inactive status." ],
  "enable_iface" => [ \&enable_iface, [ "interface", "enabled" ],
                      "Immediatly enable or disable a given interface. " .
                      "interface is the file tag value, enabled is 1 or 0." ]
    };

$tool = &xst_init ($name, $version, $description, $directives, @ARGV);
&xst_platform_ensure_supported ($tool, @platforms);
&xst_run ($tool);
