/usr/local/CPAN/Catalog/Catalog/tools/cgi.pm
#
# Copyright (C) 1997, 1998
# Free Software Foundation, Inc.
#
# This program is free software; you can redistribute it and/or modify it
# under the terms of the GNU General Public License as published by the
# Free Software Foundation; either version 2, or (at your option) any
# later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
#
package Catalog::tools::cgi;
use vars qw(@ISA $random %char2quote);
use strict;
use CGI qw(:compile);
use Carp;
use Catalog::tools::tools;
@ISA = qw(CGI);
sub init {
my($self) = shift;
$self->CGI::init(@_);
my($config) = config_load("cgi.conf");
%$self = (%$self, %$config) if(defined($config));
}
sub protect_gen {
my($self) = @_;
my($file) = time() . $$ . $random++;
my($path) = "$self->{'fct_dir'}/$file";
open(FILE, ">$path") or error("cannot open $path for writing : $!");
close(FILE);
$self->param('cgi_protect', $file);
}
sub protect_check {
my($self) = @_;
my($file) = $self->param('cgi_protect');
return undef if(!defined($file));
my($path) = "$self->{'fct_dir'}/$file";
return -f $path;
}
sub protect_clear {
my($self) = @_;
my($file) = $self->param('cgi_protect');
return undef if(!defined($file));
my($path) = "$self->{'fct_dir'}/$file";
return undef if(!-f $path);
unlink($path) or error("cannot unlink $path : $!");
return 1;
}
#
# Function like cgi call
#
sub hash2params {
my($hash) = @_;
return join("&", map { "$_=" . CGI::escape($hash->{$_}) } keys(%$hash));
}
sub fct_call {
my($self, $params, %args) = @_;
error("recursive cgi depth > 1 is not allowed") if(defined($self->param('fct_name')));
$params .= "&fct_args=" . CGI::escape(hash2params($args{'args'}));
$params .= "&fct_returned=" . CGI::escape(hash2params($args{'returned'}));
$params .= "&fct_name=$args{'name'}";
my($filebase) = time() . $$ . $random++; # see also fct_return
my($file) = "$self->{'fct_dir'}/$filebase";
open(FILE, ">$file") or error("cannot open $file for writing : $!");
$self->save('FILE');
close(FILE);
# Save base name only to minimise security issues.
# XXX still allows deletion of any file in /tmp if the
# default fct_dir value in cgi.conf is used
$params .= "&fct_stack=$filebase"; # see also fct_return
#
# Wait for the fileno() bug to be fixed
#
# my($cgi) = Catalog::tools::cgi->new($params);
my($cgi) = Catalog::tools::cgi->new();
$cgi->delete_all();
my(@pairs) = split(/[&]/, $params);
my($param,$value);
foreach (@pairs) {
($param,$value) = split('=', $_, 2);
$param = CGI::unescape($param);
$value = CGI::unescape($value);
$cgi->param($param => $value);
}
return $cgi;
}
sub reset_params {
my($self, $params) = @_;
$self->delete_all();
my(@pairs) = split(/[&]/, $params);
my($param,$value);
foreach (@pairs) {
($param,$value) = split('=', $_, 2);
$param = CGI::unescape($param);
$value = CGI::unescape($value);
$self->param($param => $value);
}
}
sub fct_args {
my($self) = @_;
return $self->fct_extract('fct_args');
}
sub fct_returned {
my($self) = @_;
return $self->fct_extract('fct_returned');
}
sub fct_name {
my($self) = @_;
return $self->param('fct_name');
}
sub fct_extract {
my($self, $what) = @_;
my(%args);
my($args) = $self->param($what);
if(defined($args)) {
my($cgi) = CGI->new(CGI::unescape($args));
my($key);
foreach $key ($cgi->param()) {
$args{$key} = $cgi->param($key);
}
}
return \%args;
}
sub fct_return {
my($self, %args) = @_;
my($filebase) = $self->param('fct_stack');
$filebase = $1 if $filebase =~ m/^(\d+)$/; # untaint
my($file) = "$self->{'fct_dir'}/$filebase";
open(FILE, "<$file") or error("cannot open $file for reading : $!");
my($cgi) = Catalog::tools::cgi->new('FILE');
close(FILE);
unlink($file);
my($key, $value);
while(($key, $value) = each(%args)) {
$cgi->param($key, $value);
}
return $cgi;
}
#
#
# Parameters reconstitution for context passing
#
sub params {
my($self, $keys, %pairs) = @_;
return $self->params_1('params', $keys, %pairs);
}
sub hidden {
my($self, $keys, %pairs) = @_;
return $self->params_1('hidden', $keys, %pairs);
}
sub params_1 {
my($self, $style, $keys, %pairs) = @_;
my($html) = '';
my($field);
#
# Build from values found in the current set of parameters
#
foreach $field (@$keys, 'cgi_protect', 'fct_name', 'fct_args', 'fct_stack', 'fct_returned') {
next if(exists($pairs{$field}) || !defined($self->param($field)));
$html .= params_format($style, $field, $self->param($field));
}
#
# Override with %pairs arguments
#
my($value);
while(($field, $value) = each(%pairs)) {
$html .= params_format($style, $field, $value);
}
#
# Remove leading &
#
$html =~ s/^\&// if($style eq 'params');
return $html;
}
sub params_format {
my($style, $key, $value) = @_;
my($html) = '';
if(defined($value)) {
if($style eq 'params') {
$html = "&$key=" . CGI::escape($value);
} else {
$value = CGI::escapeHTML($value);
$html = "<input type=hidden name=$key value=\"$value\">\n";
}
}
return $html;
}
%char2quote = (
'>' => '>',
'<' => '<',
'&' => '&',
"'" => ''',
'"' => '"',
);
sub myescapeHTML {
my($toencode) = @_;
return undef if(!defined($toencode));
$toencode =~ s;([&\"<>\']);$char2quote{$1};ge;
return $toencode;
}
sub myunescapeHTML {
my($string) = @_;
return undef if(!defined($string));
# thanks to Randal Schwartz for the correct solution to this one
$string =~ s[&(.*?);]{
local $_ = $1;
/^amp$/i ? "&" :
/^quot$/i ? '"' :
/^gt$/i ? ">" :
/^lt$/i ? "<" :
/^#(\d+)$/ ? chr($1) :
/^#x([0-9a-f]+)$/i ? chr(hex($1)) :
$_
}gex;
return $string;
}