Xcruciate::UnitConfig - OO API for reading xacerbate/xteriorize unit config files.


Xcruciate documentation Contained in the Xcruciate distribution.

Index


Code Index:

NAME

Top

Xcruciate::UnitConfig - OO API for reading xacerbate/xteriorize unit config files.

SYNOPSIS

Top

my $config=Xcruciate::UnitConfig->new('unit.conf');

my $cm=$config->chime_multiplier;

my @mdf=$config->modifiable_data_files;

DESCRIPTION

Top

Xcruciate::UnitConfig is part of the Xcruciate project (http://www.xcruciate.co.uk). It provides an OO interface to an xacerbate/xteriorize unit configuration file.

Accessor functions return scalars for <scalar/> entry types and lists for <list/> entry types. The values returned are those found in the config file, with the exception of yes_no datatypes which are converted into perlish boolean values (1 or 0).

All xte*() methods will return an undefined value unless the xte_start entry is set.

The entry() method can be used to access any entry including unofficial extensions. However, it is safer to use the named methods where possible, to avoid inventing unofficial extensions through typos.

AUTHOR

Top

Mark Howe, <melonman@cpan.org>

EXPORT

None

CREATOR METHODS

Top

new(config_file_path,verbose,stop_only [,lax])

Creates and returns an Xcruciate::UnitConfig object which can then be queried. If the optional verbose argument is perlishly true it will show its working to STDOUT. If the stop_only argument is perlishly true it will only bother about the information needed to stop processes (ie hosts and ports).

By default it looks for configuration errors and die noisily if it finds any. This is useful behaviour for management scripts - continuing to set up server daemons on the basis of broken configurations is not best practice. If the fourth argument (lax) is perlishly true, errors will be signalled but the possibly broken object will be created anyway. This is useful behaviour for development purposes, especially when changing config options, but should not be used in a production setting. Note that even the lax version of new() will die if the config file does not look anything like a config file.

UTILITY METHODS

Top

xac_file_format_description()

Returns multi-lined human-friendly description of the xac config file

ACCESSOR METHODS

Top

accept_from()

Returns the ip range from which connections are accepted.

access_log_path()

Returns the path to the access log.

backup_path()

Returns the path to which the backup zip file should be written.

boot_log_path()

Returns the path to the boot log.

chime_multiplier()

Returns the number of ticks per chime

clean_states_path()

Returns the path to the directory containing clean versions of modifiable files.

config_type()

Returns the type of config file, which in this case should always be 'unit'.

current_states_path()

Returns the path to the directory containing current versions of modifiable files.

debug_level()

Returns the xacerbate debug level.

debug_log_path()

Returns the path to the xacerbate debug log.

entry(name)

Returns the entry called name. Lists will be returned by reference. Use named methods in preference to this one where possible.

error_log_path()

Returns the path to the xacerbate error log.

log_file_paths()

Returns a list of locations to which xacerbate application code can write logs.

max_buffer_size()

Returns the maximum buffer size allowed for any one connection.

max_connections()

Returns the maximum number of connections accepted by xacerbate.

max_input_length()

Returns the maximum character length of each XML document.

modifiable_data_files()

Returns a list of modifiable data filenames.

modifiable_transform_files()

Returns a list of modifiable XSL filenames.

path()

Returns the path that is prefixed by xacerbate to various other settings.

peel_multiplier()

Returns the number of chimes per peel.

port()

Returns the port used by xacerbate.

persistent_modifiable_files()

Returns a list of modifiable files that should persist from session to session, ie they are not overwritten from clean on startup.

prepend_to_path(path)

Expects an absolute or relative path. If the path is relative, and if there was a path entry in the config file, the path entry is prepended to the relative path. Otherwise the supplied path is returned unchanged.

server_ip()

Returns the address on which xacerbate listens.

start_xte()

Returns start_xte value (true or false), ie whether xteriorize should be started alongside xacerbate.

startup_commands()

Returns a list of startup command filenames.

startup_files_path()

Returns the path to the startup command files.

tick_interval()

Returns the interval between ticks (or twice the interval between a tick and a tock).

transform_xsl()

Returns the name of the main transform file used by xacerbate.

very_persistent_modifiable_files()

Returns a list of modifiable files that should persist from session to session, even when xcruciate is reset (they will still be reinitialised by a factory reset).

xca_captcha_timeout()

Returns the time limit after which captchas time out

xca_castes()

Returns a list of site-specific castes, in ascending order of rights.

xca_confirmation_timeout()

Returns the time limit after which confirmation codes time out.

xca_date_formats()

Returns a list of date formats.

xca_datetime_formats()

Returns a list of datetime formats.

xca_default_email_contact()

Returns a flag signifying whether, by default, users accept contact via email.

xca_default_pm_contact()

Returns a flag signifying whether, by default, users accept contact via pm.

xca_failed_login_lockout()

Returns the number of failed logins after which an account will be locked.

xca_failed_login_lockout_reset()

Returns the time delay after which a locked account will be unlocked.

xca_favicon()

Returns the url of the site favicon (either a fully-qualified url or a local, absolute url).

xca_from_address()

Returns the email address used for outgoing mail.

xca_gateway_authenticate_timeout()

Returns the timeout for Xteriorize gateways authenticating with Xcathedra.

xca_http_domain()

Returns the website domain.

xca_manual_registration_activation()

Returns a flag signifying whether manual admin approval of new user accounts is currently activated.

xca_path()

Returns the path to the directory containing xcathedra (if defined).

xca_profile_template_path()

Returns the path to the site-specific template for user profiles.

xca_script_debug_caste()

Returns the minimum caste which will see extended script error diagnostics.

xca_session_timeout()

Returns the delay after which a session will time out.

xca_site_path()

Returns the path to the directory containing the site-specific files.

xca_time_offset()

Returns the default time zone offset.

xca_unique_registration_email()

Returns a flag signifying whether each user must use a unique email to register.

xte_captcha_bgcolors()

Returns a list of hex bytes (00 to FF) to be used to construct captcha background RGB values.

xte_captcha_colors()

Returns a list of hex bytes (00 to FF) to be used to construct captcha foreground RGB values.

xte_captcha_height()

Returns the pixel height of captchas.

xte_captcha_max_angle()

Returns the maximum angle by which TrueType text will be rotated in a captcha.

xte_captcha_max_line_thickness()

Returns the maximum thickness of captcha lines.

xte_captcha_max_lines()

Returns the maximum number of lines to draw on a captcha.

xte_captcha_min_line_thickness()

Returns the minimum line thickness to use in captchas.

xte_captcha_min_lines()

Returns the minimum number of lines to draw on a captcha.

xte_captcha_particle_count()

Returns the number of particles to add to a captcha.

xte_captcha_particle_size()

Returns the size of particles added to a captcha.

xte_captcha_styles()

Returns a list of GD:SecurityImage captcha styles.

xte_captcha_ttfont_size()

Returns the point size for TrueType captcha text.

xte_captcha_ttfonts()

Returns a list of TrueType fonts for captchas.

xte_captcha_width()

Returns the pixel width of captchas.

xte_check_for_waiting()

Returns the xte_check_for_waiting value (time to wait before revising number of child processes).

xte_cidr_allow()

Returns a list of allowed ip ranges for xteriorize

xte_cidr_deny()

Returns a list of denied ip ranges for xteriorize

xte_docroot()

Returns the docroot used by xteriorize.

xte_enable_captcha()

Returns true if captcha serving is enabled.

xte_enable_email()

Returns true if is email sending is enabled.

xte_enable_file_writing()

Returns true if is writing and deleting files is enabled.

xte_enable_fop()

Returns true if PDF output via Apache FOP is enabled.

xte_enable_json()

Returns true if output via XML::GenericJSON is enabled.

xte_enable_mxmlc()

Returns true if output via the Flex 3 compiler mxmlc is enabled.

xte_enable_static_serving()

Returns true if direct static file serving (ie without xacerbate) is enabled.

xte_enable_uploads()

Returns true if HTTP file uploading is enabled.

xte_enable_xmlroff()

Returns true if PDF output via xmlroff is enabled.

xte_from_address()

Returns the from address for emails sent by xteriorize

xte_gateway_auth()

Returns the from code expected by xacerbate to authorize gateway connections.

xte_group()

Returns the un*x group to use for xteriorize child processes. May be undefined.

xte_i18n_list()

Returns a list of i18n files.

xte_image_sizes()

Returns a list of sizes (eg 123x456) to which images will be scaled/cropped.

xte_log_file()

Returns the path to the xte log file.

xte_log_level()

Returns the xteriorize log level.

xte_max_image_size()

Returns the maximum dimensions (eg 123x456) beyond which an uploaded image will be cropped.

xte_max_servers()

Returns the Net::Prefork max_servers value for xteriorize.

xte_max_requests()

Returns the Net::Prefork max_requests value for xteriorize.

xte_max_spare_servers()

Returns the Net::Prefork max_spare_servers value for xteriorize.

xte_max_upload_size()

Returns the maximum permitted size in kb of file uploads.

xte_mimetype_path()

Returns the path to the mimetype lookup table for direct static file serving.

xte_min_servers()

Returns the Net::Prefork min_servers value for xteriorize.

xte_min_spare_servers()

Returns the Net::Prefork min_spare_servers value for xteriorize.

xte_port()

Returns the port used by xteriorize.

xte_post_max()

Returns the maximum character size of an http request received by xteriorize.

xte_report_benchmarks()

Returns true if timings for various aspects of Xcruciate processing are being output.

xte_server_ip()

Returns the ip on which xteriorize will listen.

xte_site_language()

Returns the site language in 2-character format.

xte_smtp_charset()

Returns the charset used for smtp by xteriorize.

xte_smtp_encoding()

Returns the encoding used for smtp by xteriorize.

xte_smtp_host()

Returns the host used for smtp by xteriorize.

xte_smtp_port()

Returns the port used for smtp by xteriorize.

xte_splurge_input()

Returns true if xte_splurge_input is enabled (copies XML sent from xteriorize to xacerbate to STDERR).

xte_splurge_output()

Returns true if xte_splurge_output is enabled (copies XML sent from xacerbate to xteriorize to STDERR).

xte_static_directories()

Returns a list of directories under docroot from which files will be served directly by Xteriorized.

xte_temporary_file_path()

Returns a directory to be used for temporary files, eg for output filters

xte_user()

Returns the un*x user to use for xteriorize child processes. May be undefined.

xte_use_xca()

Returns a flag according to whether or not xcathedra is used.

xte_xac_timeout()

Returns the delay for a response to xteriorize by xacerbate, after which xteriorize will issue a 504 ('gateway time-out') error.

local_croak()

Function for croaking

BUGS

Top

The best way to report bugs is via the Xcruciate bugzilla site (http://www.xcruciate.co.uk/bugzilla).

PREVIOUS VERSIONS

Top

0.01: First upload

0.03: First upload including module

0.04: Changed minimum perl version to 5.8.8

0.05: Added debug_list data type. Warn about unknown entries

0.06: Added stop_only option to new(), added some comments

0.07: Revised config file entry names. Check server_ip as well as port on start/stop. Attempt to put all Xcruciate modules in one PAUSE tarball.

0.08: Added xte_temporary_file_path. Added lax option to proceed despite config errors.

0.09: Use Carp for errors.

0.10: Prepend path entry to relative paths

0.11: Remove transform_xsl_path

0.12: Resolve modifiable file paths, attempt to parse XML and XSLT files

0.14: Global update

0.15: Added xte_splurge_output

0.16: Added support for xca entries. Added very_persistent_modifiable_files and xte_i18n_files. Distinquish warnings and errors in output.

0.17: use warnings.

0.18: Removed xca_time_display_function. Made v0.16 additions optional. Added nine new xca entries. Added new types to file format reporting.

0.19: Use duration type for durations. Added xca_gateway_authenticate_timeout (previously a global in Xcathedra code). Got name of xte_use_xca right. Got missing xca entry testing in right loop.

0.20: Added backup_path, xte_site_language, xte captcha entries and xte_enable entries, xca_image_sizes entries.

COPYRIGHT AND LICENSE

Top


Xcruciate documentation Contained in the Xcruciate distribution.
package Xcruciate::UnitConfig;

use Exporter;
@ISA    = ('Exporter');
@EXPORT = qw();
our $VERSION = 0.21;

use strict;
use warnings;
use Carp;
use Xcruciate::Utils 0.21;

#Records fields:
#  scalar/list
#  Optional? (1 means 'yes')
#  data type
#  data type specific fields:
#     min, max for numbers
#     required permissions for files/directories

my $xac_settings = {
    'accept_from',
    [ 'scalar', 1, 'word' ],
    'access_log_path',
    [ 'scalar', 0, 'abs_create', 'rw' ],
    'backup_path',
    [ 'scalar', 1, 'abs_create', 'rw' ],
    'boot_log_path',
    [ 'scalar', 0, 'abs_create', 'rw' ],
    'chime_multiplier',
    [ 'scalar', 0, 'integer', 2 ],
    'clean_states_path',
    [ 'scalar', 0, 'path' ],
    'config_type',
    [ 'scalar', 0, 'word' ],
    'current_states_path',
    [ 'scalar', 0, 'path' ],
    'debug_level',
    [ 'scalar', 0, 'debug_list' ],
    'debug_log_path',
    [ 'scalar', 0, 'abs_create', 'rw' ],
    'error_log_path',
    [ 'scalar', 0, 'abs_create', 'rw' ],
    'log_file_paths',
    [ 'list', 1, 'abs_create', 'rw' ],
    'max_buffer_size',
    [ 'scalar', 0, 'integer', 1 ],
    'max_connections',
    [ 'scalar', 0, 'integer', 1 ],
    'max_input_length',
    [ 'scalar', 0, 'integer', 1 ],
    'modifiable_data_files',
    [ 'list', 1, 'abs_file', 'r', 'xml', 'clean_states_path' ],
    'modifiable_transform_files',
    [ 'list', 1, 'abs_file', 'r', 'xml', 'clean_states_path' ],
    'path',
    [ 'scalar', 1, 'abs_dir', 'r' ],
    'peel_multiplier',
    [ 'scalar', 0, 'integer', 2 ],
    'persistent_modifiable_files',
    [ 'list', 1, 'abs_file', 'r', 'xml', 'clean_states_path' ],
    'port',
    [ 'scalar', 0, 'integer', 1, 65535 ],
    'server_ip',
    [ 'scalar', 0, 'ip' ],
    'start_xte',
    [ 'scalar', 0, 'yes_no' ],
    'startup_commands',
    [ 'list', 1, 'abs_file', 'r', 'xml', 'startup_files_path' ],
    'startup_files_path',
    [ 'scalar', 1, 'path' ],
    'tick_interval',
    [ 'scalar', 0, 'duration' ],
    'transform_xsl',
    [ 'scalar', 0, 'abs_file', 'r', 'xsl' ],
    'very_persistent_modifiable_files',
    [ 'list', 1, 'abs_file', 'r', 'xml', 'clean_states_path' ],
};

my $xte_settings = {
    'xte_captcha_bgcolors',           [ 'list',   0, 'hexbyte' ],
    'xte_captcha_colors',             [ 'list',   0, 'hexbyte' ],
    'xte_captcha_height',             [ 'scalar', 0, 'integer', 10, 512 ],
    'xte_captcha_max_angle',          [ 'scalar', 0, 'integer', 0, 90 ],
    'xte_captcha_max_line_thickness', [ 'scalar', 0, 'integer', 1, 10 ],
    'xte_captcha_max_lines',          [ 'scalar', 0, 'integer', 1, 10 ],
    'xte_captcha_min_line_thickness', [ 'scalar', 0, 'integer', 1, 10 ],
    'xte_captcha_min_lines',          [ 'scalar', 0, 'integer', 0, 10 ],
    'xte_captcha_particle_count',     [ 'scalar', 0, 'integer', 0, 1000 ],
    'xte_captcha_particle_size',      [ 'scalar', 0, 'integer', 1, 10 ],
    'xte_captcha_styles',             [ 'list',   0, 'captchastyle' ],
    'xte_captcha_ttfont_size',        [ 'scalar', 0, 'integer', 8, 72 ],
    'xte_captcha_ttfonts',   [ 'list',   0, 'abs_file', 'r' ],
    'xte_captcha_width',     [ 'scalar', 0, 'integer',  20, 1024 ],
    'xte_check_for_waiting', [ 'scalar', 1, 'integer',  0 ],
    'xte_cidr_allow',            [ 'list',   1, 'cidr' ],
    'xte_cidr_deny',             [ 'list',   1, 'cidr' ],
    'xte_docroot',               [ 'scalar', 0, 'abs_dir', 'rw' ],
    'xte_enable_captcha',        [ 'scalar', 1, 'yes_no' ],
    'xte_enable_email',          [ 'scalar', 1, 'yes_no' ],
    'xte_enable_file_writing',   [ 'scalar', 1, 'yes_no' ],
    'xte_enable_fop',            [ 'scalar', 1, 'yes_no' ],
    'xte_enable_json',           [ 'scalar', 1, 'yes_no' ],
    'xte_enable_mxmlc',          [ 'scalar', 1, 'yes_no' ],
    'xte_enable_static_serving', [ 'scalar', 1, 'yes_no' ],
    'xte_enable_uploads',        [ 'scalar', 1, 'yes_no' ],
    'xte_enable_xmlroff',        [ 'scalar', 1, 'yes_no' ],
    'xte_error_i18n',            [ 'scalar', 0, 'abs_file', 'r' ],
    'xte_from_address',          [ 'scalar', 0, 'email' ],
    'xte_gateway_auth',          [ 'scalar', 0, 'word' ],
    'xte_group',                 [ 'scalar', 1, 'word' ],
    'xte_image_sizes',           [ 'list',   0, 'imagesize' ],
    'xte_i18n_list',             [ 'list',   1, 'abs_file', 'r', 'xml' ],
    'xte_server_ip',             [ 'scalar', 0, 'ip' ],
    'xte_log_file', [ 'scalar', 1, 'abs_create', 'rw' ],
    'xte_log_level', [ 'scalar', 1, 'integer', 0, 4 ],
    'xte_max_image_size',    [ 'scalar', 1, 'imagesize' ],
    'xte_max_requests',      [ 'scalar', 1, 'integer', 1 ],
    'xte_max_servers',       [ 'scalar', 1, 'integer', 1 ],
    'xte_max_spare_servers', [ 'scalar', 1, 'integer', 1 ],
    'xte_max_upload_size',   [ 'scalar', 1, 'integer', 1 ],
    'xte_mimetype_path',     [ 'scalar', 1, 'abs_file', 'r' ],
    'xte_min_servers',       [ 'scalar', 1, 'integer', 1 ],
    'xte_min_spare_servers', [ 'scalar', 1, 'integer', 1 ],
    'xte_port',              [ 'scalar', 0, 'integer', 1, 65535 ],
    'xte_post_max', [ 'scalar', 0, 'integer', 1 ],
    'xte_report_benchmarks', [ 'scalar', 1, 'yes_no' ],
    'xte_site_language',     [ 'scalar', 0, 'language' ],
    'xte_smtp_charset',      [ 'scalar', 0 ],
    'xte_smtp_encoding',     [ 'scalar', 0 ],
    'xte_smtp_host',         [ 'scalar', 0, 'ip' ],
    'xte_smtp_port', [ 'scalar', 0, 'integer', 1, 65535 ],
    'xte_static_directories',  [ 'list',   1, 'word' ],
    'xte_splurge_input',       [ 'scalar', 1, 'yes_no' ],
    'xte_splurge_output',      [ 'scalar', 1, 'yes_no' ],
    'xte_temporary_file_path', [ 'scalar', 0, 'abs_create', 'rw' ],
    'xte_user',                [ 'scalar', 1, 'word' ],
    'xte_use_xca',             [ 'scalar', 0, 'yes_no' ],
    'xte_xac_timeout',         [ 'scalar', 0, 'duration' ]
};

my $xca_settings = {
    'xca_captcha_timeout',
    [ 'scalar', 0, 'duration' ],
    'xca_castes',
    [ 'list', 1, 'word' ],
    'xca_confirmation_timeout',
    [ 'scalar', 0, 'duration' ],
    'xca_date_formats',
    [ 'list', 1, 'dateformat' ],
    'xca_datetime_formats',
    [ 'list', 1, 'dateformat' ],
    'xca_default_email_contact',
    [ 'scalar', 0, 'yes_no' ],
    'xca_default_pm_contact',
    [ 'scalar', 0, 'yes_no' ],
    'xte_enable_captcha',
    [ 'scalar', 1, 'yes_no' ],
    'xte_enable_email',
    [ 'scalar', 1, 'yes_no' ],
    'xte_enable_file_writing',
    [ 'scalar', 1, 'yes_no' ],
    'xte_enable_fop',
    [ 'scalar', 1, 'yes_no' ],
    'xte_enable_json',
    [ 'scalar', 1, 'yes_no' ],
    'xte_enable_mxmlc',
    [ 'scalar', 1, 'yes_no' ],
    'xte_enable_xmlroff',
    [ 'scalar', 1, 'yes_no' ],
    'xca_favicon',
    [ 'scalar', 0, 'url' ],
    'xca_failed_login_lockout',
    [ 'scalar', 0, 'integer' ],
    'xca_failed_login_lockout_reset',
    [ 'scalar', 0, 'duration' ],
    'xca_from_address',
    [ 'scalar', 0, 'email' ],
    'xca_gateway_authenticate_timeout',
    [ 'scalar', 0, 'duration' ],
    'xca_http_domain',
    [ 'scalar', 0, 'word' ],
    'xca_manual_registration_activation',
    [ 'scalar', 0, 'yes_no' ],
    'xca_path',
    [ 'scalar', 0, 'abs_dir', 'r' ],
    'xca_profile_template_path',
    [ 'scalar', 0, 'abs_file', 'r', 'xml' ],
    'xca_script_debug_caste',
    [ 'scalar', 0, 'word' ],
    'xca_session_timeout',
    [ 'scalar', 0, 'duration' ],
    'xca_site_path',
    [ 'scalar', 0, 'abs_dir', 'rw' ],
    'xca_time_offset',
    [ 'scalar', 0, 'timeoffset' ],
    'xca_unique_registration_email',
    [ 'scalar', 0, 'yes_no' ]
};

my $stop_settings = {
    'server_ip'     => 1,
    'port'          => 1,
    'start_xte'     => 1,
    'xte_server_ip' => 1,
    'xte_port'      => 1
};

sub new {
    my $class                = shift;
    my $path                 = shift;
    my $verbose              = shift || 0;
    my $stop_only            = shift;
    my $stop_only_parse_text = "";
    $stop_only_parse_text = " (hosts and ports only)" if $stop_only;
    my $lax = shift || 0;
    my $self = {};

    # Check that there's a file at the end of the config file option
    local_croak(
        Xcruciate::Utils::check_path( 'unit config file', $path, 'r', 1 ) );

    # Parse config file
    print "Attempting to parse unit config file$stop_only_parse_text... "
      if $verbose;
    my $parser  = XML::LibXML->new();
    my $xac_dom = $parser->parse_file($path);
    print "done\n" if $verbose;

    #Bail out if config file isn't even close to what is expected
    my @config = $xac_dom->findnodes("/config/scalar");
    croak
"Config file doesn't look anything like a config file - 'xcruciate file_help' for some clues"
      unless $config[0];
    my @config_type =
      $xac_dom->findnodes("/config/scalar[\@name='config_type']/text()");
    croak "config_type entry not found in unit config file"
      unless $config_type[0];
    my $config_type = $config_type[0]->toString;
    croak
"config_type in unit config file is '$config_type' (should be 'unit') - are you confusing xcruciate and unit config files?"
      unless $config_type eq 'unit';
    my @config_path =
      $xac_dom->findnodes("/config/scalar[\@name='path']/text()");
    my $config_path = $config_path[0];
    $config_path = $config_path->toString if $config_path;

    # Work through config options in config file
    my @errors = ();
    foreach my $entry (
        $xac_dom->findnodes(
            "/config/*[(local-name() = 'scalar') or (local-name() = 'list')]")
      )
    {

        # Does it have a name attribute?
        push @errors,
          sprintf( "No name attribute for element '%s'", $entry->nodeName )
          unless $entry->hasAttribute('name');
        my $entry_record =
             $xac_settings->{ $entry->getAttribute('name') }
          || $xte_settings->{ $entry->getAttribute('name') }
          || $xca_settings->{ $entry->getAttribute('name') };

        #Skip checks if stop_only and this entry isn't needed to stop
        next
          if ( $stop_only
            and not( $stop_settings->{ $entry->getAttribute('name') } ) );

        # Warn about entries in config file that are not defined, but continue.
        if ( not defined $entry_record ) {
            carp "WARNING: Unknown unit config entry '"
              . ( $entry->getAttribute('name') ) . "'";
        }
        elsif ( not( $entry->nodeName eq $entry_record->[0] ) ) {

            # Is it a scalar or list as expected?
            push @errors,
              sprintf(
                "Entry called %s should be a %s not a %s",
                $entry->getAttribute('name'),
                $entry_record->[0], $entry->nodeName
              );
        }
        elsif ( ( not $entry->textContent )
            and
            ( ( not $entry_record->[1] ) or $entry->textContent !~ /^\s*$/s ) )
        {

            #Entry, but value missing and not optional
            push @errors,
              sprintf( "Entry called %s requires a value",
                $entry->getAttribute('name') );
        }
        elsif (
                ( $entry->nodeName eq 'scalar' )
            and $entry_record->[2]
            and (  ( not $entry_record->[1] )
                or $entry->textContent !~ /^\s*$/s
                or $entry->textContent )
          )
        {

            #Produce path for this field
            my $entry_path = $config_path;
            if ( ( $entry_record->[2] eq 'abs_file' ) and $entry_record->[5] ) {
                my @entry_config_path = $xac_dom->findnodes(
                    "/config/*[\@name='$entry_record->[5]']/text()");
                $entry_path .= '/' . $entry_config_path[0]->toString
                  if $entry_config_path[0];
            }

            #Entry is a scalar - type check
            push @errors,
              Xcruciate::Utils::type_check( $entry_path,
                $entry->getAttribute('name'),
                $entry->textContent, $entry_record );
        }
        elsif ( ( $entry->nodeName eq 'list' ) and $entry_record ) {

            #Entry is a list...

            #Non-optional list entries require at least one item
            my @items = $entry->findnodes('item/text()');
            push @errors,
              sprintf( "Entry called %s requires at least one item",
                $entry->getAttribute('name') )
              if ( ( not $entry_record->[2] ) and ( not @items ) );

            #Produce path for this field
            my $entry_path = $config_path;
            if ( ( $entry_record->[2] eq 'abs_file' ) and $entry_record->[5] ) {
                my @entry_config_path = $xac_dom->findnodes(
                    "/config/*[\@name='$entry_record->[5]']/text()");
                $entry_path .= '/' . $entry_config_path[0]->toString
                  if $entry_config_path[0];
            }

            # Type check each item in list
            my $count = 1;
            foreach my $item (@items) {
                push @errors,
                  Xcruciate::Utils::type_check( $entry_path,
                    $entry->getAttribute('name'),
                    $item->textContent, $entry_record, $count );
                $count++;
            }
        }

        # Duplicate entries not allowed
        push @errors,
          sprintf( "Duplicate entry called %s", $entry->getAttribute('name') )
          if defined $self->{ $entry->getAttribute('name') };

        # Add entry value to object hash
        if ( $entry->nodeName eq 'scalar' ) {
            $self->{ $entry->getAttribute('name') } = $entry->textContent;
        }
        else {
            $self->{ $entry->getAttribute('name') } = []
              unless defined $self->{ $entry->getAttribute('name') };
            foreach my $item ( $entry->findnodes('item/text()') ) {
                push @{ $self->{ $entry->getAttribute('name') } },
                  $item->textContent;
            }
        }
    }

    # Report missing entries
    foreach my $entry ( keys %{$xac_settings} ) {
        next if ( $stop_only and not( $stop_settings->{$entry} ) );
        push @errors, sprintf( "No xacerbate entry called %s", $entry )
          unless ( ( defined $self->{$entry} )
            or ( $xac_settings->{$entry}->[1] ) );
    }
    if ( ( defined $self->{start_xte} ) and ( $self->{start_xte} eq "yes" ) ) {
        foreach my $entry ( keys %{$xte_settings} ) {
            next if ( $stop_only and not( $stop_settings->{$entry} ) );
            push @errors, sprintf( "No xteriorize entry called %s", $entry )
              unless ( ( defined $self->{$entry} )
                or ( $xte_settings->{$entry}->[1] ) );
        }
        if (    ( defined $self->{xte_use_xca} )
            and ( $self->{xte_use_xca} eq "yes" ) )
        {
            foreach my $entry ( keys %{$xca_settings} ) {
                next if ( $stop_only and not( $stop_settings->{$entry} ) );
                push @errors, sprintf( "No xcathedra entry called %s", $entry )
                  unless ( ( defined $self->{$entry} )
                    or ( $xca_settings->{$entry}->[1] ) );
            }
        }
    }

    # And the final scores are...
    if ( @errors and $lax ) {
        if ($verbose) {
            foreach (@errors) {
                print "ERROR: $_\n";
            }
        }
        carp
"WARNING: Errors in unit config file, but lax flag set, so proceeding anyway. This could be exciting...\n";
        bless( $self, $class );
        return $self;
    }
    elsif (@errors) {
        foreach (@errors) {
            print "ERROR: $_\n";
        }
        croak
"Errors in unit config file - cannot continue (force at your own risk using --lax flag)";
    }
    else {
        bless( $self, $class );
        return $self;
    }
}

sub xac_file_format_description {
    my $self = shift;
    my $ret  = '';
    foreach my $entry (
        sort ( keys %{$xac_settings}, keys %{$xte_settings},
            keys %{$xca_settings} ) )
    {
        my $record =
             $xac_settings->{$entry}
          || $xte_settings->{$entry}
          || $xca_settings->{$entry};
        $ret .= "$entry (";
        $ret .= "optional " if $record->[1];
        $ret .= "$record->[0])";
        if ( not $record->[2] ) {
        }
        elsif ( ( $record->[2] eq 'integer' ) or ( $record->[2] eq 'float' ) ) {
            $ret .= " - $record->[2]";
            $ret .= " >= $record->[3]" if defined $record->[3];
            $ret .= " and <= $record->[4]" if defined $record->[4];
        }
        elsif ( $record->[2] eq 'ip' ) {
            $ret .= " - ip address";
        }
        elsif ( $record->[2] eq 'word' ) {
            $ret .= " - word (ie no whitespace)";
        }
        elsif ( $record->[2] eq 'path' ) {
            $ret .= " - path (currently a word)";
        }
        elsif ( $record->[2] eq 'cidr' ) {
            $ret .= " - an ip range in CIDR format";
        }
        elsif ( $record->[2] eq 'dateformat' ) {
            $ret .= " - date format";
        }
        elsif ( $record->[2] eq 'url' ) {
            $ret .= " - url (starts with http or /)";
        }
        elsif ( $record->[2] eq 'duration' ) {
            $ret .= " - duration in XML Schema format";
        }
        elsif ( $record->[2] eq 'timeoffset' ) {
            $ret .= " - timezone offset (-11 to 12)";
        }
        elsif ( $record->[2] eq 'xml_leaf' ) {
            $ret .= " - filename with an xml suffix";
        }
        elsif ( $record->[2] eq 'xsl_leaf' ) {
            $ret .= " - filename with an xsl suffix";
        }
        elsif ( $record->[2] eq 'yes_no' ) {
            $ret .= " - 'yes' or 'no'";
        }
        elsif ( $record->[2] eq 'email' ) {
            $ret .= " - email address";
        }
        elsif ( $record->[2] eq 'debug_list' ) {
            $ret .=
              " - comma-separated list of debugging options (or 'all'/'none')";
        }
        elsif ( $record->[2] eq 'abs_dir' ) {
            $ret .= " - absolute directory path with $record->[3] permissions";
        }
        elsif ( $record->[2] eq 'abs_file' ) {
            $ret .= " - absolute file path with $record->[3] permissions";
        }
        elsif ( $record->[2] eq 'abs_create' ) {
            $ret .=
" - absolute file path with $record->[3] permissions for directory";
        }
        else { $ret .= " - UNKNOWN TYPE $record->[2]" }
        $ret .= "\n";
    }
    return $ret;
}

sub accept_from {
    my $self = shift;
    return $self->{accept_from};
}

sub access_log_path {
    my $self = shift;
    return $self->{access_log_path};
}

sub backup_path {
    my $self = shift;
    return $self->{backup_path};
}

sub boot_log_path {
    my $self = shift;
    return $self->{boot_log_path};
}

sub chime_multiplier {
    my $self = shift;
    return $self->{chime_multiplier};
}

sub clean_states_path {
    my $self = shift;
    return $self->{clean_states_path};
}

sub config_type {
    my $self = shift;
    return $self->{config_type};
}

sub current_states_path {
    my $self = shift;
    return $self->{current_states_path};
}

sub debug_level {
    my $self = shift;
    return $self->{debug_level};
}

sub debug_log_path {
    my $self = shift;
    return $self->{debug_log_path};
}

sub entry {
    my $self = shift;
    my $name = shift;
    return $self->{$name};
}

sub error_log_path {
    my $self = shift;
    return $self->{error_log_path};
}

sub log_file_paths {
    my $self = shift;
    return @{ $self->{log_file_paths} || [] };
}

sub max_buffer_size {
    my $self = shift;
    return $self->{max_buffer_size};
}

sub max_connections {
    my $self = shift;
    return $self->{max_connections};
}

sub max_input_length {
    my $self = shift;
    return $self->{max_input_length};
}

sub modifiable_data_files {
    my $self = shift;
    return @{ $self->{modifiable_data_files} || [] };
}

sub modifiable_transform_files {
    my $self = shift;
    return @{ $self->{modifiable_transform_files} || [] };
}

sub path {
    my $self = shift;
    return $self->{path};
}

sub peel_multiplier {
    my $self = shift;
    return $self->{peel_multiplier};
}

sub port {
    my $self = shift;
    return $self->{port};
}

sub persistent_modifiable_files {
    my $self = shift;
    return @{ $self->{persistent_modifiable_files} || [] };
}

sub prepend_to_path {
    my $self          = shift;
    my $supplied_path = shift;
    my $config_path   = $self->path;
    if ( $config_path and $supplied_path !~ m!^/! ) {
        return "$config_path/$supplied_path";
    }
    else {
        return $supplied_path;
    }
}

sub server_ip {
    my $self = shift;
    return $self->{server_ip};
}

sub start_xte {
    my $self = shift;
    if ( lc( $self->{start_xte} ) eq 'yes' ) {
        return 1;
    }
    else { return 0 }
}

sub startup_commands {
    my $self = shift;
    return @{ $self->{startup_commands} || [] };
}

sub startup_files_path {
    my $self = shift;
    return $self->{startup_files_path};
}

sub tick_interval {
    my $self = shift;
    return $self->{tick_interval};
}

sub transform_xsl {
    my $self = shift;
    return $self->{transform_xsl};
}

sub very_persistent_modifiable_files {
    my $self = shift;
    return @{ $self->{very_persistent_modifiable_files} || [] };
}

sub xca_captcha_timeout {
    my $self = shift;
    if (    ( lc( $self->{start_xte} ) eq 'yes' )
        and ( lc( $self->{xte_use_xca} ) eq 'yes' ) )
    {
        return $self->{xca_captcha_timeout};
    }
    else {
        return undef;
    }
}

sub xca_castes {
    my $self = shift;
    if (    ( lc( $self->{start_xte} ) eq 'yes' )
        and ( lc( $self->{xte_use_xca} ) eq 'yes' ) )
    {
        return @{ $self->{xca_castes} || [] };
    }
    else {
        return undef;
    }
}

sub xca_confirmation_timeout {
    my $self = shift;
    if (    ( lc( $self->{start_xte} ) eq 'yes' )
        and ( lc( $self->{xte_use_xca} ) eq 'yes' ) )
    {
        return $self->{xca_confirmation_timeout};
    }
    else {
        return undef;
    }
}

sub xca_date_formats {
    my $self = shift;
    if ( lc( ( $self->{start_xte} ) eq 'yes' )
        and ( lc( $self->{xte_use_xca} ) eq 'yes' ) )
    {
        return @{ $self->{xca_date_formats} || [] };
    }
    else {
        return undef;
    }
}

sub xca_datetime_formats {
    my $self = shift;
    if (    ( lc( $self->{start_xte} ) eq 'yes' )
        and ( lc( $self->{xte_use_xca} ) eq 'yes' ) )
    {
        return @{ $self->{xca_datetime_formats} || [] };
    }
    else {
        return undef;
    }
}

sub xca_default_email_contact {
    my $self = shift;
    if (    ( lc( $self->{start_xte} ) eq 'yes' )
        and ( lc( $self->{xte_use_xca} ) eq 'yes' )
        and ( lc( $self->{xca_default_email_contact} ) eq 'yes' ) )
    {
        return 1;
    }
    else {
        return 0;
    }
}

sub xca_default_pm_contact {
    my $self = shift;
    if (    ( lc( $self->{start_xte} ) eq 'yes' )
        and ( lc( $self->{xte_use_xca} ) eq 'yes' )
        and ( lc( $self->{xca_default_pm_contact} ) eq 'yes' ) )
    {
        return 1;
    }
    else {
        return 0;
    }
}

sub xca_failed_login_lockout {
    my $self = shift;
    if (    ( lc( $self->{start_xte} ) eq 'yes' )
        and ( lc( $self->{xte_use_xca} ) eq 'yes' ) )
    {
        return $self->{xca_failed_login_lockout};
    }
    else {
        return undef;
    }
}

sub xca_failed_login_lockout_reset {
    my $self = shift;
    if (    ( lc( $self->{start_xte} ) eq 'yes' )
        and ( lc( $self->{xte_use_xca} ) eq 'yes' ) )
    {
        return $self->{xca_failed_login_lockout_reset};
    }
    else {
        return undef;
    }
}

sub xca_favicon {
    my $self = shift;
    if ( lc( ( $self->{start_xte} ) eq 'yes' )
        and ( lc( $self->{xte_use_xca} ) eq 'yes' ) )
    {
        return $self->{xca_favicon};
    }
    else {
        return undef;
    }
}

sub xca_from_address {
    my $self = shift;
    if (    ( lc( $self->{start_xte} ) eq 'yes' )
        and ( lc( $self->{xte_use_xca} ) eq 'yes' ) )
    {
        return $self->{xca_from_address};
    }
    else {
        return undef;
    }
}

sub xca_gateway_authenticate_timeout {
    my $self = shift;
    if (    ( lc( $self->{start_xte} ) eq 'yes' )
        and ( lc( $self->{xte_use_xca} ) eq 'yes' ) )
    {
        return $self->{xca_gateway_authenticate_timeout};
    }
    else {
        return undef;
    }
}

sub xca_http_domain {
    my $self = shift;
    if (    ( lc( $self->{start_xte} ) eq 'yes' )
        and ( lc( $self->{xte_use_xca} ) eq 'yes' ) )
    {
        return $self->{http_domain};
    }
    else {
        return undef;
    }
}

sub xca_manual_registration_activation {
    my $self = shift;
    if (    ( lc( $self->{start_xte} ) eq 'yes' )
        and ( lc( $self->{xte_use_xca} ) eq 'yes' )
        and ( lc( $self->{xca_manual_registration_activation} ) eq 'yes' ) )
    {
        return 1;
    }
    else {
        return 0;
    }
}

sub xca_path {
    my $self = shift;
    if (    ( lc( $self->{start_xte} ) eq 'yes' )
        and ( lc( $self->{xte_use_xca} ) eq 'yes' ) )
    {
        return $self->{xca_path};
    }
    else {
        return undef;
    }
}

sub xca_profile_template_path {
    my $self = shift;
    if (    ( lc( $self->{start_xte} ) eq 'yes' )
        and ( lc( $self->{xte_use_xca} ) eq 'yes' ) )
    {
        return $self->{xca_profile_template_path};
    }
    else {
        return undef;
    }
}

sub xca_script_debug_caste {
    my $self = shift;
    if (    ( lc( $self->{start_xte} ) eq 'yes' )
        and ( lc( $self->{xte_use_xca} ) eq 'yes' ) )
    {
        return $self->{xca_script_debug_caste};
    }
    else {
        return undef;
    }
}

sub xca_session_timeout {
    my $self = shift;
    if (    ( lc( $self->{start_xte} ) eq 'yes' )
        and ( lc( $self->{xte_use_xca} ) eq 'yes' ) )
    {
        return $self->{xca_session_timeout};
    }
    else {
        return undef;
    }
}

sub xca_site_path {
    my $self = shift;
    if (    ( lc( $self->{start_xte} ) eq 'yes' )
        and ( lc( $self->{xte_use_xca} ) eq 'yes' ) )
    {
        return $self->{xca_site_path};
    }
    else {
        return undef;
    }
}

sub xca_time_offset {
    my $self = shift;
    if (    ( lc( $self->{start_xte} ) eq 'yes' )
        and ( lc( $self->{xte_use_xca} ) eq 'yes' ) )
    {
        return $self->{xca_time_offset};
    }
    else {
        return undef;
    }
}

sub xca_unique_registration_email {
    my $self = shift;
    if (    ( lc( $self->{start_xte} ) eq 'yes' )
        and ( lc( $self->{xte_use_xca} ) eq 'yes' )
        and ( lc( $self->{xca_unique_registration_email} ) eq 'yes' ) )
    {
        return 1;
    }
    else {
        return 0;
    }
}

sub xte_captcha_bgcolors {
    my $self = shift;
    if ( ( lc( $self->{start_xte} ) eq 'yes' )
        and $self->{xte_captcha_bgcolors} )
    {
        return @{ $self->{xte_captcha_bgcolors} };
    }
    else {
        return ();
    }
}

sub xte_captcha_colors {
    my $self = shift;
    if ( ( lc( $self->{start_xte} ) eq 'yes' ) and $self->{xte_captcha_colors} )
    {
        return @{ $self->{xte_captcha_colors} };
    }
    else {
        return ();
    }
}

sub xte_captcha_height {
    my $self = shift;
    if ( lc( $self->{start_xte} ) eq 'yes' ) {
        return $self->{xte_captcha_height};
    }
    else {
        return undef;
    }
}

sub xte_captcha_max_angle {
    my $self = shift;
    if ( lc( $self->{start_xte} ) eq 'yes' ) {
        return $self->{xte_captcha_max_angle};
    }
    else {
        return undef;
    }
}

sub xte_captcha_max_line_thickness {
    my $self = shift;
    if ( lc( $self->{start_xte} ) eq 'yes' ) {
        return $self->{xte_captcha_max_line_thickness};
    }
    else {
        return undef;
    }
}

sub xte_captcha_max_lines {
    my $self = shift;
    if ( lc( $self->{start_xte} ) eq 'yes' ) {
        return $self->{xte_captcha_max_lines};
    }
    else {
        return undef;
    }
}

sub xte_captcha_min_line_thickness {
    my $self = shift;
    if ( lc( $self->{start_xte} ) eq 'yes' ) {
        return $self->{xte_captcha_min_line_thickness};
    }
    else {
        return undef;
    }
}

sub xte_captcha_min_lines {
    my $self = shift;
    if ( lc( $self->{start_xte} ) eq 'yes' ) {
        return $self->{xte_captcha_min_lines};
    }
    else {
        return undef;
    }
}

sub xte_captcha_particle_count {
    my $self = shift;
    if ( lc( $self->{start_xte} ) eq 'yes' ) {
        return $self->{xte_captcha_particle_count};
    }
    else {
        return undef;
    }
}

sub xte_captcha_particle_size {
    my $self = shift;
    if ( lc( $self->{start_xte} ) eq 'yes' ) {
        return $self->{xte_captcha_particle_size};
    }
    else {
        return undef;
    }
}

sub xte_captcha_styles {
    my $self = shift;
    if ( ( lc( $self->{start_xte} ) eq 'yes' ) and $self->{xte_captcha_styles} )
    {
        return @{ $self->{xte_captcha_styles} };
    }
    else {
        return ();
    }
}

sub xte_captcha_ttfont_size {
    my $self = shift;
    if ( lc( $self->{start_xte} ) eq 'yes' ) {
        return $self->{xte_captcha_ttfont_size};
    }
    else {
        return undef;
    }
}

sub xte_captcha_ttfonts {
    my $self = shift;
    if ( ( lc( $self->{start_xte} ) eq 'yes' )
        and $self->{xte_captcha_ttfonts} )
    {
        return @{ $self->{xte_captcha_ttfonts} };
    }
    else {
        return ();
    }
}

sub xte_captcha_width {
    my $self = shift;
    if ( lc( $self->{start_xte} ) eq 'yes' ) {
        return $self->{xte_captcha_width};
    }
    else {
        return undef;
    }
}

sub xte_check_for_waiting {
    my $self = shift;
    if ( lc( $self->{start_xte} ) eq 'yes' ) {
        return $self->{xte_check_for_waiting};
    }
    else {
        return undef;
    }
}

sub xte_cidr_allow {
    my $self = shift;
    if ( ( lc( $self->{start_xte} ) eq 'yes' ) and $self->{xte_cidr_allow} ) {
        return @{ $self->{xte_cidr_allow} };
    }
    else {
        return ();
    }
}

sub xte_cidr_deny {
    my $self = shift;
    if ( ( lc( $self->{start_xte} ) eq 'yes' ) and $self->{xte_cidr_deny} ) {
        return @{ $self->{xte_cidr_deny} };
    }
    else {
        return ();
    }
}

sub xte_docroot {
    my $self = shift;
    if ( lc( $self->{start_xte} ) eq 'yes' ) {
        return $self->{xte_docroot};
    }
    else {
        return undef;
    }
}

sub xte_enable_captcha {
    my $self = shift;
    if (    ( lc( $self->{start_xte} ) eq 'yes' )
        and $self->{xte_enable_captcha}
        and ( lc( $self->{xte_enable_captcha} ) eq 'yes' ) )
    {
        return 1;
    }
    else {
        return 0;
    }
}

sub xte_enable_email {
    my $self = shift;
    if (    ( lc( $self->{start_xte} ) eq 'yes' )
        and $self->{xte_enable_email}
        and ( lc( $self->{xte_enable_email} ) eq 'yes' ) )
    {
        return 1;
    }
    else {
        return 0;
    }
}

sub xte_enable_file_writing {
    my $self = shift;
    if (    ( lc( $self->{start_xte} ) eq 'yes' )
        and $self->{xte_enable_file_writing}
        and ( lc( $self->{xte_enable_file_writing} ) eq 'yes' ) )
    {
        return 1;
    }
    else {
        return 0;
    }
}

sub xte_enable_fop {
    my $self = shift;
    if (    ( lc( $self->{start_xte} ) eq 'yes' )
        and $self->{xte_enable_fop}
        and ( lc( $self->{xte_enable_fop} ) eq 'yes' ) )
    {
        return 1;
    }
    else {
        return 0;
    }
}

sub xte_enable_json {
    my $self = shift;
    if (    ( lc( $self->{start_xte} ) eq 'yes' )
        and $self->{xte_enable_json}
        and ( lc( $self->{xte_enable_json} ) eq 'yes' ) )
    {
        return 1;
    }
    else {
        return 0;
    }
}

sub xte_enable_mxmlc {
    my $self = shift;
    if (    ( lc( $self->{start_xte} ) eq 'yes' )
        and $self->{xte_enable_mxmlc}
        and ( lc( $self->{xte_enable_mxmlc} ) eq 'yes' ) )
    {
        return 1;
    }
    else {
        return 0;
    }
}

sub xte_enable_static_serving {
    my $self = shift;
    if (    ( lc( $self->{start_xte} ) eq 'yes' )
        and $self->{xte_enable_static_serving}
        and ( lc( $self->{xte_enable_static_serving} ) eq 'yes' ) )
    {
        return 1;
    }
    else {
        return 0;
    }
}

sub xte_enable_uploads {
    my $self = shift;
    if (    ( lc( $self->{start_xte} ) eq 'yes' )
        and $self->{xte_enable_uploads}
        and ( lc( $self->{xte_enable_uploads} ) eq 'yes' ) )
    {
        return 1;
    }
    else {
        return 0;
    }
}

sub xte_enable_xmlroff {
    my $self = shift;
    if (    ( lc( $self->{start_xte} ) eq 'yes' )
        and $self->{xte_enable_xmlroff}
        and ( lc( $self->{xte_enable_xmlroff} ) eq 'yes' ) )
    {
        return 1;
    }
    else {
        return 0;
    }
}

sub xte_from_address {
    my $self = shift;
    if ( lc( $self->{start_xte} ) eq 'yes' ) {
        return $self->{xte_from_address};
    }
    else {
        return undef;
    }
}

sub xte_gateway_auth {
    my $self = shift;
    if ( lc( $self->{start_xte} ) eq 'yes' ) {
        return $self->{xte_gateway_auth};
    }
    else {
        return undef;
    }
}

sub xte_group {
    my $self = shift;
    if ( lc( $self->{start_xte} ) eq 'yes' ) {
        return $self->{xte_group};
    }
    else {
        return undef;
    }
}

sub xte_i18n_list {
    my $self = shift;
    if ( lc( $self->{start_xte} ) eq 'yes' ) {
        return @{ $self->{xte_i18n_list} || [] };
    }
    else {
        return undef;
    }
}

sub xte_image_sizes {
    my $self = shift;
    if ( lc( $self->{start_xte} ) eq 'yes' ) {
        return @{ $self->{xte_image_sizes} || [] };
    }
    else {
        return undef;
    }
}

sub xte_log_file {
    my $self = shift;
    if ( lc( $self->{start_xte} ) eq 'yes' ) {
        return $self->{xte_log_file};
    }
    else {
        return undef;
    }
}

sub xte_log_level {
    my $self = shift;
    if ( lc( $self->{start_xte} ) eq 'yes' ) {
        return $self->{xte_log_level};
    }
    else {
        return undef;
    }
}

sub xte_max_image_size {
    my $self = shift;
    if ( lc( $self->{start_xte} ) eq 'yes' ) {
        return $self->{xte_max_image_size};
    }
    else {
        return undef;
    }
}

sub xte_max_servers {
    my $self = shift;
    if ( lc( $self->{start_xte} ) eq 'yes' ) {
        return $self->{xte_max_servers};
    }
    else {
        return undef;
    }
}

sub xte_max_requests {
    my $self = shift;
    if ( lc( $self->{start_xte} ) eq 'yes' ) {
        return $self->{xte_max_requests};
    }
    else {
        return undef;
    }
}

sub xte_max_spare_servers {
    my $self = shift;
    if ( lc( $self->{start_xte} ) eq 'yes' ) {
        return $self->{xte_max_spare_servers};
    }
    else {
        return undef;
    }
}

sub xte_max_upload_size {
    my $self = shift;
    if ( lc( $self->{start_xte} ) eq 'yes' ) {
        return $self->{xte_max_upload_size};
    }
    else {
        return undef;
    }
}

sub xte_mimetype_path {
    my $self = shift;
    if ( lc( $self->{start_xte} ) eq 'yes' ) {
        return $self->{xte_mimetype_path};
    }
    else {
        return undef;
    }
}

sub xte_min_servers {
    my $self = shift;
    if ( lc( $self->{start_xte} ) eq 'yes' ) {
        return $self->{xte_min_servers};
    }
    else {
        return undef;
    }
}

sub xte_min_spare_servers {
    my $self = shift;
    if ( lc( $self->{start_xte} ) eq 'yes' ) {
        return $self->{xte_min_spare_servers};
    }
    else {
        return undef;
    }
}

sub xte_port {
    my $self = shift;
    if ( lc( $self->{start_xte} ) eq 'yes' ) {
        return $self->{xte_port};
    }
    else {
        return undef;
    }
}

sub xte_post_max {
    my $self = shift;
    if ( lc( $self->{start_xte} ) eq 'yes' ) {
        return $self->{xte_post_max};
    }
    else {
        return undef;
    }
}

sub xte_report_benchmarks {
    my $self = shift;
    if ( not( lc( $self->{start_xte} ) eq 'yes' ) ) {
        return undef;
    }
    elsif ( lc( $self->{xte_report_benchmarks} ) eq 'yes' ) {
        return 1;
    }
    else { return 0 }
}

sub xte_server_ip {
    my $self = shift;
    if ( lc( $self->{start_xte} ) eq 'yes' ) {
        return $self->{xte_server_ip};
    }
    else {
        return undef;
    }
}

sub xte_site_language {
    my $self = shift;
    if ( lc( $self->{start_xte} ) eq 'yes' ) {
        return $self->{xte_site_language};
    }
    else {
        return undef;
    }
}

sub xte_smtp_charset {
    my $self = shift;
    if ( lc( $self->{start_xte} ) eq 'yes' ) {
        return $self->{xte_smtp_charset};
    }
    else {
        return undef;
    }
}

sub xte_smtp_encoding {
    my $self = shift;
    if ( lc( $self->{start_xte} ) eq 'yes' ) {
        return $self->{xte_smtp_encoding};
    }
    else {
        return undef;
    }
}

sub xte_smtp_host {
    my $self = shift;
    if ( lc( $self->{start_xte} ) eq 'yes' ) {
        return $self->{xte_smtp_host};
    }
    else {
        return undef;
    }
}

sub xte_smtp_port {
    my $self = shift;
    if ( lc( $self->{start_xte} ) eq 'yes' ) {
        return $self->{xte_smtp_port};
    }
    else {
        return undef;
    }
}

sub xte_splurge_input {
    my $self = shift;
    if ( not( lc( $self->{start_xte} ) eq 'yes' ) ) {
        return undef;
    }
    elsif ( lc( $self->{xte_splurge_input} ) eq 'yes' ) {
        return 1;
    }
    else { return 0 }
}

sub xte_splurge_output {
    my $self = shift;
    if ( not( lc( $self->{start_xte} ) eq 'yes' ) ) {
        return undef;
    }
    elsif ( lc( $self->{xte_splurge_output} ) eq 'yes' ) {
        return 1;
    }
    else { return 0 }
}

sub xte_static_directories {
    my $self = shift;
    if ( lc( $self->{start_xte} ) eq 'yes' ) {
        return @{ $self->{xte_static_directories} || [] };
    }
    else {
        return undef;
    }
}

sub xte_temporary_file_path {
    my $self = shift;
    if ( lc( $self->{start_xte} ) eq 'yes' ) {
        return $self->{xte_temporary_file_path};
    }
    else {
        return undef;
    }
}

sub xte_user {
    my $self = shift;
    if ( lc( $self->{start_xte} ) eq 'yes' ) {
        return $self->{xte_user};
    }
    else {
        return undef;
    }
}

sub xte_use_xca {
    my $self = shift;
    if (    ( lc( $self->{start_xte} ) eq 'yes' )
        and ( lc( $self->{xte_use_xca} ) eq 'yes' ) )
    {
        return 1;
    }
    elsif ( $self->{start_xte} eq 'yes' ) {
        return 0;
    }
    else {
        return undef;
    }
}

sub xte_xac_timeout {
    my $self = shift;
    if ( lc( $self->{start_xte} ) eq 'yes' ) {
        return $self->{xte_xac_timeout};
    }
    else {
        return undef;
    }
}

sub local_croak {
    my $message = shift;
    croak $message if $message;
}

1;