transparent.gif (1K)hsbannertab_apps.gif (1K)hsbannertabspacer2.gif (1K)
icon_esub.gif (1K)Subscribe | icon_faq.gif (1K)FAQs | ico_lock_sm.png (1K)Account

Utilities - Multidomain Server Status Code Template

Step 1 Step 2 Step 3 Step 4

To get you started, here's a multi-domain customized error page handler for those using Apache web servers (or similarly operational xHTML content platforms), .htaccess configuration files, Perl, and CSS. Some considerations of which you should be aware:

  • A copy of nph-statuscode.cgi should be placed in the /cgi-bin of each domain where the template is to be applied.
  • .htaccess must be available and allowed to override the core server configuration. The AllowOverride core server directive is typically allowed by default.
  • CSS stylesheets should be used for styling of output since the goal is to be strictly XHTML compliant.
  • The list of status codes may be edited to suit your needs. Should you edit the statuscode hash, confirm that the new changes are also reflected in the .htaccess ErrorDocument directives. The default install includes error codes 200, 301, 307, 401, 403, 404, 405, 408, 410, 413, 414, 500, and 501.

STEP 1 of 4 - prepare the utility script

  • Confirm that you're on *nix or any NCSA-compliant webserver, that you may use Perl, and that you have the modules POSIX and CGI installed.
  • Place nph-statuscode.cgi in your cgi-bin (or other executable) directory and set permissions to 711 (-rwx--x--x).
  • Edit the 'user configuration' section to suit your domain(s).

STEP 2 of 4 - configure the server

STEP 3 of 4 - create and modify xHTML template

STEP 4 of 4 - create and modify CSS

The first step in using this tool is to prepare the utility script:

#!/usr/bin/perl -w T
#
#################################################################
# HistoSoft Multi-domain error code template script
#  nph-statuscode.cgi - v0.01
#  [www.histosoft.com]
#
# June 23, 2005
#   Copyright(c)2005 HistoSoft Corporation. All Rights Reserved.
#   NO PART OF THIS PROGRAM OR PUBLICATION MAY BE REPRODUCED,
#   TRANSCRIBED, TRANSMITTED, STORED IN A RETRIEVAL SYSTEM, OR
#   TRANSLATED INTO ANY LANGUAGE OR COMPUTER LANGUAGE IN ANY
#   FORM OR BY ANY MEANS, ELECTRONIC, MECHANICAL, MAGNETIC,
#   OPTICAL, MANUAL, OR OTHERWISE, WITHOUT THE PRIOR WRITTEN
#   PERMISSION OF:
#                         HistoSoft Corporation
#
#  HistoSoft permits you, the enduser, to install this script
#  and its associated templates to use on domains under your
#  control. You may also edit the templates (in fact, you should)
#  to customize them for your domain(s). However, you must retain
#  any HistoSoft copyright notices in this utility but do not
#  need to link to the HistoSoft Corporation.
#  Linking, however, would be appreciated if you find this tool
#  useful.
#################################################################

use v5.6.0;

## Begin user configuration #####################################

# Define main virtual root
$conf::basedir = '/serverroot/path/to/public_html';

# The template (set to '' to disable it; default path based on main virtual root)
# $conf::templatefile = '/path/to/statuscode.tpl';                            # explicit path
# $conf::templatefile = "$conf::basedir/path/to/subdirectory/statuscode.tpl"; # or derived
$conf::templatefile = '';

# Define default domain and add up to 98 subdomains or delete extras
$conf::domainname = 'www.maindomain.com';
my %domainlist = (
    main => $conf::domainname,
    1    => 'www.subdomain1.com',
    2    => 'www.subdomain2.com',
    3    => 'www.subdomain3.com',
    # etc.
);

# The CSS stylesheet; default path based on main virtual root
$conf::templatecss = "http://$conf::domainname/path/to/stylesheets/basic.css";

# Header title
$conf::title = 'Server Status Code';

