:
#
# Copyright (c) 2003 by James A. McQuillan (McQuillan Systems)
#
# Permission is hereby granted, free of charge, to any person obtaining a
# copy of this software and associated documentation files (the "Software"),
# to deal in the Software without restriction, including without limitation
# the rights to use, copy, modify, merge, publish, distribute, sublicense,
# and/or sell copies of the Software, and to permit persons to whom the
# Software is furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
# MCQUILLAN SYSTEMS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
# WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF
# OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.
#
# 07/25/2003 -Jim McQuillan - Wrote new installer, inspired by the
#                             Ximian installer.
#
##############################################################################

##############################################################################
#
# Make sure this script is being run by the superuser.
#
if [ "${LOGNAME}" != "root" ]; then
    echo
    echo "You MUST run this script as superuser!"
    echo
    echo "If you logged in as a normal user then su'ed to become"
    echo "superuser, you also need to add a hyphen '-' to the"
    echo "su command".
    echo
    exit 1
fi

PID=$$
SCRIPT_FILE=/tmp/ltsp_installer.${PID}
cat <<'EOF' >${SCRIPT_FILE}

use strict;

my $version = "0.06";

sub get_conf_value {
  my $var = shift;
  my $val = "";
  if( @_ > 0 ){
    $val  = shift;
  }
  my $config_file = "/etc/ltsp.conf";

  if ( -f "$config_file" ){
    open FH, "<$config_file" or die "Couldn't open $config_file: $!";
    while(<FH>){
      if( /^\s*${var}\s*=/ ){
        s/^\s*${var}\s*=\s*//;
        my @fields = split /\s+/;
        $val = $fields[0];
      }
    }
    close(FH);
  }
  return($val);
}

sub set_conf_value {

  my $var = shift;
  my $val = shift;

  my $filename = "/etc/ltsp.conf";
  my $match = 0;

  if( -f "$filename" ){
    #
    # If the file already exists, we open it for updating
    #
    open( FH, "+<$filename") or die "Unable to open $filename: $!";
    my @lines = <FH>;              # Suck the entire file into an array
    truncate( FH, 0 );             # Truncate the file, to empty it
    seek( FH, 0, 0 );              # Position file pointer to the beginning

    for( my $i = 0; $i < @lines; $i++ ){
      if( $lines[$i] =~ /^\s*${var}/ ){
        printf FH "%s=%s\n", ${var}, ${val};
        $match = 1;
      }
      else{
        print FH $lines[$i];
      }
    }
  }
  else{
    #
    # If the file didn't already exist, we open it to create it
    #
    open( FH, ">$filename" ) or die "Unable to create $filename: $!";
    print FH  ":\n#\n# Configuration variables for LTSP\n#\n\n";
  }
  if( ! $match ){
    printf FH "%s=%s\n", ${var}, ${val};
  }
  close(FH);
}

open(INPUT,"</dev/tty") or die "Unable to open /dev/tty";

my $retrieve_proc;

my @groups;

my $cachedir           = "/var/cache/ltsp";
my $default_installdir = get_conf_value("LTSP_DIR", "/opt/ltsp");

my $installdir         = "";

#
# As more mirrors are created for LTSP, add the entries here
#
my $locations =
    [ { loc => "http://www.ltsp.org",             type => "wget"  },
      { loc => "Local disk (current directory)",  type => "cp"    },
    ];

my $manifest_loc = get_location();

if( ! $manifest_loc ){
  die("\nError getting location!\n\n");
}

if( $manifest_loc eq "q" ){
  print("Exiting...\n\n");
  exit 1;
}

if( $manifest_loc->{type} eq "wget" ){
  $retrieve_proc = \&retrieve_with_wget;
}
elsif( $manifest_loc->{type} eq "cp" ){
  $retrieve_proc = \&retrieve_with_cp;
}
else{
  die("\nInvalid location type!\n\n");
}

if( &$retrieve_proc( "manifest" ) ){
  die("\nError retrieving the manifest file!\n\n");
}

if( load_manifest( ) ne 0 ){
  die("\nError loading the manifest file!\n\n");
}

my $install_grp = get_group_to_install();

