package CGI::PathInfo;
use strict;
use warnings;
BEGIN {
$CGI::PathInfo::VERSION = '1.06';
$CGI::PathInfo::_mod_perl = 0;
}
# check for mod_perl and include the 'Apache' module if needed
if (exists($ENV{'MOD_PERL'}) && (0 == $CGI::PathInfo::_mod_perl)) {
$| = 1;
if (exists ($ENV{MOD_PERL_API_VERSION}) && ($ENV{MOD_PERL_API_VERSION} == 2)) {
require Apache2::RequestUtil;
require Apache2::RequestIO;
require APR::Pool;
$CGI::PathInfo::_mod_perl = 2;
} else {
require Apache;
$CGI::PathInfo::_mod_perl = 1;
}
}
sub new {
my $proto = shift;
my $package = __PACKAGE__;
my $class;
if (ref($proto)) {
$class = ref ($proto);
} elsif ($proto) {
$class = $proto;
} else {
$class = $package;
}
my $self = bless {},$class;
$self->{$package}->{'field_names'} = [];
$self->{$package}->{'field'} = {};
$self->{$package}->{'settings'} = {
'eq' => '-',
'spliton' => '/',
'stripleadingslash' => 1,
'striptrailingslash' => 1,
};
my $parms;
if ($#_ == 0) {
$parms = shift;
} elsif ($#_ > 0) {
if (0 == $#_ % 2) {
require Carp;
Carp::croak('[' . localtime(time) . "] [error] $package" . '::new() - odd number of passed parameters');
}
%$parms = @_;
} else {
$parms = {};
}
if (ref($parms) ne 'HASH') {
require Carp;
Carp::croak('[' . localtime(time) . "] [error] $package" . '::new() - Passed parameters do not appear to be valid');
}
my @parm_keys = keys %$parms;
foreach my $parm_name (@parm_keys) {
my $lc_parm_name = lc ($parm_name);
if (not exists $self->{$package}->{'settings'}->{$lc_parm_name}) {
require Carp;
Carp::croak('[' . localtime(time) . "] [error] $package" . "::new() - Passed parameter name '$parm_name' is not valid here");
}
$self->{$package}->{'settings'}->{$lc_parm_name} = $parms->{$parm_name};
}
$self->_decode_path_info;
return $self;
}
#######################################################################
sub param {
my $self = shift;
my $package = __PACKAGE__;
if (1 < @_) {
my $n_parms = @_;
if (($n_parms % 2) == 1) {
require Carp;
Carp::croak('[' . localtime(time) . "] [error] $package" . "::param() - Odd number of parameters passed");
}
my $parms = { @_ };
$self->_set($parms);
return;
}
if ((@_ == 1) and (ref ($_[0]) eq 'HASH')) {
my $parms = shift;
$self->_set($parms);
return;
}
my @result = ();
if ($#_ == -1) {
@result = @{$self->{$package}->{'field_names'}};
} else {
my ($fieldname)=@_;
if (defined($self->{$package}->{'field'}->{$fieldname})) {
@result = @{$self->{$package}->{'field'}->{$fieldname}->{'value'}};
}
}
if (wantarray) {
return @result;
} else {
return $result[0];
}
}
#######################################################################
sub calling_parms_table {
my $self = shift;
my $package = __PACKAGE__;
require HTML::Entities;
my $outputstring = "<table border=\"1\" cellspacing=\"0\"><tr><th colspan=\"2\">PATH_INFO Fields</th></tr><tr><th>Field</th><th>Value</th></tr>\n";
my @field_list = $self->param;
foreach my $fieldname (sort @field_list) {
my @values = $self->param($fieldname);
my $sub_field_counter= $#values;
for (my $fieldn=0; $fieldn <= $sub_field_counter; $fieldn++) {
my $e_fieldname = HTML::Entities::encode_entities($fieldname);
my $fieldvalue = HTML::Entities::encode_entities($values[$fieldn]);
$outputstring .= "<tr><td>$e_fieldname (#$fieldn)</td><td> $fieldvalue</td></tr>\n";
}
}
$outputstring .= "</table>\n";
return $outputstring;
}
#######################################################################
sub url_encode {
my $self = shift;
my ($line) = @_;
return '' if (! defined ($line));
$line =~ s/([^a-zA-Z0-9])/"\%".unpack("H",$1).unpack("h",$1)/egs;
return $line;
}
#######################################################################
sub url_decode {
my $self = shift;
my ($line) = @_;
return '' if (! defined ($line));
$line =~ s/\+/ /gos;
$line =~ s/%([a-fA-F0-9][a-fA-F0-9])/pack("C", hex($1))/egs;
return $line;
}
########################################################################
# Performs PATH_INFO decoding
sub _decode_path_info {
my $self = shift;
my $package = __PACKAGE__;
my $buffer = '';
if (1 == $CGI::PathInfo::_mod_perl) {
$buffer = Apache->request->path_info;
} elsif (2 == $CGI::PathInfo::_mod_perl) {
$buffer = Apache2::RequestUtil->request->path_info;
} else {
$buffer = $ENV{'PATH_INFO'} if (defined $ENV{'PATH_INFO'});
}
$self->_burst_URL_encoded_buffer($buffer);
return;
}
##########################################################################
# Bursts normal URL encoded buffers
# Takes: $buffer - the actual data to be burst
#
# parameters are presumed to be seperated by ';' characters
#
sub _burst_URL_encoded_buffer {
my $self = shift;
my $package = __PACKAGE__;
my ($buffer) = @_;
my $settings = $self->{$package}->{'settings'};
if ($settings->{'stripleadingslash'}) { $buffer =~ s#^/+##s; }
if ($settings->{'striptrailingslash'}) { $buffer =~ s#/+$##s; }
my $spliton = $settings->{'spliton'};
my $eq_mark = $settings->{'eq'};
# Split the name-value pairs on the selected split char
my @pairs = ();
if ($buffer) {
@pairs = split(/$spliton/, $buffer);
}
# Initialize the field hash and the field_names array
$self->{$package}->{'field'} = {};
$self->{$package}->{'field_names'} = [];
foreach my $pair (@pairs) {
my ($name, $data) = split(/$eq_mark/,$pair,2);
# Anything that didn't split is omitted from the output
next if (not defined $data);
# De-URL encode %-encoding
$name = $self->url_decode($name);
$data = $self->url_decode($data);
if (! defined ($self->{$package}->{'field'}->{$name}->{'count'})) {
push (@{$self->{$package}->{'field_names'}},$name);
$self->{$package}->{'field'}->{$name}->{'count'} = 0;
}
my $record = $self->{$package}->{'field'}->{$name};
my $field_count = $record->{'count'};
$record->{'count'}++;
$record->{'value'}->[$field_count] = $data;
}
return;
}
##################################################################
#
# Sets values into the object directly
# Pass an anon hash for name/value pairs. Values may be
# anon lists or simple strings
#
##################################################################
sub _set {
my $self = shift;
my $package = __PACKAGE__;
my ($parms) = @_;
foreach my $name (keys %$parms) {
my $value = $parms->{$name};
my $data = [];
my $data_type = ref $value;
if (not $data_type) {
$data = [ $value ];
} elsif ($data_type eq 'ARRAY') {
# Shallow copy the anon array to prevent action at a distance
@$data = map {$_} @$value;
} else {
require Carp;
Carp::croak ('[' . localtime(time) . "] [error] $package" . "::_set() - Parameter '$name' has illegal data type of '$data_type'");
}
if (! defined ($self->{$package}->{'field'}->{$name}->{'count'})) {
push (@{$self->{$package}->{'field_names'}},$name);
}
my $record = {};
$self->{$package}->{'field'}->{$name} = $record;
$record->{'count'} = @$data;
$record->{'value'} = $data;
}
return;
}
##########################################################################
1;