Apache::AppSamurai - An Authenticating Mod_Perl Front End

"Protect your master, even if he is without honour...."


All configuration is done within Apache. Requires Apache 1.3.x/mod_perl1 or Apache 2.0.x/mod_perl2. See "EXAMPLES" for sample configuration segments.


Apache::AppSamurai protects web applications from direct attack by unauthenticated users, and adds a flexible authentication front end to local or proxied applications with limited authentication options.

Unauthenticated users are presented with either a login form, or a basic authentication popup (depending on configuration.) User supplied credentials are checked against one or more authentication systems before the user's session is created and a session authentication cookie is passed back to the browser. Only authenticated and authorized requests are proxied through to the backend server.

Apache::AppSamurai is based on, and includes some code from, Apache::AuthCookie. Upon that core is added a full authentication and session handling framework. (No coding required.) Features include:

  • Modular authentication - Uses authentication sub-modules for the easy addition custom authentication methods

  • Form based or Basic Auth login - On the front end, supports standard form based logins, or optionally Basic Auth login. (For use with automated systems that can not process a form.)

  • Apache::Session - Used for session data handling

  • Session data encrypted on server - By default, all session data encrypted before storing to proxy's filesystem (Uses custom Apache::Session compatible session generator and session serialization modules)

  • Unified mod_perl 1 and 2 support - One module set supports both Apache 1.x/mod_perl 1.x and Apache 2.x/mod_perl 2.x


Server side session data may include sensitive information, including the basic authentication Authorization header to be sent to the backend server. (This is just a Base64 encoded value, revealing the username and password if stolen.)

To protect the data on-disk, Apache::AppSamurai includes its own HMAC based session ID generator and encrypting session serializer. (Apache::AppSamurai::Session::Generate::HMAC_SHA and Apache::AppSamurai::Session::Serialize::CryptBase64 , respectively.) These modules are configured by default and may be used directly with Apache::Session, or outside of Apache::AppSamurai if desired.


Almost all options are set using PerlSetVar statements, and can be used inside most configuration sections.

Each configuration option must be prefixed by the AuthName for the Apache::AppSamurai instance you wish to apply the option to. This AuthName is then referenced within the protected area(s). Most of setups only require one AuthName. You can call it "BOB" or "MegaAuthProtection". You can even call it "authname".

IMPORTANT NOTE - The AuthName is omitted in the configuration descriptions below for brevity. "Example" is used as the AuthName in the "EXAMPLES" section.

Most setups will include a set of global configuration values to setup the Apache::AppSamurai instance. Each protected area then points to a specific AuthName and Apache::AppSamurai methods for authentication and authorization.


Debug 0|1

(Default: 0) Set to 1 to send debugging output to the Apache logs. (Note - you must have a log configured to catch errors, including debug level errors, to see the output.)

CookieName NAME

(Default:AUTHTYPE_AUTHNAME) The name of the session cookie to send to the browser.

LoginScript PATH

(Default: undef) The URL path (location) of the proxy's login page for form based login. (Sample script provided with the Apache::AppSamurai distribution.)


(Default: /) The URL path to protect.


(Default: not set) The optional domain to set for all session cookies. Do not configure this unless you are sure you need it: A misconfigured domain can result in session stealing.

Satisfy All|Any

(Default: All) Set require behaviour within protected areas. Either All to require all authentication checks to succeed, or Any to require only one to.

Secure 0|1

(Default: 1) Set to 1 to require the secure flag to be set on the session cookie, forcing the use of SSL/TLS.

HttpOnly 0|1

(Default: 0) Set to 1 to require the Microsoft proprietary http-only flag to be set on session cookies.

LoginDestination PATH

(Default: undef) Set an optional hard coded destination URI path all users will be directed to after login. (While full URLs are allowed, a path starting in / is recommended.) This setting only applies so form based login. Basic Auth logins always follow the requested URL.

LogoutDestination PATH

(Default: undef) Set an optional hard coded destination URI path all users will be directed to after logging out. (While full URLs are allowed, a path starting in / is recommended.) This setting only applies so form based login. Basic Auth logins always follow the requested URL.

If LogoutDestination is unset and LoginDestination is set, users will be directed to LoginDestination after logout. (This is to prevent a user from logging back into the logout URI, which would log them back out again. Oh the humanity!)