# Date format: %Y = year, %m = month, %d = day
# On a Unix system use 'man strftime' to get a list of all possible options
$conf::date_format = "%Y-%m-%d";
# Time format: %H = hour, %M = minute, %S = second
$conf::time_format = "%H:%M";
# Meta date/time format: %T = %H:%M:%S,
# %z (or 05:00 for explicit CST) = time-zone hour offset from GMT
$conf::metadt_format = "%Y-%m-%dTO%T-05:00";

# The default template if no $conf::templatefile is specified:
$conf::template = <<EOF;
<!-- {{ statuscode.tpl - XHTML-body for HistoSoft™ Status Code script - DO NOT REMOVE }} -->
    <!-- begin html body -->
    <!-- begin header -->
    <h1 class="domain">##fqdn##</h1>
    <!-- end header -->
    <!-- begin content table -->
    <table summary="Main Content - status indicator">
      <tr>
        <th><h2>##code##</h2></th>
      </tr>
      <tr>
        <td><p>Oops! You've just experienced a "##explain##" error at <a
        href="http://##fqdn##">##fqdn##</a>.</p>
        <p>Read about it from RFC2616 (Status Code Definitions) at the <a
        href="http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html###w3cnote##">W3C
        Hypertext Transfer Protocol [HTTP/1.1]</a> guide.</p></td>
      </tr>
    </table>
    <!-- end content table -->
    <!-- begin footer table -->
    <table class="footer" summary="Copyright footer">
      <tr>
        <td><p>Last modified on ##date##<br />
        Copyright</a> © 2005 <a
        href="http://##fqdn##">##fqdn##</a>. All Rights Reserved.</p></td>
      </tr>
    </table>
    <!-- end footer table -->
    <!-- end html body -->
EOF
## End user configuration #######################################

use strict;
use warnings;
use POSIX qw(strftime);
use CGI;
use CGI::Carp qw(fatalsToBrowser);

# Customize CGI.pm globals
$CGI::XHTML    = 1;
$CGI::NOSTICKY = 1;
$CGI::NPH      = 1;
$CGI::USE_PARAM_SEMICOLONS = 1;
$CGI::NO_UNDEF_PARAMS      = 1;

# This is a NPH, so turn off buffering.
$| = 1;

# Define default variables
my $statuscode = '200 OK';
my %codes = ( #see http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html
    200 => {'sc' => '200 OK',                       'id' => 'sec10.2.1'},
    301 => {'sc' => '301 Moved Permanently',        'id' => 'sec10.3.2'},
    307 => {'sc' => '307 Temporary Redirect',       'id' => 'sec10.3.8'},
    401 => {'sc' => '401 Unauthorized',             'id' => 'sec10.4.2'},
    403 => {'sc' => '403 Forbidden',                'id' => 'sec10.4.4'},
    404 => {'sc' => '404 Not Found',                'id' => 'sec10.4.5'},
    405 => {'sc' => '405 Method Not Allowed',       'id' => 'sec10.4.6'},
    408 => {'sc' => '408 Request Timeout',          'id' => 'sec10.4.9'},
    410 => {'sc' => '410 Gone',                     'id' => 'sec10.4.11'},
    413 => {'sc' => '413 Request Entity Too Large', 'id' => 'sec10.4.14'},
    414 => {'sc' => '414 Request-URI Too Long',     'id' => 'sec10.4.15'},
    500 => {'sc' => '500 Internal Server Error',    'id' => 'sec10.5.1'},
    501 => {'sc' => '501 Not Implemented',          'id' => 'sec10.5.2'},
    503 => {'sc' => '503 Service Unavailable',      'id' => 'sec10.5.4'},
);

# Untaint status code from .htaccess
my $reportedcode = $codes{200}->{'sc'};
$reportedcode    = CGI::param('c') if CGI::param('c');
$reportedcode   =~ /^(\d{3})$/;
$reportedcode    = $1;
my $explain      = $codes{$1}->{'sc'};
my $w3cnote      = $codes{$1}->{'id'};
$explain        =~ / ([A-Za-z- ]{2,24})$/;
$explain         = $1;

# Untaint domain name from .htaccess
my $domainno = 0;
$domainno    = CGI::param('dom') if CGI::param('dom');
$domainno   =~ /^(\d{1,2})$/;
$domainno    = $1;

