Subject: ANNOUNCE: A tool to debug CGI scripts
From: G Dinesh Dutt <[email-masked]>
Date: 23 Jul 1995 14:20:29 GMT
Newsgroups: comp.lang.perl.announce,comp.lang.perl.misc,comp.lang.perl

Hi,

Here is a tool to  debug CGI scripts.  I thought  of this after seeing so  many
"Help me with this CGI script" mails. The script maybe still a little rough and
may need some work.

What u need are : 1) Perl4 2) Ability to run CGI. 3) A forms-capable
browser. 4) I/O Redirection via >,< and | in Perl is required.

Please let me know what you think. Comments, cribs, bugs, suggestions etc. all
welcome. I only have Unix boxes and so have only tested it with them. I am
interested in hearing from people using it on other platforms like Mac,NT,
Windows, OS/2 etc. I have no idea if it works on Perl5. I'd be happy to hear
from people if it works on Perl5.

I have included the code because I have no access to the internet and so cannot
announce an anon FTP site. Its not very big and I hope its OK. Its of course,
uuencoded format of a tarred and compressed (compress, not gzip) file.

There are 4 files included below, which are separated by ^L.

Dinesh
cgi-parse.pl is just a replacement for cgi-lib.pl. I just wanted it to be
complete and didn't have the time to send mail to Steve Brennar about
permission to include it. To use cgi-lib.pl, just replace the calls to
CGIGetInput with ReadParse & calls to &PrintHeader to "print &PrintHeader" and
all should be fine. 


README :
--------
To Install :

1) Change the path of the perl in the debugcgi.cgi script to suit your host.
2) Change the URL for this script in the debugcgi.html form.

To Use :

1. If you use forms to supply input, strip off the <FORM and </FORM> lines
   from your form and attach the resulting body of the form to the debug form
   provided (debugcgi.html). For ISINDEX interfaces, supply the arguments in the
   ISINDEX area of the form. If you don't use forms, the bare form supplied
   will suffice.
2. Bring up your favorite browser on this form.
3. Enter the full pathname of the script invoked by the form to test and 
   also supply the METHOD used to submit information. 
4. Supply the input to your form.
5. Click on submit.

If all is fine with your form, you are notified of the same and the output from
the form displayed  (It currently lacks  support to handle pure image outputs
via Location headers pointing to some image). If  not, an error message  is
displayed  and  the cause of  the error reported, including any parsing  errors
for  a script.  All  errors which  result from  a change in the environment
(user to the server) are trapped and reported. 

For using PATH_INFO, append the path info to the debugcgi.cgi link (i.e. action
of the form).

What it is not :

I haven't tested it with some really complicated scripts, but it should work
because its principle is simple : Just trap the output/stderr of the script to
be run and report to the user.  

Send bug reports, cribs, suggestions to [email-masked].



debugcgi.cgi
------------
#!/usr/local/bin/perl

################################################################################
##                                DEBUGCGI.PL                                 ##
## This is a simple script which sets up a test environment for the CGI script##
## to be executed and then traps the common errors. The PATH is set to the    ##
## minimal set by most systems, for example. All error messages are trapped   ##
## and made available to the user.                                            ##
##                                                                            ##
## This code is in the public domain for people to do whatever they wish to   ##
## with it. But, maintain this copyright notice and don't say you wrote it.   ##
## This work is distributed in the hope that its useful. But, the author is   ##
## not liable for any any incurred damages, directly or indirectly due to the ##
## use or inability to use this software.                                     ##
################################################################################

$tmpdir = "/tmp/";		# The directory under which the error file will
				# be created.

require "cgi-parse.pl";

%cgi_input = ();

&CGIGetInput(*cgi_input);

$script = $cgi_input{'DebugCgi-ScriptName'};
$method = $cgi_input{'DebugCgi-Method'};
$cmdargs = $cgi_input {'DebugCgi-CmdArgs'};
delete ($cgi_input {'DebugCgi-ScriptName'});
delete ($cgi_input {'DebugCgi-Method'});
delete ($cgi_input {'DebugCgi-CmdArgs'});