Most authentication is specific to the authentication module(s) being used. Review their specific documentation while configuring.

AuthMethods METHOD1,METHOD2...

(Default: undef) A comma separated list of the authentication sub-modules to use. The order of the list must match the order of the credentials_X parameters in the login form. (Note - credential_0 is always the username, and is passed as such to all the authentication modules.)

BasicAuthMap N1,N2,.. = REGEX

(Default: undef)

Custom mapping of Basic authentication password input to specific and separate individual credentials. This allows for AppSamurai to request basic authentication for an area, then split the input into credentials that can be checked against multiple targets, just like a form based login. This is very useful for clients, like Windows Mobile ActiveSync, that only support basic auth logins. Using this feature you can add SecurID or other additional authentication factors without having to pick only one.

The syntax is a bit odd. First, specify a list of the credential numbers you want mapped, in order they will be found within the input. Then create a regular expression that will match the input, and group each item you want mapped.


 PerlSetVar BobAuthBasicAuthMap "2,1=(.+);([^;]+)"

If the user logs into the basic auth popup with the password: myRockinPassword;1234123456 ,the map above will set credential_1 as 1234123456 and credential_2 as myRockinPassword, then proceed as if the same were entered into a form login.


Authentication submodules usually have one or more required settings. All settings are passed using PerlSetVar directives with variable names prefixed with the AuthName and the module's name.


 PerlSetVar BobAuthBasicLoginUrl C<>

For AuthName Bob, set the LoginUrl for the AuthBasic authentication module to

See Apache::AppSamurai::AuthBase for general authentication module information. If you need an authentication type that is not supported by the authentication modules shipped with AppSamurai, and is not available as an add on module, please review Apache::AppSamurai::AuthBase and use the skeletal code from, which is included under /examples/auth/ in the AppSamurai distribution.


Each Apache::AppSamurai instance must have its local (proxy server side) session handling defined. Apache::Session provides the majority of the session framework. Around Apache::Session is wrapped Apache::AppSamurai::Session, which adds features to allow for more flexible selection of sub-modules.

Most Apache::Session style configuration options can be passed directly to the session system by prefixing them with authnameSession.

Module selection is slightly different than the default supplied with Apache::Session. Plain names, without any path or ::, are handled exactly the same: Modules are loaded from within the Apache::Session tree. Two additional alternatives are provided:

  • AppSamurai/MODULE - Load MODULE from under the Apache::AppSamurai::Session tree instead of the Apache::Session tree.

  • PATH::MODULE - Load PATH::MODULE literally. Note - Since :: is required to be present, a root module name will not work.

The most common configuration options follow. See Apache::AppSamurai::Session and Apache::Session for more advanced options, like using a database for storage.

NOTE - "Session" is shown prepending each of these directives, Inside the Apache::AppSamurai::Session and Apache::Session documentation, "Session" is omitted.

SessionExpire SECONDS

(Default: 0) The maximum session lifetime in seconds. After a user has been logged in this long, they are logged out. (Ignores weather the user is idle or not.)

SessionTimeout SECONDS (Default: 3600 (1 hour)).

The maximum time a session can be idle before being removed. After a user has not accessed the protected application for this many seconds, they are logged out.

SessionStore NAME

(Default: File) The session storage module name. "File" is the default, which maps to Apache::Session::Store::File|Apache::Session::Store::File (Note - See the top of this section, "SESSION CONFIGURATION", for details on the three ways to specify a path for this option and the following options that point to a module.)

SessionLock NAME

(Default: File) The session locking module name. "File" is used by default, which maps to Apache::Session::Lock::File|Apache::Session::Lock::File

SessionGenerate NAME

(Default: AppSamurai/HMAC_SHA) The session ID generator module name. "AppSamurai/HMAC_SHA" is used by default, which maps to Apache::AppSamurai::Session::Generate::HMAC_SHA This special module takes a server key and a session authentication key and returns a HMAC code representing the local ("real") session ID. (Input and output are all SHA256 hex strings that are passed in using the sessionconfig hash.)

As this is tied closely into the current Apache::AppSamurai code, please do not use an alternate serializer without first reviewing the related code.

SessionSerialize NAME

(Default: AppSamurai/CryptBase64) The session data serializer module. "AppSamurai/CryptBase64" is used by default, which maps to Apache::AppSamurai::Session::Serialize::CryptBase64 This special module uses server key and a session authentication key to encrypt session data using a block cipher before Base64 encoding it. (All keys are 256 bit hex strings.)