my $domain = $domainlist{main};
$domain    = $domainlist{$domainno} if $domainno;

my $metatime;

# Declare the CGI object
my $query;

# Declare prototypes explicitly
sub main();
sub make_head_date();
sub get_now_date();

# Let's run
make_head_date();
main();

exit;


## Subroutines ###############
sub main() {

    # Sanity check
    if ( ! -d $conf::basedir ) {
        die "Error: $0: '$conf::basedir' does not exist, is not a directory,
            or is not readable.\n";
    }

    my $load_error = 0;
    my $template = '';
    # Check for type of template to return
    if ( $conf::templatefile ) {
        if ( ! open(INPUT, "<", $conf::templatefile) ) {
            warn "Could not open template file '$conf::templatefile': $!\n";
            $load_error = 1;
        } else {
            undef $/;
            $template = (<INPUT>);
            close(INPUT);
        }
    }
    if ( ! $conf::templatefile || $load_error ) {
        $template = $conf::template;
    }
    
    # Replace tokens in the template and then print it
    unless ($template =~ s/<!-- {{.*}} -->//g) {
        die "Invalid script template.\n";
    }
    $template =~ s/##metadate##/make_head_date();/igse;
    $template =~ s/##date##/get_now_date();/igse;
    $template =~ s/##domainname##/<base href="http:\/\/$domain\/" \/>/igs;
    $template =~ s/##fqdn##/$domain/igs;
    $template =~ s/##w3cnote##/$w3cnote/igs;
    $template =~ s/##code##/Server Error: $reportedcode/gs; #$codes{$reportedcode}
    $template =~ s/##explain##/$explain/igs;

    $query = new CGI;

    # Create NPH Server Headers
    print $query->header(
        -status     => $codes{$reportedcode}->{'sc'},
        -lang       => 'en-US',
        -connection => 'Keep-Alive',
        -keepalive  => 'timeout=10, max=10',
        -server     => 'Domain ErrorServer/1.0',
        -encoding   => 'iso-8859-1',
        -type       => 'text/html',
        -expires    => '10s'
    );

    # Create XHTML Headers
    print $query->start_html(
        -lang     => 'en-US',
        -encoding => 'iso-8859-1',
        -title    => uc($conf::domainname) . " | $conf::title | $codes{$reportedcode}->{'sc'}",
        -meta     => {
            'description'  => 'Server Status Code',
            'rating'       => 'general',
            'MSSmartTagsPreventParsing' => 'true',
            'copyright'    => 'Copyright(c)2005 HistoSoft Corporation. All Rights Reserved.',
            'date'         => $metatime,
        },
        -xbase => "http://$conf::domainname/",
        -head => [
            $query->meta(
                {
                    -http_equiv => 'cache-control',
                    -content    => 'private, no-store, max-age=60'
                }
            ),
        ],
        -style => { -src => [$conf::templatecss] },
    );

    print $template;
    print $query->end_html();
    $query->delete_all();

}

# Create or return formatted XHTML header date/time
sub make_head_date() {
    $metatime = POSIX::strftime($conf::metadt_format, localtime(time()));
}

# Get formatted current date
sub get_now_date() {
    return POSIX::strftime($conf::date_format, localtime(time()));
}

Would you like to make a suggestion or comment regarding this utility? By all means, share your opinion on the eForum.


 
         
navtop_g.png (1K) Support | Contact | Webmaster | Privacy navprv.png (1K)navupsq.png (1K)navnxt.png (1K)
Last modified on June 29, 2008
Copyright © 2002-2008 HistoSoft Corporation.
All Rights Reserved.
Level Triple-A conformance icon, W3C-WAI Web Content Accessibility Guidelines 1.0.Valid CSS!.Valid XHTML 1.1!.

About the design of this page

If you are using a "dated", non-text browser, this page may not render properly. You should be able to read all the content; however, since the styling is achieved with cascading style sheets (CSS) and old browsers do not support CSS, you must upgrade your browser. We apologize for any inconvenience.