package VSD::Old;

# Perl module for logging and backing up config changes
#  made by VSD.
# Copyright (c) 2000 Idaya Ltd.
# Written by Sol Burt <sol@idaya.co.uk>

# This file is part of the Virtual Server Administrator (FreeVSD)

# FreeVSD is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2, or (at your option)
# any later version.

# FreeVSD 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 General Public License for more details.

# You should have received a copy of the GNU General Public License
# along with FreeVSD; see the file COPYING.  If not, write to
# the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
# Boston, MA 02111-1307, USA.


use strict;
use File::Copy;

use VSD;


caller() eq 'VSD::Config' or
  die "VSD::".__PACKAGE__." is intended 2 B used as a backend to VSD::COnfig";


my $default_conf_file = $VSD::path{vsdconf};



sub new {
    my $self = {};
    bless ( $self, shift );
    return $self->_init( @_ );
}

sub _init {
    my $self = shift;
    if (@_) {
        my %init = @_;
        @$self{keys %init} = values %init;
    }

  # Refer to specified conf file or use default...
    defined $self->{file} || 
	($self->{file} = $default_conf_file );
    (-f $self->{file}) || return undef;
    $self->read_conf || return undef;
    $self->_init_inheritance;
    $self->{new}++;
    return $self;
} 


sub getval {
    my $self = shift;
    my @conf_path = @_;

# FIX baaaaad:
    ( $conf_path[0] =~ /VirtualServer/i || 
      $conf_path[0] =~ /Partition/i || 
      $conf_path[0] =~ /Skel/i ) && do {
		my $root = shift @conf_path;
		if ( @conf_path ) { $conf_path[0] = $root.'.'.$conf_path[0] }
		else { return $self->hack( $root ) };
	    };

    my %conf = %{$self->{conf}};

    my $confr;
    defined ($confr = $conf{shift @conf_path}) || return undef;
    foreach( @conf_path ) {
	if ( ref($confr) eq 'HASH' ) {
	    defined ($confr = \$confr->{$_}) || return undef;
	}
        else { return undef }
    }
    return ( ref($confr) eq 'SCALAR' ? $$confr :
	     ( ref($confr) eq 'REF' ? (@$$confr,
				       (defined @{$self->{new_skels}} ? @{$self->{new_skels}} : () )) :
	       %$confr ) );
}

# (defined $self->{new_skels} ? $self->{new_skels} : )

sub hack {
    my $self = shift;
    my $root = shift;
    my @sections = keys %{$self->{conf}};
    my @wanted;
    foreach (@sections) {
	/^$root\.(.+)$/ && push @wanted, $1;
    }
    my %wanted;
    foreach (@wanted) {
	%{$wanted{$_}} = $self->getval ( $root, $_ );
    }
    return %wanted;
}

sub test {
    my $self = shift;

    return $self->{conf};
}

sub conf {
    my $self = shift;

    return $self->{conf};
}


# FIX Add config for sections and elements to give how
#      many etc. are allowed in vsd.conf.

sub setval {
    my $self = shift;
    my $value = pop;
    my @conf_path = @_;
    my $granddaddy = $conf_path[0];

    defined $value && $value ne '' or return undef;

# baaaaad:
    ( $conf_path[0] =~ /VirtualServer/i || 
      $conf_path[0] =~ /Partition/i || 
      $conf_path[0] =~ /Skel/i ) && do {
	  my $root = shift @conf_path;
	  if ( @conf_path ) { $conf_path[0] = $root.'.'.$conf_path[0] }
	  else { die "Easy Tiger, setting Sections not possible yet." };
      };

    unless ( defined $self->{conf}{$conf_path[0]} ) {
	$self->{conf}{$conf_path[0]} = {};
	push @{$self->{new_sections}}, $conf_path[0];
    }

    my $confr = $self->{conf}{shift @conf_path};
    foreach( @conf_path ) {
	if ( ref($confr) eq 'HASH' ) {
	    $_ eq 'Skel' && $granddaddy eq 'Partition' 
                         && (!is_element(@{$confr->{Skel}}, $value)) 
                         && (push @{$self->{new_skels}}, $value);
	    $_ eq 'Skel' && $granddaddy eq 'Global' && !defined $confr->{Skel} 
                         && ($self->{new_global_skel} = $value);
            $_ eq 'Skel' && $granddaddy eq 'VirtualServer' && !defined $confr->{Skel} 
                         && ($self->{new_skel} = $value);
            $_ eq 'HostingServer' && $granddaddy eq 'VirtualServer' 
                         && !defined $confr->{HostingServer}
                         && ($self->{new_host} = $value);
            $_ eq 'CertificateAuthorityServer' && $granddaddy eq 'VirtualServer' 
                         && !defined $confr->{CertificateAuthorityServer}
                         && ($self->{new_casvr} = $value);
                $confr = \$confr->{$_};
	}
        else { # die "$0: Bad parameters passed to setval: @conf_path $value" 
	    return undef }
    }
    if ( ref $confr eq 'REF' ) {
     # push @$$confr, "NEW::".$value;
    }
    else {
      $$confr = $value;      # FIX give error return value.
    }
    defined $self->{change} || do {
	require VSD::Change;
	($self->{change} = VSD::Change->new ( file => $self->{file} ));
    };
    $self->{change}->log( 'entry', @_, $value );

    $self->{new}--;

    $self->_init_inheritance;
  #  return ( ref($confr) eq 'SCALAR' ? $$confr : ( ref($confr) eq 'REF' ? @$$confr : %$confr ) );
    return $value;
}