Base64 allows for storage in file, database, etc without worrying about binary data issues. In addition, this module allows for safer storage of data on disk, requiring both the local server key and the secret session key from the user before unlocking the data.

Crypt::CBC is used with a support block cipher module to perform encryption/decryption. (See the next section for information on configuring a cipher.)

As this is tied closely into the current Apache::AppSamurai code, please do not use an alternate serializer without first reviewing the related code.

SessionSerializeCipher CIPHER_MODULE

(Default: undef) Select the block cipher provider module for Apache::AppSamurai::Session::Serialize::CryptBase64 to use. For production, you should use this to configure a specific block cipher to use. If not set, the cipher is autodetected from the list below. (Note that autodetect is slow and picks the first cipher module it finds, which may not be the one you want.)

The following block cipher modules are currently allowed:

 Crypt::Rijndael     - AES implementation (default)
 Crypt::OpenSSL::AES - OpenSSL AES wrapper
 Crypt::Twofish      - Twofish implementation
 Crypt::Blowfish     - Blowfish implementation

See Apache::AppSamurai::Session::Serialize::CryptBase64 for more information.

SessionServerKey KEY

(Default: undef) Define the server's "server key". (This option is mutually exclusive with the SessionServerPass option.) If you configure ServerKey, it MUST be a 64 character hex string. (Use "SessionServerPass PASSPHRASE" if you prefer using an arbitrary length prase in your configuration.)

The server key is used to look up local session IDs and encrypt/decrypt them when the HMAC_SHA session generator and CryptBase64 session serializer are used.

As this is tied closely into the current AppSamurai code, it is a required configuration directive. Either ServerPass or ServerKey must be defined. Standard Apache::Session generator/serializer modules ignore this setting.

IMPORTANT NOTE FOR CLUSTERS/MULTIPLE PROXIES: If you use a shared session storage back end (database), and a cluster of AppSamurai proxies to protect a single application (using the same AuthName on each), you must use the same key or pass in the AuthName on each server in the cluster. The key is used both the authenticate the user and to decrypt the session data.

SessionServerPass PASSPHRASE

(Default: undef) Sets an arbitrary length pass code that will be passed through SHA256 to produce the server's server key. See "SessionServerKey KEY" for how that key is used.


See Apache::AppSamurai::Session and Apache::Session for more on the session system.


The Tracker system is a based on a set of special Apache::Session stores that are visible between Apache processes. (In fact, with a shared central database, they could be visible to an entire cluster of servers.) It is provided to store various state information for built-in and add-on features.

Tracker storage uses Apache::AppSamurai::Tracker, which is a modified version of Apache::AppSamurai::Session.

Security Note - The Tracker system does not use encryption, so never store sensitive information in a tracker. If you need to track sensitive items, encrypt or hash them beforehand.

TrackerStore NAME

(Default: File) The tracker storage module name. "File" is the default, which maps to Apache::Session::Store::File|Apache::Session::Store::File (Note - See the top of this section, "SESSION CONFIGURATION", for details on the three ways to specify a path for this option and the following options that point to a module.)

TrackerLock NAME

(Default: File) The tracker locking module name. "File" is used by default, which maps to Apache::Session::Lock::File|Apache::Session::Lock::File

TrackerCleanup SECONDS

(Default: undef) If defined, tracked items that have been untouched in this many seconds are removed. In the future this may be configurable per-tracker type, but for now it provides a rudimentary cleanup system.


See Apache::AppSamurai::Tracker for more on tracker system configuration.


The following features require the tracker system to be configured. These are pretty basic and static at this point. (Should probably be split out into modules.)


(Defualt: undef) Block further login attempts from IPs that send COUNT failures with no more than SECONDS seconds between each subsequent failure. Once blocked, the block will remain in effect till at least SECONDS has elapsed since the last connection attempt.

AuthUnique 0|1

(Default: 0) If set to 1, forces at least one credential to be unique per-login. (Requires dynamic token or other non-static authentication type.)

SessionUnique 0|1

(Default: 0) If 1, prohibits a new session from using the same session ID as a previous session. This is generally only relevant for non-random sessions that use the Keysource directive to calculate a pseudo-cookie value.