$inp = "";
foreach $elem (keys %cgi_input) {
    $cgi_input{$elem} = $cgi_input{$elem};
    $cgi_input{$elem} =~ s# #+#g;
    $cgi_input{$elem} =~ s#([^+A-Za-z0-9])#sprintf("%%%02x",ord($1))#ge;
    $cgi_input{$elem} =~ s#%3d#=#g;
    $inp .= "$elem=$cgi_input{$elem}&";
}
# Encode the input in the form used by HTTP.

#Turn off the include path. The script must use its own @INC and environment.

#Set the request method.
$error_file = $tmpdir.$^T;
$ENV{'REQUEST_METHOD'} = $method;
if ($method eq "GET") {
    $ENV{'QUERY_STRING'} = $inp;
    open (OUTPUT, "$script $cmdargs 2\>/tmp/errors |") || &cry ("unable to pipe script $! \n");
}
elsif ($method eq "POST") {
    $ENV{'CONTENT_LENGTH'} = length($inp);
    open (OUTPUT, "echo \"$inp\" | $script $cmdargs 2>$error_file |") || &cry ("unable to pipe script $! \n");
}
else {
    &PrintHeader;
    print "Unknown method: $method\n";
    exit (3);
}

$_ = <OUTPUT>;

if (!/^Content-type: / && !/^Location: /) {
    if (-s $error_file) {
	open (ERRF, "< $error_file") || &cry ("testcgi.cgi - Unable to open error file $!\n");
	&PrintHeader;
	print "<HTML><BODY>\n";
	@errors = <ERRF>;
	print "<H3>Script $script has an execution error !!!</H3>\n";
	print "@errors \n";
	unlink ($error_file);
	exit (4);
    }
    &PrintHeader;
    print "<HTML><BODY>\n";
    print "<H3>Script $script has an error !!!</H3>\n";
    print "It does not output the Content-type/Location header.\n";
    print "Here's what it printed as the first line.\n";
    print "<PRE>\n";
    print;
    print "</PRE></BODY></HTML>\n";
    exit (3);
}

&PrintHeader;
print "<HTML><BODY>\n";

$format = m#^Content-type:[ \t]*text/html#;
$_ = <OUTPUT>;
if (!/^$/) {
    print "<H3>Script $script has an error !!!</H3>\n";
    print "The second line it outputs must be a blank, instead I got <PRE>\n";
    print;
    print "</PRE></BODY></HTML>\n";
    exit (3);
}
print "<H3>Script $script seems OK</H3> \n";
print "<P ALIGN=Justify> Here is its output \n";
print "<PRE>\n" if (!$format) ;
print $ENV{'PATH_INFO'},"\n";
while (<OUTPUT>) {
    print;
}

print "</PRE>" if (!$format);
print "</BODY></HTML>";
exit (0);

sub cry {
    local ($message) = @_;
    &PrintHeader;
    print $message;
    exit;
}



debugcgi.html
-------------
<FORM ACTION="http://eagle:8080/~brat/cgi-bin/debugcgi.cgi" METHOD="POST">
<H1 ALIGN=CENTER>Debug CGI Form</H1>
 <P ALIGN=CENTER>
<I>This form is used as a front-end to debugcgi, a tool to debug CGI scripts.</I>
<HR>
<INPUT TYPE="text" NAME="DebugCgi-ScriptName" VALUE="">
Name of the Script to be debugged (full pathname)<BR>
<INPUT TYPE="text" NAME="DebugCgi-Method" VALUE="GET">
Method used to submit data (GET/POST)<BR>
<INPUT TYPE="text" NAME="DebugCgi-CmdArgs" VALUE="">
Command Line Arguments To Be Supplied to Script (for ISINDEX)<BR>
<HR>
<!-- Insert the form to be tested minus the FORM header and trailer -->
<!-- and the Submit and clear buttons -->
<HR>
<INPUT TYPE="submit" VALUE="Submit Form">
Submit Button<BR>
<INPUT TYPE="reset" VALUE="Clear Values">
Reset Button.
 <P>
