######################################################################
#
# $Id: Properties.pm,v 1.44 2012/01/07 07:56:13 mavrik Exp $
#
######################################################################
#
# Copyright 2007-2012 The WebJob Project, All Rights Reserved.
#
######################################################################
#
# Purpose: Global WebJob Properties.
#
######################################################################

package WebJob::Properties;

require Exporter;

use 5.008;
use strict;
use vars qw($VERSION @ISA @EXPORT @EXPORT_OK);

use Config;
use WebJob::ValidationRoutines;

@EXPORT = qw(PropertiesAddPath PropertiesGetGlobalExitCodes PropertiesGetGlobalKvps PropertiesGetGlobalRegexes PropertiesGetGlobalTemplates PropertiesGetOfficialKeyName PropertiesGetOfficialKeys);
@EXPORT_OK = ();
@ISA = qw(Exporter);
$VERSION = do { my @r = (q$Revision: 1.44 $ =~ /(\d+)/g); sprintf("%d."."%03d" x $#r, @r); };

######################################################################
#
# WebJob-specific variables.
#
######################################################################

my %hGlobalExitCodes =
(
  'webjob' =>
  {
     '1' => "XER_Usage",
     '2' => "XER_Abort",
     '3' => "XER_BootStrap",
     '4' => "XER_ProcessArguments",
     '5' => "XER_ReadProperties",
     '6' => "XER_CheckDependencies",
     '7' => "XER_Configure",
     '8' => "XER_GetStage",
     '9' => "XER_RunStage",
    '10' => "XER_PutStage",
    '11' => "XER_MultiStage",
    '12' => "XER_GetUrl",
    '13' => "XER_CloneProperties",
    '20' => "XER_Cleanup",
    '27' => "XER_Cleanup_Configure",
    '28' => "XER_Cleanup_GetStage",
    '29' => "XER_Cleanup_RunStage",
    '30' => "XER_Cleanup_PutStage",
    '31' => "XER_Cleanup_MultiStage",
  },

  'webjob-dsvtool' =>
  {
     '1' => "XER_Usage",
     '2' => "XER_BootStrap",
     '3' => "XER_ProcessArguments",
     '4' => "XER_PayloadSigningError",
     '5' => "XER_SignatureVerificationFailed",
     '6' => "XER_SignatureVerificationError",
     '7' => "XER_RunMode",
     '8' => "XER_Shutdown",
     '9' => "XER_KeyGenerationError",
    '10' => "XER_PasswordGenerationError",
  },

);

my %hGlobalKvps =
(
  'JqdJobStates'        => [ qw(hold todo sent open done pass fail foul) ],
  'JqdQueueStates'      => [ qw(active frozen locked) ],
  'WeekDays'            => [ qw(Sunday Monday Tuesday Wednesday Thursday Friday Saturday) ],
  'WeekDaysAbbreviated' => [ qw(Sun Mon Tue Wed Thu Fri Sat) ],
);

my %hGlobalRegexes =
(
  'AbsolutePath'           => qq((?:[A-Za-z]:(?:\\\\[ \\w.-]*)+|(?:/[ \\w.-]*)+)), # NOTE: This will not detect or prevent directory traversal.
  'AnyValue'               => qq(.*),
  'AnyValueExceptNothing'  => qq(.+),
  'AuthType'               => qq((?:basic|none)),
  'Base64'                 => qq([0-9A-Za-z+\/]+={0,2}),
  'BaseDirectory'          => qq((?:[A-Za-z]:)?/[\\w./-]+),
  'Cidr'                   => qq((?:\\d{1,3}[.]){3}\\d{1,3}\/\\d{1,2}), # CIDR (Classless Inter Domain Routing)
  'ClientId'               => qq((?:[A-Za-z](?:(?:[0-9A-Za-z]|[_-](?=[^.]))){0,62})(?:[.][A-Za-z](?:(?:[0-9A-Za-z]|[_-](?=[^.]))){0,62}){0,127}),
  'ClientSuppliedFilename' => qq([\\w+.:-]{1,1024}),
  'ConfigSearchOrder'      => qq((?:clients(?::(?:commands|configs))?|(?:commands|configs)(?::clients)?)),
  'DateTime'               => qq([0-9]{4}-[0-9]{2}-[0-9]{2} [0-9]{2}:[0-9]{2}:[0-9]{2}),
  'DateTimeZoneSecUsec'    => qq([0-9]{4}-[0-9]{2}-[0-9]{2} [0-9]{2}:[0-9]{2}:[0-9]{2} [A-Za-z]{2,5} [(]\\d+[.]\\d{6}[)]),
  'Decimal16Bit'           => qq(\\d{1,5}), # 65535
  'Decimal32Bit'           => qq(\\d{1,10}), # 4294967295
  'Decimal32BitSigned'     => qq(-?\\d{1,10}), # Target range: [-2147483648,2147483647] -- Actual range: [-9999999999,9999999999]
  'Decimal64Bit'           => qq(\\d{1,20}), # 18446744073709551615
  'DecimalFloatSigned'     => qq(-?\\d+[.]\\d+),
  'FileMode'               => qq(0[0-7]{1,6}),
  'FileOgid'               => qq((?:\\d+|[A-Za-z_][\\w-]{0,31})), # File owner/group ID or name.
  'FilePath'               => qq((?:[A-Za-z]:)?/[\\w./-]+),
  'FilePerm'               => qq(0[0-7]{1,4}), # File permissions.
  'FileSuffix'             => qq([\\w.-]{1,32}),
  'Filename'               => qq([\\w+.:-]{1,1024}),
  'Fingerprint128Bit'      => qq([0-9A-Fa-f]{2}(?::[0-9A-Fa-f]{2}){15}),
  'Fingerprint160Bit'      => qq([0-9A-Fa-f]{2}(?::[0-9A-Fa-f]{2}){19}),
  'FolderList'             => qq((?:|[\\w.-]+(?::[\\w.-]+)*)),
  'Fqdn'                   => qq((?:[A-Za-z](?:(?:[0-9A-Za-z]|[_-](?=[^.]))){0,62})(?:[.][A-Za-z](?:(?:[0-9A-Za-z]|[_-](?=[^.]))){0,62}){1,127}),
  'Group'                  => qq([\\w-]{1,32}),
  'GroupToken'             => qq(%[\\w-]{1,32}),
  'Hash128Bit'             => qq([0-9A-Fa-f]{32}),
  'Hash160Bit'             => qq([0-9A-Fa-f]{40}),
  'Hash256Bit'             => qq([0-9A-Fa-f]{64}),
  'HashVariableBit'        => qq((?:[0-9A-Fa-f]{32}|[0-9A-Fa-f]{40}|[0-9A-Fa-f]{64})),
  'HashType'               => qq((?:[Mm][Dd]5|[Ss][Hh][Aa](?:1|256))),
  'HttpRequestMethod'      => qq((?:GET|JOB|PUT)),
  'IntegerMap'             => qq((?:\\d+:\\d+(?:,\\d+:\\d+)*)?), # This is a comma-delimited list of zero or more from:to integer values (e.g., [x1:y1[,x2:y2[...]]]).
  'Ip'                     => qq((?:\\d{1,3}[.]){3}\\d{1,3}),
  'IpV4'                   => qq((?:\\d{1,3}[.]){3}\\d{1,3}),
  'JobId'                  => qq([\\w-]{1,64}_\\d{10}(?:_\\d{6})?_\\d{5}),
  'JqdPriority'            => qq(\\d{2}),
  'JqdQueueTag'            => qq([ps]\\d{3,5}_(?:\\d{2}_)?\\d{10}_(?:\\d{6}_)?[\\dA-Fa-f]{5,8}),
  'JqdQueueTagOrNa'        => qq((?:NA|[ps]\\d{3,5}_(?:\\d{2}_)?\\d{10}_(?:\\d{6}_)?[\\dA-Fa-f]{5,8})),
  'MldbmKeyName'           => qq([0-9A-Za-z]+(?:[._+-]+[0-9A-Za-z]+)*),
  'OsClass'                => qq((?:UNIX|WINX)),
  'Priority'               => qq((?:low|below_normal|normal|above_normal|high)),
  'ProcessId'              => qq(\\d{5}),
  'PutNameFormat'          => qq([\\w%+./:-]+),
  'RelativePath'           => qq((?:[ \\w.-]+(?:\\\\[ \\w.-]*)*|[ \\w.-]+(?:/[ \\w.-]*)*)), # NOTE: This will not detect or prevent directory traversal.
  'RegistrationCode'       => qq([0-9A-Za-z]{5}(?:-[0-9A-Za-z]{5}){2,11}),
  'RunType'                => qq((?:linktest|snapshot)),
  'ServerSuppliedPath'     => qq([\\w+./:\\\\-]+),
  'SystemVersion'          => qq([\\w ()+,./:-]{1,64}),
  'TrueFalse'              => qq((?i)(?:[10]|t(rue)?|f(alse)?)),
  'Username'               => qq([A-Za-z_][\\w-]{0,31}),
  'WebJobVersion'          => qq(webjob[\\w (),.-]{1,64}),
  'YesNo'                  => qq([YyNn]),
  'strftime_A'             => "(?:" . join("|", @{$hGlobalKvps{'WeekDays'}}) . ")",
  'strftime_a'             => "(?:" . join("|", @{$hGlobalKvps{'WeekDaysAbbreviated'}}) . ")",
  'strftime_H'             => qq(\\d{2}),
  'strftime_M'             => qq(\\d{2}),
  'strftime_S'             => qq(\\d{2}),
  'strftime_Y'             => qq(\\d{4}),
  'strftime_d'             => qq(\\d{2}),
  'strftime_m'             => qq(\\d{2}),
  'strftime_s'             => qq(\\d{10}),
  'strftime_u'             => qq(\\d{1}),
  'strftime_w'             => qq(\\d{1}),
);
$hGlobalRegexes{'Host'}    = "(?:" . $hGlobalRegexes{'ClientId'} . "|" . $hGlobalRegexes{'IpV4'} . ")";
$hGlobalRegexes{'JqdQueueName'} = "(?:" . $hGlobalRegexes{'ClientId'} . "|" . $hGlobalRegexes{'ClientSuppliedFilename'} . ")";

my %hGlobalTemplates =
(
  'generic' =>
  {
    qq([A-Za-z][\\w.-]*)                 => $hGlobalRegexes{'AnyValue'},
  },

  'jqd.job' =>
  {
    'Command'                            => $hGlobalRegexes{'ClientSuppliedFilename'},
    'CommandAlias'                       => $hGlobalRegexes{'ClientSuppliedFilename'},
    'CommandLine'                        => $hGlobalRegexes{'AnyValueExceptNothing'},
    'CommandMd5'                         => $hGlobalRegexes{'Hash128Bit'},
    'CommandPath'                        => $hGlobalRegexes{'AnyValueExceptNothing'},
    'CommandSha1'                        => $hGlobalRegexes{'Hash160Bit'},
    'CommandSize'                        => $hGlobalRegexes{'Decimal64Bit'},
    'Comment'                            => $hGlobalRegexes{'AnyValue'},
    'Created'                            => $hGlobalRegexes{'DateTime'},
    'Creator'                            => $hGlobalRegexes{'Username'},
    'JobGroup'                           => $hGlobalRegexes{'Group'},
    'JobId'                              => $hGlobalRegexes{'JobId'},
    'RdyFile'                            => $hGlobalRegexes{'ServerSuppliedPath'},
  },

  'nph-config.custom' =>
  {
    'CapContentLength'                   => $hGlobalRegexes{'YesNo'},
    'EnableGetService'                   => $hGlobalRegexes{'YesNo'},
    'FolderList'                         => $hGlobalRegexes{'FolderList'},
    'GetHookCommandLine'                 => $hGlobalRegexes{'AnyValue'},
    'GetHookEnable'                      => $hGlobalRegexes{'YesNo'},
    'GetHookLogDivertedOutput'           => $hGlobalRegexes{'YesNo'},
    'GetHookStatus'                      => $hGlobalRegexes{'Decimal32Bit'},
    'GetHookStatusMap'                   => $hGlobalRegexes{'IntegerMap'},
    'MaxContentLength'                   => $hGlobalRegexes{'Decimal64Bit'},
    'RequireMatch'                       => $hGlobalRegexes{'YesNo'},
    'RequireUser'                        => $hGlobalRegexes{'YesNo'},
    'SslRequireCn'                       => $hGlobalRegexes{'YesNo'},
    'SslRequireMatch'                    => $hGlobalRegexes{'YesNo'},
  },

  'nph-config.global' =>
  {
    'BaseDirectory'                      => $hGlobalRegexes{'BaseDirectory'},
    'CapContentLength'                   => $hGlobalRegexes{'YesNo'},
    'ConfigDirectory'                    => $hGlobalRegexes{'BaseDirectory'},
    'ConfigSearchOrder'                  => $hGlobalRegexes{'ConfigSearchOrder'},
    'DynamicDirectory'                   => $hGlobalRegexes{'BaseDirectory'},
    'EnableConfigOverrides'              => $hGlobalRegexes{'YesNo'},
    'EnableGetService'                   => $hGlobalRegexes{'YesNo'},
    'EnableHostAccessList'               => $hGlobalRegexes{'YesNo'},
    'EnableLogging'                      => $hGlobalRegexes{'YesNo'},
    'FolderList'                         => $hGlobalRegexes{'FolderList'},
    'GetHookCommandLine'                 => $hGlobalRegexes{'AnyValue'},
    'GetHookEnable'                      => $hGlobalRegexes{'YesNo'},
    'GetHookLogDivertedOutput'           => $hGlobalRegexes{'YesNo'},
    'GetHookStatus'                      => $hGlobalRegexes{'Decimal32Bit'},
    'GetHookStatusMap'                   => $hGlobalRegexes{'IntegerMap'},
    'LogfilesDirectory'                  => $hGlobalRegexes{'BaseDirectory'},
    'MaxContentLength'                   => $hGlobalRegexes{'Decimal64Bit'},
    'ProfilesDirectory'                  => $hGlobalRegexes{'BaseDirectory'},
    'RequireMatch'                       => $hGlobalRegexes{'YesNo'},
    'RequireUser'                        => $hGlobalRegexes{'YesNo'},
    'ServerId'                           => $hGlobalRegexes{'ClientId'},
    'SpoolDirectory'                     => $hGlobalRegexes{'BaseDirectory'},
    'SslRequireSsl'                      => $hGlobalRegexes{'YesNo'},
    'SslRequireCn'                       => $hGlobalRegexes{'YesNo'},
    'SslRequireMatch'                    => $hGlobalRegexes{'YesNo'},
    'UseGmt'                             => $hGlobalRegexes{'YesNo'},
  },

  'nph-webjob.custom' =>
  {
    'CapContentLength'                   => $hGlobalRegexes{'YesNo'},
    'EnableGetService'                   => $hGlobalRegexes{'YesNo'},
    'EnablePutService'                   => $hGlobalRegexes{'YesNo'},
    'EnableRequestTracking'              => $hGlobalRegexes{'YesNo'},
    'FolderList'                         => $hGlobalRegexes{'FolderList'},
    'GetHookCommandLine'                 => $hGlobalRegexes{'AnyValue'},
    'GetHookEnable'                      => $hGlobalRegexes{'YesNo'},
    'GetHookLogDivertedOutput'           => $hGlobalRegexes{'YesNo'},
    'GetHookStatus'                      => $hGlobalRegexes{'Decimal32Bit'},
    'GetHookStatusMap'                   => $hGlobalRegexes{'IntegerMap'},
    'GetTriggerCommandLine'              => $hGlobalRegexes{'AnyValue'},
    'GetTriggerEnable'                   => $hGlobalRegexes{'YesNo'},
    'JobQueueActive'                     => $hGlobalRegexes{'YesNo'},
    'JobQueuePqActiveLimit'              => $hGlobalRegexes{'Decimal16Bit'},
    'JobQueuePqAnswerLimit'              => $hGlobalRegexes{'Decimal16Bit'},
    'JobQueueSqActiveLimit'              => $hGlobalRegexes{'Decimal16Bit'},
    'JobQueueSqAnswerLimit'              => $hGlobalRegexes{'Decimal16Bit'},
    'MaxContentLength'                   => $hGlobalRegexes{'Decimal64Bit'},
    'OverwriteExistingFiles'             => $hGlobalRegexes{'YesNo'},
    'PutHookCommandLine'                 => $hGlobalRegexes{'AnyValue'},
    'PutHookEnable'                      => $hGlobalRegexes{'YesNo'},
    'PutHookLogDivertedOutput'           => $hGlobalRegexes{'YesNo'},
    'PutHookStatus'                      => $hGlobalRegexes{'Decimal32Bit'},
    'PutHookStatusMap'                   => $hGlobalRegexes{'IntegerMap'},
    'PutNameFormat'                      => $hGlobalRegexes{'PutNameFormat'},
    'PutTriggerCommandLine'              => $hGlobalRegexes{'AnyValue'},
    'PutTriggerEnable'                   => $hGlobalRegexes{'YesNo'},
    'RequireMatch'                       => $hGlobalRegexes{'YesNo'},
    'RequireUser'                        => $hGlobalRegexes{'YesNo'},
    'SslRequireCn'                       => $hGlobalRegexes{'YesNo'},
    'SslRequireMatch'                    => $hGlobalRegexes{'YesNo'},
  },

  'nph-webjob.global' =>
  {
    'BaseDirectory'                      => $hGlobalRegexes{'BaseDirectory'},
    'CapContentLength'                   => $hGlobalRegexes{'YesNo'},
    'ConfigDirectory'                    => $hGlobalRegexes{'BaseDirectory'},
    'ConfigSearchOrder'                  => $hGlobalRegexes{'ConfigSearchOrder'},
    'DsvMaxSignatureLength'              => $hGlobalRegexes{'Decimal32Bit'},
    'DsvNullSignature'                   => "(?:|$hGlobalRegexes{'Base64'})", # An empty value is allowed.
    'DsvRequireSignatures'               => $hGlobalRegexes{'YesNo'},
    'DsvSignatureSuffix'                 => $hGlobalRegexes{'FileSuffix'},
    'DynamicDirectory'                   => $hGlobalRegexes{'BaseDirectory'},
    'EnableConfigOverrides'              => $hGlobalRegexes{'YesNo'},
    'EnableGetService'                   => $hGlobalRegexes{'YesNo'},
    'EnableHostAccessList'               => $hGlobalRegexes{'YesNo'},
    'EnableJobQueues'                    => $hGlobalRegexes{'YesNo'},
    'EnableLogging'                      => $hGlobalRegexes{'YesNo'},
    'EnablePutService'                   => $hGlobalRegexes{'YesNo'},
    'EnableRequestTracking'              => $hGlobalRegexes{'YesNo'},
    'FolderList'                         => $hGlobalRegexes{'FolderList'},
    'GetHookCommandLine'                 => $hGlobalRegexes{'AnyValue'},
    'GetHookEnable'                      => $hGlobalRegexes{'YesNo'},
    'GetHookLogDivertedOutput'           => $hGlobalRegexes{'YesNo'},
    'GetHookStatus'                      => $hGlobalRegexes{'Decimal32Bit'},
    'GetHookStatusMap'                   => $hGlobalRegexes{'IntegerMap'},
    'GetTriggerCommandLine'              => $hGlobalRegexes{'AnyValue'},
    'GetTriggerEnable'                   => $hGlobalRegexes{'YesNo'},
    'IncomingDirectory'                  => $hGlobalRegexes{'BaseDirectory'},
    'JobQueueActive'                     => $hGlobalRegexes{'YesNo'},
    'JobQueueDirectory'                  => $hGlobalRegexes{'BaseDirectory'},
    'JobQueuePqActiveLimit'              => $hGlobalRegexes{'Decimal16Bit'},
    'JobQueuePqAnswerLimit'              => $hGlobalRegexes{'Decimal16Bit'},
    'JobQueueSqActiveLimit'              => $hGlobalRegexes{'Decimal16Bit'},
    'JobQueueSqAnswerLimit'              => $hGlobalRegexes{'Decimal16Bit'},
    'LogfilesDirectory'                  => $hGlobalRegexes{'BaseDirectory'},
    'MaxContentLength'                   => $hGlobalRegexes{'Decimal64Bit'},
    'OverwriteExistingFiles'             => $hGlobalRegexes{'YesNo'},
    'ProfilesDirectory'                  => $hGlobalRegexes{'BaseDirectory'},
    'PutHookCommandLine'                 => $hGlobalRegexes{'AnyValue'},
    'PutHookEnable'                      => $hGlobalRegexes{'YesNo'},
    'PutHookLogDivertedOutput'           => $hGlobalRegexes{'YesNo'},
    'PutHookStatus'                      => $hGlobalRegexes{'Decimal32Bit'},
    'PutHookStatusMap'                   => $hGlobalRegexes{'IntegerMap'},
    'PutNameFormat'                      => $hGlobalRegexes{'PutNameFormat'},
    'PutTriggerCommandLine'              => $hGlobalRegexes{'AnyValue'},
    'PutTriggerEnable'                   => $hGlobalRegexes{'YesNo'},
    'RequireMatch'                       => $hGlobalRegexes{'YesNo'},
    'RequireUser'                        => $hGlobalRegexes{'YesNo'},
    'ServerId'                           => $hGlobalRegexes{'ClientId'},
    'SpoolDirectory'                     => $hGlobalRegexes{'BaseDirectory'},
    'SslRequireCn'                       => $hGlobalRegexes{'YesNo'},
    'SslRequireMatch'                    => $hGlobalRegexes{'YesNo'},
    'SslRequireSsl'                      => $hGlobalRegexes{'YesNo'},
    'UseGmt'                             => $hGlobalRegexes{'YesNo'},
  },

  'nph-webjob.env' =>
  {
    'ClientId'                           => $hGlobalRegexes{'ClientId'},
    'Command'                            => $hGlobalRegexes{'Filename'},
    'CommandLine'                        => $hGlobalRegexes{'AnyValueExceptNothing'},
    'ConfigFile'                         => qq((?:$hGlobalRegexes{'AbsolutePath'}|$hGlobalRegexes{'RelativePath'})(?:,(?:$hGlobalRegexes{'AbsolutePath'}|$hGlobalRegexes{'RelativePath'}))*),
    'ConfigList'                         => qq((?:$hGlobalRegexes{'AbsolutePath'}|$hGlobalRegexes{'RelativePath'})(?:,(?:$hGlobalRegexes{'AbsolutePath'}|$hGlobalRegexes{'RelativePath'}))*),
    'DsvSigner'                          => $hGlobalRegexes{'AnyValue'},
    'ConfigFile'                         => qq((?:$hGlobalRegexes{'AbsolutePath'}|$hGlobalRegexes{'RelativePath'})(?:,(?:$hGlobalRegexes{'AbsolutePath'}|$hGlobalRegexes{'RelativePath'}))*),
    'DsvStatus'                          => $hGlobalRegexes{'Decimal32BitSigned'},
    'EmbeddedCommand'                    => $hGlobalRegexes{'Filename'},
    'EmbeddedCommandLine'                => $hGlobalRegexes{'AnyValueExceptNothing'},
    'GetEpoch'                           => $hGlobalRegexes{'DateTimeZoneSecUsec'},
    'GetError'                           => $hGlobalRegexes{'AnyValue'},
    'GetHookCommandLine'                 => $hGlobalRegexes{'AnyValueExceptNothing'},
    'GetHookStatus'                      => qq($hGlobalRegexes{'Decimal32BitSigned'}/$hGlobalRegexes{'Decimal32BitSigned'}),
    'GetRequest'                         => $hGlobalRegexes{'Filename'},
    'HashType'                           => $hGlobalRegexes{'HashType'},
    'Hostname'                           => $hGlobalRegexes{'ClientId'},
    'Jid'                                => $hGlobalRegexes{'JobId'},
    'JobEpoch'                           => $hGlobalRegexes{'DateTimeZoneSecUsec'},
    'KidPid'                             => $hGlobalRegexes{'Decimal32BitSigned'},
    'KidReason'                          => $hGlobalRegexes{'AnyValue'},
    'KidSignal'                          => $hGlobalRegexes{'Decimal32BitSigned'},
    'KidStatus'                          => $hGlobalRegexes{'Decimal32BitSigned'},
    'Pid'                                => $hGlobalRegexes{'Decimal32BitSigned'}, # Some PIDs are negative on Windows.
    'Priority'                           => qq(|$hGlobalRegexes{'Priority'}),
    'PutEpoch'                           => $hGlobalRegexes{'DateTimeZoneSecUsec'},
    'QueueTag'                           => qq(|$hGlobalRegexes{'JqdQueueTagOrNa'}),
    'RunEpoch'                           => $hGlobalRegexes{'DateTimeZoneSecUsec'},
    'RunError'                           => $hGlobalRegexes{'AnyValue'},
    'StdErrHash'                         => $hGlobalRegexes{'HashVariableBit'},
    'StdErrSize'                         => $hGlobalRegexes{'Decimal64Bit'},
    'StdOutHash'                         => $hGlobalRegexes{'HashVariableBit'},
    'StdOutSize'                         => $hGlobalRegexes{'Decimal64Bit'},
    'SystemOS'                           => $hGlobalRegexes{'SystemVersion'},
    'TempDirectory'                      => $hGlobalRegexes{'AbsolutePath'},
    'UrlGetProxyHost'                    => $hGlobalRegexes{'Host'},
    'UrlGetProxyPort'                    => $hGlobalRegexes{'Decimal16Bit'},
    'UrlPutProxyHost'                    => $hGlobalRegexes{'Host'},
    'UrlPutProxyPort'                    => $hGlobalRegexes{'Decimal16Bit'},
    'Version'                            => $hGlobalRegexes{'WebJobVersion'},
    'WorkDirectory'                      => $hGlobalRegexes{'AbsolutePath'},
  },

  'nph-webjob.get' =>
  {
    'BytesRx'                            => qq((?:|$hGlobalRegexes{'Decimal32Bit'})),
    'BytesTx'                            => qq((?:|$hGlobalRegexes{'Decimal32Bit'})),
    'ClientId'                           => qq((?:|$hGlobalRegexes{'ClientId'})),
    'Command'                            => qq((?:|$hGlobalRegexes{'Filename'})),
    'Duration'                           => qq((?:|$hGlobalRegexes{'DecimalFloatSigned'})),
    'Ip'                                 => qq((?:|$hGlobalRegexes{'Ip'})),
    'Jid'                                => qq((?:|$hGlobalRegexes{'JobId'})),
    'Jqt'                                => qq((?:|$hGlobalRegexes{'JqdQueueTagOrNa'})),
    'Method'                             => qq((?:|$hGlobalRegexes{'HttpRequestMethod'})),
    'QueueName'                          => qq((?:|$hGlobalRegexes{'ClientId'})),
    'Reason'                             => qq((?:|$hGlobalRegexes{'AnyValue'})),
    'Status'                             => qq((?:|$hGlobalRegexes{'Decimal32Bit'})),
    'Timestamp'                          => qq((?:|$hGlobalRegexes{'DateTime'})),
    'Username'                           => qq((?:|$hGlobalRegexes{'ClientId'})),
  },

  'nph-webjob.job' =>
  {
    'BytesRx'                            => qq((?:|$hGlobalRegexes{'Decimal32Bit'})),
    'BytesTx'                            => qq((?:|$hGlobalRegexes{'Decimal32Bit'})),
    'ClientId'                           => qq((?:|$hGlobalRegexes{'ClientId'})),
    'Duration'                           => qq((?:|$hGlobalRegexes{'DecimalFloatSigned'})),
    'Ip'                                 => qq((?:|$hGlobalRegexes{'Ip'})),
    'Jid'                                => qq((?:|$hGlobalRegexes{'JobId'})),
    'Method'                             => qq((?:|$hGlobalRegexes{'HttpRequestMethod'})),
    'QueueName'                          => qq((?:|$hGlobalRegexes{'ClientId'})),
    'Reason'                             => qq((?:|$hGlobalRegexes{'AnyValue'})),
    'Status'                             => qq((?:|$hGlobalRegexes{'Decimal32Bit'})),
    'Timestamp'                          => qq((?:|$hGlobalRegexes{'DateTime'})),
    'Username'                           => qq((?:|$hGlobalRegexes{'ClientId'})),
  },

  'nph-webjob.put' =>
  {
    'BytesRx'                            => qq((?:|$hGlobalRegexes{'Decimal32Bit'})),
    'BytesTx'                            => qq((?:|$hGlobalRegexes{'Decimal32Bit'})),
    'ClientId'                           => qq((?:|$hGlobalRegexes{'ClientId'})),
    'Command'                            => qq((?:|$hGlobalRegexes{'Filename'})),
    'Duration'                           => qq((?:|$hGlobalRegexes{'DecimalFloatSigned'})),
    'Ip'                                 => qq((?:|$hGlobalRegexes{'Ip'})),
    'Jid'                                => qq((?:|$hGlobalRegexes{'JobId'})),
    'Jqt'                                => qq((?:|$hGlobalRegexes{'JqdQueueTagOrNa'})),
    'Method'                             => qq((?:|$hGlobalRegexes{'HttpRequestMethod'})),
    'QueueName'                          => qq((?:|$hGlobalRegexes{'ClientId'})),
    'RdyFile'                            => qq((?:|$hGlobalRegexes{'AbsolutePath'})),
    'Reason'                             => qq((?:|$hGlobalRegexes{'AnyValue'})),
    'Status'                             => qq((?:|$hGlobalRegexes{'Decimal32Bit'})),
    'Timestamp'                          => qq((?:|$hGlobalRegexes{'DateTime'})),
    'Username'                           => qq((?:|$hGlobalRegexes{'ClientId'})),
  },

  'webjob.execute' =>
  {
    'ClientId'                           => $hGlobalRegexes{'ClientId'},
    'DsvCertificateTree'                 => $hGlobalRegexes{'AnyValueExceptNothing'},
    'DsvVerifySignature'                 => $hGlobalRegexes{'YesNo'},
    'GetHookCommandLine'                 => $hGlobalRegexes{'AnyValueExceptNothing'},
    'GetHookEnable'                      => $hGlobalRegexes{'YesNo'},
    'GetHookStatus'                      => $hGlobalRegexes{'Decimal16Bit'},
    'GetHookSuffix'                      => $hGlobalRegexes{'FileSuffix'},
    'GetTimeLimit'                       => $hGlobalRegexes{'Decimal32Bit'},
    'HashType'                           => $hGlobalRegexes{'HashType'},
    'Import'                             => $hGlobalRegexes{'AnyValueExceptNothing'},
    'OverwriteExecutable'                => $hGlobalRegexes{'YesNo'},
    'Priority'                           => $hGlobalRegexes{'Priority'},
    'PutTimeLimit'                       => $hGlobalRegexes{'Decimal32Bit'},
    'RunTimeLimit'                       => $hGlobalRegexes{'Decimal32Bit'},
    'RunType'                            => $hGlobalRegexes{'RunType'},
    'SslBundledCAsFile'                  => $hGlobalRegexes{'AnyValueExceptNothing'},
    'SslExpectedPeerCN'                  => $hGlobalRegexes{'AnyValueExceptNothing'},
    'SslMaxChainLength'                  => $hGlobalRegexes{'Decimal16Bit'},
    'SslPassPhrase'                      => $hGlobalRegexes{'AnyValueExceptNothing'},
    'SslPrivateKeyFile'                  => $hGlobalRegexes{'AnyValueExceptNothing'},
    'SslPublicCertFile'                  => $hGlobalRegexes{'AnyValueExceptNothing'},
    'SslUseCertificate'                  => $hGlobalRegexes{'YesNo'},
    'SslVerifyPeerCert'                  => $hGlobalRegexes{'YesNo'},
    'TempDirectory'                      => $hGlobalRegexes{'AnyValueExceptNothing'},
    'TimeoutSignal'                      => $hGlobalRegexes{'Decimal16Bit'},
    'UnlinkExecutable'                   => $hGlobalRegexes{'YesNo'},
    'UnlinkOutput'                       => $hGlobalRegexes{'YesNo'},
    'UploadOnGetFailure'                 => $hGlobalRegexes{'YesNo'},
    'UrlAuthType'                        => $hGlobalRegexes{'AuthType'},
    'UrlDownloadLimit'                   => $hGlobalRegexes{'Decimal32Bit'},
    'UrlGetProxyAuthType'                => $hGlobalRegexes{'AuthType'},
    'UrlGetProxyEnable'                  => $hGlobalRegexes{'YesNo'},
    'UrlGetProxyHost'                    => $hGlobalRegexes{'Host'},
    'UrlGetProxyPassword'                => $hGlobalRegexes{'AnyValueExceptNothing'},
    'UrlGetProxyPort'                    => $hGlobalRegexes{'Decimal16Bit'},
    'UrlGetProxyUsername'                => $hGlobalRegexes{'Username'},
    'UrlGetUrl'                          => $hGlobalRegexes{'AnyValueExceptNothing'},
    'UrlPassword'                        => $hGlobalRegexes{'AnyValueExceptNothing'},
    'UrlPutProxyAuthType'                => $hGlobalRegexes{'AuthType'},
    'UrlPutProxyEnable'                  => $hGlobalRegexes{'YesNo'},
    'UrlPutProxyHost'                    => $hGlobalRegexes{'Host'},
    'UrlPutProxyPassword'                => $hGlobalRegexes{'AnyValueExceptNothing'},
    'UrlPutProxyPort'                    => $hGlobalRegexes{'Decimal16Bit'},
    'UrlPutProxyUsername'                => $hGlobalRegexes{'Username'},
    'UrlPutUrl'                          => $hGlobalRegexes{'AnyValueExceptNothing'},
    'UrlUsername'                        => $hGlobalRegexes{'Username'},
  },

  'webjob.get-url' =>
  {
    'Import'                             => $hGlobalRegexes{'AnyValueExceptNothing'},
    'SslBundledCAsFile'                  => $hGlobalRegexes{'AnyValueExceptNothing'},
    'SslExpectedPeerCN'                  => $hGlobalRegexes{'AnyValueExceptNothing'},
    'SslMaxChainLength'                  => $hGlobalRegexes{'Decimal16Bit'},
    'SslPassPhrase'                      => $hGlobalRegexes{'AnyValueExceptNothing'},
    'SslPrivateKeyFile'                  => $hGlobalRegexes{'AnyValueExceptNothing'},
    'SslPublicCertFile'                  => $hGlobalRegexes{'AnyValueExceptNothing'},
    'SslUseCertificate'                  => $hGlobalRegexes{'YesNo'},
    'SslVerifyPeerCert'                  => $hGlobalRegexes{'YesNo'},
    'UrlAuthType'                        => $hGlobalRegexes{'AuthType'},
    'UrlGetProxyAuthType'                => $hGlobalRegexes{'AuthType'},
    'UrlGetProxyEnable'                  => $hGlobalRegexes{'YesNo'},
    'UrlGetProxyHost'                    => $hGlobalRegexes{'Host'},
    'UrlGetProxyPassword'                => $hGlobalRegexes{'AnyValueExceptNothing'},
    'UrlGetProxyPort'                    => $hGlobalRegexes{'Decimal16Bit'},
    'UrlGetProxyUsername'                => $hGlobalRegexes{'Username'},
    'UrlPassword'                        => $hGlobalRegexes{'AnyValueExceptNothing'},
    'UrlUsername'                        => $hGlobalRegexes{'Username'},
  },

  'webjob.server' =>
  {
    'ApacheConfDirectory'                => $hGlobalRegexes{'FilePath'},
    'ApacheGroup'                        => $hGlobalRegexes{'FileOgid'},
    'ApacheOwner'                        => $hGlobalRegexes{'FileOgid'},
    'ApacheSslBundledCAsFile'            => $hGlobalRegexes{'FilePath'},
    'ApacheSslPublicCertFile'            => $hGlobalRegexes{'FilePath'},
    'DsvRequireSignatures'               => $hGlobalRegexes{'YesNo'},
    'EnableAutoActivation'               => $hGlobalRegexes{'YesNo'},
    'EnableAutoRegistration'             => $hGlobalRegexes{'YesNo'},
    'EnableHostAccessList'               => $hGlobalRegexes{'YesNo'},
    'Hostname'                           => $hGlobalRegexes{'Fqdn'},
    'WebJobArchivesDirectory'            => $hGlobalRegexes{'FilePath'},
    'WebJobArchivesDirectoryGroup'       => $hGlobalRegexes{'FileOgid'},
    'WebJobArchivesDirectoryOwner'       => $hGlobalRegexes{'FileOgid'},
    'WebJobArchivesDirectoryPermissions' => $hGlobalRegexes{'FilePerm'},
    'WebJobBinDirectory'                 => $hGlobalRegexes{'FilePath'},
    'WebJobBinDirectoryGroup'            => $hGlobalRegexes{'FileOgid'},
    'WebJobBinDirectoryOwner'            => $hGlobalRegexes{'FileOgid'},
    'WebJobBinDirectoryPermissions'      => $hGlobalRegexes{'FilePerm'},
    'WebJobCgiDirectory'                 => $hGlobalRegexes{'FilePath'},
    'WebJobCgiDirectoryGroup'            => $hGlobalRegexes{'FileOgid'},
    'WebJobCgiDirectoryOwner'            => $hGlobalRegexes{'FileOgid'},
    'WebJobCgiDirectoryPermissions'      => $hGlobalRegexes{'FilePerm'},
    'WebJobConfigDirectory'              => $hGlobalRegexes{'FilePath'},
    'WebJobConfigDirectoryGroup'         => $hGlobalRegexes{'FileOgid'},
    'WebJobConfigDirectoryOwner'         => $hGlobalRegexes{'FileOgid'},
    'WebJobConfigDirectoryPermissions'   => $hGlobalRegexes{'FilePerm'},
    'WebJobDbDirectory'                  => $hGlobalRegexes{'FilePath'},
    'WebJobDbDirectoryGroup'             => $hGlobalRegexes{'FileOgid'},
    'WebJobDbDirectoryOwner'             => $hGlobalRegexes{'FileOgid'},
    'WebJobDbDirectoryPermissions'       => $hGlobalRegexes{'FilePerm'},
    'WebJobDirectory'                    => $hGlobalRegexes{'FilePath'},
    'WebJobDirectoryGroup'               => $hGlobalRegexes{'FileOgid'},
    'WebJobDirectoryOwner'               => $hGlobalRegexes{'FileOgid'},
    'WebJobDirectoryPermissions'         => $hGlobalRegexes{'FilePerm'},
    'WebJobDsvDirectory'                 => $hGlobalRegexes{'FilePath'},
    'WebJobDsvDirectoryGroup'            => $hGlobalRegexes{'FileOgid'},
    'WebJobDsvDirectoryOwner'            => $hGlobalRegexes{'FileOgid'},
    'WebJobDsvDirectoryPermissions'      => $hGlobalRegexes{'FilePerm'},
    'WebJobDynamicDirectory'             => $hGlobalRegexes{'FilePath'},
    'WebJobDynamicDirectoryGroup'        => $hGlobalRegexes{'FileOgid'},
    'WebJobDynamicDirectoryOwner'        => $hGlobalRegexes{'FileOgid'},
    'WebJobDynamicDirectoryPermissions'  => $hGlobalRegexes{'FilePerm'},
    'WebJobEtcDirectory'                 => $hGlobalRegexes{'FilePath'},
    'WebJobEtcDirectoryGroup'            => $hGlobalRegexes{'FileOgid'},
    'WebJobEtcDirectoryOwner'            => $hGlobalRegexes{'FileOgid'},
    'WebJobEtcDirectoryPermissions'      => $hGlobalRegexes{'FilePerm'},
    'WebJobGroup'                        => $hGlobalRegexes{'FileOgid'},
    'WebJobIncomingDirectory'            => $hGlobalRegexes{'FilePath'},
    'WebJobIncomingDirectoryGroup'       => $hGlobalRegexes{'FileOgid'},
    'WebJobIncomingDirectoryOwner'       => $hGlobalRegexes{'FileOgid'},
    'WebJobIncomingDirectoryPermissions' => $hGlobalRegexes{'FilePerm'},
    'WebJobLibDirectory'                 => $hGlobalRegexes{'FilePath'},
    'WebJobLibDirectoryGroup'            => $hGlobalRegexes{'FileOgid'},
    'WebJobLibDirectoryOwner'            => $hGlobalRegexes{'FileOgid'},
    'WebJobLibDirectoryPermissions'      => $hGlobalRegexes{'FilePerm'},
    'WebJobLogfilesDirectory'            => $hGlobalRegexes{'FilePath'},
    'WebJobLogfilesDirectoryGroup'       => $hGlobalRegexes{'FileOgid'},
    'WebJobLogfilesDirectoryOwner'       => $hGlobalRegexes{'FileOgid'},
    'WebJobLogfilesDirectoryPermissions' => $hGlobalRegexes{'FilePerm'},
    'WebJobOwner'                        => $hGlobalRegexes{'FileOgid'},
    'WebJobProfilesDirectory'            => $hGlobalRegexes{'FilePath'},
    'WebJobProfilesDirectoryGroup'       => $hGlobalRegexes{'FileOgid'},
    'WebJobProfilesDirectoryOwner'       => $hGlobalRegexes{'FileOgid'},
    'WebJobProfilesDirectoryPermissions' => $hGlobalRegexes{'FilePerm'},
    'WebJobRunDirectory'                 => $hGlobalRegexes{'FilePath'},
    'WebJobRunDirectoryGroup'            => $hGlobalRegexes{'FileOgid'},
    'WebJobRunDirectoryOwner'            => $hGlobalRegexes{'FileOgid'},
    'WebJobRunDirectoryPermissions'      => $hGlobalRegexes{'FilePerm'},
    'WebJobServerDirectory'              => $hGlobalRegexes{'FilePath'},
    'WebJobServerDirectoryGroup'         => $hGlobalRegexes{'FileOgid'},
    'WebJobServerDirectoryOwner'         => $hGlobalRegexes{'FileOgid'},
    'WebJobServerDirectoryPermissions'   => $hGlobalRegexes{'FilePerm'},
    'WebJobSpoolDirectory'               => $hGlobalRegexes{'FilePath'},
    'WebJobSpoolDirectoryGroup'          => $hGlobalRegexes{'FileOgid'},
    'WebJobSpoolDirectoryOwner'          => $hGlobalRegexes{'FileOgid'},
    'WebJobSpoolDirectoryPermissions'    => $hGlobalRegexes{'FilePerm'},
    'WebJobUmask'                        => $hGlobalRegexes{'FilePerm'},
    'WebJobWebDirectory'                 => $hGlobalRegexes{'FilePath'},
    'WebJobWebDirectoryGroup'            => $hGlobalRegexes{'FileOgid'},
    'WebJobWebDirectoryOwner'            => $hGlobalRegexes{'FileOgid'},
    'WebJobWebDirectoryPermissions'      => $hGlobalRegexes{'FilePerm'},
  },

);
$hGlobalTemplates{'webjob.env'}          = $hGlobalTemplates{'nph-webjob.env'};

######################################################################
#
# PropertiesAddPath
#
######################################################################

sub PropertiesAddPath
{
  my ($phPArgs) = @_;

  ####################################################################
  #
  # Make sure that required inputs are defined.
  #
  ####################################################################

  my %hLArgs =
  (
    'Hash' => $phPArgs,
    'Keys' =>
    [
#     'First',            # Optional
      'Path',             # Optional
#     'RemoveDuplicates', # Optional
#     'Separator',        # Optional
    ],
  );
  if (!defined(VerifyHashKeys(\%hLArgs)))
  {
    $$phPArgs{'Error'} = $hLArgs{'Error'} if (defined($phPArgs));
    return undef;
  }

  ####################################################################
  #
  # Assign default values where necessary.
  #
  ####################################################################

  my $sFirst = (exists($$phPArgs{'First'}) && $$phPArgs{'First'}) ? 1 : 0;

  my $sSeparator = (exists($$phPArgs{'Separator'}) && defined($$phPArgs{'Separator'}) && length($$phPArgs{'Separator'})) ? $$phPArgs{'Separator'} : $Config::Config{'path_sep'};

  my $sRemoveDuplicates = (exists($$phPArgs{'RemoveDuplicates'}) && $$phPArgs{'RemoveDuplicates'}) ? 1 : 0;

  ####################################################################
  #
  # Make sure we have the necessary prerequisites.
  #
  ####################################################################

  if (!length($$phPArgs{'Path'}))
  {
    $$phPArgs{'Error'} = "Empty strings are not considered a valid path.";
    return undef;
  }

  if ($$phPArgs{'Path'} =~ /$sSeparator/)
  {
    $$phPArgs{'Error'} = "A separator ($sSeparator) was found in the specified path ($$phPArgs{'Path'}), and that is not allowed.";
    return undef;
  }

  ####################################################################
  #
  # Update the system PATH by prepending or appending the specified
  # path. Conditionally remove items that match the specified path, so
  # that there are no duplicates and to ensure that the specified path
  # is located where the caller wanted it (i.e., either first or last,
  # but not somewhere in the middle).
  #
  ####################################################################

  my (@aNewPath);

  foreach my $sItem (split(/$sSeparator/, $ENV{'PATH'}))
  {
    push(@aNewPath, $sItem) unless ($sRemoveDuplicates && $$phPArgs{'Path'} eq $sItem);
  }
  if ($sFirst)
  {
    unshift(@aNewPath, $$phPArgs{'Path'});
  }
  else
  {
    push(@aNewPath, $$phPArgs{'Path'});
  }
  $ENV{'PATH'} = join($sSeparator, @aNewPath);

  1;
}


######################################################################
#
# PropertiesGetGlobalExitCodes
#
######################################################################

sub PropertiesGetGlobalExitCodes
{
  return \%hGlobalExitCodes;
}


######################################################################
#
# PropertiesGetGlobalKvps
#
######################################################################

sub PropertiesGetGlobalKvps
{
  return \%hGlobalKvps;
}


######################################################################
#
# PropertiesGetGlobalRegexes
#
######################################################################

sub PropertiesGetGlobalRegexes
{
  return \%hGlobalRegexes;
}


######################################################################
#
# PropertiesGetGlobalTemplates
#
######################################################################

sub PropertiesGetGlobalTemplates
{
  return \%hGlobalTemplates;
}


######################################################################
#
# PropertiesGetOfficialKeyName
#
######################################################################

sub PropertiesGetOfficialKeyName
{
  my ($sType, $sKey) = @_;

  ####################################################################
  #
  # If the keys in the template of the specified type are regular
  # expressions, then there is no official name, so simply return the
  # name that was supplied by the caller -- assuming it passes as a
  # valid key.
  #
  ####################################################################

  if ($sType =~ /^generic$/)
  {
    foreach my $sOfficialKey (keys(%{$hGlobalTemplates{$sType}}))
    {
      if ($sKey =~ /^$sOfficialKey$/)
      {
        return $sKey;
      }
    }
  }
  else
  {
    foreach my $sOfficialKey (keys(%{$hGlobalTemplates{$sType}}))
    {
      if (lc($sKey) eq lc($sOfficialKey))
      {
        return $sOfficialKey;
      }
    }
  }

  return undef;
}


######################################################################
#
# PropertiesGetOfficialKeys
#
######################################################################

sub PropertiesGetOfficialKeys
{
  my ($sType) = @_;

  if (exists($hGlobalTemplates{$sType}))
  {
    return sort(keys(%{$hGlobalTemplates{$sType}}));
  }

  return undef;
}

1;

__END__

=head1 NAME

WebJob::Properties - Global WebJob Properties

=head1 SYNOPSIS

    use WebJob::Properties;

    my $phGlobalRegexes = PropertiesGetGlobalRegexes();

=head1 DESCRIPTION

This module contains a collection of properties commonly used by
various WebJob server-side utilities (e.g., regular expressions).

=head1 AUTHOR

Klayton Monroe

=head1 LICENSE

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

=cut