The following methods are to be used directly by Apache. (This is not a full list of all Apache::AppSamurai methods.)


Should be configured in the Apache config as the PerlAuthenHandler for areas protected by Apache::AppSamurai.

authenticate() is called by object reference and expects an Apache request object as input.authenticate() uses a session authentication key, either from a cookie or from the optional Keysource, and tries to open the session tied to the session authentication key.

If the session exists and is valid, the username is extracted from the session and the method returns OK to allow the request through.

If no key is present, if the session is not present, or if the session is invalid, a login request is returned. (Either a redirect to a login form, or in the case of an area set to basic authentication, a 401 Authorization Required code.)


Should be configured in the Apache config as the PerlAuthzHandler for areas protected by Apache::AppSamurai.

authorize() is called by object reference and expects an Apache request object as input. It then checks the authorization requirements for the requested location. In most cases, "require valid-user" is used in conjunction with the "Satisfy All" Apache::AppSamurai setting. This authorizes any logged in user to pass. This method could be replaced or expanded at a later date if more granular authorization is required. (Groups, roles, etc.)

OK is returned if conditions are satisfied, otherwise HTTP_FORBIDDEN is returned.


Should be configured in the Apache config as the PerlHandler, (or "PerlResponseHandler" for mod_perl 2.x), for a special pseudo file under the AppSamurai/ directory. In example configs and the example form page, the pseudo file is named LOGIN.

login() expects an Apache request with a list of credentials included as arguments. credential_0 is the username. All further credentials are mapped in order to the authentication modules defined in "AuthMethods". Each configured authentication method is checked, in order. If all succeed, a session is created and a session authentication cookie is returned along with a redirect to the page requested by the web browser.

If login fails, the browser is redirected to the login form.


Should be called directly by your logout page or logout pseudo file. This expects an Apache request handle. It can also take a second option, which should be a scalar URI path to redirect users to after logout. logout() attempts to look up and destroy the session tied to the passed in session authentication key.