</FORM>



cgi-parse.pl :
--------------
#!/usr/local/bin/perl --  # -*-Perl-*-
'di';
'ig00';
# cgi-parse.pl	-- G Dinesh Dutt
#
# $Log: cgi-parse.pl,v $
# Revision 1.1  1995/07/15  20:49:37  brat
# Initial revision
#
$RCSHEADER = '$Header: /disk2/users/brat/html/pub/cgi-bin/RCS/cgi-parse.pl,v 1.1 1995/07/15 20:49:37 brat Exp brat $';	#'
'$Revision: 1.1 $'  =~ /^\$\w+:\s+([.1234567890]+)\s+\$$/;	#'
$VERSION = $1;
#################################################################################
##                                CGI-PARSE.PL                                 ##
## A small library to read and parse the input available from forms as per the ##
## CGI 1.1 specification.                                                      ##
##                                                                             ##
## This code is in the public domain for people to do whatever they wish to    ## 
## with it. But, maintain this copyright notice and don't say you wrote it.    ##
## This work is distributed in the hope that its useful. But, the author is not##
## liable for any any incurred damages, directly or indirectly due to the use  ##
## or inability to use this software.                                          ##
#################################################################################
#################################################################################
## CGIGetInput                                                                 ##
##                                                                             ##
## This is a small function which decodes the forms input. It looks at the     ##
## REQUEST_METHOD environment variable to decide where to get the input from.  ##
## The user can invoke this subroutine thus :                                  ##
##              &CGIGetInput (*cgi_in);                                        ##
## and the input is returned in an associative array called cgi_in, with the   ##
## key being the name of the field and its value being the value of the field  ##
## as supplied by the user. If the field does not have any input, the entry in ##
## the associative array will be undefined.                                    ##
#################################################################################

sub CGIGetInput {
    local (*input) = @_;
    local ($buffer,@nv_pairs);
    if ($ENV{'REQUEST_METHOD'} eq "GET") {
	$buffer = $ENV{'QUERY_STRING'};
    }
    elsif ($ENV{'REQUEST_METHOD'} eq "POST") {
	read (STDIN, $buffer, $ENV{'CONTENT_LENGTH'});
    }
    else {
	return -1;
    }
# one shot conversion of + to spaces 
    $buffer =~ tr/+/ /;
    $buffer =~ s#%(..)#pack("c",hex($1))#ge;
    @nv_pairs = split (/\&/,$buffer);

    foreach $nvp (0..$#nv_pairs) {
        ($key, $keyword) = split (/=/, $nv_pairs[$nvp], 2);
	$input{$key} .= '\0' if (defined ($input{$key}));
        $input{$key} .= $keyword;
    }
    return 1;
}

#################################################################################
## &PrintHeader (type/URL, is_it_a_URL)                                        ##
## This function prints the default header. If a type is specified, that is    ##
## printed, else the default text/html is printed. If the second parameter is  ##
## 1, then the Location header is printed instead of the text/html header.     ##
##                                                                             ##
## Example invocations :                                                       ##
## 		&PrintHeader ("text/plain", 0)                                 ##
##              &PrintHeader ("http://www.halcyon.com/hedlund/cgi-faq/",1)     ##
##              &PrintHeader ("",0)                                            ##
#################################################################################

sub PrintHeader {
    local ($toprint, $url_p) = @_;

    if ($toprint eq "") {
	print "Content-type: text/html\n\n";
    }
    elsif ($url_p) {
	print "Location: $toprint\n\n";
    }
    else {
	print "Content-type: $toprint\n\n";
    }
}
1;

###############################################################################
"Whatever you can do, or dream you can, begin it. Boldness has genius, power
and magic in it." - Goethe.
G. Dinesh Dutt,					email : [email-masked]
Hinditron Tektronix Instruments Ltd.,		voice : 8217649/8212262
SDF-2, Unit 63-A, SEEPZ, Andheri (east), Bombay - 400096.
###############################################################################