if( $install_grp ){

  my $install_component = get_component_to_install( $install_grp );

  if( $install_component ){
    if( $install_component eq "q" ){
      print("Exiting...\n\n");
      exit 1;
    }

    while(1){

      if( $install_component eq "a" ){
        printf("\n\nAre you sure you want to install ALL components? ");
      }
      else{
        printf("\n\nAre you sure you want to install %s? ",
                 $install_component->{name});
      }

      my $answer = <INPUT>;
      chomp $answer;
      next if ! $answer;
      $answer = lc $answer;
      if( $answer =~ /^y/i ){
        last;
      }
      elsif( $answer =~ /^n/i ){
        print("Exiting...\n");
        exit 1;
      }
      invalid_answer();
    }

    while( 1 ){
      printf("\n");
      printf("In which directory would you like to place LTSP? [$default_installdir] ");
      $installdir = <INPUT>;
      chomp $installdir;
      $installdir = $default_installdir if ! $installdir;

      if( -d $installdir ){
        if( -d "$installdir/i386" ){
          printf("\n");
          printf("$installdir already exists, and it appears to contain a\n");
          printf("previous installation of LTSP.\n");
          while(1){
            printf("\n");
            printf("Are you sure you want to continue installing to $installdir? ");
            my $answer = <INPUT>;
            chomp $answer;
            next if ! $answer;
            $answer = lc $answer;
            if( $answer =~ /^y/i ){
              last;
            }
            elsif( $answer =~ /^n/i ){
              print("Exiting...\n");
              exit 1;
            }
            invalid_answer();
          }
        }
        last;
      }
      else{
        while(1){
          printf("$installdir does not exist, create? ");
          my $answer = <INPUT>;
          chomp $answer;
          next if ! $answer;
          $answer = lc $answer;
          if( $answer =~ /^y/i ){
            system("mkdir -p $installdir");
            last;
          }
          elsif( $answer =~ /^n/i ){
            print("Exiting...\n");
            exit 1;
          }
          invalid_answer();
        }
        last if -d $installdir;
      }
    }

    set_conf_value( "LTSP_DIR", $installdir );

    if( $install_component eq "a" ){
      printf("\n\nGoing to install ALL components\n");
      my $comps = $install_grp->{components};
      for( my $idx = 0; $idx < @$comps; $idx++ ){
        install_component( $comps->[$idx] );
      }
    }
    else{
      install_component( $install_component );
    }

    if( ! -d "/var/opt" ){
      system( "mkdir /var/opt" );
    }

    if( ! -d "/var/opt/ltsp" ){
      system( "mkdir /var/opt/ltsp" );
    }

    if( ! -d "/var/opt/ltsp/swapfiles" ){
      system( "mkdir /var/opt/ltsp/swapfiles" );
    }

  }
}

###############################################################################

sub get_location {

  while ( 1 ){

    header();

    print("Welcome to the LTSP Installer.\n\n");

    print("This program will retrieve the LTSP packages and install them\n");
    print("for you.\n\n");

    print("Please select where you want to retrieve the packages from:\n\n");

    for( my $idx = 0; $idx < @$locations; $idx++ ){
      printf( " %2d)  %s\n\n", $idx+1, $locations->[$idx]->{loc} );
    }

    print( "  q)  Quit installation\n\n");

    print "Make a selection: ";

    my $answer = <INPUT>;
    chomp $answer;

    next if ! $answer;

    $answer = lc $answer;

    if( $answer eq "q" ){
      return( "q" );
    }
    if( $answer =~ m/^[0-9]+$/ ){
      if( $answer >= 1 && $answer <= @$locations ){
        return( $locations->[$answer-1] );
      }
    }
    invalid_answer();
  }
}

sub retrieve_with_wget {

  my $filename = shift;

  make_cachedir();  

  my $url = "";

  if( $filename eq "manifest" ){
    $url = "http://www.ltsp.org/manifest";
  }
  else{
    $url = "http://www.ltsp.org/$filename";
    ##$url = "ftp://ftp.sourceforge.net/pub/sourceforge/$filename";
  }

  my @args = ( "wget",
               "--tries=5",
               "--passive-ftp",
               "--directory-prefix=" . "$cachedir",
               $url );
  if( system(@args) != 0 ){
    die("md5sum of %s failed!\n", $url);
  }

  return(0);

}
#------------------------------------------------------------------------------

sub retrieve_with_cp {

  my $filename = shift;

  printf("Retrieving: $filename\n");

  #
  # We have to get a manifest file from this location.
  #

  if( ! -f $filename ){
    print("Error!  No $filename found in the current directory\n");
    exit 1;
  }

  make_cachedir();  

  my $cmd = sprintf("cp %s %s", $filename, $cachedir );
  system($cmd);

  return(0);
}

#------------------------------------------------------------------------------

sub make_cachedir {
  system("mkdir -p $cachedir") if ! -d $cachedir;
}

#------------------------------------------------------------------------------