sub is_element {
    my $value = pop;
    foreach my $skel (@_) {
	$skel eq $value && return 1;
    }
    return 0;
}


sub delval {
    my $self = shift;
    my @conf_path = @_;

# baaaaad:
    ( $conf_path[0] =~ /VirtualServer/i || 
      $conf_path[0] =~ /Partition/i || 
      $conf_path[0] =~ /Skel/i ) && do {
	  my $root = shift @conf_path;
	  if ( @conf_path ) { $conf_path[0] = $root.'.'.$conf_path[0] }
	  else { die "Easy Tiger, setting Sections not possible yet." };
      };

    my $confr = $self->{conf}{shift @conf_path};

    my $key = shift @conf_path;


    if ( ref $confr->{$key} eq 'ARRAY' ) {
	my $element = shift @conf_path;
	for ( my $c=0;$c<@{$confr->{$key}};$c++ ) {
	    ${$confr->{$key}}[$c] eq $element && (splice @{$confr->{$key}}, $c, 1);
    }
    } else {
        delete $confr->{$key};
    }

    defined $self->{change} || ($self->{change} = VSD::Change->new ( file => $self->{file} ));
    $self->{change}->log( 'entry', @_ );

    $self->_init_inheritance;

    $self->{new}--;
}





sub read_conf {
    my $self = shift;
    my $conf = $self->{file};

    defined $self->{old} && 
	die "$0: Can't call method read_conf twice on same object, yet."; # FIX 

    open IN, "$conf" or die "$0: Can't open $conf: $!";

  EEK: while (<IN>) {
      push @{$self->{old}}, $_;
      chomp;
      /^\s*#/ && next EEK;
      /<(.+?)>/ && element( $1, $self );
    }
    close IN;
 #   return %conf;
}



sub element {
    $_ = shift;
    my $self = shift;

    /^(\S+)(\s*(\S+))?/ or die "$0 Bad element name in vsd.conf";
    my $name = $1.((defined $3 && $3 ne '') ? ".$3" : '');
    while (<IN>) {
	push @{$self->{old}}, $_;
	chomp;
	/<\/.+>/ && return;
	/\S/ && data ( $name, $_, $self );
    }
}



sub data {
    my $parent = shift;
    $_ = shift;
    my $self = shift;

    /\s*#/ && return;
#    /^\s*(\S+)\s+(\S+)(\s+(\S+))?\s*?$/ or die "Bad data line in vsd.conf: $_";
    if ( /^\s*(\S+)\s+(\S+)(\s+(\S+))?\s*?$/ ) {
	# HACK...
	if ( $1 eq 'Skel' && (substr($parent, 0, 9) eq 'Partition') ) {
	    push @{$self->{conf}->{$parent}->{$1}}, $2;
	}
	else {
	    $self->{conf}->{$parent}->{$1} = $2.(defined $3 ? " $4" : '')
	}
    } elsif ( /^\s*(\S+)\s*$/ ){
	$self->{conf}->{$parent}->{$1} = undef;
    } else {
	return;# /^\s*(\S+)(\s*)$/ or return;
    }
}




sub DESTROY {
    my $self = shift;

    # write unsaved config to log and file.

    $self->{new} <= 0 && do {

     # HACK...
      $self->{skel_index} = -1; # FIX need counter for each partition soon...

#	copy $self->{file}, $self->{file}.'.tmp' or
#	    die "$0: Can't copy $self->{file} to $self->{file}.tmp: $!";
	open NEW, ">$self->{file}" or die "$0: $!";

	while ( $_ = shift @{$self->{old}} ) {
	    chomp;
	    /^\s*#/ && next;
	    /<(.+?)>/ && ($_ = welement( $1, $self ));
	}
        continue {
	    $_ =~ /\/Global/i && defined $self->{new_global_skel} 
                              && (print NEW "Skel $self->{conf}->{Global}->{Skel}\n");
            $_ =~ /\/VirtualServer/i && defined $self->{new_skel} 
                              && (print NEW "Skel $self->{new_skel}\n");
            $_ =~ /\/VirtualServer/i && defined $self->{new_host}
                              && (print NEW "HostingServer $self->{new_host}\n"); 
            $_ =~ /\/VirtualServer/i && defined $self->{new_casvr}
                              && (print NEW "CertificateAuthorityServer $self->{new_casvr}\n");
	    print NEW "$_\n";
	}

	  if ( defined $self->{new_sections} ) {
	      print NEW "\n";
	      foreach my $section ( @{$self->{new_sections}} ) {
		  my $element = $section;
		  if ( $section =~ /^([^\.\s]+?)\.([^\s]+)$/ ) {
		      print NEW "<$1 $2>\n";
		      $element = $1;
		  }
		  else {
		      print NEW "<$section>\n";
		  }
		  foreach my $key ( keys %{$self->{conf}{$section}} ) {
		      print NEW "$key $self->{conf}{$section}->{$key}\n"
			  if defined $self->{conf}{$section}->{$key};
		  }
		  print NEW "</$element>\n";
	      }
	  }

	close NEW;
    }
}

