#!/usr/bin/perl -w
######################################################################
#
# $Id: webjob-cfg-create-list,v 1.12 2012/01/07 08:01:15 mavrik Exp $
#
######################################################################
#
# Copyright 2009-2012 The WebJob Project, All Rights Reserved.
#
######################################################################
#
# Purpose: Create a new KVP list.
#
######################################################################

use strict;
use File::Basename;
use File::Path;
use FindBin qw($Bin $RealBin); use lib ("$Bin/../lib/perl5/site_perl", "$RealBin/../lib/perl5/site_perl", "/usr/local/webjob/lib/perl5/site_perl", "/opt/local/webjob/lib/perl5/site_perl");
use Getopt::Std;
use WebJob::KvpRoutines 1.029;
use WebJob::Properties;

BEGIN
{
  ####################################################################
  #
  # The Properties hash is essentially private. Those parts of the
  # program that wish to access or modify the data in this hash need
  # to call GetProperties() to obtain a reference.
  #
  ####################################################################

  my (%hProperties);

  ####################################################################
  #
  # Initialize regex variables.
  #
  ####################################################################

  $hProperties{'CommonRegexes'} = PropertiesGetGlobalRegexes();

  sub GetProperties
  {
    return \%hProperties;
  }
}

######################################################################
#
# Main Routine
#
######################################################################

  ####################################################################
  #
  # Punch in and go to work.
  #
  ####################################################################

  my ($phProperties);

  $phProperties = GetProperties();

  $$phProperties{'Program'} = basename(__FILE__);

  ####################################################################
  #
  # Get Options.
  #
  ####################################################################

  my (%hOptions);

  if (!getopts('d:f:k:t:', \%hOptions))
  {
    Usage($$phProperties{'Program'});
  }

  ####################################################################
  #
  # A Delimiter, '-d', optional.
  #
  ####################################################################

  $$phProperties{'Delimiter'} = (exists($hOptions{'d'})) ? $hOptions{'d'} : ",";

  if ($$phProperties{'Delimiter'} !~ /^[ ,:;|]$/)
  {
    print STDERR "$$phProperties{'Program'}: Error='Invalid delimiter ($$phProperties{'Delimiter'}).'\n";
    exit(2);
  }

  ####################################################################
  #
  # A config file, '-f', is required.
  #
  ####################################################################

  $$phProperties{'ConfigFile'} = (exists($hOptions{'f'})) ? $hOptions{'f'} : undef;

  if (!defined($$phProperties{'ConfigFile'}))
  {
    Usage($$phProperties{'Program'});
  }

  ####################################################################
  #
  # A key, '-k', is required.
  #
  ####################################################################

  $$phProperties{'Key'} = (exists($hOptions{'k'})) ? $hOptions{'k'} : undef;

  if (!defined($$phProperties{'Key'}))
  {
    Usage($$phProperties{'Program'});
  }

  ####################################################################
  #
  # A type, '-t', is optional.
  #
  ####################################################################

  my ($sKeyRegex, $sListRegex, $sValueRegex);

  $$phProperties{'Type'} = (exists($hOptions{'t'})) ? $hOptions{'t'} : "Generic";

  if ($$phProperties{'Type'} =~ /^Generic$/i)
  {
    $sKeyRegex = qq([\\w.-]+);
    $sValueRegex = qq([^$$phProperties{'Delimiter'}]*);
  }
  elsif ($$phProperties{'Type'} =~ /^ClientAcl$/i)
  {
    $sKeyRegex = $$phProperties{'CommonRegexes'}{'ClientId'};
    $sValueRegex = $$phProperties{'CommonRegexes'}{'Cidr'};
  }
  else
  {
    print STDERR "$$phProperties{'Program'}: Error='Invalid type ($$phProperties{'Type'}).'\n";
    exit(2);
  }
  $sListRegex = "(?:|$sValueRegex(?:[$$phProperties{'Delimiter'}]$sValueRegex)*)"; # Empty lists are allowed.

  ####################################################################
  #
  # Validate the key.
  #
  ####################################################################

  if ($$phProperties{'Key'} !~ /^$sKeyRegex$/)
  {
    print STDERR "$$phProperties{'Program'}: Error='The key ($$phProperties{'Key'}) does not pass muster.'\n";
    exit(2);
  }

  ####################################################################
  #
  # Validate each value.
  #
  ####################################################################

  my (%hFailValues, %hPassValues);

  foreach my $sValue (@ARGV)
  {
    if ($sValue =~ /^$sValueRegex$/)
    {
      $hPassValues{$sValue}++;
    }
    else
    {
      $hFailValues{$sValue}++;
    }
  }

  if (%hFailValues)
  {
    print STDERR "$$phProperties{'Program'}: Error='One or more values (" . join($$phProperties{'Delimiter'}, map('"' . $_ . '"', sort(keys(%hFailValues)))) . ") do not pass muster.'\n";
    exit(2);
  }

  ####################################################################
  #
  # Conditionally create a lock file. This is an exclusive lock.
  #
  ####################################################################

  my (%hLockArgs);

  if ($$phProperties{'ConfigFile'} ne "-")
  {
    %hLockArgs =
    (
      'LockFile'   => $$phProperties{'ConfigFile'} . ".lock",
      'LockRemove' => 1,
    );
    if (!KvpLockFile(\%hLockArgs))
    {
      print STDERR "$$phProperties{'Program'}: Error='$hLockArgs{'Error'}'\n";
      exit(2);
    }
  }

  ####################################################################
  #
  # Create KVP map.
  #
  ####################################################################

  my (%hKvpMap) = ();

  if (-f $$phProperties{'ConfigFile'} || $$phProperties{'ConfigFile'} eq "-")
  {
    my %hLArgs =
    (
      'File'           => $$phProperties{'ConfigFile'},
      'Properties'     => \%hKvpMap,
      'Template'       => { $sKeyRegex => $sListRegex, },
      'VerifyValues'   => 1,
    );
    if (!KvpGetKvps(\%hLArgs))
    {
      print STDERR "$$phProperties{'Program'}: Error='$hLArgs{'Error'}'\n";
      exit(2);
    }
  }
  else
  {
    print STDERR "$$phProperties{'Program'}: Error='File ($$phProperties{'ConfigFile'}) does not exist or is not regular.'\n";
    exit(2);
  }

  ####################################################################
  #
  # Make sure that the specified list does not exist.
  #
  ####################################################################

  if (exists($hKvpMap{$$phProperties{'Key'}}))
  {
    print STDERR "$$phProperties{'Program'}: Error='Client ($$phProperties{'Key'}) already exists.'\n";
    exit(2);
  }

  ####################################################################
  #
  # Build the specified list.
  #
  ####################################################################

  $hKvpMap{$$phProperties{'Key'}} = join($$phProperties{'Delimiter'}, sort(keys(%hPassValues)));

  ####################################################################
  #
  # Write the new config file.
  #
  ####################################################################

  my %hLArgs =
  (
    'File'           => $$phProperties{'ConfigFile'},
    'Properties'     => \%hKvpMap,
    'Template'       => { $sKeyRegex => $sListRegex, },
    'VerifyValues'   => 0,
  );
  if (!KvpSetKvps(\%hLArgs))
  {
    print STDERR "$$phProperties{'Program'}: Error='$hLArgs{'Error'}'\n";
    exit(2);
  }

  ####################################################################
  #
  # Shutdown and go home.
  #
  ####################################################################

  if ($$phProperties{'ConfigFile'} ne "-")
  {
    KvpUnlockFile(\%hLockArgs);
  }

  1;