sub load_manifest {

  my $manifest_file = "$cachedir/manifest";

  open( MANIFEST, "<$manifest_file" )
     or die("Unable to open $manifest_file: $!\n");

  my $newgrp_flag   = 0;
  my $cur_group     = "";
  my $cur_component = "";
  my $cur_section   = "";
  my $pkg;

  # @groups is an array that contains one or more groups
  # 
  # A Group is a hash, which contains:
  # 
  #   A name
  #   A description
  #   reference to an array of one or more components
  # 
  # A Component is a hash, which contains:
  # 
  #   A name
  #   A description
  #   Reference to an array of one or more packages
  # 
  # A Package is a hash, which contains:
  # 
  #   A filename
  #   An md5 checksum
  # 

  while(<MANIFEST>){

    chomp;                                        # Remove the trailing newline

    s/#.*$//;                                     # Strip out comments

    next if( /^\s*$/ );                           # Skip blank lines

    if( /\[group\]/i ) {
      $cur_section = "group";
      if( $cur_group ){
        if( $cur_component ){
          push @{$cur_group->{components}}, $cur_component;
          $cur_component = "";
        }
        push @groups, $cur_group;
      }
      $cur_group = {};                            # Create a hash to hold
                                                  # our new group.
    }
    elsif( /\[component\]/i ){
      $cur_section = "component";
      if( $cur_component ){
        push @{$cur_group->{components}}, $cur_component;
      }
      $cur_component = {};                        # Create a hash to hold
                                                  # our new component
      $cur_component->{pkgs} = [];
    }
    elsif( /.*=.*/ ){                             # Look for a name=value pair
      my ($keyword, $value ) = split /\s*=\s*/;
      $keyword =~ s/\s*//g;                       # Remove whitespace
      $value   =~ s/^\s*//g;                      # Remove leading whitespace
      $value   =~ s/\s*$//g;                      # Remove trailing whitespace

      for( $keyword ){
        if( /name/i ){
          if( $cur_section eq "group" ){
            $cur_group->{name} = $value;
          }
          elsif( $cur_section eq "component" ){
            $cur_component->{name} = $value;
          }
        }
        elsif( /desc/i ){
          if( $cur_section eq "group" ){
            $cur_group->{desc} = $value;
          }
          elsif( $cur_section eq "component" ){
            $cur_component->{desc} = $value;
          }
        }
        elsif( /pkg/i ){
          $pkg = {};
          $pkg->{name} = $value;
        }
        elsif( /md5sum/i ){
          $pkg->{md5sum} = $value;
          push @{$cur_component->{pkgs}}, $pkg;
        }
        else{
          printf("Warning, unknown keyword '$_'\n");
        }
      }
    }
  }

  if( $cur_group ){
    if( $cur_component ){
      push @{$cur_group->{components}}, $cur_component;
    }
    push @groups, $cur_group;
  }
  close( MANIFEST );
  return(0);
}

sub dump_tables {
  #
  # Dump the tables out for debugging
  #
  printf("\n\n");

  for( my $idx = 0; $idx < scalar(@groups); $idx++ ){

    my $group = $groups[$idx];

    printf( "%s - %s\n", $group->{name}, $group->{desc} );

    my $components = $group->{components};   # Get a reference to
                                             # the components array

    for( my $i = 0; $i < scalar( @$components ); $i++ ){

        my $component = $components->[$i];

        printf("  %s - %s\n", $component->{name}, $component->{desc} );

        my $pkgs = $component->{pkgs};       # Get a reference to the pkgs array

        for( my $pkg_idx = 0; $pkg_idx < scalar( @$pkgs ) ; $pkg_idx++ ){
            my $pkgname = $pkgs->[$pkg_idx]{name};
            my $md5sum  = $pkgs->[$pkg_idx]{md5sum};
            printf(" %s", $pkgname);
        }
        printf("\n\n");
    }
  }
  printf("\n\n");
}


sub get_group_to_install {
  while ( 1 ){
    header();
    printf("Select which group of packages you want to install:\n\n");

    for( my $group_idx = 0; $group_idx < scalar(@groups); $group_idx++ ){
      my $group = $groups[$group_idx];
      printf( "  %d)  %-10s %s\n\n", $group_idx+1,
                                     $group->{name},
                                     $group->{desc} );
    }
    printf("Make a selection (Q=Quit): ");

    my $answer = <INPUT>;
    chomp $answer;

    next if ! $answer;

    $answer = lc $answer;

    if( $answer eq "q" ){
      return;
    }
    elsif( $answer =~ m/^[0-9]+$/ ){
      if( $answer >= 1 && $answer <= scalar(@groups) ){
        return( $groups[$answer-1] );
      }
    }
    invalid_answer();
  }
}