Like login(), you may create a special pseudo file named LOGOUT and use PerlHandler, (or "PerlResponseHandler" for mod_perl 2.x), to map it to the logout() method. This is particularly handy when paired with mod_rewrite to map a specific application URI to a pseudo file mapped to logout() (See "EXAMPLES" for a sample config that uses this method.)


 ## This is a partial configuration example showing most supported
 ## configuration options and a reverse proxy setup.  See examples/conf/
 ## in the Apache::AppSamurai distribution for real-world example configs.

 ## Apache 1.x/mod_perl 1.x settings are enabled with Apache 2.x/mod_perl 2.x
 ## config alternatives commented out. ("*FOR MODPERL2 USE:" precedes
 ## the Apache 2.x/mod_perl 2.x version of any alternative config items.)
 ## Note that example configs in examples/conf/ use IfDefine to support
 ## both version sets without having to comment out items. Also note that it
 ## is far too ugly looking to include in this example.

 ## General mod_perl setup
 # Apache::AppSamurai is always strict, warn, and taint clean. (Unless
 # I mucked something up ;)
 PerlWarn On
 PerlTaintCheck On
 PerlModule Apache::Registry
 # PerlSwitches -wT
 # PerlModule ModPerl::Registry

 # Load the main module and define configuration options for the 
 # "Example" auth_name
 PerlModule Apache::AppSamurai
 PerlSetVar ExampleDebug 0
 PerlSetVar ExampleCookieName MmmmCookies
 PerlSetVar ExamplePath /
 PerlSetVar ExampleLoginScript /

 # Defaults to All by may also be Any
 #PerlSetVar ExampleSatisty All
 # Optional session cookie domain (Avoid unless absolutely needed.)
 #PerlSetVar ExampleDomain ""

 # Require secure sessions (default: 1)
 #PerlSetVar ExampleSecure 1

 # Set proprietary MS flag
 PerlSetVar ExampleHttpOnly 1

 # Define authentication sources, in order
 PerlSetVar ExampleAuthMethods "AuthRadius,AuthBasic"

 # Custom mapping of xxxxxx;yyyyyy Basic authentication password input
 # to specific and separate individual credentials. (default: undef)
 PerlSetVar ExampleBasicAuthMap "2,1=(.+);([^;]+)"

 ## Apache::AppSamurai::AuthRadius options ##
 # (Note - See L<Apache::AppSamurai::AuthRadius> for more info)
 PerlSetVar ExampleAuthRadiusConnect ""
 PerlSetVar ExampleAuthRadiusSecret "radiuspassword"

 ## Apache::AppSamurai::AuthBasic options.##
 # (Note - See L<Apache::AppSamurai::AuthBasic> for more info)
 # Set the URL to send Basic auth checks to
 PerlSetVar ExampleAuthBasicLoginUrl "https://ex.amp.le/thing/login"
 # Always send Basic authentication header to backend server
 PerlSetVar ExampleAuthBasicKeepAuth 1
 # Capture cookies from AuthBasic login and set in client browser
 PerlSetVar ExampleAuthBasicPassBackCookies 1
 # Abort the check unless the "realm" returned by the server matches
 PerlSetVar ExampleAuthBasicRequireRealm "blah.bleh.blech"
 # Pass the named header directly through to the AuthBasic server 
 PerlSetVar ExampleAuthBasicUserAgent "header:User-Agent"

 ## Session storage options ##
 # (Note - See L<Apache::AppSamurai::Session> and L<Apache::Session> for
 # more information.)
 # Inactivity timeout (in seconds)
 PerlSetVar ExampleSessionTimeout 1800

 # Use the File storage and lock types from Apache::Session
 PerlSetVar ExampleSessionStore "File"
 PerlSetVar ExampleSessionLock "File"

 # File storage options (Relevant only to File storage and lock types)
 PerlSetVar ExampleSessionDirectory "/var/www/session/sessions"
 PerlSetVar ExampleSessionLockDirectory "/var/www/session/slock"

 # Use the Apache::AppSamurai::Session::Generate::HMAC_SHA generator
 PerlSetVar ExampleSessionGenerate "AppSamurai/HMAC_SHA"

 # Use the Apache::AppSamurai::Session::Serialize::CryptBase64
 # data serializer module with Crypt::Rijndael (AES) as the block
 # cipher provider
 PerlSetVar ExampleSessionSerialize "AppSamurai/CryptBase64"
 PerlSetVar ExampleSessionSerializeCipher "Crypt::Rijndael"

 # Set the server's encryption passphrase (for use with HMAC session
 # generation and/or encrypted session storage)
 PerlSetVar ExampleSessionServerPass "This is an example passphrase"

 ## Tracker storage options ##
 # Cleanup tracker entries that have not changed in 1 day 
 PerlSetVar ExampleTrackerCleanup 86400

 # Block further login attempts from IPs that send 10 failures with
 # no more than 60 seconds between each subsequent failure
 PerlSetVar ExampleIPFailures "10:60"

 # Force at least one credential to be unique per-login.  (Requires
 # token or other non-static authentication type.)
 PerlSetVar ExampleAuthUnique 1

 # Prohibit a new session from using the same session ID as a previous
 # session.  (Only relevant for non-random sessions that use the
 # Keysource directive to calculate a pseudo-cookie.)
 PerlSetVar ExampleSessionUnique 1

 ## Special AppSamurai directory options ##
 # (These will vary widely depending on your specific setup and requirements.)
 <Directory "/var/www/htdocs/AppSamurai">
  AllowOverride None
  deny from all
  <FilesMatch "\.pl$">
   SetHandler perl-script
   Options +ExecCGI
   AuthType Apache::AppSamurai
   AuthName "Example"
   PerlHandler Apache::Registry
   #PerlResponseHandler ModPerl::Registry
   allow from all
  <Files LOGIN>
   SetHandler perl-script
   AuthType Apache::AppSamurai
   AuthName "Example"

   PerlHandler Apache::AppSamurai->login
   #PerlResponseHandler Apache::AppSamurai->login

   allow from all

  <Files LOGOUT>
   SetHandler perl-script
   AuthType Apache::AppSamurai
   AuthName "Example"

   PerlHandler Apache::AppSamurai->logout
   #PerlResponseHandler Apache::AppSamurai->logout

   allow from all
 <Directory "/var/www/htdocs/AppSamurai/images">
  Options None
  allow from all

 # Protected/proxied resource config 1: Form based
 <Directory "proxy:https://ex.amp.le/thing/*">
 #<Proxy "https://ex.amp.le/thing/*">
  AuthType Apache::AppSamurai
  AuthName "Example"
  PerlAuthenHandler Apache::AppSamurai->authenticate
  PerlAuthzHandler Apache::AppSamurai->authorize
  Order deny,allow
  Allow from all
  require valid-user

 # Protected/proxied resource config 2: Basic auth
 <Directory "proxy:https://ex.amp.le/thaang/*">
 #<Proxy "https://ex.amp.le/thaang/*">

  AuthType Basic
  AuthName "Example"
  PerlAuthenHandler Apache::AppSamurai->authenticate
  PerlAuthzHandler Apache::AppSamurai->authorize

  # Add some local overrides to this directory.  (Has
  # no affect on other directories/locations)

  # Switch from an inactivity timeout to a hard expiration
  PerlSetVar ExampleSessionExpire 3600
  PerlSetVar ExampleSessionTimeout 0

  # In lieu of cookies, calculate the session key using the
  # basic auth header from the client, and an argument called
  # "Sessionthing" from the request URL.  (NOTE - Keysource
  # should be used with care!  Do not use it unless you are
  # sure of what you are doing!!!)
  PerlAddVar ExampleKeysource header:Authorization
  PerlAddVar ExampleKeysource arg:Sessionthing

  Order deny,allow
  Allow from all
  require valid-user


 # Do not allow forward proxying
 ProxyRequests Off
 # Proxy requests for /thing/* to  https://ex.amp.le/thing/*
 RewriteRule ^/thing/(.*)$ https://ex.amp.le/thing/$1 [P]

 # Similar for /thaang/*
 RewriteRule ^/thaang/(.*)$ https://ex.amp.le/thaang/$1 [P]

 # Redirect requests to / into our default app
 RewriteRule ^/?$ /thing/ [R,L]

 # Allow in AppSamurai requests to proxy server
 RewriteRule ^/AppSamurai -

 # Capture logout URL from app and send to a pseudo page mapped to logout() 
 RewriteRule ^/thing/logout\.asp$ /AppSamurai/LOGOUT

 # Block all other requests
 RewriteRule .* - [F]

 # RELEVANT VirtualHost SECTIONS (For most Apache2 setups, this would be
 # the "<VirtualHost _default_:443>" section inside ssl.conf)
 ## Enable rewrite engine inside virtualhost
 #RewriteEngine on
 ## Inherit rewrite settings from parent (global)
 #RewriteOptions inherit
 ## Enable proxy connections to SSL
 #SSLProxyEngine on