######################################################################
#
# Usage
#
######################################################################

sub Usage
{
  my ($sProgram) = @_;
  print STDERR "\n";
  print STDERR "Usage: $sProgram [-d delimiter] [-t type] -f {file|-} -k key [value [value ...]]\n";
  print STDERR "\n";
  exit(1);
}


=pod

=head1 NAME

webjob-cfg-create-list - Create a new KVP list.

=head1 SYNOPSIS

B<webjob-cfg-create-list> B<[-d delimiter]> B<[-t type]> B<-f {file|-}> B<-k key> B<[value [value ...]]>

=head1 DESCRIPTION

This utility creates a new KVP (Key/Value Pair) list having zero or
more delimited values.  Duplicate values (if any) are automatically
squashed.

=head1 OPTIONS

=over 4

=item B<-d delimiter>

Specifies the list delimiter.  Valid delimiters include the following
characters: space ' ', comma ',', colon ':', semi-colon ';', and pipe
'|'.  The default delimiter is a comma.  Note that parse errors are
likely to occur if the specified delimiter appears in any of the
values.

=item B<-f {file|-}>

Specifies the config file to operate on.

=item B<-k key>

Specifies the key to create.

=item B<-t type>

Specifies the type of config file being operated on.  Each supported
type has a custom template that is used to validate keys and values.
Currently, the following types are supported: ClientAcl and Generic.
The default type is Generic.

=back

=head1 AUTHOR

Klayton Monroe

=head1 SEE ALSO

webjob-cfg-create-list(1), webjob-cfg-delete-list(1), webjob-cfg-update-list(1)

=head1 LICENSE

All documentation and code are distributed under same terms and
conditions as B<WebJob>.

=cut