sub get_component_to_install {

  my $cur_group = shift;

  my $comps = $cur_group->{components};

  return $comps->[0] if( @$comps == 1 );

  while ( 1 ){
    header();
    print "This group contains more than 1 component, which component\n";
    print "do you want to install:\n\n";

    for( my $comp_idx = 0; $comp_idx < scalar(@$comps); $comp_idx++ ){
      my $comp = $comps->[$comp_idx];
      printf( "  %2d)  %s\n        %s\n\n",
              $comp_idx+1, $comp->{name}, $comp->{desc} );
    }
    printf("Make a selection (Q=Quit, A=All): ");

    my $answer = <INPUT>;
    chomp $answer;

    next if ! $answer;

    $answer = lc $answer;

    return("q") if( $answer eq "q" );
    return("a") if( $answer eq "a" );

    if( $answer =~ m/^[0-9]+$/ ){
      if( $answer >= 1 && $answer <= scalar(@$comps) ){
        return( $comps->[$answer-1] );
      }
    }
    #
    # If we make it this far, then it's an invalid answer
    #
    invalid_answer();
  }
}

sub header {
  system("tput clear");
  print "LTSP Installer - v$version\n\n";
}

sub invalid_answer {
  print("\a\nInvalid Answer!\n");
  sleep(1);
}

sub install_component {
  my $comp = shift;

  printf( "Installing %s\n", $comp->{name} );

  # Notes on installing packages:
  # 
  #  Loop through the packages
  #    If the file exist in the cache
  #       test the checksum
  #       if the checksum is bad
  #         delete the file from the cache
  #         download the file
  #         test the checksum
  #         if the checksum is bad
  #           Display error message and quit
  #         endif
  #       endif
  #    else
  #       download the file
  #       test the checksum
  #       if the checksum is bad
  #         Display error message and quit
  #       endif
  #    endif
  #
  #  If download/testing was good:
  #    Loop through the packages
  #      install each package
  #
 
  my $pkgs = $comp->{pkgs};

  #
  # Download and test the packages
  #
  printf("Downloading/Testing the packages:\n");
  for( my $idx = 0; $idx < @$pkgs; $idx++ ){

    my $filename = $pkgs->[$idx]->{name};
    my $target   = $filename;
    $target      =~ s/.*\///;
    my $md5sum   = $pkgs->[$idx]->{md5sum};

    my $fullpath = $cachedir . "/" . $target;
    if( -f $fullpath ){
      if( test_checksum( $fullpath, $md5sum ) != 0 ){
        unlink($fullpath);
        &$retrieve_proc( $filename );
        if( test_checksum( $fullpath, $md5sum ) != 0 ){
          die( "File $fullpath failed the checksum test\n");
        }
      }
    }
    else{
      &$retrieve_proc( $filename );
      if( test_checksum( $fullpath, $md5sum ) != 0 ){
        die( "File $fullpath failed the checksum test\n");
      }
    }
  } 
  printf("\n");

  #
  # Create the destination directory, if needed
  #
##  system("mkdir -p $installdir") if ! -d $installdir;

  #
  # Install the packages
  #
  printf("Installing the packages:\n");
  for( my $idx = 0; $idx < @$pkgs; $idx++ ){
    printf("  %s\n", $pkgs->[$idx]->{name} );

    my $filename = $pkgs->[$idx]->{name};
    my $target   = $filename;
    $target      =~ s/.*\///;
    my $fullpath = $cachedir . "/" . $target;

    if( -f $fullpath ){
      my $cmd = "cd $installdir; tar xzf $fullpath";
      my $rs = system($cmd);
    }
    else{
      die("Internal error, $fullpath not found!\n");
    }
  } 
  printf("\n");
}

sub test_checksum {
  my $file        = shift;
  my $checksum    = shift;
  my $md5sum_file = "/tmp/md5sum.$$";

  open( FH, ">$md5sum_file" ) or die "Unable to open md5sum file: $!";
  printf( FH "%s  %s\n", $checksum, $file );
  close( FH );
  my $cmd = "md5sum -c $md5sum_file";
  printf("Checking the md5sum: ");
  my $rs = system($cmd);
  if( $rs == 0 ){
    unlink($md5sum_file);
  }
  return($rs);
}

###############################################################################

printf("\n");

close(INPUT);

printf("LTSP is now installed in $installdir.\n\n");
printf("Use ltspcfg to configure the services.\n\n");

0;

EOF

perl ${SCRIPT_FILE}