sub welement {
    print NEW "$_\n";
    my $element = shift;
    my $self = shift;
    $element =~ /Partition/ && do {
	foreach my $ns (@{$self->{new_skels}}) {
	    print NEW "Skel $ns\n";
	}
    };
    $element =~ /^(\S+)(\s*(\S+))?/ or die "Bad element name in vsd.conf";
    my $name = $1.((defined $3 && $3 ne '') ? ".$3" : '');
    while ( $_ = shift @{$self->{old}} ) {
	chomp;
	/<\/.+>/ && return $_;
	/\S/ && ($_ = wdata ( $name, $_, $self ));
    }
    continue {  print NEW "$_\n" unless $_ =~ /^\s*$/}
}

sub wdata {
    my $parent = shift;
    $_ = shift;
    my $self = shift;

    /\s*#/ && return $_;

    /Skel/i && $parent =~ /Partition/i && $self->{skel_index}++;  # FIX need counter for each partition soon...

  #  /^\s*(\S+)(\s*)$/ and return $_

  # Apologies for the next line to anyone who reads this. It evolved.

#   s/^\s*(\S+)\s+(\S+)(\s+(\S+))?\s*?$/(($1 eq 'Skel' &&
#					  (substr($parent, 0, 9) eq 'Partition')) ?
#	(defined ${$self->{conf}->{$parent}->{$1}}[$self->{skel_index}] ?
#        new_skels(\@{$self->{conf}->{$parent}->{Skel}})."$1 ".(${$self->{conf}->{$parent}->{$1}}[$self->{skel_index}]) : '' ) :
#	    (defined ($self->{conf}->{$parent}->{$1}) ? "$1 ".($self->{conf}->{$parent}->{$1}) : '') )/e

    s/^\s*(\S+)\s+(\S+)(\s+(\S+))?\s*?$/(($1 eq 'Skel' &&
					  (substr($parent, 0, 9) eq 'Partition')) ?
	(defined ${$self->{conf}->{$parent}->{$1}}[$self->{skel_index}] ?
        new_skels(\@{$self->{conf}->{$parent}->{Skel}})."$1 ".(${$self->{conf}->{$parent}->{$1}}[$self->{skel_index}]) : '' ) :
	    (defined ($self->{conf}->{$parent}->{$1}) ? "$1 ".($self->{conf}->{$parent}->{$1}) : '') )/e

    or do {
	if ( /^\s*(\S+)\s*$/ ) {
	    return $_;
	} else {
	    die "$0: Bad line in vsd.conf";
	}
    };

  return $_;
}



sub new_skels {
  my $skels = $_[0];
  my $data = '';
  while ($$skels[(@$skels)-1] =~ s/^NEW::(.+)$/$1/) {
    $data .= "Skel ".(pop @$skels)."\n";
  }
  return $data;
}


sub grid {

    return {
	    Global => {
		       UIDRange => '',
		       UIDAllocChunk => '',
		       PrimaryNS => '',
		       SecondaryNS => '',
		       ErrorLog => '',
		       BWBurst => '',
		       BWLimit => '',
		      },
	    Partition => {
			  name => '',
			  Mount => '',
			  MaxVS => '',
			  Skel => []
			 },
	    VirtualServer => {
			      name => '',
			      IP => '',
			      StartUID => '',
			      Users => '',
			      Status => '',
			      Quota => '',
			      Partition => '',
			      Skel => '',
			      AliasIP => [],
			      BWLimit => '',
			      BWBurst => '',
			      PrimaryNS => '',
			      SecondaryNS => '',
                              HostinServer => '',
                              CertificateAuthorityServer => ''
			     }
	   }
}

sub _init_inheritance {
    my $self = shift;
    defined $self->{conf} or die "Can't call _init_inheritance before {conf} is around";

    my %inheritance =
      (
       VirtualServer => {
			 StartUID => '0',
			 Users => '0',
			 Skel => '$self->{conf}{Global}->{Skel}',
			 BWLimit => '$self->{conf}{Global}->{BWLimit}',
			 BWBurst => '$self->{conf}{Global}->{BWBurst}',
			 PrimaryNS => '$self->{conf}{Global}->{PrimaryNS}',
			 SecondaryNS => '$self->{conf}{Global}->{SecondaryNS}'
			}
      );

    my @sections = keys %{$self->{conf}};

    foreach my $section ( @sections ) {
	$section =~ /VirtualServer/ && do {
	    my @keys = keys %{$inheritance{VirtualServer}};
	    foreach my $key ( @keys ) {
		defined $inheritance{VirtualServer}->{$key} &&
		  !defined $self->{conf}{$section}->{$key} &&
		    (
		     $self->{conf}{$section}->{$key} = eval $inheritance{VirtualServer}->{$key}
		    );
	    }
	};
    }
}





1;