Additional authentication modules, tracking features, and other options can be added to Apache::AppSamurai. In the case of authentication modules, all that is required is creating a new module that inherits from Apache::AppSamurai::AuthBase.

Other features may be more difficult to add. (Apache::AppSamurai could use some refactoring.)

Interface and utility methods are not documented at this time. Please consult the code, and also the Apache::AuthCookie documentation.



Directory that holds Apache::AppSamurai login/logout pages and related content. This must be served by Apache and reachable. (This is generally mapped to /AppSamurai/ on the server.) When starting from scratch, copy the contents of /examples/htdocs/ from the Apache-AppSamurai distribution into this directory.


The default login mod_perl script. Must be modified to match your setup.


The default HTML login form template. (Split out from to ease customization.)


Generic "deny all" robots file. (You don't want your login area appearing on Google. Note that the default login page also has a META tag to prevent indexing.)


Image files for login page.


Apache::AppSamurai::Session, Apache::AppSamurai::Tracker, Apache::AppSamurai::AuthBase, Apache::AppSamurai::AuthBasic, Apache::AppSamurai::AuthRadius, Apache::AppSamurai::AuthSimple, Apache::AppSamurai::Util,Apache::AppSamurai::Session::Generate::HMAC_SHA, Apache::AppSamurai::Session::Serialize::CryptBase64, Apache::Session


Paul M. Hirsch, <paul at>


Please report any bugs or feature requests to <paul at>


You can find documentation for this module with the perldoc command.

    perldoc Apache::AppSamurai

You can also look for information at:

ACKNOWLEDGEMENTS (the main Apache::AppSamurai module), contains some code from Apache::AuthCookie, which was developed by Ken Williams and others. The included Apache::AuthCookie code is under the same licenses as Perl and under the following copyright:

Copyright (c) 2000 Ken Williams. All rights reserved.


Copyright 2008 Paul M. Hirsch, all rights reserved.

This program is free software; you can redistribute it and/or modify it under the same terms as Perl itself.