Updated contact information
[CGIscriptor.git] / CGIscriptor.pl
bloba8ae711a3ad4362b1f72a34b9dccdfd35b1da635
1 #! /usr/bin/perl
3 # (configure the first line to contain YOUR path to perl 5.000+)
5 # CGIscriptor.pl
6 # Version 2.3
7 # 15 January 2002
9 # YOU NEED:
11 # perl 5.0 or higher (see: "http://www.perl.org/")
13 # Notes:
15 if(grep(/\-\-help/i, @ARGV))
17 print << 'ENDOFPREHELPTEXT1';
18 # CGIscriptor.pl is a Perl program will run on any WWW server that
19 # runs Perl scripts, just add a line like the following to your
20 # httpd.conf file (Apache example):
22 # ScriptAlias /SHTML/ "/real-path/CGIscriptor.pl/"
24 # URL's that refer to http://www.your.address/SHTML/... will now be handled
25 # by CGIscriptor.pl, which can use a private directory tree (default is the
26 # DOCUMENT_ROOT directory tree, but it can be anywhere, see below).
27 # NOTE: if you cannot use a ScriptAlias, there is a way to use .htaccess
28 # instead. See below.
30 # This file contains all documentation as comments. These comments
31 # can be removed to speed up loading (e.g., `egrep -v '^#' CGIscriptor.pl` >
32 # leanScriptor.pl). A bare bones version of CGIscriptor.pl, lacking
33 # documentation, most comments, access control, example functions etc.
34 # (but still with the copyright notice and some minimal documentation)
35 # can be obtained by calling CGIscriptor.pl with the '-slim'
36 # command line argument, e.g.,
37 # >CGIscriptor.pl -slim >slimCGIscriptor.pl
39 # CGIscriptor.pl can be run from the command line as
40 # `CGIscriptor.pl <path> <query>`, inside a perl script with
41 # 'do CGIscriptor.pl' after setting $ENV{PATH_INFO} and $ENV{QUERY_STRING},
42 # or CGIscriptor.pl can be loaded with 'require "/real-path/CGIscriptor.pl"'.
43 # In the latter case, requests are processed by 'Handle_Request();'
44 # (again after setting $ENV{PATH_INFO} and $ENV{QUERY_STRING}).
46 # The --help command line switch will print the manual.
48 # Running demo's and more information can be found at
49 # http://www.fon.hum.uva.nl/rob/OSS/OSS.html
51 # A pocket-size HTTP daemon, CGIservlet.pl, is available from my web site
52 # or CPAN that can use CGIscriptor.pl as the base of a µWWW server and
53 # demonstrates its use.
55 ENDOFPREHELPTEXT1
57 # Configuration, copyright notice, and user manual follow the next
58 # (Changes) section.
60 ############################################################################
62 # Changes (document ALL changes with date, name and email here):
63 # 21 May 2012 - Corrected the links generated by CGIscriptor::BrowseDirs
64 # Will link to current base URL when the HTTP server is '.' or '~'
65 # 29 Oct 2009 - Adapted David A. Wheeler's suggestion about filenames:
66 # CGIsafeFileName does not accept filenames starting with '-'
67 # (http://www.dwheeler.com/essays/fixing-unix-linux-filenames.html)
68 # 08 Oct 2009 - Some corrections in the README.txt file, eg, new email address
69 # 28 Jan 2005 - Added a file selector to performTranslation.
70 # Changed %TranslationTable to @TranslationTable
71 # and patterns to lists.
72 # 27 Jan 2005 - Added a %TranslationTable with associated
73 # performTranslation(\$text) function to allow
74 # run changes in the web pages. Say, to translate
75 # legacy pages with <%=...%> delimiters to the new
76 # <SCRIPT TYPE=..></SCRIPT> format.
77 # 27 Jan 2005 - Small bug of extra '\n' in output removed from the
78 # Other Languages Code.
79 # 10 May 2004 - Belated upload of latest version (2.3) to CPAN
80 # 07 Oct 2003 - Corrected error '\s' -> '\\s' in rebol scripting
81 # language call
82 # 07 Oct 2003 - Corrected omitted INS tags in <DIV><INS> handling
83 # 20 May 2003 - Added a --help switch to print the manual.
84 # 06 Mar 2003 - Adapted the blurb at the end of the file.
85 # 03 Mar 2003 - Added a user definable dieHandler function to catch all
86 # "die" calls. Also "enhanced" the STDERR printout.
87 # 10 Feb 2003 - Split off the reading of the POST part of a query
88 # from Initialize_output. This was suggested by Gerd Franke
89 # to allow for the catching of the file_path using a
90 # POST based lookup. That is, he needed the POST part
91 # to change the file_path.
92 # 03 Feb 2003 - %{$name}; => %{$name} = (); in defineCGIvariableHash.
93 # 03 Feb 2003 - \1 better written as $1 in
94 # $directive =~ s/[^\\\$]\#[^\n\f\r]*([\n\f\r])/$1/g
95 # 29 Jan 2003 - This makes "CLASS="ssperl" CSS-compatible Gerd Franke
96 # added:
97 # $ServerScriptContentClass = "ssperl";
98 # changed in ProcessFile():
99 # unless(($CurrentContentType =~
100 # 28 Jan 2003 - Added 'INS' Tag! Gerd Franke
101 # 20 Dec 2002 - Removed useless $Directoryseparator variable.
102 # Update comments and documentation.
103 # 18 Dec 2002 - Corrected bug in Accept/Reject processing.
104 # Files didn't work.
105 # 24 Jul 2002 - Added .htaccess documentation (from Gerd Franke)
106 # Also added a note that RawFilePattern can be a
107 # complete file name.
108 # 19 Mar 2002 - Added SRC pseudo-files PREFIX and POSTFIX. These
109 # switch to prepending or to appending the content
110 # of the SRC attribute. Default is prefixing. You
111 # can add as many of these switches as you like.
112 # 13 Mar 2002 - Do not search for tag content if a tag closes with
113 # />, i.e., <DIV ... /> will be handled the XML/XHTML way.
114 # 25 Jan 2002 - Added 'curl' and 'snarf' to SRC attribute URL handling
115 # (replaces wget).
116 # 25 Jan 2002 - Found a bug in SAFEqx, now executes qx() in a scalar context
117 # (i.o. a list context). This is necessary for binary results.
118 # 24 Jan 2002 - Disambiguated -T $SRCfile to -T "$SRCfile" (and -e) and
119 # changed the order of if/elsif to allow removing these
120 # conditions in systems with broken -T functions.
121 # (I also removed a spurious ')' bracket)
122 # 17 Jan 2002 - Changed DIV tag SRC from <SOURCE> to sysread(SOURCE,...)
123 # to support binary files.
124 # 17 Jan 2002 - Removed WhiteSpace from $FileAllowedCharacters.
125 # 17 Jan 2002 - Allow "file://" prefix in SRC attribute. It is simply
126 # stipped from the path.
127 # 15 Jan 2002 - Version 2.2
128 # 15 Jan 2002 - Debugged and completed URL support (including
129 # CGIscriptor::read_url() function)
130 # 07 Jan 2002 - Added automatic (magic) URL support to the SRC attribute
131 # with the main::GET_URL function. Uses wget -O underlying.
132 # 04 Jan 2002 - Added initialization of $NewDirective in InsertForeignScript
133 # (i.e., my $NewDirective = "";) to clear old output
134 # (this was a realy anoying bug).
135 # 03 Jan 2002 - Added a <DIV CLASS='text/ssperl' ID='varname'></DIV>
136 # tags that assign the body text as-is (literally)
137 # to $varname. Allows standard HTML-tools to handle
138 # Cascading Style Sheet templates. This implements a
139 # design by Gerd Franke (franke@roo.de).
140 # 03 Jan 2002 - I finaly gave in and allowed SRC files to expand ~/.
141 # 12 Oct 2001 - Normalized spelling of "CGIsafFileName" in documentation.
142 # 09 Oct 2001 - Added $ENV{'CGI_BINARY_FILE'} to log files to
143 # detect unwanted indexing of TAR files by webcrawlers.
144 # 10 Sep 2001 - Added $YOUR_SCRIPTS directory to @INC for 'require'.
145 # 22 Aug 2001 - Added .txt (Content-type: text/plain) as a default
146 # processed file type. Was processed via BinaryMapFile.
147 # 31 May 2001 - Changed =~ inside CGIsafeEmailAddress that was buggy.
148 # 29 May 2001 - Updated $CGI_HOME to point to $ENV{DOCUMENT_ROOT} io
149 # the root of PATH_TRANSLATED. DOCUMENT_ROOT can now
150 # be manipulated to achieve a "Sub Root".
151 # NOTE: you can have $YOUR_HTML_FILES != DOCUMENT_ROOT
152 # 28 May 2001 - Changed CGIscriptor::BrowsDirs function for security
153 # and debugging (it now works).
154 # 21 May 2001 - defineCGIvariableHash will ADD values to existing
155 # hashes,instead of replacing existing hashes.
156 # 17 May 2001 - Interjected a '&' when pasting POST to GET data
157 # 24 Apr 2001 - Blocked direct requests for BinaryMapFile.
158 # 16 Aug 2000 - Added hash table extraction for CGI parameters with
159 # CGIparseValueHash (used with structured parameters).
160 # Use: CGI='%<CGI-partial-name>' (fill in your name in <>)
161 # Will collect all <CGI-partial-name><key>=value pairs in
162 # $<CGI-partial-name>{<key>} = value;
163 # 16 Aug 2000 - Adapted SAFEqx to protect @PARAMETER values.
164 # 09 Aug 2000 - Added support for non-filesystem input by way of
165 # the CGI_FILE_CONTENTS and CGI_DATA_ACCESS_CODE
166 # environment variables.
167 # 26 Jul 2000 - On the command-line, file-path '-' indicates STDIN.
168 # This allows CGIscriptor to be used in pipes.
169 # Default, $BLOCK_STDIN_HTTP_REQUEST=1 will block this
170 # in an HTTP request (i.e., in a web server).
171 # 26 Jul 2000 - Blocked 'Content-type: text/html' if the SERVER_PROTOCOL
172 # is not HTTP or another protocol. Changed the default
173 # source directory to DOCUMENT_ROOT (i.o. the incorrect
174 # SERVER_ROOT).
175 # 24 Jul 2000 - -slim Command-line argument added to remove all
176 # comments, security, etc.. Updated documentation.
177 # 05 Jul 2000 - Added IF and UNLESS attributes to make the
178 # execution of all <META> and <SCRIPT> code
179 # conditional.
180 # 05 Jul 2000 - Rewrote and isolated the code for extracting
181 # quoted items from CGI and SRC attributes.
182 # Now all attributes expect the same set of
183 # quotes: '', "", ``, (), {}, [] and the same
184 # preceded by a \, e.g., "\((aap)\)" will be
185 # extracted as "(aap)".
186 # 17 Jun 2000 - Construct @ARGV list directly in CGIexecute
187 # name-space (i.o. by evaluation) from
188 # CGI attributes to prevent interference with
189 # the processing for non perl scripts.
190 # Changed CGIparseValueList to prevent runaway
191 # loops.
192 # 16 Jun 2000 - Added a direct (interpolated) display mode
193 # (text/ssdisplay) and a user log mode
194 # (text/sslogfile).
195 # 06 Jun 2000 - Replace "print $Result" with a syswrite loop to
196 # allow large string output.
197 # 02 Jun 2000 - Corrected shrubCGIparameter($CGI_VALUE) to realy
198 # remove all control characters. Changed Interpreter
199 # initialization to shrub interpolated CGI parameters.
200 # Added 'text/ssmailto' interpreter script.
201 # 22 May 2000 - Changed some of the comments
202 # 09 May 2000 - Added list extraction for CGI parameters with
203 # CGIparseValueList (used with multiple selections).
204 # Use: CGI='@<CGI-parameter>' (fill in your name in <>)
205 # 09 May 2000 - Added a 'Not Present' condition to CGIparseValue.
206 # 27 Apr 2000 - Updated documentation to reflect changes.
207 # 27 Apr 2000 - SRC attribute "cleaned". Supported for external
208 # interpreters.
209 # 27 Apr 2000 - CGI attribute can be used in <SCRIPT> tag.
210 # 27 Apr 2000 - Gprolog, M4 support added.
211 # 26 Apr 2000 - Lisp (rep) support added.
212 # 20 Apr 2000 - Use of external interpreters now functional.
213 # 20 Apr 2000 - Removed bug from extracting Content types (RegExp)
214 # 10 Mar 2000 - Qualified unconditional removal of '#' that preclude
215 # the use of $#foo, i.e., I changed
216 # s/[^\\]\#[^\n\f\r]*([\n\f\r])/\1/g
217 # to
218 # s/[^\\\$]\#[^\n\f\r]*([\n\f\r])/\1/g
219 # 03 Mar 2000 - Added a '$BlockPathAccess' variable to "hide"
220 # things like, e.g., CVS information in CVS subtrees
221 # 10 Feb 2000 - URLencode/URLdecode have been made case-insensitive
222 # 10 Feb 2000 - Added a BrowseDirs function (CGIscriptor package)
223 # 01 Feb 2000 - A BinaryMapFile in the ~/ directory has precedence
224 # over a "burried" BinaryMapFile.
225 # 04 Oct 1999 - Added two functions to check file names and email addresses
226 # (CGIscriptor::CGIsafeFileName and
227 # CGIscriptor::CGIsafeEmailAddress)
228 # 28 Sept 1999 - Corrected bug in sysread call for reading POST method
229 # to allow LONG posts.
230 # 28 Sept 1999 - Changed CGIparseValue to handle multipart/form-data.
231 # 29 July 1999 - Refer to BinaryMapFile from CGIscriptor directory, if
232 # this directory exists.
233 # 07 June 1999 - Limit file-pattern matching to LAST extension
234 # 04 June 1999 - Default text/html content type is printed only once.
235 # 18 May 1999 - Bug in replacement of ~/ and ./ removed.
236 # (Rob van Son, R.J.J.H.vanSon@uva.nl)
237 # 15 May 1999 - Changed the name of the execute package to CGIexecute.
238 # Changed the processing of the Accept and Reject file.
239 # Added a full expression evaluation to Access Control.
240 # (Rob van Son, R.J.J.H.vanSon@uva.nl)
241 # 27 Apr 1999 - Brought CGIscriptor under the GNU GPL. Made CGIscriptor
242 # Version 1.1 a module that can be called with 'require "CGIscriptor.pl"'.
243 # Requests are serviced by "Handle_Request()". CGIscriptor
244 # can still be called as a isolated perl script and a shell
245 # command.
246 # Changed the "factory default setting" so that it will run
247 # from the DOCUMENT_ROOT directory.
248 # (Rob van Son, R.J.J.H.vanSon@uva.nl)
249 # 29 Mar 1999 - Remove second debugging STDERR switch. Moved most code
250 # to subroutines to change CGIscriptor into a module.
251 # Added mapping to process unsupported file types (e.g., binary
252 # pictures). See $BinaryMapFile.
253 # (Rob van Son, R.J.J.H.vanSon@uva.nl)
254 # 24 Sept 1998 - Changed text of license (Rob van Son, R.J.J.H.vanSon@uva.nl)
255 # Removed a double setting of filepatterns and maximum query
256 # size. Changed email address. Removed some typos from the
257 # comments.
258 # 02 June 1998 - Bug fixed in URLdecode. Changing the foreach loop variable
259 # caused quiting CGIscriptor.(Rob van Son, R.J.J.H.vanSon@uva.nl)
260 # 02 June 1998 - $SS_PUB and $SS_SCRIPT inserted an extra /, removed.
261 # (Rob van Son, R.J.J.H.vanSon@uva.nl)
264 # Known Bugs:
266 # 23 Mar 2000
267 # It is not possible to use operators or variables to construct variable names,
268 # e.g., $bar = \@{$foo}; won't work. However, eval('$bar = \@{'.$foo.'};');
269 # will indeed work. If someone could tell me why, I would be obliged.
272 ############################################################################
274 # OBLIGATORY USER CONFIGURATION
276 # Configure the directories where all user files can be found (this
277 # is the equivalent of the server root directory of a WWW-server).
278 # These directories can be located ANYWHERE. For security reasons, it is
279 # better to locate them outside the WWW-tree of your HTTP server, unless
280 # CGIscripter handles ALL requests.
282 # For convenience, the defaults are set to the root of the WWW server.
283 # However, this might not be safe!
285 # ~/ text files
286 # $YOUR_HTML_FILES = "/usr/pub/WWW/SHTML"; # or SS_PUB as environment var
287 # (patch to use the parent directory of CGIscriptor as document root, should be removed)
288 if($ENV{'SCRIPT_FILENAME'}) # && $ENV{'SCRIPT_FILENAME'} !~ /\Q$ENV{'DOCUMENT_ROOT'}\E/)
290 $ENV{'DOCUMENT_ROOT'} = $ENV{'SCRIPT_FILENAME'};
291 $ENV{'DOCUMENT_ROOT'} =~ s@/CGIscriptor.*$@@ig;
294 # Just enter your own directory path here
295 $YOUR_HTML_FILES = $ENV{'DOCUMENT_ROOT'}; # default is the DOCUMENT_ROOT
297 # ./ script files (recommended to be different from the previous)
298 # $YOUR_SCRIPTS = "/usr/pub/WWW/scripts"; # or SS_SCRIPT as environment var
299 $YOUR_SCRIPTS = $YOUR_HTML_FILES; # This might be a SECURITY RISK
301 # End of obligatory user configuration
302 # (note: there is more non-essential user configuration below)
304 ############################################################################
306 # OPTIONAL USER CONFIGURATION (all values are used CASE INSENSITIVE)
308 # Script content-types: TYPE="Content-type" (user defined mime-type)
309 $ServerScriptContentType = "text/ssperl"; # Server Side Perl scripts
310 # CSS require a simple class
311 $ServerScriptContentClass = $ServerScriptContentType =~ m!/! ?
312 $' : "ssperl"; # Server Side Perl CSS classes
314 $ShellScriptContentType = "text/osshell"; # OS shell scripts
315 # # (Server Side perl ``-execution)
317 # Accessible file patterns, block any request that doesn't match.
318 # Matches any file with the extension .(s)htm(l), .txt, or .xmr
319 # (\. is used in regexp)
320 # Note: die unless $PATH_INFO =~ m@($FilePattern)$@is;
321 $FilePattern = ".shtml|.htm|.html|.xml|.xmr|.txt";
323 # The table with the content type MIME types
324 # (allows to differentiate MIME types, if needed)
325 %ContentTypeTable =
327 '.html' => 'text/html',
328 '.shtml' => 'text/html',
329 '.htm' => 'text/html',
330 '.xml' => 'text/xml',
331 '.txt' => 'text/plain'
335 # File pattern post-processing
336 $FilePattern =~ s/([@.])/\\$1/g; # Convert . and @ to \. and \@
338 # Raw files must contain their own Content-type (xmr <- x-multipart-replace).
339 # THIS IS A SUBSET OF THE FILES DEFINED IN $FilePattern
340 $RawFilePattern = ".xmr";
341 # (In principle, this could contain a full file specification, e.g.,
342 # ".xmr|relocated.html")
344 # Raw File pattern post-processing
345 $RawFilePattern =~ s/([@.])/\\$1/g; # Convert . and @ to \. and \@
347 # Server protocols for which "Content-type: text/html\n\n" should be printed
348 # (you should not bother with these, except for HTTP, they are mostly imaginary)
349 $ContentTypeServerProtocols = 'HTTP|MAIL|MIME';
351 # Block access to all (sub-) paths and directories that match the
352 # following (URL) path (is used as:
353 # 'die if $BlockPathAccess && $ENV{'PATH_INFO'} =~ m@$BlockPathAccess@;' )
354 $BlockPathAccess = '/CVS/'; # Protect CVS information
356 # All (blocked) other file-types can be mapped to a single "binary-file"
357 # processor (a kind of pseudo-file path). This can either be an error
358 # message (e.g., "illegal file") or contain a script that serves binary
359 # files.
360 # Note: the real file path wil be stored in $ENV{CGI_BINARY_FILE}.
361 $BinaryMapFile = "/BinaryMapFile.xmr";
362 # Allow for the addition of a CGIscriptor directory
363 # Note that a BinaryMapFile in the root "~/" directory has precedence
364 $BinaryMapFile = "/CGIscriptor".$BinaryMapFile
365 if ! -e "$YOUR_HTML_FILES".$BinaryMapFile
366 && -e "$YOUR_HTML_FILES/CGIscriptor".$BinaryMapFile;
369 # List of all characters that are allowed in file names and paths.
370 # All requests containing illegal characters are blocked. This
371 # blocks most tricks (e.g., adding "\000", "\n", or other control
372 # characters, also blocks URI's using %FF)
373 # THIS IS A SECURITY FEATURE
374 # (this is also used to parse filenames in SRC= features, note the
375 # '-quotes, they are essential)
376 $FileAllowedChars = '\w\.\~\/\:\*\?\-'; # Covers Unix and Mac, but NO spaces
378 # Maximum size of the Query (number of characters clients can send
379 # covers both GET & POST combined)
380 $MaximumQuerySize = 2**20 - 1; # = 2**14 - 1
383 # Embeded URL get function used in SRC attributes and CGIscriptor::read_url
384 # (returns a string with the PERL code to transfer the URL contents, e.g.,
385 # "SAFEqx(\'curl \"http://www.fon.hum.uva.nl\"\')")
386 # "SAFEqx(\'wget --quiet --output-document=- \"http://www.fon.hum.uva.nl\"\')")
387 # Be sure to handle <BASE HREF='URL'> and allow BOTH
388 # direct printing GET_URL($URL [, 0]) and extracting the content of
389 # the $URL for post-processing GET_URL($URL, 1).
390 # You get the WHOLE file, including HTML header.
391 # The shell command Use $URL where the URL should go
392 # ('wget', 'snarf' or 'curl', uncomment the one you would like to use)
393 my $GET_URL_shell_command = 'wget --quiet --output-document=- $URL';
394 #my $GET_URL_shell_command = 'snarf $URL -';
395 #my $GET_URL_shell_command = 'curl $URL';
397 sub GET_URL # ($URL, $ValueNotPrint) -> content_of_url
399 my $URL = shift || return;
400 my $ValueNotPrint = shift || 0;
402 # Check URL for illegal characters
403 return "print '<h1>Illegal URL<h1>'\"\n\";" if $URL =~ /[^$FileAllowedChars\%]/;
405 # Include URL in final command
406 my $CurrentCommand = $GET_URL_shell_command;
407 $CurrentCommand =~ s/\$URL/$URL/g;
409 # Print to STDOUT or return a value
410 my $BlockPrint = "print STDOUT ";
411 $BlockPrint = "" if $ValueNotPrint;
413 my $Commands = <<"GETURLCODE";
414 # Get URL
416 my \$Page = "";
418 # Simple, using shell command
419 \$Page = SAFEqx('$CurrentCommand');
421 # Add a BASE tage to the header
422 \$Page =~ s!\\</head!\\<base href='$URL'\\>\\</head!ig unless \$Page =~ m!\\<base!;
424 # Print the URL value, or return it as a value
425 $BlockPrint\$Page;
427 GETURLCODE
428 return $Commands;
431 # As files can get rather large (and binary), you might want to use
432 # some more intelligent reading procedure, e.g.,
433 # Direct Perl
434 # # open(URLHANDLE, '/usr/bin/wget --quiet --output-document=- "$URL"|') || die "wget: \$!";
435 # #open(URLHANDLE, '/usr/bin/snarf "$URL" -|') || die "snarf: \$!";
436 # open(URLHANDLE, '/usr/bin/curl "$URL"|') || die "curl: \$!";
437 # my \$text = "";
438 # while(sysread(URLHANDLE,\$text, 1024) > 0)
440 # \$Page .= \$text;
441 # };
442 # close(URLHANDLE) || die "\$!";
443 # However, this doesn't work with the CGIexecute->evaluate() function.
444 # You get an error: 'No child processes at (eval 16) line 15, <file0> line 8.'
446 # You can forget the next two variables, they are only needed when
447 # you don't want to use a regular file system (i.e., with open)
448 # but use some kind of database/RAM image for accessing (generating)
449 # the data.
451 # Name of the environment variable that contains the file contents
452 # when reading directly from Database/RAM. When this environment variable,
453 # $ENV{$CGI_FILE_CONTENTS}, is not false, no real file will be read.
454 $CGI_FILE_CONTENTS = 'CGI_FILE_CONTENTS';
455 # Uncomment the following if you want to force the use of the data access code
456 # $ENV{$CGI_FILE_CONTENTS} = '-'; # Force use of $ENV{$CGI_DATA_ACCESS_CODE}
458 # Name of the environment variable that contains the RAM access perl
459 # code needed to read additional "files", i.e.,
460 # $ENV{$CGI_FILE_CONTENTS} = eval("\@_=('$file_path'); do{$ENV{$CGI_DATA_ACCESS_CODE}}");
461 # When $ENV{$CGI_FILE_CONTENTS} eq '-', this code is executed to generate the data.
462 $CGI_DATA_ACCESS_CODE = 'CGI_DATA_ACCESS_CODE';
464 # You can, of course, fill this yourself, e.g.,
465 # $ENV{$CGI_DATA_ACCESS_CODE} =
466 # 'open(INPUT, "<$_[0]"); while(<INPUT>){print;};close(INPUT);'
469 # DEBUGGING
471 # Suppress error messages, this can be changed for debugging or error-logging
472 #open(STDERR, "/dev/null"); # (comment out for use in debugging)
474 # SPECIAL: Remove Comments, security, etc. if the command line is
475 # '>CGIscriptor.pl -slim >slimCGIscriptor.pl'
476 $TrimDownCGIscriptor = 1 if $ARGV[0] =~ /^\-slim/i;
478 # If CGIscriptor is used from the command line, the command line
479 # arguments are interpreted as the file (1st) and the Query String (rest).
480 # Get the arguments
481 $ENV{'PATH_INFO'} = shift(@ARGV) unless exists($ENV{'PATH_INFO'}) || grep(/\-\-help/i, @ARGV);
482 $ENV{'QUERY_STRING'} = join("&", @ARGV) unless exists($ENV{'QUERY_STRING'});
485 # Handle bail-outs in a user definable way.
486 # Catch Die and replace it with your own function.
487 # Ends with a call to "die $_[0];"
489 sub dieHandler # ($ErrorCode, "Message", @_) -> DEAD
491 my $ErrorCode = shift;
492 my $ErrorMessage = shift;
494 # Place your own reporting functions here
496 # Now, kill everything (default)
497 print STDERR "$ErrorCode: $ErrorMessage\n";
498 die $ErrorMessage;
502 # End of optional user configuration
503 # (note: there is more non-essential user configuration below)
505 if(grep(/\-\-help/i, @ARGV))
507 print << 'ENDOFPREHELPTEXT2';
509 ###############################################################################
511 # Author and Copyright (c):
512 # Rob van Son, © 1995,1996,1997,1998,1999,2000,2001,2002-2012
513 # NKI-AVL Amsterdam
514 # r.v.son@nki.nl
515 # Institute of Phonetic Sciences & IFOTT/ACLS
516 # University of Amsterdam
517 # Email: R.J.J.H.vanSon@gmail.com
518 # Email: R.J.J.H.vanSon@uva.nl
519 # WWW : http://www.fon.hum.uva.nl/rob/
521 # License for use and disclaimers
523 # CGIscriptor merges plain ASCII HTML files transparantly
524 # with CGI variables, in-line PERL code, shell commands,
525 # and executable scripts in other scripting languages.
527 # This program is free software; you can redistribute it and/or
528 # modify it under the terms of the GNU General Public License
529 # as published by the Free Software Foundation; either version 2
530 # of the License, or (at your option) any later version.
532 # This program is distributed in the hope that it will be useful,
533 # but WITHOUT ANY WARRANTY; without even the implied warranty of
534 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
535 # GNU General Public License for more details.
537 # You should have received a copy of the GNU General Public License
538 # along with this program; if not, write to the Free Software
539 # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
542 # Contributors:
543 # Rob van Son (R.J.J.H.vanSon@uva.nl)
544 # Gerd Franke franke@roo.de (designed the <DIV> behaviour)
546 #######################################################
547 ENDOFPREHELPTEXT2
549 #######################################################>>>>>>>>>>Start Remove
551 # You can skip the following code, it is an auto-splice
552 # procedure.
554 # Construct a slimmed down version of CGIscriptor
555 # (i.e., CGIscriptor.pl -slim > slimCGIscriptor.pl)
557 if($TrimDownCGIscriptor)
559 open(CGISCRIPTOR, "<CGIscriptor.pl")
560 || dieHandler(1, "<CGIscriptor.pl not slimmed down: $!\n");
561 my $SKIPtext = 0;
562 my $SKIPComments = 0;
564 while(<CGISCRIPTOR>)
566 my $SKIPline = 0;
568 ++$LineCount;
570 # Start of SKIP text
571 $SKIPtext = 1 if /[\>]{10}Start Remove/;
572 $SKIPComments = 1 if $SKIPtext == 1;
574 # Skip this line?
575 $SKIPline = 1 if $SKIPtext || ($SKIPComments && /^\s*\#/);
577 ++$PrintCount unless $SKIPline;
579 print STDOUT $_ unless $SKIPline;
581 # End of SKIP text ?
582 $SKIPtext = 0 if /[\<]{10}End Remove/;
584 # Ready!
585 print STDERR "\# Printed $PrintCount out of $LineCount lines\n";
586 exit;
589 #######################################################
591 if(grep(/\-\-help/i, @ARGV))
593 print << 'ENDOFHELPTEXT';
595 # HYPE
597 # CGIscriptor merges plain ASCII HTML files transparantly and safely
598 # with CGI variables, in-line PERL code, shell commands, and executable
599 # scripts in many languages (on-line and real-time). It combines the
600 # "ease of use" of HTML files with the versatillity of specialized
601 # scripts and PERL programs. It hides all the specifics and
602 # idiosyncrasies of correct output and CGI coding and naming. Scripts
603 # do not have to be aware of HTML, HTTP, or CGI conventions just as HTML
604 # files can be ignorant of scripts and the associated values. CGIscriptor
605 # complies with the W3C HTML 4.0 recommendations.
606 # In addition to its use as a WWW embeded CGI processor, it can
607 # be used as a command-line document preprocessor (text-filter).
609 # THIS IS HOW IT WORKS
611 # The aim of CGIscriptor is to execute "plain" scripts inside a text file
612 # using any required CGIparameters and environment variables. It
613 # is optimized to transparantly process HTML files inside a WWW server.
614 # The native language is Perl, but many other scripting languages
615 # can be used.
617 # CGIscriptor reads text files from the requested input file (i.e., from
618 # $YOUR_HTML_FILES$PATH_INFO) and writes them to <STDOUT> (i.e., the
619 # client requesting the service) preceded by the obligatory
620 # "Content-type: text/html\n\n" or "Content-type: text/plain\n\n" string
621 # (except for "raw" files which supply their own Content-type message
622 # and only if the SERVER_PROTOCOL supports HTTP, MAIL, or MIME).
624 # When CGIscriptor encounters an embedded script, indicated by an HTML4 tag
626 # <SCRIPT TYPE="text/ssperl" [CGI="$VAR='default value'"] [SRC="ScriptSource"]>
627 # PERL script
628 # </SCRIPT>
630 # or
632 # <SCRIPT TYPE="text/osshell" [CGI="$name='default value'"] [SRC="ScriptSource"]>
633 # OS Shell script
634 # </SCRIPT>
636 # construct (anything between []-brackets is optional, other MIME-types
637 # and scripting languages are supported), the embedded script is removed
638 # and both the contents of the source file (i.e., "do 'ScriptSource'")
639 # AND the script are evaluated as a PERL program (i.e., by eval()),
640 # shell script (i.e., by a "safe" version of `Command`, qx) or an external
641 # interpreter. The output of the eval() function takes the place of the
642 # original <SCRIPT></SCRIPT> construct in the output string. Any CGI
643 # parameters declared by the CGI attribute are available as simple perl
644 # variables, and can subsequently be made available as variables to other
645 # scripting languages (e.g., bash, python, or lisp).
647 # Example: printing "Hello World"
648 # <HTML><HEAD><TITLE>Hello World</TITLE>
649 # <BODY>
650 # <H1><SCRIPT TYPE="text/ssperl">"Hello World"</SCRIPT></H1>
651 # </BODY></HTML>
653 # Save this in a file, hello.html, in the directory you indicated with
654 # $YOUR_HTML_FILES and access http://your_server/SHTML/hello.html
655 # (or to whatever name you use as an alias for CGIscriptor.pl).
656 # This is realy ALL you need to do to get going.
658 # You can use any values that are delivered in CGI-compliant form (i.e.,
659 # the "?name=value" type URL additions) transparently as "$name" variables
660 # in your scripts IFF you have declared them in the CGI attribute of
661 # a META or SCRIPT tag before e.g.:
662 # <META CONTENT="text/ssperl; CGI='$name = `default value`'
663 # [SRC='ScriptSource']">
664 # or
665 # <SCRIPT TYPE="text/ssperl" CGI="$name = 'default value'"
666 # [SRC='ScriptSource']>
667 # After such a 'CGI' attribute, you can use $name as an ordinary PERL variable
668 # (the ScriptSource file is immediately evaluated with "do 'ScriptSource'").
669 # The CGIscriptor script allows you to write ordinary HTML files which will
670 # include dynamic CGI aware (run time) features, such as on-line answers
671 # to specific CGI requests, queries, or the results of calculations.
673 # For example, if you wanted to answer questions of clients, you could write
674 # a Perl program called "Answer.pl" with a function "AnswerQuestion()"
675 # that prints out the answer to requests given as arguments. You then write
676 # an HTML page "Respond.html" containing the following fragment:
678 # <center>
679 # The Answer to your question
680 # <META CONTENT="text/ssperl; CGI='$Question'">
681 # <h3><SCRIPT TYPE="text/ssperl">$Question</SCRIPT></h3>
682 # is
683 # <h3><SCRIPT TYPE="text/ssperl" SRC="./PATH/Answer.pl">
684 # AnswerQuestion($Question);
685 # </SCRIPT></h3>
686 # </center>
687 # <FORM ACTION=Respond.html METHOD=GET>
688 # Next question: <INPUT NAME="Question" TYPE=TEXT SIZE=40><br>
689 # <INPUT TYPE=SUBMIT VALUE="Ask">
690 # </FORM>
692 # The output could look like the following (in HTML-speak):
694 # <CENTER>
695 # The Answer to your question
696 # <h3>What is the capital of the Netherlands?</h3>
697 # is
698 # <h3>Amsterdam</h3>
699 # </CENTER>
700 # <FORM ACTION=Respond.html METHOD=GET>
701 # Next question: <INPUT NAME="Question" TYPE=TEXT SIZE=40><br>
702 # <INPUT TYPE=SUBMIT VALUE="Ask">
704 # Note that the function "Answer.pl" does know nothing about CGI or HTML,
705 # it just prints out answers to arguments. Likewise, the text has no
706 # provisions for scripts or CGI like constructs. Also, it is completely
707 # trivial to extend this "program" to use the "Answer" later in the page
708 # to call up other information or pictures/sounds. The final text never
709 # shows any cue as to what the original "source" looked like, i.e.,
710 # where you store your scripts and how they are called.
712 # There are some extra's. The argument of the files called in a SRC= tag
713 # can access the CGI variables declared in the preceding META tag from
714 # the @ARGV array. Executable files are called as:
715 # `file '$ARGV[0]' ... ` (e.g., `Answer.pl \'$Question\'`;)
716 # The files called from SRC can even be (CGIscriptor) html files which are
717 # processed in-line. Furthermore, the SRC= tag can contain a perl block
718 # that is evaluated. That is,
719 # <META CONTENT="text/ssperl; CGI='$Question' SRC='{$Question}'">
720 # will result in the evaluation of "print do {$Question};" and the VALUE
721 # of $Question will be printed. Note that these "SRC-blocks" can be
722 # preceded and followed by other file names, but only a single block is
723 # allowed in a SRC= tag.
725 # One of the major hassles of dynamic WWW pages is the fact that several
726 # mutually incompatible browsers and platforms must be supported. For example,
727 # the way sound is played automatically is different for Netscape and
728 # Internet Explorer, and for each browser it is different again on
729 # Unix, MacOS, and Windows. Realy dangerous is processing user-supplied
730 # (form-) values to construct email addresses, file names, or database
731 # queries. All Apache WWW-server exploits reported in the media are
732 # based on faulty CGI-scripts that didn't check their user-data properly.
734 # There is no panacee for these problems, but a lot of work and problems
735 # can be saved by allowing easy and transparent control over which
736 # <SCRIPT></SCRIPT> blocks are executed on what CGI-data. CGIscriptor
737 # supplies such a method in the form of a pair of attributes:
738 # IF='...condition..' and UNLESS='...condition...'. When added to a
739 # script tag, the whole block (including the SRC attribute) will be
740 # ignored if the condition is false (IF) or true (UNLESS).
741 # For example, the following block will NOT be evaluated if the value
742 # of the CGI variable FILENAME is NOT a valid filename:
744 # <SCRIPT TYPE='text/ssperl' CGI='$FILENAME'
745 # IF='CGIscriptor::CGIsafeFileName($FILENAME)'>
746 # .....
747 # </SCRIPT>
749 # (the function CGIsafeFileName(String) returns an empty string ("")
750 # if the String argument is not a valid filename).
751 # The UNLESS attribute is the mirror image of IF.
753 # A user manual follows the HTML 4 and security paragraphs below.
755 ##########################################################################
757 # HTML 4 compliance
759 # In general, CGIscriptor.pl complies with the HTML 4 recommendations of
760 # the W3C. This means that any software to manage Web sites will be able
761 # to handle CGIscriptor files, as will web agents.
763 # All script code should be placed between <SCRIPT></SCRIPT> tags, the
764 # script type is indicated with TYPE="mime-type", the LANGUAGE
765 # feature is ignored, and a SRC feature is implemented. All CGI specific
766 # features are delegated to the CGI attribute.
768 # However, the behavior deviates from the W3C recommendations at some
769 # points. Most notably:
770 # 0- The scripts are executed at the server side, invissible to the
771 # client (i.e., the browser)
772 # 1- The mime-types are personal and idiosyncratic, but can be adapted.
773 # 2- Code in the body of a <SCRIPT></SCRIPT> tag-pair is still evaluated
774 # when a SRC feature is present.
775 # 3- The SRC attribute reads a list of files.
776 # 4- The files in a SRC attribute are processed according to file type.
777 # 5- The SRC attribute evaluates inline Perl code.
778 # 6- Processed META, DIV, INS tags are removed from the output
779 # document.
780 # 7- All attributes of the processed META tags, except CONTENT, are ignored
781 # (i.e., deleted from the output).
782 # 8- META tags can be placed ANYWHERE in the document.
783 # 9- Through the SRC feature, META tags can have visible output in the
784 # document.
785 # 10- The CGI attribute that declares CGI parameters, can be used
786 # inside the <SCRIPT> tag.
787 # 11- Use of an extended quote set, i.e., '', "", ``, (), {}, []
788 # and their \-slashed combinations: \'\', \"\", \`\`, \(\),
789 # \{\}, \[\].
790 # 12- IF and UNLESS attributes to <SCRIPT>, <META>, <DIV>, <INS> tags.
791 # 13- <DIV> tags cannot be nested, DIV tags are not
792 # rendered with new-lines.
793 # 14- The XML style <TAG .... /> is recognized and handled correctly.
794 # (i.e., no content is processed)
796 # The reasons for these choices are:
797 # You can still write completely HTML4 compliant documents. CGIscriptor
798 # will not force you to write "deviant" code. However, it allows you to
799 # do so (which is, in fact, just as bad). The prime design principle
800 # was to allow users to include plain Perl code. The code itself should
801 # be "enhancement free". Therefore, extra features were needed to
802 # supply easy access to CGI and Web site components. For security
803 # reasons these have to be declared explicitly. The SRC feature
804 # transparently manages access to external files, especially the safe
805 # use of executable files.
806 # The CGI attribute handles the declarations of external (CGI) variables
807 # in the SCRIPT and META tag's.
808 # EVERYTHING THE CGI ATTRIBUTE AND THE META TAG DO CAN BE DONE INSIDE
809 # A <SCRIPT></SCRIPT> TAG CONSTRUCT.
811 # The reason for the IF, UNLESS, and SRC attributes (and their Perl code
812 # evaluation) were build into the META and SCRIPT tags is part laziness,
813 # part security. The SRC blocks allows more compact documents and easier
814 # debugging. The values of the CGI variables can be immediately screened
815 # for security by IF or UNLESS conditions, and even SRC attributes (e.g.,
816 # email addresses and file names), and a few commands can be called
817 # without having to add another Perl TAG pair. This is especially important
818 # for documents that require the use of other (more restricted) "scripting"
819 # languages and facilities that lag transparent control structures.
821 ##########################################################################
823 # SECURITY
825 # Your WWW site is a few keystrokes away from a few hundred million internet
826 # users. A fair percentage of these users knows more about your computer
827 # than you do. And some of these just might have bad intentions.
829 # To ensure uncompromized operation of your server and platform, several
830 # features are incorporated in CGIscriptor.pl to enhance security.
831 # First of all, you should check the source of this program. No security
832 # measures will help you when you download programs from anonymous sources.
833 # If you want to use THIS file, please make sure that it is uncompromized.
834 # The best way to do this is to contact the source and try to determine
835 # whether s/he is reliable (and accountable).
837 # BE AWARE THAT ANY PROGRAMMER CAN CHANGE THIS PROGRAM IN SUCH A WAY THAT
838 # IT WILL SET THE DOORS TO YOUR SYSTEM WIDE OPEN
840 # I would like to ask any user who finds bugs that could compromise
841 # security to report them to me (and any other bug too,
842 # Email: R.J.J.H.vanSon@uva.nl or ifa@hum.uva.nl).
844 # Security features
846 # 1 Invisibility
847 # The inner workings of the HTML source files are completely hidden
848 # from the client. Only the HTTP header and the ever changing content
849 # of the output distinguish it from the output of a plain, fixed HTML
850 # file. Names, structures, and arguments of the "embedded" scripts
851 # are invisible to the client. Error output is suppressed except
852 # during debugging (user configurable).
854 # 2 Separate directory trees
855 # Directories containing Inline text and script files can reside on
856 # separate trees, distinct from those of the HTTP server. This means
857 # that NEITHER the text files, NOR the script files can be read by
858 # clients other than through CGIscriptor.pl, UNLESS they are
859 # EXPLICITELY made available.
861 # 3 Requests are NEVER "evaluated"
862 # All client supplied values are used as literal values (''-quoted).
863 # Client supplied ''-quotes are ALWAYS removed. Therefore, as long as the
864 # embedded scripts do NOT themselves evaluate these values, clients CANNOT
865 # supply executable commands. Be sure to AVOID scripts like:
867 # <META CONTENT="text/ssperl; CGI='$UserValue'">
868 # <SCRIPT TYPE="text/ssperl">$dir = `ls -1 $UserValue`;</SCRIPT>
870 # These are a recipe for disaster. However, the following quoted
871 # form should be save (but is still not adviced):
873 # <SCRIPT TYPE="text/ssperl">$dir = `ls -1 \'$UserValue\'`;</SCRIPT>
875 # A special function, SAFEqx(), will automatically do exactly this,
876 # e.g., SAFEqx('ls -1 $UserValue') will execute `ls -1 \'$UserValue\'`
877 # with $UserValue interpolated. I recommend to use SAFEqx() instead
878 # of backticks whenever you can. The OS shell scripts inside
880 # <SCRIPT TYPE="text/osshell">ls -1 $UserValue</SCRIPT>
882 # are handeld by SAFEqx and automatically ''-quoted.
884 # 4 Logging of requests
885 # All requests can be logged separate from the Host server. The level of
886 # detail is user configurable: Including or excluding the actual queries.
887 # This allows for the inspection of (im-) proper use.
889 # 5 Access control: Clients
890 # The Remote addresses can be checked against a list of authorized
891 # (i.e., accepted) or non-authorized (i.e., rejected) clients. Both
892 # REMOTE_HOST and REMOTE_ADDR are tested so clients without a proper
893 # HOST name can be (in-) excluded by their IP-address. Client patterns
894 # containing all numbers and dots are considered IP-addresses, all others
895 # domain names. No wild-cards or regexp's are allowed, only partial
896 # addresses.
897 # Matching of names is done from the back to the front (domain first,
898 # i.e., $REMOTE_HOST =~ /\Q$pattern\E$/is), so including ".edu" will
899 # accept or reject all clients from the domain EDU. Matching of
900 # IP-addresses is done from the front to the back (domain first, i.e.,
901 # $REMOTE_ADDR =~ /^\Q$pattern\E/is), so including "128." will (in-)
902 # exclude all clients whose IP-address starts with 128.
903 # There are two special symbols: "-" matches HOSTs with no name and "*"
904 # matches ALL HOSTS/clients.
905 # For those needing more expressional power, lines starting with
906 # "-e" are evaluated by the perl eval() function. E.g.,
907 # '-e $REMOTE_HOST =~ /\.edu$/is;' will accept/reject clients from the
908 # domain '.edu'.
910 # 6 Access control: Files
911 # In principle, CGIscriptor could read ANY file in the directory
912 # tree as discussed in 1. However, for security reasons this is
913 # restricted to text files. It can be made more restricted by entering
914 # a global file pattern (e.g., ".html"). This is done by default.
915 # For each client requesting access, the file pattern(s) can be made
916 # more restrictive than the global pattern by entering client specific
917 # file patterns in the Access Control files (see 5).
918 # For example: if the ACCEPT file contained the lines
919 # * DEMO
920 # .hum.uva.nl LET
921 # 145.18.230.
922 # Then all clients could request paths containing "DEMO" or "demo", e.g.
923 # "/my/demo/file.html" ($PATH_INFO =~ /\Q$pattern\E/), Clients from
924 # *.hum.uva.nl could also request paths containing "LET or "let", e.g.
925 # "/my/let/file.html", and clients from the local cluster
926 # 145.18.230.[0-9]+ could access ALL files.
927 # Again, for those needing more expressional power, lines starting with
928 # "-e" are evaluated. For instance:
929 # '-e $REMOTE_HOST =~ /\.edu$/is && $PATH_INFO =~ m@/DEMO/@is;'
930 # will accept/reject requests for files from the directory "/demo/" from
931 # clients from the domain '.edu'.
933 # 7 Query length limiting
934 # The length of the Query string can be limited. If CONTENT_LENGTH is larger
935 # than this limit, the request is rejected. The combined length of the
936 # Query string and the POST input is checked before any processing is done.
937 # This will prevent clients from overloading the scripts.
938 # The actual, combined, Query Size is accessible as a variable through
939 # $CGI_Content_Length.
941 # 8 Illegal filenames, paths, and protected directories
942 # One of the primary security concerns in handling CGI-scripts is the
943 # use of "funny" characters in the requests that con scripts in executing
944 # malicious commands. Examples are inserting ';', null bytes, or <newline>
945 # characters in URL's and filenames, followed by executable commands. A
946 # special variable $FileAllowedChars stores a string of all allowed
947 # characters. Any request that translates to a filename with a character
948 # OUTSIDE this set will be rejected.
949 # In general, all (readable files) in the DocumentRoot tree are accessible.
950 # This might not be what you want. For instance, your DocumentRoot directory
951 # might be the working directory of a CVS project and contain sensitive
952 # information (e.g., the password to get to the repository). You can block
953 # access to these subdirectories by adding the corresponding patterns to
954 # the $BlockPathAccess variable. For instance, $BlockPathAccess = '/CVS/'
955 # will block any request that contains '/CVS/' or:
956 # die if $BlockPathAccess && $ENV{'PATH_INFO'} =~ m@$BlockPathAccess@;
958 # 9 The execution of code blocks can be controlled in a transparent way
959 # by adding IF or UNLESS conditions in the tags themselves. That is,
960 # a simple check of the validity of filenames or email addresses can
961 # be done before any code is executed.
963 ###############################################################################
965 # USER MANUAL (sort of)
967 # CGIscriptor removes embedded scripts, indicated by an HTML 4 type
968 # <SCRIPT TYPE='text/ssperl'> </SCRIPT> or <SCRIPT TYPE='text/osshell'>
969 # </SCRIPT> constructs. CGIscriptor also recognizes XML-type
970 # <SCRIPT TYPE='text/ssperl'/> constructs. These are usefull when
971 # the necessary code is already available in the TAG itself (e.g.,
972 # using external files). The contents of the directive are executed by
973 # the PERL eval() and `` functions (in a separate name space). The
974 # result of the eval() function replaces the <SCRIPT> </SCRIPT> construct
975 # in the output file. You can use the values that are delivered in
976 # CGI-compliant form (i.e., the "?name=value&.." type URL additions)
977 # transparently as "$name" variables in your directives after they are
978 # defined in a <META> or <SCRIPT> tag.
979 # If you define the variable "$CGIscriptorResults" in a CGI attribute, all
980 # subsequent <SCRIPT> and <META> results (including the defining
981 # tag) will also be pushed onto a stack: @CGIscriptorResults. This list
982 # behaves like any other, ordinary list and can be manipulated.
984 # Both GET and POST requests are accepted. These two methods are treated
985 # equal. Variables, i.e., those values that are determined when a file is
986 # processed, are indicated in the CGI attribute by $<name> or $<name>=<default>
987 # in which <name> is the name of the variable and <default> is the value
988 # used when there is NO current CGI value for <name> (you can use
989 # white-spaces in $<name>=<default> but really DO make sure that the
990 # default value is followed by white space or is quoted). Names can contain
991 # any alphanumeric characters and _ (i.e., names match /[\w]+/).
992 # If the Content-type: is 'multipart/*', the input is treated as a
993 # MIME multipart message and automatically delimited. CGI variables get
994 # the "raw" (i.e., undecoded) body of the corresponding message part.
996 # Variables can be CGI variables, i.e., those from the QUERY_STRING,
997 # environment variables, e.g., REMOTE_USER, REMOTE_HOST, or REMOTE_ADDR,
998 # or predefined values, e.g., CGI_Decoded_QS (The complete, decoded,
999 # query string), CGI_Content_Length (the length of the decoded query
1000 # string), CGI_Year, CGI_Month, CGI_Time, and CGI_Hour (the current
1001 # date and time).
1003 # All these are available when defined in a CGI attribute. All environment
1004 # variables are accessible as $ENV{'name'}. So, to access the REMOTE_HOST
1005 # and the REMOTE_USER, use, e.g.:
1007 # <SCRIPT TYPE='text/ssperl'>
1008 # ($ENV{'REMOTE_HOST'}||"-")." $ENV{'REMOTE_USER'}"
1009 # </SCRIPT>
1011 # (This will print a "-" if REMOTE_HOST is not known)
1012 # Another way to do this is:
1014 # <META CONTENT="text/ssperl; CGI='$REMOTE_HOST = - $REMOTE_USER'">
1015 # <SCRIPT TYPE='text/ssperl'>"$REMOTE_HOST $REMOTE_USER"</SCRIPT>
1016 # or
1017 # <META CONTENT='text/ssperl; CGI="$REMOTE_HOST = - $REMOTE_USER"
1018 # SRC={"$REMOTE_HOST $REMOTE_USER\n"}'>
1020 # This is possible because ALL environment variables are available as
1021 # CGI variables. The environment variables take precedence over CGI
1022 # names in case of a "name clash". For instance:
1023 # <META CONTENT="text/ssperl; CGI='$HOME' SRC={$HOME}">
1024 # Will print the current HOME directory (environment) irrespective whether
1025 # there is a CGI variable from the query
1026 # (e.g., Where do you live? <INPUT TYPE="TEXT" NAME="HOME">)
1027 # THIS IS A SECURITY FEATURE. It prevents clients from changing
1028 # the values of defined environment variables (e.g., by supplying
1029 # a bogus $REMOTE_ADDR). Although $ENV{} is not changed by the META tags,
1030 # it would make the use of declared variables insecure. You can still
1031 # access CGI variables after a name clash with
1032 # CGIscriptor::CGIparseValue(<name>).
1034 # Some CGI variables are present several times in the query string
1035 # (e.g., from multiple selections). These should be defined as
1036 # @VARIABLENAME=default in the CGI attribute. The list @VARIABLENAME
1037 # will contain ALL VARIABLENAME values from the query, or a single
1038 # default value. If there is an ENVIRONMENT variable of the
1039 # same name, it will be used instead of the default AND the query
1040 # values. The corresponding function is
1041 # CGIscriptor::CGIparseValueList(<name>)
1043 # CGI variables collected in a @VARIABLENAME list are unordered.
1044 # When more structured variables are needed, a hash table can be used.
1045 # A variable defined as %VARIABLE=default will collect all
1046 # CGI-parameters whose name start with 'VARIABLE' in a hash table with
1047 # the remainder of the name as a key. For instance, %PERSON will
1048 # collect PERSONname='John Doe', PERSONbirthdate='01 Jan 00', and
1049 # PERSONspouse='Alice' into a hash table %PERSON such that $PERSON{'spouse'}
1050 # equals 'Alice'. Any default value or environment value will be stored
1051 # under the "" key. If there is an ENVIRONMENT variable of the same name,
1052 # it will be used instead of the default AND the query values. The
1053 # corresponding function is CGIscriptor::CGIparseValueHash(<name>)
1055 # This method of first declaring your environment and CGI variables
1056 # before being able to use them in the scripts might seem somewhat
1057 # clumsy, but it protects you from inadvertedly printing out the values of
1058 # system environment variables when their names coincide with those used
1059 # in the CGI forms. It also prevents "clients" from supplying CGI
1060 # parameter values for your private variables.
1061 # THIS IS A SECURITY FEATURE!
1064 # NON-HTML CONTENT TYPES
1066 # Normally, CGIscriptor prints the standard "Content-type: text/html\n\n"
1067 # message before anything is printed. This has been extended to include
1068 # plain text (.txt) files, for which the Content-type (MIME type)
1069 # 'text/plain' is printed. In all other respects, text files are treated
1070 # as HTML files (this can be switched off by removing '.txt' from the
1071 # $FilePattern variable) . When the content type should be something else,
1072 # e.g., with multipart files, use the $RawFilePattern (.xmr, see also next
1073 # item). CGIscriptor will not print a Content-type message for this file
1074 # type (which must supply its OWN Content-type message). Raw files must
1075 # still conform to the <SCRIPT></SCRIPT> and <META> tag specifications.
1078 # NON-HTML FILES
1080 # CGIscriptor is intended to process HTML and text files only. You can
1081 # create documents of any mime-type on-the-fly using "raw" text files,
1082 # e.g., with the .xmr extension. However, CGIscriptor will not process
1083 # binary files of any type, e.g., pictures or sounds. Given the sheer
1084 # number of formats, I do not have any intention to do so. However,
1085 # an escape route has been provided. You can construct a genuine raw
1086 # (.xmr) text file that contains the perl code to service any file type
1087 # you want. If the global $BinaryMapFile variable contains the path to
1088 # this file (e.g., /BinaryMapFile.xmr), this file will be called
1089 # whenever an unsupported (non-HTML) file type is requested. The path
1090 # to the requested binary file is stored in $ENV('CGI_BINARY_FILE')
1091 # and can be used like any other CGI-variable. Servicing binary files
1092 # then becomes supplying the correct Content-type (e.g., print
1093 # "Content-type: image/jpeg\n\n";) and reading the file and writing it
1094 # to STDOUT (e.g., using sysread() and syswrite()).
1097 # THE META TAG
1099 # All attributes of a META tag are ignored, except the
1100 # CONTENT='text/ssperl; CGI=" ... " [SRC=" ... "]' attribute. The string
1101 # inside the quotes following the CONTENT= indication (white-space is
1102 # ignored, "" '' `` (){}[]-quote pairs are allowed, plus their \ versions)
1103 # MUST start with any of the CGIscriptor mime-types (e.g.: text/ssperl or
1104 # text/osshell) and a comma or semicolon.
1105 # The quoted string following CGI= contains a white-space separated list
1106 # of declarations of the CGI (and Environment) values and default values
1107 # used when no CGI values are supplied by the query string.
1109 # If the default value is a longer string containing special characters,
1110 # possibly spanning several lines, the string must be enclosed in quotes.
1111 # You may use any pair of quotes or brackets from the list '', "", ``, (),
1112 # [], or {} to distinguish default values (or preceded by \, e.g., \(...\)
1113 # is different from (...)). The outermost pair will always be used and any
1114 # other quotes inside the string are considered to be part of the string
1115 # value, e.g.,
1117 # $Value = {['this'
1118 # "and" (this)]}
1119 # will result in $Value getting the default value: ['this'
1120 # "and" (this)]
1121 # (NOTE that the newline is part of the default value!).
1123 # Internally, for defining and initializing CGI (ENV) values, the META
1124 # and SCRIPT tags use the functions "defineCGIvariable($name, $default)"
1125 # (scalars) and "defineCGIvariableList($name, $default)" (lists).
1126 # These functions can be used inside scripts as
1127 # "CGIscriptor::defineCGIvariable($name, $default)" and
1128 # "CGIscriptor::defineCGIvariableList($name, $default)".
1129 # "CGIscriptor::defineCGIvariableHash($name, $default)".
1131 # The CGI attribute will be processed exactly identical when used inside
1132 # the <SCRIPT> tag. However, this use is not according to the
1133 # HTML 4.0 specifications of the W3C.
1136 # THE DIV/INS TAGS
1138 # There is a problem when constructing html files containing
1139 # server-side perl scripts with standard HTML tools. These
1140 # tools will refuse to process any text between <SCRIPT></SCRIPT>
1141 # tags. This is quite annoying when you want to use large
1142 # HTML templates where you will fill in values.
1144 # For this purpose, CGIscriptor will read the neutral
1145 # <DIV CLASS="ssperl" ID="varname"></DIV> or
1146 # <INS CLASS="ssperl" ID="varname"></INS>
1147 # tag (in Cascading Style Sheet manner) Note that
1148 # "varname" has NO '$' before it, it is a bare name.
1149 # Any text between these <DIV ...></DIV> or
1150 # <INS ...></INS>tags will be assigned to '$varname'
1151 # as is (e.g., as a literal).
1152 # No processing or interpolation will be performed.
1153 # There is also NO nesting possible. Do NOT nest a
1154 # </DIV> inside a <DIV></DIV>! Moreover, neither INS nor
1155 # DIV tags do ensure a block structure in the final
1156 # rendering (i.e., no empty lines).
1158 # Note that <DIV CLASS="ssperl" ID="varname"/>
1159 # is handled the XML way. No content is processed,
1160 # but varname is defined, and any SRC directives are
1161 # processed.
1163 # You can use $varname like any other variable name.
1164 # However, $varname is NOT a CGI variable and will be
1165 # completely internal to your script. There is NO
1166 # interaction between $varname and the outside world.
1168 # To interpolate a DIV derived text, you can use:
1169 # $varname =~ s/([\]])/\\\1/g; # Mark ']'-quotes
1170 # $varname = eval("qq[$varname]"); # Interpolate all values
1172 # The DIV tags will process IF, UNLESS, CGI and
1173 # SRC attributes. The SRC files will be pre-pended to the
1174 # body text of the tag. SRC blocks are NOT executed.
1176 # CONDITIONAL PROCESSING: THE 'IF' AND 'UNLESS' ATTRIBUTES
1178 # It is often necessary to include code-blocks that should be executed
1179 # conditionally, e.g., only for certain browsers or operating system.
1180 # Furthermore, quite often sanity and security checks are necessary
1181 # before user (form) data can be processed, e.g., with respect to
1182 # email addresses and filenames.
1184 # Checks added to the code are often difficult to find, interpret or
1185 # maintain and in general mess up the code flow. This kind of confussion
1186 # is dangerous.
1187 # Also, for many of the supported "foreign" scripting languages, adding
1188 # these checks is cumbersome or even impossible.
1190 # As a uniform method for asserting the correctness of "context", two
1191 # attributes are added to all supported tags: IF and UNLESS.
1192 # They both evaluate their value and block execution when the
1193 # result is <FALSE> (IF) or <TRUE> (UNLESS) in Perl, e.g.,
1194 # UNLESS='$NUMBER \> 100;' blocks execution if $NUMBER <= 100. Note that
1195 # the backslash in the '\>' is removed and only used to differentiate
1196 # this conditional '>' from the tag-closing '>'. For symmetry, the
1197 # backslash in '\<' is also removed. Inside these conditionals,
1198 # ~/ and ./ are expanded to their respective directory root paths.
1200 # For example, the following tag will be ignored when the filename is
1201 # invalid:
1203 # <SCRIPT TYPE='text/ssperl' CGI='$FILENAME'
1204 # IF='CGIscriptor::CGIsafeFileName($FILENAME);'>
1205 # ...
1206 # </SCRIPT>
1208 # The IF and UNLESS values must be quoted. The same quotes are supported
1209 # as with the other attributes. The SRC attribute is ignored when IF and
1210 # UNLESS block execution.
1212 # NOTE: 'IF' and 'UNLESS' always evaluate perl code.
1215 # THE MAGIC SOURCE ATTRIBUTE (SRC=)
1217 # The SRC attribute inside tags accepts a list of filenames and URL's
1218 # separated by "," comma's (or ";" semicolons).
1219 # ALL the variable values defined in the CGI attribute are available
1220 # in @ARGV as if the file or block was executed from the command line,
1221 # in the exact order in which they were declared in the preceding CGI
1222 # attribute.
1224 # First, a SRC={}-block will be evaluated as if the code inside the
1225 # block was part of a <SCRIPT></SCRIPT> construct, i.e.,
1226 # "print do { code };'';" or `code` (i.e., SAFEqx('code)).
1227 # Only a single block is evaluated. Note that this is processed less
1228 # efficiently than <SCRIPT> </SCRIPT> blocks. Type of evaluation
1229 # depends on the content-type: Perl for text/ssperl and OS shell for
1230 # text/osshell. For other mime types (scripting languages), anything in
1231 # the source block is put in front of the code block "inside" the tag.
1233 # Second, executable files (i.e., -x filename != 0) are evaluated as:
1234 # print `filename \'$ARGV[0]\' \'$ARGV[1]\' ...`
1235 # That is, you can actually call executables savely from the SRC tag.
1237 # Third, text files that match the file pattern, used by CGIscriptor to
1238 # check whether files should be processed ($FilePattern), are
1239 # processed in-line (i.e., recursively) by CGIscriptor as if the code
1240 # was inserted in the original source file. Recursions, i.e., calling
1241 # a file inside itself, are blocked. If you need them, you have to code
1242 # them explicitely using "main::ProcessFile($file_path)".
1244 # Fourth, Perl text files (i.e., -T filename != 0) are evaluated as:
1245 # "do FileName;'';".
1247 # Last, URL's (i.e., starting with 'HTTP://', 'FTP://', 'GOPHER://',
1248 # 'TELNET://', 'WHOIS://' etc.) are loaded
1249 # and printed. The loading and handling of <BASE> and document header
1250 # is done by a command generated by main::GET_URL($URL [, 0]). You can enter your
1251 # own code (default is curl, wget, or snarf and some post-processing to add a <BASE> tag).
1253 # There are two pseudo-file names: PREFIX and POSTFIX. These implement
1254 # a switch from prefixing the SRC code/files (PREFIX, default) before the
1255 # content of the tag to appending the code after the content of the tag
1256 # (POSTFIX). The switches are done in the order in which the PREFIX and
1257 # POSTFIX labels are encountered. You can mix PREFIX and POSTFIX labels
1258 # in any order with the SRC files. Note that the ORDER of file execution
1259 # is determined for prefixed and postfixed files seperately.
1261 # File paths can be preceded by the URL protocol prefix "file://". This
1262 # is simply STRIPPED from the name.
1264 # Example:
1265 # The request
1266 # "http://cgi-bin/Action_Forms.pl/Statistics/Sign_Test.html?positive=8&negative=22
1267 # will result in printing "${SS_PUB}/Statistics/Sign_Test.html"
1268 # With QUERY_STRING = "positive=8&negative=22"
1270 # on encountering the lines:
1271 # <META CONTENT="text/osshell; CGI='$positive=11 $negative=3'">
1272 # <b><SCRIPT LANGUAGE=PERL TYPE="text/ssperl" SRC="./Statistics/SignTest.pl">
1273 # </SCRIPT></b><p>"
1275 # This line will be processed as:
1276 # "<b>`${SS_SCRIPT}/Statistics/SignTest.pl '8' '22'`</b><p>"
1278 # In which "${SS_SCRIPT}/Statistics/SignTest.pl" is an executable script,
1279 # This line will end up printed as:
1280 # "<b>p <= 0.0161</b><p>"
1282 # Note that the META tag itself will never be printed, and is invisible to
1283 # the outside world.
1285 # The SRC files in a DIV or INS tag will be added (pre-pended) to the body
1286 # of the <DIV></DIV> tag. Blocks are NOT executed! If you do not
1287 # need any content, you can use the <DIV...../> format.
1290 # THE CGISCRIPTOR ROOT DIRECTORIES ~/ AND ./
1292 # Inside <SCRIPT></SCRIPT> tags, filepaths starting
1293 # with "~/" are replaced by "$YOUR_HTML_FILES/", this way files in the
1294 # public directories can be accessed without direct reference to the
1295 # actual paths. Filepaths starting with "./" are replaced by
1296 # "$YOUR_SCRIPTS/" and this should only be used for scripts.
1298 # Note: this replacement can seriously affect Perl scripts. Watch
1299 # out for constructs like $a =~ s/aap\./noot./g, use
1300 # $a =~ s@aap\.@noot.@g instead.
1302 # CGIscriptor.pl will assign the values of $SS_PUB and $SS_SCRIPT
1303 # (i.e., $YOUR_HTML_FILES and $YOUR_SCRIPTS) to the environment variables
1304 # $SS_PUB and $SS_SCRIPT. These can be accessed by the scripts that are
1305 # executed.
1306 # Values not preceded by $, ~/, or ./ are used as literals
1309 # OS SHELL SCRIPT EVALUATION (CONTENT-TYPE=TEXT/OSSHELL)
1311 # OS scripts are executed by a "safe" version of the `` operator (i.e.,
1312 # SAFEqx(), see also below) and any output is printed. CGIscriptor will
1313 # interpolate the script and replace all user-supplied CGI-variables by
1314 # their ''-quoted values (actually, all variables defined in CGI attributes
1315 # are quoted). Other Perl variables are interpolated in a simple fasion,
1316 # i.e., $scalar by their value, @list by join(' ', @list), and %hash by
1317 # their name=value pairs. Complex references, e.g., @$variable, are all
1318 # evaluated in a scalar context. Quotes should be used with care.
1319 # NOTE: the results of the shell script evaluation will appear in the
1320 # @CGIscriptorResults stack just as any other result.
1321 # All occurrences of $@% that should NOT be interpolated must be
1322 # preceeded by a "\". Interpolation can be switched off completely by
1323 # setting $CGIscriptor::NoShellScriptInterpolation = 1
1324 # (set to 0 or undef to switch interpolation on again)
1325 # i.e.,
1326 # <SCRIPT TYPE="text/ssperl">
1327 # $CGIscriptor::NoShellScriptInterpolation = 1;
1328 # </SCRIPT>
1331 # RUN TIME TRANSLATION OF INPUT FILES
1333 # Allows general and global conversions of files using Regular Expressions.
1334 # Very handy (but costly) to rewrite legacy pages to a new format.
1335 # Select files to use it on with
1336 # my $TranslationPaths = 'filepattern';
1337 # This is costly. For efficiency, define:
1338 # $TranslationPaths = ''; when not using translations.
1339 # Accepts general regular expressions: [$pattern, $replacement]
1341 # Define:
1342 # my $TranslationPaths = 'filepattern'; # Pattern matching PATH_INFO
1344 # push(@TranslationTable, ['pattern', 'replacement']);
1345 # e.g. (for Ruby Rails):
1346 # push(@TranslationTable, ['<%=', '<SCRIPT TYPE="text/ssruby">']);
1347 # push(@TranslationTable, ['%>', '</SCRIPT>']);
1349 # Runs:
1350 # my $currentRegExp;
1351 # foreach $currentRegExp (@TranslationTable)
1353 # my ($pattern, $replacement) = @$currentRegExp;
1354 # $$text =~ s!$pattern!$replacement!msg;
1355 # };
1358 # EVALUATION OF OTHER SCRIPTING LANGUAGES
1360 # Adding a MIME-type and an interpreter command to
1361 # %ScriptingLanguages automatically will catch any other
1362 # scripting language in the standard
1363 # <SCRIPT TYPE="[mime]"></SCRIPT> manner.
1364 # E.g., adding: $ScriptingLanguages{'text/sspython'} = 'python';
1365 # will actually execute the folowing code in an HTML page
1366 # (ignore 'REMOTE_HOST' for the moment):
1367 # <SCRIPT TYPE="text/sspython">
1368 # # A Python script
1369 # x = ["A","real","python","script","Hello","World","and", REMOTE_HOST]
1370 # print x[4:8] # Prints the list ["Hello","World","and", REMOTE_HOST]
1371 # </SCRIPT>
1373 # The script code is NOT interpolated by perl, EXCEPT for those
1374 # interpreters that cannot handle variables themselves.
1375 # Currently, several interpreters are pre-installed:
1377 # Perl test - "text/testperl" => 'perl',
1378 # Python - "text/sspython" => 'python',
1379 # Ruby - "text/ssruby" => 'ruby',
1380 # Tcl - "text/sstcl" => 'tcl',
1381 # Awk - "text/ssawk" => 'awk -f-',
1382 # Gnu Lisp - "text/sslisp" => 'rep | tail +5 '.
1383 # "| egrep -v '> |^rep. |^nil\\\$'",
1384 # XLispstat - "text/xlispstat" => 'xlispstat | tail +7 '.
1385 # "| egrep -v '> \\\$|^NIL'",
1386 # Gnu Prolog- "text/ssprolog" => 'gprolog',
1387 # M4 macro's- "text/ssm4" => 'm4',
1388 # Born shell- "text/sh" => 'sh',
1389 # Bash - "text/bash" => 'bash',
1390 # C-shell - "text/csh" => 'csh',
1391 # Korn shell- "text/ksh" => 'ksh',
1392 # Praat - "text/sspraat" => "praat - | sed 's/Praat > //g'",
1393 # R - "text/ssr" => "R --vanilla --slave | sed 's/^[\[0-9\]*] //g'",
1394 # REBOL - "text/ssrebol" =>
1395 # "rebol --quiet|egrep -v '^[> ]* == '|sed 's/^\s*\[> \]* //g'",
1396 # PostgreSQL- "text/postgresql" => 'psql 2>/dev/null',
1397 # (psql)
1399 # Note that the "value" of $ScriptingLanguages{mime} must be a command
1400 # that reads Standard Input and writes to standard output. Any extra
1401 # output of interactive interpreters (banners, echo's, prompts)
1402 # should be removed by piping the output through 'tail', 'grep',
1403 # 'sed', or even 'awk' or 'perl'.
1405 # For access to CGI variables there is a special hashtable:
1406 # %ScriptingCGIvariables.
1407 # CGI variables can be accessed in three ways.
1408 # 1. If the mime type is not present in %ScriptingCGIvariables,
1409 # nothing is done and the script itself should parse the relevant
1410 # environment variables.
1411 # 2. If the mime type IS present in %ScriptingCGIvariables, but it's
1412 # value is empty, e.g., $ScriptingCGIvariables{"text/sspraat"} = '';,
1413 # the script text is interpolated by perl. That is, all $var, @array,
1414 # %hash, and \-slashes are replaced by their respective values.
1415 # 3. In all other cases, the CGI and environment variables are added
1416 # in front of the script according to the format stored in
1417 # %ScriptingCGIvariables. That is, the following (pseudo-)code is
1418 # executed for each CGI- or Environment variable defined in the CGI-tag:
1419 # printf(INTERPRETER, $ScriptingCGIvariables{$mime}, $CGI_NAME, $CGI_VALUE);
1421 # For instance, "text/testperl" => '$%s = "%s";' defines variable
1422 # definitions for Perl, and "text/sspython" => '%s = "%s"' for Python
1423 # (note that these definitions are not save, the real ones contain '-quotes).
1425 # THIS WILL NOT WORK FOR @VARIABLES, the (empty) $VARIABLES will be used
1426 # instead.
1428 # The $CGI_VALUE parameters are "shrubed" of all control characters
1429 # and quotes (by &shrubCGIparameter($CGI_VALUE)) for the options 2 and 3.
1430 # Control characters are replaced by \0<octal ascii value> (the exception
1431 # is \015, the newline, which is replaced by \n) and quotes
1432 # and backslashes by their HTML character
1433 # value (' -> &#39; ` -> &#96; " -> &quot; \ -> &#92; & -> &amper;).
1434 # For example:
1435 # if a client would supply the string value (in standard perl, e.g.,
1436 # \n means <newline>)
1437 # "/dev/null';\nrm -rf *;\necho '"
1438 # it would be processed as
1439 # '/dev/null&#39;;\nrm -rf *;\necho &#39;'
1440 # (e.g., sh or bash would process the latter more according to your
1441 # intentions).
1442 # If your intepreter requires different protection measures, you will
1443 # have to supply these in %main::SHRUBcharacterTR (string => translation),
1444 # e.g., $SHRUBcharacterTR{"\'"} = "&#39;";
1446 # Currently, the following definitions are used:
1447 # %ScriptingCGIvariables = (
1448 # "text/testperl" => "\$\%s = '\%s';", # Perl $VAR = 'value' (for testing)
1449 # "text/sspython" => "\%s = '\%s'", # Python VAR = 'value'
1450 # "text/ssruby" => '@%s = "%s"', # Ruby @VAR = "value"
1451 # "text/sstcl" => 'set %s "%s"', # TCL set VAR "value"
1452 # "text/ssawk" => '%s = "%s";', # Awk VAR = "value";
1453 # "text/sslisp" => '(setq %s "%s")', # Gnu lisp (rep) (setq VAR "value")
1454 # "text/xlispstat" => '(setq %s "%s")', # Xlispstat (setq VAR "value")
1455 # "text/ssprolog" => '', # Gnu prolog (interpolated)
1456 # "text/ssm4" => "define(`\%s', `\%s')", # M4 macro's define(`VAR', `value')
1457 # "text/sh" => "\%s='\%s';", # Born shell VAR='value';
1458 # "text/bash" => "\%s='\%s';", # Born again shell VAR='value';
1459 # "text/csh" => "\$\%s = '\%s';", # C shell $VAR = 'value';
1460 # "text/ksh" => "\$\%s = '\%s';", # Korn shell $VAR = 'value';
1461 # "text/sspraat" => '', # Praat (interpolation)
1462 # "text/ssr" => '%s <- "%s";', # R VAR <- "value";
1463 # "text/ssrebol" => '%s: copy "%s"', # REBOL VAR: copy "value"
1464 # "text/postgresql" => '', # PostgreSQL (interpolation)
1465 # "" => ""
1466 # );
1468 # Four tables allow fine-tuning of interpreter with code that should be
1469 # added before and after each code block:
1471 # Code added before each script block
1472 # %ScriptingPrefix = (
1473 # "text/testperl" => "\# Prefix Code;", # Perl script testing
1474 # "text/ssm4" => 'divert(0)' # M4 macro's (open STDOUT)
1475 # );
1476 # Code added at the end of each script block
1477 # %ScriptingPostfix = (
1478 # "text/testperl" => "\# Postfix Code;", # Perl script testing
1479 # "text/ssm4" => 'divert(-1)' # M4 macro's (block STDOUT)
1480 # );
1481 # Initialization code, inserted directly after opening (NEVER interpolated)
1482 # %ScriptingInitialization = (
1483 # "text/testperl" => "\# Initialization Code;", # Perl script testing
1484 # "text/ssawk" => 'BEGIN {', # Server Side awk scripts
1485 # "text/sslisp" => '(prog1 nil ', # Lisp (rep)
1486 # "text/xlispstat" => '(prog1 nil ', # xlispstat
1487 # "text/ssm4" => 'divert(-1)' # M4 macro's (block STDOUT)
1488 # );
1489 # Cleanup code, inserted before closing (NEVER interpolated)
1490 # %ScriptingCleanup = (
1491 # "text/testperl" => "\# Cleanup Code;", # Perl script testing
1492 # "text/sspraat" => 'Quit',
1493 # "text/ssawk" => '};', # Server Side awk scripts
1494 # "text/sslisp" => '(princ "\n" standard-output)).' # Closing print to rep
1495 # "text/xlispstat" => '(print "" *standard-output*)).' # Closing print to xlispstat
1496 # "text/postgresql" => '\q',
1497 # );
1500 # The SRC attribute is NOT magical for these interpreters. In short,
1501 # all code inside a source file or {} block is written verbattim
1502 # to the interpreter. No (pre-)processing or executional magic is done.
1504 # A serious shortcomming of the described mechanism for handling other
1505 # (scripting) languages, with respect to standard perl scripts
1506 # (i.e., 'text/ssperl'), is that the code is only executed when
1507 # the pipe to the interpreter is closed. So the pipe has to be
1508 # closed at the end of each block. This means that the state of the
1509 # interpreter (e.g., all variable values) is lost after the closing of
1510 # the next </SCRIPT> tag. The standard 'text/ssperl' scripts retain
1511 # all values and definitions.
1513 # APPLICATION MIME TYPES
1515 # To ease some important auxilliary functions from within the
1516 # html pages I have added them as MIME types. This uses
1517 # the mechanism that is also used for the evaluation of
1518 # other scripting languages, with interpolation of CGI
1519 # parameters (and perl-variables). Actually, these are
1520 # defined exactly like any other "scripting language".
1522 # text/ssdisplay: display some (HTML) text with interpolated
1523 # variables (uses `cat`).
1524 # text/sslogfile: write (append) the interpolated block to the file
1525 # mentioned on the first, non-empty line
1526 # (the filename can be preceded by 'File: ',
1527 # note the space after the ':',
1528 # uses `awk .... >> <filename>`).
1529 # text/ssmailto: send email directly from within the script block.
1530 # The first line of the body must contain
1531 # To:Name@Valid.Email.Address
1532 # (note: NO space between 'To:' and the email adres)
1533 # For other options see the mailto man pages.
1534 # It works by directly sending the (interpolated)
1535 # content of the text block to a pipe into the
1536 # Linux program 'mailto'.
1538 # In these script blocks, all Perl variables will be
1539 # replaced by their values. All CGI variables are cleaned before
1540 # they are used. These CGI variables must be redefined with a
1541 # CGI attribute to restore their original values.
1542 # In general, this will be more secure than constructing
1543 # e.g., your own email command lines. For instance, Mailto will
1544 # not execute any odd (forged) email addres, but just stops
1545 # when the email address is invalid and awk will construct
1546 # any filename you give it (e.g. '<File;rm\\\040-f' would end up
1547 # as a "valid" UNIX filename). Note that it will also gladly
1548 # store this file anywhere (/../../../etc/passwd will work!).
1549 # Use the CGIscriptor::CGIsafeFileName() function to clean the
1550 # filename.
1552 # SHELL SCRIPT PIPING
1554 # If a shell script starts with the UNIX style "#! <shell command> \n"
1555 # line, the rest of the shell script is piped into the indicated command,
1556 # i.e.,
1557 # open(COMMAND, "| command");print COMMAND $RestOfScript;
1559 # In many ways this is equivalent to the MIME-type profiling for
1560 # evaluating other scripting languages as discussed above. The
1561 # difference breaks down to convenience. Shell script piping is a
1562 # "raw" implementation. It allows you to control all aspects of
1563 # execution. Using the MIME-type profiling is easier, but has a
1564 # lot of defaults built in that might get in the way. Another
1565 # difference is that shell script piping uses the SAFEqx() function,
1566 # and MIME-type profiling does not.
1568 # Execution of shell scripts is under the control of the Perl Script blocks
1569 # in the document. The MIME-type triggered execution of <SCRIPT></SCRIPT>
1570 # blocks can be simulated easily. You can switch to a different shell,
1571 # e.g. tcl, completely by executing the following Perl commands inside
1572 # your document:
1574 # <SCRIPT TYPE="text/ssperl">
1575 # $main::ShellScriptContentType = "text/ssTcl"; # Yes, you can do this
1576 # CGIscriptor::RedirectShellScript('/usr/bin/tcl'); # Pipe to Tcl
1577 # $CGIscriptor::NoShellScriptInterpolation = 1;
1578 # </SCRIPT>
1580 # After this script is executed, CGIscriptor will parse scripts of
1581 # TYPE="text/ssTcl" and pipe their contents into '|/usr/bin/tcl'
1582 # WITHOUT interpolation (i.e., NO substitution of Perl variables).
1583 # The crucial function is :
1584 # CGIscriptor::RedirectShellScript('/usr/bin/tcl')
1585 # After executing this function, all shell scripts AND all
1586 # calls to SAFEqx()) are piped into '|/usr/bin/tcl'. If the argument
1587 # of RedirectShellScript is empty, e.g., '', the original (default)
1588 # value is reset.
1590 # The standard output, STDOUT, of any pipe is send to the client.
1591 # Currently, you should be carefull with quotes in such a piped script.
1592 # The results of a pipe is NOT put on the @CGIscriptorResults stack.
1593 # As a result, you do not have access to the output of any piped (#!)
1594 # process! If you want such access, execute
1595 # <SCRIPT TYPE="text/osshell">echo "script"|command</SCRIPT>
1596 # or
1597 # <SCRIPT TYPE="text/ssperl">
1598 # $resultvar = SAFEqx('echo "script"|command');
1599 # </SCRIPT>.
1601 # Safety is never complete. Although SAFEqx() prevents some of the
1602 # most obvious forms of attacks and security slips, it cannot prevent
1603 # them all. Especially, complex combinations of quotes and intricate
1604 # variable references cannot be handled safely by SAFEqx. So be on
1605 # guard.
1608 # PERL CODE EVALUATION (CONTENT-TYPE=TEXT/SSPERL)
1610 # All PERL scripts are evaluated inside a PERL package. This package
1611 # has a separate name space. This isolated name space protects the
1612 # CGIscriptor.pl program against interference from user code. However,
1613 # some variables, e.g., $_, are global and cannot be protected. You are
1614 # advised NOT to use such global variable names. You CAN write
1615 # directives that directly access the variables in the main program.
1616 # You do so at your own risk (there is definitely enough rope available
1617 # to hang yourself). The behavior of CGIscriptor becomes undefined if
1618 # you change its private variables during run time. The PERL code
1619 # directives are used as in:
1620 # $Result = eval($directive); print $Result;'';
1621 # ($directive contains all text between <SCRIPT></SCRIPT>).
1622 # That is, the <directive> is treated as ''-quoted string and
1623 # the result is treated as a scalar. To prevent the VALUE of the code
1624 # block from appearing on the client's screen, end the directive with
1625 # ';""</SCRIPT>'. Evaluated directives return the last value, just as
1626 # eval(), blocks, and subroutines, but only as a scalar.
1628 # IMPORTANT: All PERL variables defined are persistent. Each <SCRIPT>
1629 # </SCRIPT> construct is evaluated as a {}-block with associated scope
1630 # (e.g., for "my $var;" declarations). This means that values assigned
1631 # to a PERL variable can be used throughout the document unless they
1632 # were declared with "my". The following will actually work as intended
1633 # (note that the ``-quotes in this example are NOT evaluated, but used
1634 # as simple quotes):
1636 # <META CONTENT="text/ssperl; CGI=`$String='abcdefg'`">
1637 # anything ...
1638 # <SCRIPT TYPE=text/ssperl>@List = split('', $String);</SCRIPT>
1639 # anything ...
1640 # <SCRIPT TYPE=text/ssperl>join(", ", @List[1..$#List]);</SCRIPT>
1642 # The first <SCRIPT TYPE=text/ssperl></SCRIPT> construct will return the
1643 # value scalar(@List), the second <SCRIPT TYPE=text/ssperl></SCRIPT>
1644 # construct will print the elements of $String separated by commas, leaving
1645 # out the first element, i.e., $List[0].
1647 # Another warning: './' and '~/' are ALWAYS replaced by the values of
1648 # $YOUR_SCRIPTS and $YOUR_HTML_FILES, respectively . This can interfere
1649 # with pattern matching, e.g., $a =~ s/aap\./noot\./g will result in the
1650 # evaluations of $a =~ s/aap\\${YOUR_SCRIPTS}noot\\${YOUR_SCRIPTS}g. Use
1651 # s@<regexp>.@<replacement>.@g instead.
1654 # USER EXTENSIONS
1656 # A CGIscriptor package is attached to the bottom of this file. With
1657 # this package you can personalize your version of CGIscriptor by
1658 # including often used perl routines. These subroutines can be
1659 # accessed by prefixing their names with CGIscriptor::, e.g.,
1660 # <SCRIPT LANGUAGE=PERL TYPE=text/ssperl>
1661 # CGIscriptor::ListDocs("/Books/*") # List all documents in /Books
1662 # </SCRIPT>
1663 # It already contains some useful subroutines for Document Management.
1664 # As it is a separate package, it has its own namespace, isolated from
1665 # both the evaluator and the main program. To access variables from
1666 # the document <SCRIPT></SCRIPT> blocks, use $CGIexecute::<var>.
1668 # Currently, the following functions are implemented
1669 # (precede them with CGIscriptor::, see below for more information)
1670 # - SAFEqx ('String') -> result of qx/"String"/ # Safe application of ``-quotes
1671 # Is used by text/osshell Shell scripts. Protects all CGI
1672 # (client-supplied) values with single quotes before executing the
1673 # commands (one of the few functions that also works WITHOUT CGIscriptor::
1674 # in front)
1675 # - defineCGIvariable ($name[, $default) -> 0/1 (i.e., failure/success)
1676 # Is used by the META tag to define and initialize CGI and ENV
1677 # name/value pairs. Tries to obtain an initializing value from (in order):
1678 # $ENV{$name}
1679 # The Query string
1680 # The default value given (if any)
1681 # (one of the few functions that also works WITHOUT CGIscriptor::
1682 # in front)
1683 # - CGIsafeFileName (FileName) -> FileName or ""
1684 # Check a string against the Allowed File Characters (and ../ /..).
1685 # Returns an empty string for unsafe filenames.
1686 # - CGIsafeEmailAddress (Email) -> Email or ""
1687 # Check a string against correct email address pattern.
1688 # Returns an empty string for unsafe addresses.
1689 # - RedirectShellScript ('CommandString') -> FILEHANDLER or undef
1690 # Open a named PIPE for SAFEqx to receive ALL shell scripts
1691 # - URLdecode (URL encoded string) -> plain string # Decode URL encoded argument
1692 # - URLencode (plain string) -> URL encoded string # Encode argument as URL code
1693 # - CGIparseValue (ValueName [, URL_encoded_QueryString]) -> Decoded value
1694 # Extract the value of a CGI variable from the global or a private
1695 # URL-encoded query (multipart POST raw, NOT decoded)
1696 # - CGIparseValueList (ValueName [, URL_encoded_QueryString])
1697 # -> List of decoded values
1698 # As CGIparseValue, but now assembles ALL values of ValueName into a list.
1699 # - CGIparseHeader (ValueName [, URL_encoded_QueryString]) -> Header
1700 # Extract the header of a multipart CGI variable from the global or a private
1701 # URL-encoded query ("" when not a multipart variable or absent)
1702 # - CGIparseForm ([URL_encoded_QueryString]) -> Decoded Form
1703 # Decode the complete global URL-encoded query or a private
1704 # URL-encoded query
1705 # - read_url(URL) # Returns the page from URL (with added base tag, both FTP and HTTP)
1706 # Uses main::GET_URL(URL, 1) to get at the command to read the URL.
1707 # - BrowseDirs(RootDirectory [, Pattern, Startdir, CGIname]) # print browsable directories
1708 # - ListDocs(Pattern [,ListType]) # Prints a nested HTML directory listing of
1709 # all documents, e.g., ListDocs("/*", "dl");.
1710 # - HTMLdocTree(Pattern [,ListType]) # Prints a nested HTML listing of all
1711 # local links starting from a given document, e.g.,
1712 # HTMLdocTree("/Welcome.html", "dl");
1715 # THE RESULTS STACK: @CGISCRIPTORRESULTS
1717 # If the pseudo-variable "$CGIscriptorResults" has been defined in a
1718 # META tag, all subsequent SCRIPT and META results are pushed
1719 # on the @CGIscriptorResults stack. This list is just another
1720 # Perl variable and can be used and manipulated like any other list.
1721 # $CGIscriptorResults[-1] is always the last result.
1722 # This is only of limited use, e.g., to use the results of an OS shell
1723 # script inside a Perl script. Will NOT contain the results of Pipes
1724 # or code from MIME-profiling.
1727 # USEFULL CGI PREDEFINED VARIABLES (DO NOT ASSIGN TO THESE)
1729 # $CGI_HOME - The DocumentRoot directory
1730 # $CGI_Decoded_QS - The complete decoded Query String
1731 # $CGI_Content_Length - The ACTUAL length of the Query String
1732 # $CGI_Date - Current date and time
1733 # $CGI_Year $CGI_Month $CGI_Day $CGI_WeekDay - Current Date
1734 # $CGI_Time - Current Time
1735 # $CGI_Hour $CGI_Minutes $CGI_Seconds - Current Time, split
1736 # GMT Date/Time:
1737 # $CGI_GMTYear $CGI_GMTMonth $CGI_GMTDay $CGI_GMTWeekDay $CGI_GMTYearDay
1738 # $CGI_GMTHour $CGI_GMTMinutes $CGI_GMTSeconds $CGI_GMTisdst
1741 # USEFULL CGI ENVIRONMENT VARIABLES
1743 # Variables accessible (in APACHE) as $ENV{<name>}
1744 # (see: "http://hoohoo.ncsa.uiuc.edu/cgi/env.html"):
1746 # QUERY_STRING - The query part of URL, that is, everything that follows the
1747 # question mark.
1748 # PATH_INFO - Extra path information given after the script name
1749 # PATH_TRANSLATED - Extra pathinfo translated through the rule system.
1750 # (This doesn't always make sense.)
1751 # REMOTE_USER - If the server supports user authentication, and the script is
1752 # protected, this is the username they have authenticated as.
1753 # REMOTE_HOST - The hostname making the request. If the server does not have
1754 # this information, it should set REMOTE_ADDR and leave this unset
1755 # REMOTE_ADDR - The IP address of the remote host making the request.
1756 # REMOTE_IDENT - If the HTTP server supports RFC 931 identification, then this
1757 # variable will be set to the remote user name retrieved from
1758 # the server. Usage of this variable should be limited to logging
1759 # only.
1760 # AUTH_TYPE - If the server supports user authentication, and the script
1761 # is protected, this is the protocol-specific authentication
1762 # method used to validate the user.
1763 # CONTENT_TYPE - For queries which have attached information, such as HTTP
1764 # POST and PUT, this is the content type of the data.
1765 # CONTENT_LENGTH - The length of the said content as given by the client.
1766 # SERVER_SOFTWARE - The name and version of the information server software
1767 # answering the request (and running the gateway).
1768 # Format: name/version
1769 # SERVER_NAME - The server's hostname, DNS alias, or IP address as it
1770 # would appear in self-referencing URLs
1771 # GATEWAY_INTERFACE - The revision of the CGI specification to which this
1772 # server complies. Format: CGI/revision
1773 # SERVER_PROTOCOL - The name and revision of the information protocol this
1774 # request came in with. Format: protocol/revision
1775 # SERVER_PORT - The port number to which the request was sent.
1776 # REQUEST_METHOD - The method with which the request was made. For HTTP,
1777 # this is "GET", "HEAD", "POST", etc.
1778 # SCRIPT_NAME - A virtual path to the script being executed, used for
1779 # self-referencing URLs.
1780 # HTTP_ACCEPT - The MIME types which the client will accept, as given by
1781 # HTTP headers. Other protocols may need to get this
1782 # information from elsewhere. Each item in this list should
1783 # be separated by commas as per the HTTP spec.
1784 # Format: type/subtype, type/subtype
1785 # HTTP_USER_AGENT - The browser the client is using to send the request.
1786 # General format: software/version library/version.
1789 # INSTRUCTIONS FOR RUNNING CGIscriptor ON UNIX
1791 # CGIscriptor.pl will run on any WWW server that runs Perl scripts, just add
1792 # a line like the following to your srm.conf file (Apache example):
1794 # ScriptAlias /SHTML/ /real-path/CGIscriptor.pl/
1796 # URL's that refer to http://www.your.address/SHTML/... will now be handled
1797 # by CGIscriptor.pl, which can use a private directory tree (default is the
1798 # DOCUMENT_ROOT directory tree, but it can be anywhere, see manual).
1800 # If your hosting ISP won't let you add ScriptAlias lines you can use
1801 # the following "rewrite"-based "scriptalias" in .htaccess
1802 # (from Gerd Franke)
1804 # RewriteEngine On
1805 # RewriteBase /
1806 # RewriteCond %{REQUEST_FILENAME} .html$
1807 # RewriteCond %{SCRIPT_FILENAME} !cgiscriptor.pl$
1808 # RewriteCond %{REQUEST_FILENAME} -f
1809 # RewriteRule ^(.*)$ /cgi-bin/cgiscriptor.pl/$1?&%{QUERY_STRING}
1811 # Everthing with the extension ".html" and not including "cgiscriptor.pl"
1812 # in the url and where the file "path/filename.html" exists is redirected
1813 # to "/cgi.bin/cgiscriptor.pl/path/filename.html?query".
1814 # The user configuration should get the same path-level as the
1815 # .htaccess-file:
1817 # # Just enter your own directory path here
1818 # $YOUR_HTML_FILES = "$ENV{'DOCUMENT_ROOT'}";
1819 # # use DOCUMENT_ROOT only, if .htaccess lies in the root-directory.
1821 # If this .htaccess goes in a specific directory, the path to this
1822 # directory must be added to $ENV{'DOCUMENT_ROOT'}.
1824 # The CGIscriptor file contains all documentation as comments. These
1825 # comments can be removed to speed up loading (e.g., `egrep -v '^#'
1826 # CGIscriptor.pl` > leanScriptor.pl). A bare bones version of
1827 # CGIscriptor.pl, lacking documentation, most comments, access control,
1828 # example functions etc. (but still with the copyright notice and some
1829 # minimal documentation) can be obtained by calling CGIscriptor.pl on the
1830 # command line with the '-slim' command line argument, e.g.,
1832 # >CGIscriptor.pl -slim > slimCGIscriptor.pl
1834 # CGIscriptor.pl can be run from the command line with <path> and <query> as
1835 # arguments, as `CGIscriptor.pl <path> <query>`, inside a perl script
1836 # with 'do CGIscriptor.pl' after setting $ENV{PATH_INFO}
1837 # and $ENV{QUERY_STRING}, or CGIscriptor.pl can be loaded with 'require
1838 # "/real-path/CGIscriptor.pl"'. In the latter case, requests are processed
1839 # by 'Handle_Request();' (again after setting $ENV{PATH_INFO} and
1840 # $ENV{QUERY_STRING}).
1842 # Using the command line execution option, CGIscriptor.pl can be used as a
1843 # document (meta-)preprocessor. If the first argument is '-', STDIN will be read.
1844 # For example:
1846 # > cat MyDynamicDocument.html | CGIscriptor.pl - '[QueryString]' > MyStaticFile.html
1848 # This command line will produce a STATIC file with the DYNAMIC content of
1849 # MyDocument.html "interpolated".
1851 # This option would be very dangerous when available over the internet.
1852 # If someone could sneak a 'http://www.your.domain/-' URL past your
1853 # server, CGIscriptor could EXECUTE any POSTED contend.
1854 # Therefore, for security reasons, STDIN will NOT be read
1855 # if ANY of the HTTP server environment variables is set (e.g.,
1856 # SERVER_PORT, SERVER_PROTOCOL, SERVER_NAME, SERVER_SOFTWARE,
1857 # HTTP_USER_AGENT, REMOTE_ADDR).
1858 # This block on processing STDIN on HTTP requests can be lifted by setting
1859 # $BLOCK_STDIN_HTTP_REQUEST = 0;
1860 # In the security configuration. Butbe carefull when doing this.
1861 # It can be very dangerous.
1863 # Running demo's and more information can be found at
1864 # http://www.fon.hum.uva.nl/~rob/OSS/OSS.html
1866 # A pocket-size HTTP daemon, CGIservlet.pl, is available from my web site or
1867 # CPAN that can use CGIscriptor.pl as the base of a µWWW server and
1868 # demonstrates its use.
1871 # PROCESSING NON-FILESYSTEM DATA
1873 # Normally, HTTP (WWW) requests map onto file that can be accessed
1874 # using the perl open() function. That is, the web server runs on top of
1875 # some directory structure. However, we can envission (and put to good
1876 # use) other systems that do not use a normal file system. The whole CGI
1877 # was developed to make dynamic document generation possible.
1879 # A special case is where we want to have it both: A normal web server
1880 # with normal "file data", but not a normal files system. For instance,
1881 # we want or normal Web Site to run directly from a RAM hash table or
1882 # other database, instead of from disk. But we do NOT want to code the
1883 # whole site structure in CGI.
1885 # CGIscriptor can do this. If the web server fills an environment variable
1886 # $ENV{'CGI_FILE_CONTENT'} with the content of the "file", then the content
1887 # of this variable is processed instead of opening a file. If this environment
1888 # variable has the value '-', the content of another environment variable,
1889 # $ENV{'CGI_DATA_ACCESS_CODE'} is executed as:
1890 # eval("\@_ = ($file_path); do {$ENV{'CGI_DATA_ACCESS_CODE'}};")
1891 # and the result is processed as if it was the content of the requested
1892 # file.
1893 # (actually, the names of the environment variables are user configurable,
1894 # they are stored in the local variables $CGI_FILE_CONTENT and
1895 # $CGI_DATA_ACCESS_CODE)
1897 # When using this mechanism, the SRC attribute mechanism will only partially work.
1898 # Only the "recursive" calls to CGIscriptor (the ProcessFile() function)
1899 # will work, the automagical execution of SRC files won't. (In this case,
1900 # the SRC attribute won't work either for other scripting languages)
1903 # NON-UNIX PLATFORMS
1905 # CGIscriptor.pl was mainly developed and tested on UNIX. However, as I
1906 # coded part of the time on an Apple Macintosh under MacPerl, I made sure
1907 # CGIscriptor did run under MacPerl (with command line options). But only
1908 # as an independend script, not as part of a HTTP server. I have used it
1909 # under Apache in Windows XP.
1911 ENDOFHELPTEXT
1912 exit;
1914 ###############################################################################
1916 # SECURITY CONFIGURATION
1918 # Special configurations related to SECURITY
1919 # (i.e., optional, see also environment variables below)
1921 # LOGGING
1922 # Log Clients and the requested paths (Redundant when loging Queries)
1924 $ClientLog = "./Client.log"; # (uncomment for use)
1926 # Format: Localtime | REMOTE_USER REMOTE_IDENT REMOTE_HOST REMOTE_ADDRESS \
1927 # PATH_INFO CONTENT_LENGTH (actually, the real query+post length)
1929 # Log Clients and the queries, the CGIQUERYDECODE is required if you want
1930 # to log queries. If you log Queries, the loging of Clients is redundant
1931 # (note that queries can be quite long, so this might not be a good idea)
1933 #$QueryLog = "./Query.log"; # (uncomment for use)
1935 # ACCESS CONTROL
1936 # the Access files should contain Hostnames or IP addresses,
1937 # i.e. REMOTE_HOST or REMOTE_ADDR, each on a separate line
1938 # optionally followed by one ore more file patterns, e.g., "edu /DEMO".
1939 # Matching is done "domain first". For example ".edu" matches all
1940 # clients whose "name" ends in ".edu" or ".EDU". The file pattern
1941 # "/DEMO" matches all paths that contain the strings "/DEMO" or "/demo"
1942 # (both matchings are done case-insensitive).
1943 # The name special symbol "-" matches ALL clients who do not supply a
1944 # REMOTE_HOST name, "*" matches all clients.
1945 # Lines starting with '-e' are evaluated. A non-zero return value indicates
1946 # a match. You can use $REMOTE_HOST, $REMOTE_ADDR, and $PATH_INFO. These
1947 # lines are evaluated in the program's own name-space. So DO NOT assign to
1948 # variables.
1950 # Accept the following users (remove comment # and adapt filename)
1951 $CGI_Accept = -s "$YOUR_SCRIPTS/ACCEPT.lis" ? "$YOUR_SCRIPTS/ACCEPT.lis" : ''; # (uncomment for use)
1953 # Reject requests from the following users (remove comment # and
1954 # adapt filename, this is only of limited use)
1955 $CGI_Reject = -s "$YOUR_SCRIPTS/REJECT.lis" ? "$YOUR_SCRIPTS/REJECT.lis" : ''; # (uncomment for use)
1957 # Empty lines or comment lines starting with '#' are ignored in both
1958 # $CGI_Accept and $CGI_Reject.
1960 # Block STDIN (i.e., '-') requests when servicing an HTTP request
1961 # Comment this out if you realy want to use STDIN in an on-line web server
1962 $BLOCK_STDIN_HTTP_REQUEST = 1;
1965 # End of security configuration
1967 ##################################################<<<<<<<<<<End Remove
1969 # PARSING CGI VALUES FROM THE QUERY STRING (USER CONFIGURABLE)
1971 # The CGI parse commands. These commands extract the values of the
1972 # CGI variables from the URL encoded Query String.
1973 # If you want to use your own CGI decoders, you can call them here
1974 # instead, using your own PATH and commenting/uncommenting the
1975 # appropriate lines
1977 # CGI parse command for individual values
1978 # (if $List > 0, returns a list value, if $List < 0, a hash table, this is optional)
1979 sub YOUR_CGIPARSE # ($Name [, $List]) -> Decoded value
1981 my $Name = shift;
1982 my $List = shift || 0;
1983 # Use one of the following by uncommenting
1984 if(!$List) # Simple value
1986 return CGIscriptor::CGIparseValue($Name) ;
1988 elsif($List < 0) # Hash tables
1990 return CGIscriptor::CGIparseValueHash($Name); # Defined in CGIscriptor below
1992 else # Lists
1994 return CGIscriptor::CGIparseValueList($Name); # Defined in CGIscriptor below
1997 # return `/PATH/cgiparse -value $Name`; # Shell commands
1998 # require "/PATH/cgiparse.pl"; return cgivalue($Name); # Library
2000 # Complete queries
2001 sub YOUR_CGIQUERYDECODE
2003 # Use one of the following by uncommenting
2004 return CGIscriptor::CGIparseForm(); # Defined in CGIscriptor below
2005 # return `/PATH/cgiparse -form`; # Shell commands
2006 # require "/PATH/cgiparse.pl"; return cgiform(); # Library
2009 # End of configuration
2011 #######################################################################
2013 # Translating input files.
2014 # Allows general and global conversions of files using Regular Expressions
2015 # Translations are applied in the order of definition.
2017 # Define:
2018 # my $TranslationPaths = 'pattern'; # Pattern matching PATH_INFO
2020 # push(@TranslationTable, ['pattern', 'replacement']);
2021 # e.g. (for Ruby Rails):
2022 # push(@TranslationTable, ['<%=', '<SCRIPT TYPE="text/ssruby">']);
2023 # push(@TranslationTable, ['%>', '</SCRIPT>']);
2025 # Runs:
2026 # my $currentRegExp;
2027 # foreach $currentRegExp (keys(%TranslationTable))
2029 # my $currentRegExp;
2030 # foreach $currentRegExp (@TranslationTable)
2032 # my ($pattern, $replacement) = @$currentRegExp;
2033 # $$text =~ s!$pattern!$replacement!msg;
2034 # };
2035 # };
2037 # Configuration section
2039 #######################################################################
2041 # The file paths on which to apply the translation
2042 my $TranslationPaths = ''; # NO files
2043 #$TranslationPaths = '.'; # ANY file
2044 # $TranslationPaths = '\.html'; # HTML files
2046 my @TranslationTable = ();
2047 # Some legacy code
2048 push(@TranslationTable, ['\<\s*CGI\s+([^\>])*\>', '\<SCRIPT TYPE=\"text/ssperl\"\>$1\<\/SCRIPT>']);
2049 # Ruby Rails?
2050 push(@TranslationTable, ['<%=', '<SCRIPT TYPE="text/ssruby">']);
2051 push(@TranslationTable, ['%>', '</SCRIPT>']);
2053 sub performTranslation # (\$text)
2055 my $text = shift || return;
2056 if(@TranslationTable && $TranslationPaths && $ENV{'PATH_INFO'} =~ m!$TranslationPaths!)
2058 my $currentRegExp;
2059 foreach $currentRegExp (@TranslationTable)
2061 my ($pattern, $replacement) = @$currentRegExp;
2062 $$text =~ s!$pattern!$replacement!msg;
2067 #######################################################################
2069 # Seamless access to other (Scripting) Languages
2070 # TYPE='text/ss<interpreter>'
2072 # Configuration section
2074 #######################################################################
2076 # OTHER SCRIPTING LANGUAGES AT THE SERVER SIDE (MIME => OScommand)
2077 # Yes, it realy is this simple! (unbelievable, isn't it)
2078 # NOTE: Some interpreters require some filtering to obtain "clean" output
2080 %ScriptingLanguages = (
2081 "text/testperl" => 'perl', # Perl for testing
2082 "text/sspython" => 'python', # Python
2083 "text/ssruby" => 'ruby', # Ruby
2084 "text/sstcl" => 'tcl', # TCL
2085 "text/ssawk" => 'awk -f-', # Awk
2086 "text/sslisp" => # lisp (rep, GNU)
2087 'rep | tail +4 '."| egrep -v '> |^rep. |^nil\\\$'",
2088 "text/xlispstat" => # xlispstat
2089 'xlispstat | tail +7 ' ."| egrep -v '> \\\$|^NIL'",
2090 "text/ssprolog" => # Prolog (GNU)
2091 "gprolog | tail +4 | sed 's/^| ?- //'",
2092 "text/ssm4" => 'm4', # M4 macro's
2093 "text/sh" => 'sh', # Born shell
2094 "text/bash" => 'bash', # Born again shell
2095 "text/csh" => 'csh', # C shell
2096 "text/ksh" => 'ksh', # Korn shell
2097 "text/sspraat" => # Praat (sound/speech analysis)
2098 "praat - | sed 's/Praat > //g'",
2099 "text/ssr" => # R
2100 "R --vanilla --slave | sed 's/^[\[0-9\]*] //'",
2101 "text/ssrebol" => # REBOL
2102 "rebol --quiet|egrep -v '^[> ]* == '|sed 's/^\\s*\[> \]* //'",
2103 "text/postgresql" => 'psql 2>/dev/null',
2105 # Not real scripting, but the use of other applications
2106 "text/ssmailto" => "awk 'NF||F{F=1;print \\\$0;}'|mailto >/dev/null", # Send mail from server
2107 "text/ssdisplay" => 'cat', # Display, (interpolation)
2108 "text/sslogfile" => # Log to file, (interpolation)
2109 "awk 'NF||L {if(!L){L=tolower(\\\$1)~/^file:\\\$/ ? \\\$2 : \\\$1;}else{print \\\$0 >> L;};}'",
2111 "" => ""
2114 # To be able to access the CGI variables in your script, they
2115 # should be passed to the scripting language in a readable form
2116 # Here you can enter how they should be printed (the first %s
2117 # is replaced by the NAME of the CGI variable as it apears in the
2118 # META tag, the second by its VALUE).
2119 # For Perl this would be:
2120 # "text/testperl" => '$%s = "%s";',
2121 # which would be executed as
2122 # printf('$%s = "%s";', $CGI_NAME, $CGI_VALUE);
2124 # If the hash table value doesn't exist, nothing is done
2125 # (you have to parse the Environment variables yourself).
2126 # If it DOES exist but is empty (e.g., "text/sspraat" => '',)
2127 # Perl string interpolation of variables (i.e., $var, @array,
2128 # %hash) is performed. This means that $@%\ must be protected
2129 # with a \.
2131 %ScriptingCGIvariables = (
2132 "text/testperl" => "\$\%s = '\%s';", # Perl $VAR = 'value'; (for testing)
2133 "text/sspython" => "\%s = '\%s'", # Python VAR = 'value'
2134 "text/ssruby" => '@%s = "%s"', # Ruby @VAR = 'value'
2135 "text/sstcl" => 'set %s "%s"', # TCL set VAR "value"
2136 "text/ssawk" => '%s = "%s";', # Awk VAR = 'value';
2137 "text/sslisp" => '(setq %s "%s")', # Gnu lisp (rep) (setq VAR "value")
2138 "text/xlispstat" => '(setq %s "%s")', # xlispstat (setq VAR "value")
2139 "text/ssprolog" => '', # Gnu prolog (interpolated)
2140 "text/ssm4" => "define(`\%s', `\%s')", # M4 macro's define(`VAR', `value')
2141 "text/sh" => "\%s='\%s'", # Born shell VAR='value'
2142 "text/bash" => "\%s='\%s'", # Born again shell VAR='value'
2143 "text/csh" => "\$\%s='\%s';", # C shell $VAR = 'value';
2144 "text/ksh" => "\$\%s='\%s';", # Korn shell $VAR = 'value';
2146 "text/ssrebol" => '%s: copy "%s"', # REBOL VAR: copy "value"
2147 "text/sspraat" => '', # Praat (interpolation)
2148 "text/ssr" => '%s <- "%s";', # R VAR <- "value";
2149 "text/postgresql" => '', # PostgreSQL (interpolation)
2151 # Not real scripting, but the use of other applications
2152 "text/ssmailto" => '', # MAILTO, (interpolation)
2153 "text/ssdisplay" => '', # Display, (interpolation)
2154 "text/sslogfile" => '', # Log to file, (interpolation)
2156 "" => ""
2159 # If you want something added in front or at the back of each script
2160 # block as send to the interpreter add it here.
2161 # mime => "string", e.g., "text/sspython" => "python commands"
2162 %ScriptingPrefix = (
2163 "text/testperl" => "\# Prefix Code;", # Perl script testing
2164 "text/ssm4" => 'divert(0)', # M4 macro's (open STDOUT)
2166 "" => ""
2168 # If you want something added at the end of each script block
2169 %ScriptingPostfix = (
2170 "text/testperl" => "\# Postfix Code;", # Perl script testing
2171 "text/ssm4" => 'divert(-1)', # M4 macro's (block STDOUT)
2173 "" => ""
2175 # If you need initialization code, directly after opening
2176 %ScriptingInitialization = (
2177 "text/testperl" => "\# Initialization Code;", # Perl script testing
2178 "text/ssawk" => 'BEGIN {', # Server Side awk scripts (VAR = "value")
2179 "text/sslisp" => '(prog1 nil ', # Lisp (rep)
2180 "text/xlispstat" => '(prog1 nil ', # xlispstat
2181 "text/ssm4" => 'divert(-1)', # M4 macro's (block STDOUT)
2183 "" => ""
2185 # If you need cleanup code before closing
2186 %ScriptingCleanup = (
2187 "text/testperl" => "\# Cleanup Code;", # Perl script testing
2188 "text/sspraat" => 'Quit',
2189 "text/ssawk" => '};', # Server Side awk scripts (VAR = "value")
2190 "text/sslisp" => '(princ "\n" standard-output)).', # Closing print to rep
2191 "text/xlispstat" => '(print ""))', # Closing print to xlispstat
2192 "text/postgresql" => '\q', # quit psql
2193 "text/ssdisplay" => "", # close cat
2195 "" => ""
2198 # End of configuration for foreign scripting languages
2200 ###############################################################################
2202 # Initialization Code
2205 sub Initialize_Request
2207 ###############################################################################
2209 # ENVIRONMENT VARIABLES
2211 # Use environment variables to configure CGIscriptor on a temporary basis.
2212 # If you define any of the configurable variables as environment variables,
2213 # these are used instead of the "hard coded" values above.
2215 $SS_PUB = $ENV{'SS_PUB'} || $YOUR_HTML_FILES;
2216 $SS_SCRIPT = $ENV{'SS_SCRIPT'} || $YOUR_SCRIPTS;
2219 # Substitution strings, these are used internally to handle the
2220 # directory separator strings, e.g., '~/' -> 'SS_PUB:' (Mac)
2221 $HOME_SUB = $SS_PUB;
2222 $SCRIPT_SUB = $SS_SCRIPT;
2225 # Make sure all script are reliably loaded
2226 push(@INC, $SS_SCRIPT);
2229 # Add the directory separator to the "home" directories.
2230 # (This is required for ~/ and ./ substitution)
2231 $HOME_SUB .= '/' if $HOME_SUB;
2232 $SCRIPT_SUB .= '/' if $SCRIPT_SUB;
2234 $CGI_HOME = $ENV{'DOCUMENT_ROOT'};
2235 $ENV{'PATH_TRANSLATED'} =~ /$ENV{'PATH_INFO'}/is;
2236 $CGI_HOME = $` unless $ENV{'DOCUMENT_ROOT'}; # Get the DOCUMENT_ROOT directory
2237 $default_values{'CGI_HOME'} = $CGI_HOME;
2238 $ENV{'HOME'} = $CGI_HOME;
2239 # Set SS_PUB and SS_SCRIPT as Environment variables (make them available
2240 # to the scripts)
2241 $ENV{'SS_PUB'} = $SS_PUB unless $ENV{'SS_PUB'};
2242 $ENV{'SS_SCRIPT'} = $SS_SCRIPT unless $ENV{'SS_SCRIPT'};
2244 $FilePattern = $ENV{'FilePattern'} || $FilePattern;
2245 $MaximumQuerySize = $ENV{'MaximumQuerySize'} || $MaximumQuerySize;
2246 $ClientLog = $ENV{'ClientLog'} || $ClientLog;
2247 $QueryLog = $ENV{'QueryLog'} || $QueryLog;
2248 $CGI_Accept = $ENV{'CGI_Accept'} || $CGI_Accept;
2249 $CGI_Reject = $ENV{'CGI_Reject'} || $CGI_Reject;
2251 # Parse file names
2252 $CGI_Accept =~ s@^\~/@$HOME_SUB@g if $CGI_Accept;
2253 $CGI_Reject =~ s@^\~/@$HOME_SUB@g if $CGI_Reject;
2254 $ClientLog =~ s@^\~/@$HOME_SUB@g if $ClientLog;
2255 $QueryLog =~ s@^\~/@$HOME_SUB@g if $QueryLog;
2257 $CGI_Accept =~ s@^\./@$SCRIPT_SUB@g if $CGI_Accept;
2258 $CGI_Reject =~ s@^\./@$SCRIPT_SUB@g if $CGI_Reject;
2259 $ClientLog =~ s@^\./@$SCRIPT_SUB@g if $ClientLog;
2260 $QueryLog =~ s@^\./@$SCRIPT_SUB@g if $QueryLog;
2262 @CGIscriptorResults = (); # A stack of results
2264 # end of Environment variables
2266 #############################################################################
2268 # Define and Store "standard" values
2270 # BEFORE doing ANYTHING check the size of Query String
2271 length($ENV{'QUERY_STRING'}) <= $MaximumQuerySize || dieHandler(2, "QUERY TOO LONG\n");
2273 # The Translated Query String and the Actual length of the (decoded)
2274 # Query String
2275 if($ENV{'QUERY_STRING'})
2277 # If this can contain '`"-quotes, be carefull to use it QUOTED
2278 $default_values{CGI_Decoded_QS} = YOUR_CGIQUERYDECODE();
2279 $default_values{CGI_Content_Length} = length($default_values{CGI_Decoded_QS});
2282 # Get the current Date and time and store them as default variables
2284 # Get Local Time
2285 $LocalTime = localtime;
2287 # CGI_Year CGI_Month CGI_Day CGI_WeekDay CGI_Time
2288 # CGI_Hour CGI_Minutes CGI_Seconds
2290 $default_values{CGI_Date} = $LocalTime;
2291 ($default_values{CGI_WeekDay},
2292 $default_values{CGI_Month},
2293 $default_values{CGI_Day},
2294 $default_values{CGI_Time},
2295 $default_values{CGI_Year}) = split(' ', $LocalTime);
2296 ($default_values{CGI_Hour},
2297 $default_values{CGI_Minutes},
2298 $default_values{CGI_Seconds}) = split(':', $default_values{CGI_Time});
2300 # GMT:
2301 # CGI_GMTYear CGI_GMTMonth CGI_GMTDay CGI_GMTWeekDay CGI_GMTYearDay
2302 # CGI_GMTHour CGI_GMTMinutes CGI_GMTSeconds CGI_GMTisdst
2304 ($default_values{CGI_GMTSeconds},
2305 $default_values{CGI_GMTMinutes},
2306 $default_values{CGI_GMTHour},
2307 $default_values{CGI_GMTDay},
2308 $default_values{CGI_GMTMonth},
2309 $default_values{CGI_GMTYear},
2310 $default_values{CGI_GMTWeekDay},
2311 $default_values{CGI_GMTYearDay},
2312 $default_values{CGI_GMTisdst}) = gmtime;
2316 # End of Initialize Request
2318 ###################################################################
2320 # SECURITY: ACCESS CONTROL
2322 # Check the credentials of each client (use pattern matching, domain first).
2323 # This subroutine will kill-off (die) the current process whenever access
2324 # is denied.
2326 sub Access_Control
2328 # >>>>>>>>>>Start Remove
2330 # ACCEPTED CLIENTS
2332 # Only accept clients which are authorized, reject all unnamed clients
2333 # if REMOTE_HOST is given.
2334 # If file patterns are given, check whether the user is authorized for
2335 # THIS file.
2336 if($CGI_Accept)
2338 # Use local variables, REMOTE_HOST becomes '-' if undefined
2339 my $REMOTE_HOST = $ENV{REMOTE_HOST} || '-';
2340 my $REMOTE_ADDR = $ENV{REMOTE_ADDR};
2341 my $PATH_INFO = $ENV{'PATH_INFO'};
2343 open(CGI_Accept, "<$CGI_Accept") || dieHandler(3, "$CGI_Accept: $!\n");
2344 $NoAccess = 1;
2345 while(<CGI_Accept>)
2347 next unless /\S/; # Skip empty lines
2348 next if /^\s*\#/; # Skip comments
2350 # Full expressions
2351 if(/^\s*-e\s/is)
2353 my $Accept = $'; # Get the expression
2354 $NoAccess &&= eval($Accept); # evaluate the expresion
2356 else
2358 my ($Accept, @FilePatternList) = split;
2359 if($Accept eq '*' # Always match
2360 ||$REMOTE_HOST =~ /\Q$Accept\E$/is # REMOTE_HOST matches
2361 || (
2362 $Accept =~ /^[0-9\.]+$/
2363 && $REMOTE_ADDR =~ /^\Q$Accept\E/ # IP address matches
2367 if($FilePatternList[0])
2369 foreach $Pattern (@FilePatternList)
2371 # Check whether this patterns is accepted
2372 $NoAccess &&= ($PATH_INFO !~ m@\Q$Pattern\E@is);
2375 else
2377 $NoAccess = 0; # No file patterns -> Accepted
2381 # Blocked
2382 last unless $NoAccess;
2384 close(CGI_Accept);
2385 if($NoAccess){ dieHandler(4, "No Access: $PATH_INFO\n");};
2389 # REJECTED CLIENTS
2391 # Reject named clients, accept all unnamed clients
2392 if($CGI_Reject)
2394 # Use local variables, REMOTE_HOST becomes '-' if undefined
2395 my $REMOTE_HOST = $ENV{'REMOTE_HOST'} || '-';
2396 my $REMOTE_ADDR = $ENV{'REMOTE_ADDR'};
2397 my $PATH_INFO = $ENV{'PATH_INFO'};
2399 open(CGI_Reject, "<$CGI_Reject") || dieHandler(5, "$CGI_Reject: $!\n");
2400 $NoAccess = 0;
2401 while(<CGI_Reject>)
2403 next unless /\S/; # Skip empty lines
2404 next if /^\s*\#/; # Skip comments
2406 # Full expressions
2407 if(/^-e\s/is)
2409 my $Reject = $'; # Get the expression
2410 $NoAccess ||= eval($Reject); # evaluate the expresion
2412 else
2414 my ($Reject, @FilePatternList) = split;
2415 if($Reject eq '*' # Always match
2416 ||$REMOTE_HOST =~ /\Q$Reject\E$/is # REMOTE_HOST matches
2417 ||($Reject =~ /^[0-9\.]+$/
2418 && $REMOTE_ADDR =~ /^\Q$Reject\E/is # IP address matches
2422 if($FilePatternList[0])
2424 foreach $Pattern (@FilePatternList)
2426 $NoAccess ||= ($PATH_INFO =~ m@\Q$Pattern\E@is);
2429 else
2431 $NoAccess = 1; # No file patterns -> Rejected
2435 last if $NoAccess;
2437 close(CGI_Reject);
2438 if($NoAccess){ dieHandler(6, "Request rejected: $PATH_INFO\n");};
2441 ##########################################################<<<<<<<<<<End Remove
2444 # Get the filename
2446 # Does the filename contain any illegal characters (e.g., |, >, or <)
2447 dieHandler(7, "Illegal request: $ENV{'PATH_INFO'}\n") if $ENV{'PATH_INFO'} =~ /[^$FileAllowedChars]/;
2448 # Does the pathname contain an illegal (blocked) "directory"
2449 dieHandler(8, "Illegal request: $ENV{'PATH_INFO'}\n") if $BlockPathAccess && $ENV{'PATH_INFO'} =~ m@$BlockPathAccess@; # Access is blocked
2450 # Does the pathname contain a direct referencer to BinaryMapFile
2451 dieHandler(9, "Illegal request: $ENV{'PATH_INFO'}\n") if $BinaryMapFile && $ENV{'PATH_INFO'} =~ m@\Q$BinaryMapFile\E@; # Access is blocked
2453 # SECURITY: Is PATH_INFO allowed?
2454 if($FilePattern && $ENV{'PATH_INFO'} && $ENV{'PATH_INFO'} ne '-' &&
2455 ($ENV{'PATH_INFO'} !~ m@($FilePattern)$@is))
2457 # Unsupported file types can be processed by a special raw-file
2458 if($BinaryMapFile)
2460 $ENV{'CGI_BINARY_FILE'} = $ENV{'PATH_INFO'};
2461 $ENV{'PATH_INFO'} = $BinaryMapFile;
2463 else
2465 dieHandler(10, "Illegal file\n");
2471 # End of Security Access Control
2474 ############################################################################
2476 # Get the POST part of the query and add it to the QUERY_STRING.
2479 sub Get_POST_part_of_query
2482 # If POST, Read data from stdin to QUERY_STRING
2483 if($ENV{'REQUEST_METHOD'} =~ /POST/is)
2485 # SECURITY: Check size of Query String
2486 $ENV{'CONTENT_LENGTH'} <= $MaximumQuerySize || dieHandler(11, "Query too long: $ENV{'CONTENT_LENGTH'}\n"); # Query too long
2487 my $QueryRead = 0;
2488 my $SystemRead = $ENV{'CONTENT_LENGTH'};
2489 $ENV{'QUERY_STRING'} .= '&' if length($ENV{'QUERY_STRING'}) > 0;
2490 while($SystemRead > 0)
2492 $QueryRead = sysread(STDIN, $Post, $SystemRead); # Limit length
2493 $ENV{'QUERY_STRING'} .= $Post;
2494 $SystemRead -= $QueryRead;
2496 # Update decoded Query String
2497 $default_values{CGI_Decoded_QS} = YOUR_CGIQUERYDECODE();
2498 $default_values{CGI_Content_Length} =
2499 length($default_values{CGI_Decoded_QS});
2503 # End of getting POST part of query
2506 ############################################################################
2508 # Start (HTML) output and logging
2509 # (if there are irregularities, it can kill the current process)
2512 sub Initialize_output
2514 # Construct the REAL file path (except for STDIN on the command line)
2515 my $file_path = $ENV{'PATH_INFO'} ne '-' ? $SS_PUB . $ENV{'PATH_INFO'} : '-';
2516 $file_path =~ s/\?.*$//; # Remove query
2517 # This is only necessary if your server does not catch ../ directives
2518 $file_path !~ m@\.\./@ || dieHandler(12, "Illegal ../ Construct\n"); # SECURITY: Do not allow ../ constructs
2520 # Block STDIN use (-) if CGIscriptor is servicing a HTTP request
2521 if($file_path eq '-')
2523 dieHandler(13, "STDIN request in On Line system\n") if $BLOCK_STDIN_HTTP_REQUEST
2524 && ($ENV{'SERVER_SOFTWARE'}
2525 || $ENV{'SERVER_NAME'}
2526 || $ENV{'GATEWAY_INTERFACE'}
2527 || $ENV{'SERVER_PROTOCOL'}
2528 || $ENV{'SERVER_PORT'}
2529 || $ENV{'REMOTE_ADDR'}
2530 || $ENV{'HTTP_USER_AGENT'});
2535 if($ClientLog)
2537 open(ClientLog, ">>$ClientLog");
2538 print ClientLog "$LocalTime | ",
2539 ($ENV{REMOTE_USER} || "-"), " ",
2540 ($ENV{REMOTE_IDENT} || "-"), " ",
2541 ($ENV{REMOTE_HOST} || "-"), " ",
2542 $ENV{REMOTE_ADDR}, " ",
2543 $ENV{PATH_INFO}, " ",
2544 $ENV{'CGI_BINARY_FILE'}, " ",
2545 ($default_values{CGI_Content_Length} || "-"),
2546 "\n";
2547 close(ClientLog);
2549 if($QueryLog)
2551 open(QueryLog, ">>$QueryLog");
2552 print QueryLog "$LocalTime\n",
2553 ($ENV{REMOTE_USER} || "-"), " ",
2554 ($ENV{REMOTE_IDENT} || "-"), " ",
2555 ($ENV{REMOTE_HOST} || "-"), " ",
2556 $ENV{REMOTE_ADDR}, ": ",
2557 $ENV{PATH_INFO}, " ",
2558 $ENV{'CGI_BINARY_FILE'}, "\n";
2560 # Write Query to Log file
2561 print QueryLog $default_values{CGI_Decoded_QS}, "\n\n";
2562 close(QueryLog);
2565 # Return the file path
2566 return $file_path;
2569 # End of Initialize output
2572 ############################################################################
2574 # Handle foreign interpreters (i.e., scripting languages)
2576 # Insert perl code to execute scripts in foreign scripting languages.
2577 # Actually, the scripts inside the <SCRIPT></SCRIPT> blocks are piped
2578 # into an interpreter.
2579 # The code presented here is fairly confusing because it
2580 # actually writes perl code code to the output.
2582 # A table with the file handles
2583 %SCRIPTINGINPUT = ();
2585 # A function to clean up Client delivered CGI parameter values
2586 # (i.e., quote all odd characters)
2587 %SHRUBcharacterTR =
2589 "\'" => '&#39;',
2590 "\`" => '&#96;',
2591 "\"" => '&quot;',
2592 '&' => '&amper;',
2593 "\\" => '&#92;'
2596 sub shrubCGIparameter # ($String) -> Cleaned string
2598 my $String = shift || "";
2600 # Change all quotes [`'"] into HTML character entities
2601 my ($Char, $Transcript) = ('&', $SHRUBcharacterTR{'&'});
2603 # Protect &
2604 $String =~ s/\Q$Char\E/$Transcript/isg if $Transcript;
2606 while( ($Char, $Transcript) = each %SHRUBcharacterTR)
2608 next if $Char eq '&';
2609 $String =~ s/\Q$Char\E/$Transcript/isg;
2612 # Replace newlines
2613 $String =~ s/[\n]/\\n/g;
2614 # Replace control characters with their backslashed octal ordinal numbers
2615 $String =~ s/([^\S \t])/(sprintf("\\0%o", ord($1)))/eisg; #
2616 $String =~ s/([\x00-\x08\x0A-\x1F])/(sprintf("\\0%o", ord($1)))/eisg; #
2618 return $String;
2622 # The initial open statements: Open a pipe to the foreign script interpreter
2623 sub OpenForeignScript # ($ContentType) -> $DirectivePrefix
2625 my $ContentType = lc(shift) || return "";
2626 my $NewDirective = "";
2628 return $NewDirective if($SCRIPTINGINPUT{$ContentType});
2630 # Construct a unique file handle name
2631 $SCRIPTINGFILEHANDLE = uc($ContentType);
2632 $SCRIPTINGFILEHANDLE =~ s/\W/\_/isg;
2633 $SCRIPTINGINPUT{$ContentType} = $SCRIPTINGFILEHANDLE
2634 unless $SCRIPTINGINPUT{$ContentType};
2636 # Create the relevant script: Open the pipe to the interpreter
2637 $NewDirective .= <<"BLOCKCGISCRIPTOROPEN";
2638 # Open interpreter for '$ContentType'
2639 # Open pipe to interpreter (if it isn't open already)
2640 open($SCRIPTINGINPUT{$ContentType}, "|$ScriptingLanguages{$ContentType}") || main::dieHandler(14, "$ContentType: \$!\\n");
2641 BLOCKCGISCRIPTOROPEN
2643 # Insert Initialization code and CGI variables
2644 $NewDirective .= InitializeForeignScript($ContentType);
2646 # Ready
2647 return $NewDirective;
2651 # The final closing code to stop the interpreter
2652 sub CloseForeignScript # ($ContentType) -> $DirectivePrefix
2654 my $ContentType = lc(shift) || return "";
2655 my $NewDirective = "";
2657 # Do nothing unless the pipe realy IS open
2658 return "" unless $SCRIPTINGINPUT{$ContentType};
2660 # Initial comment
2661 $NewDirective .= "\# Close interpreter for '$ContentType'\n";
2664 # Write the Postfix code
2665 $NewDirective .= CleanupForeignScript($ContentType);
2667 # Create the relevant script: Close the pipe to the interpreter
2668 $NewDirective .= <<"BLOCKCGISCRIPTORCLOSE";
2669 close($SCRIPTINGINPUT{$ContentType}) || main::dieHandler(15, \"$ContentType: \$!\\n\");
2670 select(STDOUT); \$|=1;
2672 BLOCKCGISCRIPTORCLOSE
2674 # Remove the file handler of the foreign script
2675 delete($SCRIPTINGINPUT{$ContentType});
2677 return $NewDirective;
2681 # The initialization code for the foreign script interpreter
2682 sub InitializeForeignScript # ($ContentType) -> $DirectivePrefix
2684 my $ContentType = lc(shift) || return "";
2685 my $NewDirective = "";
2687 # Add initialization code
2688 if($ScriptingInitialization{$ContentType})
2690 $NewDirective .= <<"BLOCKCGISCRIPTORINIT";
2691 # Initialization Code for '$ContentType'
2692 # Select relevant output filehandle
2693 select($SCRIPTINGINPUT{$ContentType}); \$|=1;
2695 # The Initialization code (if any)
2696 print $SCRIPTINGINPUT{$ContentType} <<'${ContentType}INITIALIZATIONCODE';
2697 $ScriptingInitialization{$ContentType}
2698 ${ContentType}INITIALIZATIONCODE
2700 BLOCKCGISCRIPTORINIT
2703 # Add all CGI variables defined
2704 if(exists($ScriptingCGIvariables{$ContentType}))
2706 # Start writing variable definitions to the Interpreter
2707 if($ScriptingCGIvariables{$ContentType})
2709 $NewDirective .= <<"BLOCKCGISCRIPTORVARDEF";
2710 # CGI variables (from the %default_values table)
2711 print $SCRIPTINGINPUT{$ContentType} << '${ContentType}CGIVARIABLES';
2712 BLOCKCGISCRIPTORVARDEF
2715 my ($N, $V);
2716 foreach $N (keys(%default_values))
2718 # Determine whether the parameter has been defined
2719 # (the eval is a workaround to get at the variable value)
2720 next unless eval("defined(\$CGIexecute::$N)");
2722 # Get the value from the EXECUTION environment
2723 $V = eval("\$CGIexecute::$N");
2724 # protect control characters (i.e., convert them to \0.. form)
2725 $V = shrubCGIparameter($V);
2727 # Protect interpolated variables
2728 eval("\$CGIexecute::$N = '$V';") unless $ScriptingCGIvariables{$ContentType};
2730 # Print the actual declaration for this scripting language
2731 if($ScriptingCGIvariables{$ContentType})
2733 $NewDirective .= sprintf($ScriptingCGIvariables{$ContentType}, $N, $V);
2734 $NewDirective .= "\n";
2738 # Stop writing variable definitions to the Interpreter
2739 if($ScriptingCGIvariables{$ContentType})
2741 $NewDirective .= <<"BLOCKCGISCRIPTORVARDEFEND";
2742 ${ContentType}CGIVARIABLES
2743 BLOCKCGISCRIPTORVARDEFEND
2748 $NewDirective .= << "BLOCKCGISCRIPTOREND";
2750 # Select STDOUT filehandle
2751 select(STDOUT); \$|=1;
2753 BLOCKCGISCRIPTOREND
2755 return $NewDirective;
2759 # The cleanup code for the foreign script interpreter
2760 sub CleanupForeignScript # ($ContentType) -> $DirectivePrefix
2762 my $ContentType = lc(shift) || return "";
2763 my $NewDirective = "";
2765 # Return if not needed
2766 return $NewDirective unless $ScriptingCleanup{$ContentType};
2768 # Create the relevant script: Open the pipe to the interpreter
2769 $NewDirective .= <<"BLOCKCGISCRIPTORSTOP";
2770 # Cleanup Code for '$ContentType'
2771 # Select relevant output filehandle
2772 select($SCRIPTINGINPUT{$ContentType}); \$|=1;
2773 # Print Cleanup code to foreign script
2774 print $SCRIPTINGINPUT{$ContentType} <<'${ContentType}SCRIPTSTOP';
2775 $ScriptingCleanup{$ContentType}
2776 ${ContentType}SCRIPTSTOP
2778 # Select STDOUT filehandle
2779 select(STDOUT); \$|=1;
2780 BLOCKCGISCRIPTORSTOP
2782 return $NewDirective;
2786 # The prefix code for each <script></script> block
2787 sub PrefixForeignScript # ($ContentType) -> $DirectivePrefix
2789 my $ContentType = lc(shift) || return "";
2790 my $NewDirective = "";
2792 # Return if not needed
2793 return $NewDirective unless $ScriptingPrefix{$ContentType};
2795 my $Quote = "\'";
2796 # If the CGIvariables parameter is defined, but empty, interpolate
2797 # code string (i.e., $var .= << "END" i.s.o. $var .= << 'END')
2798 $Quote = '"' if exists($ScriptingCGIvariables{$ContentType}) &&
2799 !$ScriptingCGIvariables{$ContentType};
2801 # Add initialization code
2802 $NewDirective .= <<"BLOCKCGISCRIPTORPREFIX";
2803 # Prefix Code for '$ContentType'
2804 # Select relevant output filehandle
2805 select($SCRIPTINGINPUT{$ContentType}); \$|=1;
2807 # The block Prefix code (if any)
2808 print $SCRIPTINGINPUT{$ContentType} <<$Quote${ContentType}PREFIXCODE$Quote;
2809 $ScriptingPrefix{$ContentType}
2810 ${ContentType}PREFIXCODE
2811 # Select STDOUT filehandle
2812 select(STDOUT); \$|=1;
2813 BLOCKCGISCRIPTORPREFIX
2815 return $NewDirective;
2819 # The postfix code for each <script></script> block
2820 sub PostfixForeignScript # ($ContentType) -> $DirectivePrefix
2822 my $ContentType = lc(shift) || return "";
2823 my $NewDirective = "";
2825 # Return if not needed
2826 return $NewDirective unless $ScriptingPostfix{$ContentType};
2828 my $Quote = "\'";
2829 # If the CGIvariables parameter is defined, but empty, interpolate
2830 # code string (i.e., $var .= << "END" i.s.o. $var .= << 'END')
2831 $Quote = '"' if exists($ScriptingCGIvariables{$ContentType}) &&
2832 !$ScriptingCGIvariables{$ContentType};
2834 # Create the relevant script: Open the pipe to the interpreter
2835 $NewDirective .= <<"BLOCKCGISCRIPTORPOSTFIX";
2836 # Postfix Code for '$ContentType'
2837 # Select filehandle to interpreter
2838 select($SCRIPTINGINPUT{$ContentType}); \$|=1;
2839 # Print postfix code to foreign script
2840 print $SCRIPTINGINPUT{$ContentType} <<$Quote${ContentType}SCRIPTPOSTFIX$Quote;
2841 $ScriptingPostfix{$ContentType}
2842 ${ContentType}SCRIPTPOSTFIX
2843 # Select STDOUT filehandle
2844 select(STDOUT); \$|=1;
2845 BLOCKCGISCRIPTORPOSTFIX
2847 return $NewDirective;
2850 sub InsertForeignScript # ($ContentType, $directive, @SRCfile) -> $NewDirective
2852 my $ContentType = lc(shift) || return "";
2853 my $directive = shift || return "";
2854 my @SRCfile = @_;
2855 my $NewDirective = "";
2857 my $Quote = "\'";
2858 # If the CGIvariables parameter is defined, but empty, interpolate
2859 # code string (i.e., $var .= << "END" i.s.o. $var .= << 'END')
2860 $Quote = '"' if exists($ScriptingCGIvariables{$ContentType}) &&
2861 !$ScriptingCGIvariables{$ContentType};
2863 # Create the relevant script
2864 $NewDirective .= <<"BLOCKCGISCRIPTORINSERT";
2865 # Insert Code for '$ContentType'
2866 # Select filehandle to interpreter
2867 select($SCRIPTINGINPUT{$ContentType}); \$|=1;
2868 BLOCKCGISCRIPTORINSERT
2870 # Use SRC feature files
2871 my $ThisSRCfile;
2872 while($ThisSRCfile = shift(@_))
2874 # Handle blocks
2875 if($ThisSRCfile =~ /^\s*\{\s*/)
2877 my $Block = $';
2878 $Block = $` if $Block =~ /\s*\}\s*$/;
2879 $NewDirective .= <<"BLOCKCGISCRIPTORSRCBLOCK";
2880 print $SCRIPTINGINPUT{$ContentType} <<$Quote${ContentType}SRCBLOCKCODE$Quote;
2881 $Block
2882 ${ContentType}SRCBLOCKCODE
2883 BLOCKCGISCRIPTORSRCBLOCK
2885 next;
2888 # Handle files
2889 $NewDirective .= <<"BLOCKCGISCRIPTORSRCFILES";
2890 # Read $ThisSRCfile
2891 open(SCRIPTINGSOURCE, "<$ThisSRCfile") || main::dieHandler(16, "$ThisSRCfILE: \$!");
2892 while(<SCRIPTINGSOURCE>)
2894 print $SCRIPTINGINPUT{$ContentType} \$_;
2896 close(SCRIPTINGSOURCE);
2898 BLOCKCGISCRIPTORSRCFILES
2902 # Add the directive
2903 if($directive)
2905 $NewDirective .= <<"BLOCKCGISCRIPTORINSERT";
2906 print $SCRIPTINGINPUT{$ContentType} <<$Quote${ContentType}DIRECTIVECODE$Quote;
2907 $directive
2908 ${ContentType}DIRECTIVECODE
2909 BLOCKCGISCRIPTORINSERT
2913 $NewDirective .= <<"BLOCKCGISCRIPTORSELECT";
2914 # Select STDOUT filehandle
2915 select(STDOUT); \$|=1;
2916 BLOCKCGISCRIPTORSELECT
2918 # Ready
2919 return $NewDirective;
2922 sub CloseAllForeignScripts # Call CloseForeignScript on all open scripts
2924 my $ContentType;
2925 foreach $ContentType (keys(%SCRIPTINGINPUT))
2927 my $directive = CloseForeignScript($ContentType);
2928 print STDERR "\nDirective $CGI_Date: ", $directive;
2929 CGIexecute->evaluate($directive);
2934 # End of handling foreign (external) scripting languages.
2936 ############################################################################
2938 # A subroutine to handle "nested" quotes, it cuts off the leading
2939 # item or quoted substring
2940 # E.g.,
2941 # ' A_word and more words' -> @('A_word', ' and more words')
2942 # '"quoted string" The rest' -> @('quoted string', ' The rest')
2943 # (this is needed for parsing the <TAGS> and their attributes)
2944 my $SupportedQuotes = "\'\"\`\(\{\[";
2945 my %QuotePairs = ('('=>')','['=>']','{'=>'}'); # Brackets
2946 sub ExtractQuotedItem # ($String) -> @($QuotedString, $RestOfString)
2948 my @Result = ();
2949 my $String = shift || return @Result;
2951 if($String =~ /^\s*([\w\/\-\.]+)/is)
2953 push(@Result, $1, $');
2955 elsif($String =~ /^\s*(\\?)([\Q$SupportedQuotes\E])/is)
2957 my $BackSlash = $1 || "";
2958 my $OpenQuote = $2;
2959 my $CloseQuote = $OpenQuote;
2960 $CloseQuote = $QuotePairs{$OpenQuote} if $QuotePairs{$OpenQuote};
2962 if($BackSlash)
2964 $String =~ /^\s*\\\Q$OpenQuote\E/i;
2965 my $Onset = $';
2966 $Onset =~ /\\\Q$CloseQuote\E/i;
2967 my $Rest = $';
2968 my $Item = $`;
2969 push(@Result, $Item, $Rest);
2972 else
2974 $String =~ /^\s*\Q$OpenQuote\E([^\Q$CloseQuote\E]*)\Q$CloseQuote\E/i;
2975 push(@Result, $1, $');
2978 else
2980 push(@Result, "", $String);
2982 return @Result;
2985 # Now, start with the real work
2987 # Control the output of the Content-type: text/html\n\n message
2988 my $SupressContentType = 0;
2990 # Process a file
2991 sub ProcessFile # ($file_path)
2993 my $file_path = shift || return 0;
2996 # Generate a unique file handle (for recursions)
2997 my @SRClist = ();
2998 my $FileHandle = "file";
2999 my $n = 0;
3000 while(!eof($FileHandle.$n)) {++$n;};
3001 $FileHandle .= $n;
3003 # Start HTML output
3004 # Use the default Content-type if this is NOT a raw file
3005 unless(($RawFilePattern && $ENV{'PATH_INFO'} =~ m@($RawFilePattern)$@i)
3006 || $SupressContentType)
3008 $ENV{'PATH_INFO'} =~ m@($FilePattern)$@i;
3009 my $ContentType = $ContentTypeTable{$1};
3010 print "Content-type: $ContentType\n";
3011 print "\n";
3012 $SupressContentType = 1; # Content type has been printed
3016 # Get access to the actual data. This can be from RAM (by way of an
3017 # environment variable) or by opening a file.
3019 # Handle the use of RAM images (file-data is stored in the
3020 # $CGI_FILE_CONTENTS environment variable)
3021 # Note that this environment variable will be cleared, i.e., it is strictly for
3022 # single-use only!
3023 if($ENV{$CGI_FILE_CONTENTS})
3025 # File has been read already
3026 $_ = $ENV{$CGI_FILE_CONTENTS};
3027 # Sorry, you have to do the reading yourself (dynamic document creation?)
3028 # NOTE: you must read the whole document at once
3029 if($_ eq '-')
3031 $_ = eval("\@_=('$file_path'); do{$ENV{$CGI_DATA_ACCESS_CODE}}");
3033 else # Clear environment variable
3035 $ENV{$CGI_FILE_CONTENTS} = '-';
3038 # Open Only PLAIN TEXT files (or STDIN) and NO executable files (i.e., scripts).
3039 # THIS IS A SECURITY FEATURE!
3040 elsif($file_path eq '-' || (-e "$file_path" && -r _ && -T _ && -f _ && ! (-x _ || -X _) ))
3042 open($FileHandle, $file_path) || dieHandler(17, "<h2>File not found</h2>\n");
3043 push(@OpenFiles, $file_path);
3044 $_ = <$FileHandle>; # Read first line
3046 else
3048 print "<h2>File not found</h2>\n";
3049 dieHandler(18, "$file_path\n");
3052 $| = 1; # Flush output buffers
3054 # Initialize variables
3055 my $METAarguments = ""; # The CGI arguments from the latest META tag
3056 my @METAvalues = (); # The ''-quoted CGI values from the latest META tag
3057 my $ClosedTag = 0; # <TAG> </TAG> versus <TAG/>
3060 # Send document to output
3061 # Process the requested document.
3062 # Do a loop BEFORE reading input again (this catches the RAM/Database
3063 # type of documents).
3064 do {
3067 # Handle translations if needed
3069 performTranslation(\$_) if $TranslationPaths;
3071 # Catch <SCRIPT LANGUAGE="PERL" TYPE="text/ssperl" > directives in $_
3072 # There can be more than 1 <SCRIPT> or META tags on a line
3073 while(/\<\s*(SCRIPT|META|DIV|INS)\s/is)
3075 my $directive = "";
3076 # Store rest of line
3077 my $Before = $`;
3078 my $ScriptTag = $&;
3079 my $After = $';
3080 my $TagType = uc($1);
3081 # The before part can be send to the output
3082 print $Before;
3084 # Read complete Tag from after and/or file
3085 until($After =~ /([^\\])\>/)
3087 $After .= <$FileHandle>;
3088 performTranslation(\$After) if $TranslationPaths;
3091 if($After =~ /([^\\])\>/)
3093 $ScriptTag .= $`.$&; # Keep the Script Tag intact
3094 $After = $';
3096 else
3098 dieHandler(19, "Closing > not found\n");
3101 # The tag could be closed by />, we handle this in the XML way
3102 # and don't process any content (we ignore whitespace)
3103 $ClosedTag = ($ScriptTag =~ m@[^\\]/\s*\>\s*$@) ? 1 : 0;
3106 # TYPE or CLASS?
3107 my $TypeName = ($TagType =~ /META/is) ? "CONTENT" : "TYPE";
3108 $TypeName = "CLASS" if $TagType eq 'DIV' || $TagType eq 'INS';
3110 # Parse <SCRIPT> or <META> directive
3111 # If NOT (TYPE|CONTENT)="text/ssperl" (i.e., $ServerScriptContentType),
3112 # send the line to the output and go to the next loop
3113 my $CurrentContentType = "";
3114 if($ScriptTag =~ /(^|\s)$TypeName\s*=\s*/is)
3116 my ($Type) = ExtractQuotedItem($');
3117 $Type =~ /^\s*([\w\/\-]+)\s*[\,\;]?/;
3118 $CurrentContentType = lc($1); # Note: mime-types are "case-less"
3119 # CSS classes are aliases of $ServerScriptContentType
3120 if($TypeName eq "CLASS" && $CurrentContentType eq $ServerScriptContentClass)
3122 $CurrentContentType = $ServerScriptContentType;
3127 # Not a known server-side content type, print and continue
3128 unless(($CurrentContentType =~
3129 /$ServerScriptContentType|$ShellScriptContentType/is) ||
3130 $ScriptingLanguages{$CurrentContentType})
3132 print $ScriptTag;
3133 $_ = $After;
3134 next;
3138 # A known server-side content type, evaluate
3140 # First, handle \> and \<
3141 $ScriptTag =~ s/\\\>/\>/isg;
3142 $ScriptTag =~ s/\\\</\</isg;
3144 # Extract the CGI, SRC, ID, IF and UNLESS attributes
3145 my %ScriptTagAttributes = ();
3146 while($ScriptTag =~ /(^|\s)(CGI|IF|UNLESS|SRC|ID)\s*=\s*/is)
3148 my $Attribute = $2;
3149 my $Rest = $';
3150 my $Value = "";
3151 ($Value, $ScriptTag) = ExtractQuotedItem($Rest);
3152 $ScriptTagAttributes{uc($Attribute)} = $Value;
3156 # The attribute used to define the CGI variables
3157 # Extract CGI-variables from
3158 # <META CONTENT="text/ssperl; CGI='' SRC=''">
3159 # <SCRIPT TYPE='text/ssperl' CGI='' SRC=''>
3160 # <DIV CLASS='ssperl' CGI='' SRC='' ID=""> tags
3161 # <INS CLASS='ssperl' CGI='' SRC='' ID=""> tags
3162 if($ScriptTagAttributes{'CGI'})
3164 @ARGV = (); # Reset ARGV
3165 $ARGC = 0;
3166 $METAarguments = ""; # Reset the META CGI arguments
3167 @METAvalues = ();
3168 my $Meta_CGI = $ScriptTagAttributes{'CGI'};
3170 # Process default values of variables ($<name> = 'default value')
3171 # Allowed quotes are '', "", ``, (), [], and {}
3172 while($Meta_CGI =~ /(^\s*|[^\\])([\$\@\%]?)([\w\-]+)\s*/is)
3174 my $varType = $2 || '$'; # Variable or list
3175 my $name = $3; # The Name
3176 my $default = "";
3177 $Meta_CGI = $';
3179 if($Meta_CGI =~ /^\s*\=\s*/is)
3181 # Locate (any) default value
3182 ($default, $Meta_CGI) = ExtractQuotedItem($'); # Cut the parameter from the CGI
3184 $RemainingTag = $Meta_CGI;
3187 # Define CGI (or ENV) variable, initalize it from the
3188 # Query string or the default value
3190 # Also construct the @ARGV and @_ arrays. This allows other (SRC=) Perl
3191 # scripts to access the CGI arguments defined in the META tag
3192 # (Not for CGI inside <SCRIPT> tags)
3193 if($varType eq '$')
3195 CGIexecute::defineCGIvariable($name, $default)
3196 || dieHandler(20, "INVALID CGI name/value pair ($name, $default)\n");
3197 push(@METAvalues, "'".${"CGIexecute::$name"}."'");
3198 # Add value to the @ARGV list
3199 push(@ARGV, ${"CGIexecute::$name"});
3200 ++$ARGC;
3202 elsif($varType eq '@')
3204 CGIexecute::defineCGIvariableList($name, $default)
3205 || dieHandler(21, "INVALID CGI name/value list pair ($name, $default)\n");
3206 push(@METAvalues, "'".join("'", @{"CGIexecute::$name"})."'");
3207 # Add value to the @ARGV list
3208 push(@ARGV, @{"CGIexecute::$name"});
3209 $ARGC = scalar(@CGIexecute::ARGV);
3211 elsif($varType eq '%')
3213 CGIexecute::defineCGIvariableHash($name, $default)
3214 || dieHandler(22, "INVALID CGI name/value hash pair ($name, $default)\n");
3215 my @PairList = map {"$_ => ".${"CGIexecute::$name"}{$_}} keys(%{"CGIexecute::$name"});
3216 push(@METAvalues, "'".join("'", @PairList)."'");
3217 # Add value to the @ARGV list
3218 push(@ARGV, %{"CGIexecute::$name"});
3219 $ARGC = scalar(@CGIexecute::ARGV);
3222 # Store the values for internal and later use
3223 $METAarguments .= "$varType".$name.","; # A string of CGI variable names
3225 push(@METAvalues, "\'".eval("\"$varType\{CGIexecute::$name\}\"")."\'"); # ALWAYS add '-quotes around values
3230 # The IF (conditional execution) Attribute
3231 # Evaluate the condition and stop unless it evaluates to true
3232 if($ScriptTagAttributes{'IF'})
3234 my $IFcondition = $ScriptTagAttributes{'IF'};
3236 # Convert SCRIPT calls, ./<script>
3237 $IFcondition =~ s@([\W]|^)\./([\S])@$1$SCRIPT_SUB$2@g;
3239 # Convert FILE calls, ~/<file>
3240 $IFcondition =~ s@([\W])\~/([\S])@$1$HOME_SUB$2@g;
3242 # Block execution if necessary
3243 unless(CGIexecute->evaluate($IFcondition))
3245 %ScriptTagAttributes = ();
3246 $CurrentContentType = "";
3250 # The UNLESS (conditional execution) Attribute
3251 # Evaluate the condition and stop if it evaluates to true
3252 if($ScriptTagAttributes{'UNLESS'})
3254 my $UNLESScondition = $ScriptTagAttributes{'UNLESS'};
3256 # Convert SCRIPT calls, ./<script>
3257 $UNLESScondition =~ s@([\W]|^)\./([\S])@$1$SCRIPT_SUB$2@g;
3259 # Convert FILE calls, ~/<file>
3260 $UNLESScondition =~ s@([\W])\~/([\S])@$1$HOME_SUB$2@g;
3262 # Block execution if necessary
3263 if(CGIexecute->evaluate($UNLESScondition))
3265 %ScriptTagAttributes = ();
3266 $CurrentContentType = "";
3270 # The SRC (Source File) Attribute
3271 # Extract any source script files and add them in
3272 # front of the directive
3273 # The SRC list should be emptied
3274 @SRClist = ();
3275 my $SRCtag = "";
3276 my $Prefix = 1;
3277 my $PrefixDirective = "";
3278 my $PostfixDirective = "";
3279 # There is a SRC attribute
3280 if($ScriptTagAttributes{'SRC'})
3282 $SRCtag = $ScriptTagAttributes{'SRC'};
3283 # Remove "file://" prefixes
3284 $SRCtag =~ s@([^\w\/\\]|^)file\://([^\s\/\@\=])@$1$2@gis;
3285 # Expand script filenames "./Script"
3286 $SRCtag =~ s@([^\w\/\\]|^)\./([^\s\/\@\=])@$1$SCRIPT_SUB/$2@gis;
3287 # Expand script filenames "~/Script"
3288 $SRCtag =~ s@([^\w\/\\]|^)\~/([^\s\/\@\=])@$1$HOME_SUB/$2@gis;
3291 # File source tags
3292 while($SRCtag =~ /\S/is)
3294 my $SRCdirective = "";
3296 # Pseudo file, just a switch to go from PREFIXING to POSTFIXING
3297 # SRC files
3298 if($SRCtag =~ /^[\s\;\,]*(POSTFIX|PREFIX)([^$FileAllowedChars]|$)/is)
3300 my $InsertionPlace = $1;
3301 $SRCtag = $2.$';
3303 $Prefix = $InsertionPlace =~ /POSTFIX/i ? 0 : 1;
3304 # Go to next round
3305 next;
3307 # {}-blocks are just evaluated by "do"
3308 elsif($SRCtag =~ /^[\s\;\,]*\{/is)
3310 my $SRCblock = $';
3311 if($SRCblock =~ /\}[\s\;\,]*([^\}]*)$/is)
3313 $SRCblock = $`;
3314 $SRCtag = $1.$';
3315 # SAFEqx shell script blocks
3316 if($CurrentContentType =~ /$ShellScriptContentType/is)
3318 # Handle ''-quotes inside the script
3319 $SRCblock =~ s/[\']/\\$&/gis;
3321 $SRCblock = "print do { SAFEqx(\'".$SRCblock."\'); };'';";
3322 $SRCdirective .= $SRCblock."\n";
3324 # do { SRCblocks }
3325 elsif($CurrentContentType =~ /$ServerScriptContentType/is)
3327 $SRCblock = "print do { $SRCblock };'';";
3328 $SRCdirective .= $SRCblock."\n";
3330 else # The interpreter should handle this
3332 push(@SRClist, "{ $SRCblock }");
3336 else
3337 { dieHandler(23, "Closing \} missing\n");};
3339 # Files are processed as Text or Executable files
3340 elsif($SRCtag =~ /[\s\;\,]*([$FileAllowedChars]+)[\;\,\s]*/is)
3342 my $SrcFile = $1;
3343 $SRCtag = $';
3345 # We are handling one of the external interpreters
3346 if($ScriptingLanguages{$CurrentContentType})
3348 push(@SRClist, $SrcFile);
3350 # We are at the start of a DIV tag, just load all SRC files and/or URL's
3351 elsif($TagType eq 'DIV' || $TagType eq 'INS') # All files are prepended in DIV's
3353 # $SrcFile is a URL pointing to an HTTP or FTP server
3354 if($SrcFile =~ m!^([a-z]+)\://!)
3356 my $URLoutput = CGIscriptor::read_url($SrcFile);
3357 $SRCdirective .= $URLoutput;
3359 # SRC file is an existing file
3360 elsif(-e "$SrcFile")
3362 open(DIVSOURCE, "<$SrcFile") || dieHandler(24, "<$SrcFile: $!\n");
3363 my $Content;
3364 while(sysread(DIVSOURCE, $Content, 1024) > 0)
3366 $SRCdirective .= $Content;
3368 close(DIVSOURCE);
3371 # Executable files are executed as
3372 # `$SrcFile 'ARGV[0]' 'ARGV[1]'`
3373 elsif(-x "$SrcFile")
3375 $SRCdirective .= "print \`$SrcFile @METAvalues\`;'';\n";
3377 # Handle 'standard' files, using ProcessFile
3378 elsif((-T "$SrcFile" || $ENV{$CGI_FILE_CONTENTS})
3379 && $SrcFile =~ m@($FilePattern)$@) # A recursion
3382 # Do not process still open files because it can lead
3383 # to endless recursions
3384 if(grep(/^$SrcFile$/, @OpenFiles))
3385 { dieHandler(25, "$SrcFile allready opened (endless recursion)\n")};
3386 # Prepare meta arguments
3387 $SRCdirective .= '@ARGV = (' .$METAarguments.");\n" if $METAarguments;
3388 # Process the file
3389 $SRCdirective .= "main::ProcessFile(\'$SrcFile\');'';\n";
3391 elsif($SrcFile =~ m!^([a-z]+)\://!) # URL's are loaded and printed
3393 $SRCdirective .= GET_URL($SrcFile);
3395 elsif(-T "$SrcFile") # Textfiles are "do"-ed (Perl execution)
3397 $SRCdirective .= '@ARGV = (' .$METAarguments.");\n" if $METAarguments;
3398 $SRCdirective .= "do \'$SrcFile\';'';\n";
3400 else # This one could not be resolved (should be handled by BinaryMapFile)
3402 $SRCdirective .= 'print "'.$SrcFile.' cannot be used"'."\n";
3407 # Postfix or Prefix
3408 if($Prefix)
3410 $PrefixDirective .= $SRCdirective;
3412 else
3414 $PostfixDirective .= $SRCdirective;
3417 # The prefix should be handled immediately
3418 $directive .= $PrefixDirective;
3419 $PrefixDirective = "";
3423 # Handle the content of the <SCRIPT></SCRIPT> tags
3424 # Do not process the content of <SCRIPT/>
3425 if($TagType =~ /SCRIPT/is && !$ClosedTag) # The <SCRIPT> TAG
3427 my $EndScriptTag = "";
3429 # Execute SHELL scripts with SAFEqx()
3430 if($CurrentContentType =~ /$ShellScriptContentType/is)
3432 $directive .= "SAFEqx(\'";
3435 # Extract Program
3436 while($After !~ /\<\s*\/SCRIPT[^\>]*\>/is && !eof($FileHandle))
3438 $After .= <$FileHandle>;
3439 performTranslation(\$After) if $TranslationPaths;
3442 if($After =~ /\<\s*\/SCRIPT[^\>]*\>/is)
3444 $directive .= $`;
3445 $EndScriptTag = $&;
3446 $After = $';
3448 else
3450 dieHandler(26, "Missing </SCRIPT> end tag in $ENV{'PATH_INFO'}\n");
3453 # Process only when content should be executed
3454 if($CurrentContentType)
3457 # Remove all comments from Perl scripts
3458 # (NOT from OS shell scripts)
3459 $directive =~ s/[^\\\$]\#[^\n\f\r]*([\n\f\r])/$1/g
3460 if $CurrentContentType =~ /$ServerScriptContentType/i;
3462 # Convert SCRIPT calls, ./<script>
3463 $directive =~ s@([\W]|^)\./([\S])@$1$SCRIPT_SUB$2@g;
3465 # Convert FILE calls, ~/<file>
3466 $directive =~ s@([\W])\~/([\S])@$1$HOME_SUB$2@g;
3468 # Execute SHELL scripts with SAFEqx(), closing bracket
3469 if($CurrentContentType =~ /$ShellScriptContentType/i)
3471 # Handle ''-quotes inside the script
3472 $directive =~ /SAFEqx\(\'/;
3473 $directive = $`.$&;
3474 my $Executable = $';
3475 $Executable =~ s/[\']/\\$&/gs;
3477 $directive .= $Executable."\');"; # Closing bracket
3480 else
3482 $directive = "";
3485 # Handle the content of the <DIV></DIV> tags
3486 # Do not process the content of <DIV/>
3487 elsif(($TagType eq 'DIV' || $TagType eq 'INS') && !$ClosedTag) # The <DIV> TAGs
3489 my $EndScriptTag = "";
3491 # Extract Text
3492 while($After !~ /\<\s*\/$TagType[^\>]*\>/is && !eof($FileHandle))
3494 $After .= <$FileHandle>;
3495 performTranslation(\$After) if $TranslationPaths;
3498 if($After =~ /\<\s*\/$TagType[^\>]*\>/is)
3500 $directive .= $`;
3501 $EndScriptTag = $&;
3502 $After = $';
3504 else
3506 dieHandler(27, "Missing </$TagType> end tag in $ENV{'PATH_INFO'}\n");
3509 # Add the Postfixed directives (but only when it contains something printable)
3510 $directive .= "\n".$PostfixDirective if $PostfixDirective =~ /\S/;
3511 $PostfixDirective = "";
3514 # Process only when content should be handled
3515 if($CurrentContentType)
3518 # Get the name (ID), and clean it (i.e., remove anything that is NOT part of
3519 # a valid Perl name). Names should not contain $, but we can handle it.
3520 my $name = $ScriptTagAttributes{'ID'};
3521 $name =~ /^\s*[\$\@\%]?([\w\-]+)/;
3522 $name = $1;
3524 # Assign DIV contents to $NAME value OUTSIDE the CGI values!
3525 CGIexecute::defineCGIexecuteVariable($name, $directive);
3526 $directive = "";
3529 # Nothing to execute
3530 $directive = "";
3534 # Handle Foreign scripting languages
3535 if($ScriptingLanguages{$CurrentContentType})
3537 my $newDirective = "";
3538 $newDirective .= OpenForeignScript($CurrentContentType); # Only if not already done
3539 $newDirective .= PrefixForeignScript($CurrentContentType);
3540 $newDirective .= InsertForeignScript($CurrentContentType, $directive, @SRClist);
3541 $newDirective .= PostfixForeignScript($CurrentContentType);
3542 $newDirective .= CloseForeignScript($CurrentContentType); # This shouldn't be necessary
3544 $newDirective .= '"";';
3546 $directive = $newDirective;
3550 # Add the Postfixed directives (but only when it contains something printable)
3551 $directive .= "\n".$PostfixDirective if $PostfixDirective =~ /\S/;
3552 $PostfixDirective = "";
3555 # EXECUTE the script and print the results
3557 # Use this to debug the program
3558 # print STDERR "Directive $CGI_Date: \n", $directive, "\n\n";
3560 my $Result = CGIexecute->evaluate($directive) if $directive; # Evaluate as PERL code
3561 $Result =~ s/\n$//g; # Remove final newline
3563 # Print the Result of evaluating the directive
3564 # (this will handle LARGE, >64 kB output)
3565 my $BytesWritten = 1;
3566 while($Result && $BytesWritten)
3568 $BytesWritten = syswrite(STDOUT, $Result, 64);
3569 $Result = substr($Result, $BytesWritten);
3571 # print $Result; # Could be used instead of above code
3573 # Store result if wanted, i.e., if $CGIscriptorResults has been
3574 # defined in a <META> tag.
3575 push(@CGIexecute::CGIscriptorResults, $Result)
3576 if exists($default_values{'CGIscriptorResults'});
3578 # Process the rest of the input line (this could contain
3579 # another directive)
3580 $_ = $After;
3582 print $_;
3583 } while(<$FileHandle>); # Read and Test AFTER first loop!
3585 close ($FileHandle);
3586 dieHandler(28, "Error in recursion\n") unless pop(@OpenFiles) == $file_path;
3590 ###############################################################################
3592 # Call the whole package
3594 sub Handle_Request
3596 my $file_path = "";
3598 # Initialization Code
3599 Initialize_Request();
3601 # SECURITY: ACCESS CONTROL
3602 Access_Control();
3604 # Read the POST part of the query, if there is one
3605 Get_POST_part_of_query();
3607 # Start (HTML) output and logging
3608 $file_path = Initialize_output();
3610 # Record which files are still open (to avoid endless recursions)
3611 my @OpenFiles = ();
3613 # Record whether the default HTML ContentType has already been printed
3614 # but only if the SERVER uses HTTP or some other protocol that might interpret
3615 # a content MIME type.
3617 $SupressContentType = !("$ENV{'SERVER_PROTOCOL'}" =~ /($ContentTypeServerProtocols)/i);
3619 # Process the specified file
3620 ProcessFile($file_path) if $file_path ne $SS_PUB;
3622 # Cleanup all open external (foreign) interpreters
3623 CloseAllForeignScripts();
3626 "" # SUCCESS
3629 # Make a single call to handle an (empty) request
3630 Handle_Request();
3633 # END OF PACKAGE MAIN
3636 ####################################################################################
3638 # The CGIEXECUTE PACKAGE
3640 ####################################################################################
3642 # Isolate the evaluation of directives as PERL code from the rest of the program.
3643 # Remember that each package has its own name space.
3644 # Note that only the FIRST argument of execute->evaluate is actually evaluated,
3645 # all other arguments are accessible inside the first argument as $_[0] to $_[$#_].
3647 package CGIexecute;
3649 sub evaluate
3651 my $self = shift;
3652 my $directive = shift;
3653 $directive = eval($directive);
3654 warn $@ if $@; # Write an error message to STDERR
3655 $directive; # Return value of directive
3659 # defineCGIexecuteVariable($name [, $value]) -> 0/1
3661 # Define and intialize variables inside CGIexecute
3662 # Does no sanity checking, for internal use only
3664 sub defineCGIexecuteVariable # ($name [, $value]) -> 0/1
3666 my $name = shift || return 0; # The Name
3667 my $value = shift || ""; # The value
3669 ${$name} = $value;
3671 return 1;
3674 # defineCGIvariable($name [, $default]) -> 0/1
3676 # Define and intialize CGI variables
3677 # Tries (in order) $ENV{$name}, the Query string and the
3678 # default value.
3679 # Removes all '-quotes etc.
3681 sub defineCGIvariable # ($name [, $default]) -> 0/1
3683 my $name = shift || return 0; # The Name
3684 my $default = shift || ""; # The default value
3686 # Remove \-quoted characters
3687 $default =~ s/\\(.)/$1/g;
3688 # Store default values
3689 $::default_values{$name} = $default if $default;
3691 # Process variables
3692 my $temp = undef;
3693 # If there is a user supplied value, it replaces the
3694 # default value.
3696 # Environment values have precedence
3697 if(exists($ENV{$name}))
3699 $temp = $ENV{$name};
3701 # Get name and its value from the query string
3702 elsif($ENV{QUERY_STRING} =~ /$name/) # $name is in the query string
3704 $temp = ::YOUR_CGIPARSE($name);
3706 # Defined values must exist for security
3707 elsif(!exists($::default_values{$name}))
3709 $::default_values{$name} = undef;
3712 # SECURITY, do not allow '- and `-quotes in
3713 # client values.
3714 # Remove all existing '-quotes
3715 $temp =~ s/([\r\f]+\n)/\n/g; # Only \n is allowed
3716 $temp =~ s/[\']/&#8217;/igs; # Remove all single quotes
3717 $temp =~ s/[\`]/&#8216;/igs; # Remove all backtick quotes
3718 # If $temp is empty, use the default value (if it exists)
3719 unless($temp =~ /\S/ || length($temp) > 0) # I.e., $temp is empty
3721 $temp = $::default_values{$name};
3722 # Remove all existing '-quotes
3723 $temp =~ s/([\r\f]+\n)/\n/g; # Only \n is allowed
3724 $temp =~ s/[\']/&#8217;/igs; # Remove all single quotes
3725 $temp =~ s/[\`]/&#8216;/igs; # Remove all backtick quotes
3727 else # Store current CGI values and remove defaults
3729 $::default_values{$name} = $temp;
3731 # Define the CGI variable and its value (in the execute package)
3732 ${$name} = $temp;
3734 # return SUCCES
3735 return 1;
3738 sub defineCGIvariableList # ($name [, $default]) -> 0/1)
3740 my $name = shift || return 0; # The Name
3741 my $default = shift || ""; # The default value
3743 # Defined values must exist for security
3744 if(!exists($::default_values{$name}))
3746 $::default_values{$name} = $default;
3749 my @temp = ();
3752 # For security:
3753 # Environment values have precedence
3754 if(exists($ENV{$name}))
3756 push(@temp, $ENV{$name});
3758 # Get name and its values from the query string
3759 if($ENV{QUERY_STRING} =~ /$name/) # $name is in the query string
3761 push(@temp, ::YOUR_CGIPARSE($name, 1)); # Extract LIST
3763 else
3765 push(@temp, $::default_values{$name});
3769 # SECURITY, do not allow '- and `-quotes in
3770 # client values.
3771 # Remove all existing '-quotes
3772 @temp = map {s/([\r\f]+\n)/\n/g; $_} @temp; # Only \n is allowed
3773 @temp = map {s/[\']/&#8217;/igs; $_} @temp; # Remove all single quotes
3774 @temp = map {s/[\`]/&#8216;/igs; $_} @temp; # Remove all backtick quotes
3776 # Store current CGI values and remove defaults
3777 $::default_values{$name} = $temp[0];
3779 # Define the CGI variable and its value (in the execute package)
3780 @{$name} = @temp;
3782 # return SUCCES
3783 return 1;
3786 sub defineCGIvariableHash # ($name [, $default]) -> 0/1) Note: '$name{""} = $default';
3788 my $name = shift || return 0; # The Name
3789 my $default = shift || ""; # The default value
3791 # Defined values must exist for security
3792 if(!exists($::default_values{$name}))
3794 $::default_values{$name} = $default;
3797 my %temp = ();
3800 # For security:
3801 # Environment values have precedence
3802 if(exists($ENV{$name}))
3804 $temp{""} = $ENV{$name};
3806 # Get name and its values from the query string
3807 if($ENV{QUERY_STRING} =~ /$name/) # $name is in the query string
3809 %temp = ::YOUR_CGIPARSE($name, -1); # Extract HASH table
3811 elsif($::default_values{$name} ne "")
3813 $temp{""} = $::default_values{$name};
3817 # SECURITY, do not allow '- and `-quotes in
3818 # client values.
3819 # Remove all existing '-quotes
3820 my $Key;
3821 foreach $Key (keys(%temp))
3823 $temp{$Key} =~ s/([\r\f]+\n)/\n/g; # Only \n is allowed
3824 $temp{$Key} =~ s/[\']/&#8217;/igs; # Remove all single quotes
3825 $temp{$Key} =~ s/[\`]/&#8216;/igs; # Remove all backtick quotes
3828 # Store current CGI values and remove defaults
3829 $::default_values{$name} = $temp{""};
3831 # Define the CGI variable and its value (in the execute package)
3832 %{$name} = ();
3833 my $tempKey;
3834 foreach $tempKey (keys(%temp))
3836 ${$name}{$tempKey} = $temp{$tempKey};
3839 # return SUCCES
3840 return 1;
3844 # SAFEqx('CommandString')
3846 # A special function that is a safe alternative to backtick quotes (and qx//)
3847 # with client-supplied CGI values. All CGI variables are surrounded by
3848 # single ''-quotes (except between existing \'\'-quotes, don't try to be
3849 # too smart). All variables are then interpolated. Simple (@) lists are
3850 # expanded with join(' ', @List), and simple (%) hash tables expanded
3851 # as a list of "key=value" pairs. Complex variables, e.g., @$var, are
3852 # evaluated in a scalar context (e.g., as scalar(@$var)). All occurrences of
3853 # $@% that should NOT be interpolated must be preceeded by a "\".
3854 # If the first line of the String starts with "#! interpreter", the
3855 # remainder of the string is piped into interpreter (after interpolation), i.e.,
3856 # open(INTERPRETER, "|interpreter");print INTERPRETER remainder;
3857 # just like in UNIX. There are some problems with quotes. Be carefull in
3858 # using them. You do not have access to the output of any piped (#!)
3859 # process! If you want such access, execute
3860 # <SCRIPT TYPE="text/osshell">echo "script"|interpreter</SCRIPT> or
3861 # <SCRIPT TYPE="text/ssperl">$resultvar = SAFEqx('echo "script"|interpreter');
3862 # </SCRIPT>.
3864 # SAFEqx ONLY WORKS WHEN THE STRING ITSELF IS SURROUNDED BY SINGLE QUOTES
3865 # (SO THAT IT IS NOT INTERPOLATED BEFORE IT CAN BE PROTECTED)
3866 sub SAFEqx # ('String') -> result of executing qx/"String"/
3868 my $CommandString = shift;
3869 my $NewCommandString = "";
3871 # Only interpolate when required (check the On/Off switch)
3872 unless($CGIscriptor::NoShellScriptInterpolation)
3875 # Handle existing single quotes around CGI values
3876 while($CommandString =~ /\'[^\']+\'/s)
3878 my $CurrentQuotedString = $&;
3879 $NewCommandString .= $`;
3880 $CommandString = $'; # The remaining string
3881 # Interpolate CGI variables between quotes
3882 # (e.g., '$CGIscriptorResults[-1]')
3883 $CurrentQuotedString =~
3884 s/(^|[^\\])([\$\@])((\w*)([\{\[][\$\@\%]?[\:\w\-]+[\}\]])*)/if(exists($main::default_values{$4})){
3885 "$1".eval("$2$3")}else{"$&"}/egs;
3887 # Combine result with previous result
3888 $NewCommandString .= $CurrentQuotedString;
3890 $CommandString = $NewCommandString.$CommandString;
3892 # Select known CGI variables and surround them with single quotes,
3893 # then interpolate all variables
3894 $CommandString =~
3895 s/(^|[^\\])([\$\@\%]+)((\w*)([\{\[][\w\:\$\"\-]+[\}\]])*)/
3896 if($2 eq '$' && exists($main::default_values{$4}))
3897 {"$1\'".eval("\$$3")."\'";}
3898 elsif($2 eq '@'){$1.join(' ', @{"$3"});}
3899 elsif($2 eq '%'){my $t=$1;map {$t.=" $_=".${"$3"}{$_}}
3900 keys(%{"$3"});$t}
3901 else{$1.eval("${2}$3");
3902 }/egs;
3904 # Remove backslashed [$@%]
3905 $CommandString =~ s/\\([\$\@\%])/$1/gs;
3908 # Debugging
3909 # return $CommandString;
3911 # Handle UNIX style "#! shell command\n" constructs as
3912 # a pipe into the shell command. The output cannot be tapped.
3913 my $ReturnValue = "";
3914 if($CommandString =~ /^\s*\#\!([^\f\n\r]+)[\f\n\r]/is)
3916 my $ShellScripts = $';
3917 my $ShellCommand = $1;
3918 open(INTERPRETER, "|$ShellCommand") || dieHandler(29, "\'$ShellCommand\' PIPE not opened: &!\n");
3919 select(INTERPRETER);$| = 1;
3920 print INTERPRETER $ShellScripts;
3921 close(INTERPRETER);
3922 select(STDOUT);$| = 1;
3924 # Shell scripts which are redirected to an existing named pipe.
3925 # The output cannot be tapped.
3926 elsif($CGIscriptor::ShellScriptPIPE)
3928 CGIscriptor::printSAFEqxPIPE($CommandString);
3930 else # Plain ``-backtick execution
3932 # Execute the commands
3933 $ReturnValue = qx/$CommandString/;
3935 return $ReturnValue;
3938 ####################################################################################
3940 # The CGIscriptor PACKAGE
3942 ####################################################################################
3944 # Isolate the evaluation of CGIscriptor functions, i.e., those prefixed with
3945 # "CGIscriptor::"
3947 package CGIscriptor;
3950 # The Interpolation On/Off switch
3951 my $NoShellScriptInterpolation = undef;
3952 # The ShellScript redirection pipe
3953 my $ShellScriptPIPE = undef;
3955 # Open a named PIPE for SAFEqx to receive ALL shell scripts
3956 sub RedirectShellScript # ('CommandString')
3958 my $CommandString = shift || undef;
3960 if($CommandString)
3962 $ShellScriptPIPE = "ShellScriptNamedPipe";
3963 open($ShellScriptPIPE, "|$CommandString")
3964 || main::dieHandler(30, "\'|$CommandString\' PIPE open failed: $!\n");
3966 else
3968 close($ShellScriptPIPE);
3969 $ShellScriptPIPE = undef;
3971 return $ShellScriptPIPE;
3974 # Print to redirected shell script pipe
3975 sub printSAFEqxPIPE # ("String") -> print return value
3977 my $String = shift || undef;
3979 select($ShellScriptPIPE); $| = 1;
3980 my $returnvalue = print $ShellScriptPIPE ($String);
3981 select(STDOUT); $| = 1;
3983 return $returnvalue;
3986 # a pointer to CGIexecute::SAFEqx
3987 sub SAFEqx # ('String') -> result of qx/"String"/
3989 my $CommandString = shift;
3990 return CGIexecute::SAFEqx($CommandString);
3994 # a pointer to CGIexecute::defineCGIvariable
3995 sub defineCGIvariable # ($name[, $default]) ->0/1
3997 my $name = shift;
3998 my $default = shift;
3999 return CGIexecute::defineCGIvariable($name, $default);
4003 # Decode URL encoded arguments
4004 sub URLdecode # (URL encoded input) -> string
4006 my $output = "";
4007 my $char;
4008 my $Value;
4009 foreach $Value (@_)
4011 my $EncodedValue = $Value; # Do not change the loop variable
4012 # Convert all "+" to " "
4013 $EncodedValue =~ s/\+/ /g;
4014 # Convert all hexadecimal codes (%FF) to their byte values
4015 while($EncodedValue =~ /\%([0-9A-F]{2})/i)
4017 $output .= $`.chr(hex($1));
4018 $EncodedValue = $';
4020 $output .= $EncodedValue; # The remaining part of $Value
4022 $output;
4025 # Encode arguments as URL codes.
4026 sub URLencode # (input) -> URL encoded string
4028 my $output = "";
4029 my $char;
4030 my $Value;
4031 foreach $Value (@_)
4033 my @CharList = split('', $Value);
4034 foreach $char (@CharList)
4036 if($char =~ /\s/)
4037 { $output .= "+";}
4038 elsif($char =~ /\w\-/)
4039 { $output .= $char;}
4040 else
4042 $output .= uc(sprintf("%%%2.2x", ord($char)));
4046 $output;
4049 # Extract the value of a CGI variable from the URL-encoded $string
4050 # Also extracts the data blocks from a multipart request. Does NOT
4051 # decode the multipart blocks
4052 sub CGIparseValue # (ValueName [, URL_encoded_QueryString [, \$QueryReturnReference]]) -> Decoded value
4054 my $ValueName = shift;
4055 my $QueryString = shift || $main::ENV{'QUERY_STRING'};
4056 my $ReturnReference = shift || undef;
4057 my $output = "";
4059 if($QueryString =~ /(^|\&)$ValueName\=([^\&]*)(\&|$)/)
4061 $output = URLdecode($2);
4062 $$ReturnReference = $' if ref($ReturnReference);
4064 # Get multipart POST or PUT methods
4065 elsif($main::ENV{'CONTENT_TYPE'} =~ m@(multipart/([\w\-]+)\;\s+boundary\=([\S]+))@i)
4067 my $MultipartType = $2;
4068 my $BoundaryString = $3;
4069 # Remove the boundary-string
4070 my $temp = $QueryString;
4071 $temp =~ /^\Q--$BoundaryString\E/m;
4072 $temp = $';
4074 # Identify the newline character(s), this is the first character in $temp
4075 my $NewLine = "\r\n"; # Actually, this IS the correct one
4076 unless($temp =~ /^(\-\-|\r\n)/) # However, you never realy can be sure
4078 # Is this correct??? I have to check.
4079 $NewLine = "\r\n" if $temp =~ /^(\r\n)/; # Double (CRLF, the correct one)
4080 $NewLine = "\n\r" if $temp =~ /^(\n\r)/; # Double
4081 $NewLine = "\n" if $temp =~ /^([\n])/; # Single Line Feed
4082 $NewLine = "\r" if $temp =~ /^([\r])/; # Single Return
4085 # search through all data blocks
4086 while($temp =~ /^\Q--$BoundaryString\E/m)
4088 my $DataBlock = $`;
4089 $temp = $';
4090 # Get the empty line after the header
4091 $DataBlock =~ /$NewLine$NewLine/;
4092 $Header = $`;
4093 $output = $';
4094 my $Header = $`;
4095 $output = $';
4097 # Remove newlines from the header
4098 $Header =~ s/$NewLine/ /g;
4100 # Look whether this block is the one you are looking for
4101 # Require the quotes!
4102 if($Header =~ /name\s*=\s*[\"\']$ValueName[\"\']/m)
4104 my $i;
4105 for($i=length($NewLine); $i; --$i)
4107 chop($output);
4109 # OK, get out
4110 last;
4112 # reinitialize the output
4113 $output = "";
4115 $$ReturnReference = $temp if ref($ReturnReference);
4117 elsif($QueryString !~ /(^|\&)$ValueName\=/) # The value simply isn't there
4119 return undef;
4120 $$ReturnReference = undef if ref($ReturnReference);
4122 else
4124 print "ERROR: $ValueName $main::ENV{'CONTENT_TYPE'}\n";
4126 return $output;
4130 # Get a list of values for the same ValueName. Uses CGIparseValue
4132 sub CGIparseValueList # (ValueName [, URL_encoded_QueryString]) -> List of decoded values
4134 my $ValueName = shift;
4135 my $QueryString = shift || $main::ENV{'QUERY_STRING'};
4136 my @output = ();
4137 my $RestQueryString;
4138 my $Value;
4139 while($QueryString &&
4140 (($Value = CGIparseValue($ValueName, $QueryString, \$RestQueryString))
4141 || defined($Value)))
4143 push(@output, $Value);
4144 $QueryString = $RestQueryString; # QueryString is consumed!
4146 # ready, return list with values
4147 return @output;
4150 sub CGIparseValueHash # (ValueName [, URL_encoded_QueryString]) -> Hash table of decoded values
4152 my $ValueName = shift;
4153 my $QueryString = shift || $main::ENV{'QUERY_STRING'};
4154 my $RestQueryString;
4155 my %output = ();
4156 while($QueryString && $QueryString =~ /(^|\&)$ValueName([\w]*)\=/)
4158 my $Key = $2;
4159 my $Value = CGIparseValue("$ValueName$Key", $QueryString, \$RestQueryString);
4160 $output{$Key} = $Value;
4161 $QueryString = $RestQueryString; # QueryString is consumed!
4163 # ready, return list with values
4164 return %output;
4167 sub CGIparseForm # ([URL_encoded_QueryString]) -> Decoded Form (NO multipart)
4169 my $QueryString = shift || $main::ENV{'QUERY_STRING'};
4170 my $output = "";
4172 $QueryString =~ s/\&/\n/g;
4173 $output = URLdecode($QueryString);
4175 $output;
4178 # Extract the header of a multipart CGI variable from the POST input
4179 sub CGIparseHeader # (ValueName [, URL_encoded_QueryString]) -> Decoded value
4181 my $ValueName = shift;
4182 my $QueryString = shift || $main::ENV{'QUERY_STRING'};
4183 my $output = "";
4185 if($main::ENV{'CONTENT_TYPE'} =~ m@(multipart/([\w\-]+)\;\s+boundary\=([\S]+))@i)
4187 my $MultipartType = $2;
4188 my $BoundaryString = $3;
4189 # Remove the boundary-string
4190 my $temp = $QueryString;
4191 $temp =~ /^\Q--$BoundaryString\E/m;
4192 $temp = $';
4194 # Identify the newline character(s), this is the first character in $temp
4195 my $NewLine = "\r\n"; # Actually, this IS the correct one
4196 unless($temp =~ /^(\-\-|\r\n)/) # However, you never realy can be sure
4198 $NewLine = "\n" if $temp =~ /^([\n])/; # Single Line Feed
4199 $NewLine = "\r" if $temp =~ /^([\r])/; # Single Return
4200 $NewLine = "\r\n" if $temp =~ /^(\r\n)/; # Double (CRLF, the correct one)
4201 $NewLine = "\n\r" if $temp =~ /^(\n\r)/; # Double
4204 # search through all data blocks
4205 while($temp =~ /^\Q--$BoundaryString\E/m)
4207 my $DataBlock = $`;
4208 $temp = $';
4209 # Get the empty line after the header
4210 $DataBlock =~ /$NewLine$NewLine/;
4211 $Header = $`;
4212 my $Header = $`;
4214 # Remove newlines from the header
4215 $Header =~ s/$NewLine/ /g;
4217 # Look whether this block is the one you are looking for
4218 # Require the quotes!
4219 if($Header =~ /name\s*=\s*[\"\']$ValueName[\"\']/m)
4221 $output = $Header;
4222 last;
4224 # reinitialize the output
4225 $output = "";
4228 return $output;
4232 # Checking variables for security (e.g., file names and email addresses)
4233 # File names are tested against the $::FileAllowedChars and $::BlockPathAccess variables
4234 sub CGIsafeFileName # FileName -> FileName or ""
4236 my $FileName = shift || "";
4237 return "" if $FileName =~ m?[^$::FileAllowedChars]?;
4238 return "" if $FileName =~ m!(^|/|\:)\-!;
4239 return "" if $FileName =~ m@\.\.\Q$::DirectorySeparator\E@; # Higher directory not allowed
4240 return "" if $FileName =~ m@\Q$::DirectorySeparator\E\.\.@; # Higher directory not allowed
4241 return "" if $::BlockPathAccess && $FileName =~ m@$::BlockPathAccess@; # Invisible (blocked) file
4243 return $FileName;
4246 sub CGIsafeEmailAddress # email -> email or ""
4248 my $Email = shift || "";
4249 return "" unless $Email =~ m/^[\w\.\-]+[\@][\w\.\-\:]+$/;
4250 return $Email;
4253 # Get a URL from the web. Needs main::GET_URL($URL) function
4254 # (i.e., curl, snarf, or wget)
4255 sub read_url # ($URL) -> page/file
4257 my $URL = shift || return "";
4259 # Get the commands to read the URL, do NOT add a print command
4260 my $URL_command = main::GET_URL($URL, 1);
4261 # execute the commands, i.e., actually read it
4262 my $URLcontent = CGIexecute->evaluate($URL_command);
4264 # Ready, return the content.
4265 return $URLcontent;
4268 ################################################>>>>>>>>>>Start Remove
4270 # BrowseDirs(RootDirectory [, Pattern, Start])
4272 # usage:
4273 # <SCRIPT TYPE='text/ssperl'>
4274 # CGIscriptor::BrowseDirs('Sounds', '\.aifc$', 'Speech', 'DIRECTORY')
4275 # </SCRIPT>
4277 # Allows to browse subdirectories. Start should be relative to the RootDirectory,
4278 # e.g., the full path of the directory 'Speech' is '~/Sounds/Speech'.
4279 # Only files which fit /$Pattern/ and directories are displayed.
4280 # Directories down or up the directory tree are supplied with a
4281 # GET request with the name of the CGI variable in the fourth argument (default
4282 # is 'BROWSEDIRS'). So the correct call for a subdirectory could be:
4283 # CGIscriptor::BrowseDirs('Sounds', '\.aifc$', $DIRECTORY, 'DIRECTORY')
4285 sub BrowseDirs # (RootDirectory [, Pattern, Start, CGIvariable, HTTPserver]) -> Print HTML code
4287 my $RootDirectory = shift; # || return 0;
4288 my $Pattern = shift || '\S';
4289 my $Start = shift || "";
4290 my $CGIvariable = shift || "BROWSEDIRS";
4291 my $HTTPserver = shift || '';
4293 $Start = CGIscriptor::URLdecode($Start); # Sometimes, too much has been encoded
4294 $Start =~ s@//+@/@g;
4295 $Start =~ s@[^/]+/\.\.@@ig;
4296 $Start =~ s@^\.\.@@ig;
4297 $Start =~ s@/\.$@@ig;
4298 $Start =~ s!/+$!!g;
4299 $Start .= "/" if $Start;
4301 my @Directory = glob("$::CGI_HOME/$RootDirectory/$Start");
4302 $CurrentDirectory = shift(@Directory);
4303 $CurrentDirectory = $' if $CurrentDirectory =~ m@(/\.\./)+@;
4304 $CurrentDirectory =~ s@^$::CGI_HOME@@g;
4305 print "<h1>";
4306 print "$CurrentDirectory" if $CurrentDirectory;
4307 print "</h1>\n";
4308 opendir(BROWSE, "$::CGI_HOME/$RootDirectory/$Start") || main::dieHandler(31, "$::CGI_HOME/$RootDirectory/$Start $!");
4309 my @AllFiles = sort grep(!/^([\.]+[^\.]|\~)/, readdir(BROWSE));
4311 # Print directories
4312 my $file;
4313 print "<pre><ul TYPE='NONE'>\n";
4314 foreach $file (@AllFiles)
4316 next unless -d "$::CGI_HOME/$RootDirectory/$Start$file";
4317 # Check whether this file should be visible
4318 next if $::BlockPathAccess &&
4319 "/$RootDirectory/$Start$file/" =~ m@$::BlockPathAccess@;
4321 my $NewURL = $Start ? "$Start$file" : $file;
4322 $NewURL = CGIscriptor::URLencode($NewURL);
4323 print "<dt><a href='";
4324 print "$ENV{SCRIPT_NAME}" if $ENV{SCRIPT_NAME} !~ m@[^\w+\-/]@;
4325 print "$ENV{PATH_INFO}?$CGIvariable=$NewURL'>$file</a></dt>\n";
4327 print "</ul></pre>\n";
4329 # Print files
4330 print "<pre><ul TYPE='CIRCLE'>\n";
4331 my $TotalSize = 0;
4332 foreach $file (@AllFiles)
4334 next if $file =~ /^\./;
4335 next if -d "$::CGI_HOME/$RootDirectory/$Start$file";
4336 next if -l "$::CGI_HOME/$RootDirectory/$Start$file";
4337 # Check whether this file should be visible
4338 next if $::BlockPathAccess &&
4339 "$::CGI_HOME/$RootDirectory/$Start$file" =~ m@$::BlockPathAccess@;
4341 if($file =~ m@$Pattern@)
4343 my $Date = localtime($^T - (-M "$::CGI_HOME/$RootDirectory/$Start$file")*3600*24);
4344 my $Size = -s "$::CGI_HOME/$RootDirectory/$Start$file";
4345 $Size = sprintf("%6.0F kB", $Size/1024);
4346 my $Type = `file $::CGI_HOME/$RootDirectory/$Start$file`;
4347 $Type =~ s@\s*$::CGI_HOME/$RootDirectory/$Start$file\s*\:\s*@@ig;
4348 chomp($Type);
4350 print "<li>";
4351 if($HTTPserver =~ /^\s*[\.\~]\s*$/)
4353 print "<a href='$Start$file'>";
4355 elsif($HTTPserver)
4357 print "<a href='$HTTPserver/$RootDirectory/$Start$file'>";
4359 printf("%-40s", "$file</a>") if $HTTPserver;
4360 printf("%-40s", "$file") unless $HTTPserver;
4361 print "\t$Size\t$Date\t$Type";
4362 print "</li>\n";
4365 print "</ul></pre>";
4367 return 1;
4371 # ListDocs(Pattern [,ListType])
4373 # usage:
4374 # <SCRIPT TYPE=text/ssperl>
4375 # CGIscriptor::ListDocs("/*", "dl");
4376 # </SCRIPT>
4378 # This subroutine is very usefull to manage collections of independent
4379 # documents. The resulting list will display the tree-like directory
4380 # structure. If this routine is too slow for online use, you can
4381 # store the result and use a link to that stored file.
4383 # List HTML and Text files with title and first header (HTML)
4384 # or filename and first meaningfull line (general text files).
4385 # The listing starts at the ServerRoot directory. Directories are
4386 # listed recursively.
4388 # You can change the list type (default is dl).
4389 # e.g.,
4390 # <dt><a href=<file.html>>title</a>
4391 # <dd>First Header
4392 # <dt><a href=<file.txt>>file.txt</a>
4393 # <dd>First meaningfull line of text
4395 sub ListDocs # ($Pattern [, prefix]) e.g., ("/Books/*", [, "dl"])
4397 my $Pattern = shift;
4398 $Pattern =~ /\*/;
4399 my $ListType = shift || "dl";
4400 my $Prefix = lc($ListType) eq "dl" ? "dt" : "li";
4401 my $URL_root = "http://$::ENV{'SERVER_NAME'}\:$::ENV{'SERVER_PORT'}";
4402 my @FileList = glob("$::CGI_HOME$Pattern");
4403 my ($FileName, $Path, $Link);
4405 # Print List markers
4406 print "<$ListType>\n";
4408 # Glob all files
4409 File: foreach $FileName (@FileList)
4411 # Check whether this file should be visible
4412 next if $::BlockPathAccess && $FileName =~ m@$::BlockPathAccess@;
4414 # Recursively list files in all directories
4415 if(-d $FileName)
4417 $FileName =~ m@([^/]*)$@;
4418 my $DirName = $1;
4419 print "<$Prefix>$DirName\n";
4420 $Pattern =~ m@([^/]*)$@;
4421 &ListDocs("$`$DirName/$1", $ListType);
4422 next;
4424 # Use textfiles
4425 elsif(-T "$FileName")
4427 open(TextFile, $FileName) || next;
4429 # Ignore all other file types
4430 else
4431 { next;};
4433 # Get file path for link
4434 $FileName =~ /$::CGI_HOME/;
4435 print "<$Prefix><a href=$URL_root$'>";
4436 # Initialize all variables
4437 my $Line = "";
4438 my $TitleFound = 0;
4439 my $Caption = "";
4440 my $Title = "";
4441 # Read file and step through
4442 while(<TextFile>)
4444 chop $_;
4445 $Line = $_;
4446 # HTML files
4447 if($FileName =~ /\.ht[a-zA-Z]*$/i)
4449 # Catch Title
4450 while(!$Title)
4452 if($Line =~ m@<title>([^<]*)</title>@i)
4454 $Title = $1;
4455 $Line = $';
4457 else
4459 $Line .= <TextFile> || goto Print;
4460 chop $Line;
4463 # Catch First Header
4464 while(!$Caption)
4466 if($Line =~ m@</h1>@i)
4468 $Caption = $`;
4469 $Line = $';
4470 $Caption =~ m@<h1>@i;
4471 $Caption = $';
4472 $Line = $`.$Caption.$Line;
4474 else
4476 $Line .= <TextFile> || goto Print;
4477 chop $Line;
4481 # Other text files
4482 else
4484 # Title equals file name
4485 $FileName =~ /([^\/]+)$/;
4486 $Title = $1;
4487 # Catch equals First Meaningfull line
4488 while(!$Caption)
4490 if($Line =~ /[A-Z]/ &&
4491 ($Line =~ /subject|title/i || $Line =~ /^[\w,\.\s\?\:]+$/)
4492 && $Line !~ /Newsgroup/ && $Line !~ /\:\s*$/)
4494 $Line =~ s/\<[^\>]+\>//g;
4495 $Caption = $Line;
4497 else
4499 $Line = <TextFile> || goto Print;
4503 Print: # Print title and subject
4504 print "$Title</a>\n";
4505 print "<dd>$Caption\n" if $ListType eq "dl";
4506 $TitleFound = 0;
4507 $Caption = "";
4508 close TextFile;
4509 next File;
4512 # Print Closing List Marker
4513 print "</$ListType>\n";
4514 ""; # Empty return value
4518 # HTMLdocTree(Pattern [,ListType])
4520 # usage:
4521 # <SCRIPT TYPE=text/ssperl>
4522 # CGIscriptor::HTMLdocTree("/Welcome.html", "dl");
4523 # </SCRIPT>
4525 # The following subroutine is very usefull for checking large document
4526 # trees. Starting from the root (s), it reads all files and prints out
4527 # a nested list of links to all attached files. Non-existing or misplaced
4528 # files are flagged. This is quite a file-i/o intensive routine
4529 # so you would not like it to be accessible to everyone. If you want to
4530 # use the result, save the whole resulting page to disk and use a link
4531 # to this file.
4533 # HTMLdocTree takes an HTML file or file pattern and constructs nested lists
4534 # with links to *local* files (i.e., only links to the local server are
4535 # followed). The list entries are the document titles.
4536 # If the list type is <dl>, the first <H1> header is used too.
4537 # For each file matching the pattern, a list is made recursively of all
4538 # HTML documents that are linked from it and are stored in the same directory
4539 # or a sub-directory. Warnings are given for missing files.
4540 # The listing starts for the ServerRoot directory.
4541 # You can change the default list type <dl> (<dl>, <ul>, <ol>).
4543 %LinkUsed = ();
4545 sub HTMLdocTree # ($Pattern [, listtype])
4546 # e.g., ("/Welcome.html", [, "ul"])
4548 my $Pattern = shift;
4549 my $ListType = shift || "dl";
4550 my $Prefix = lc($ListType) eq "dl" ? "dt" : "li";
4551 my $URL_root = "http://$::ENV{'SERVER_NAME'}\:$::ENV{'SERVER_PORT'}";
4552 my ($Filename, $Path, $Link);
4553 my %LocalLinks = {};
4555 # Read files (glob them for expansion of wildcards)
4556 my @FileList = glob("$::CGI_HOME$Pattern");
4557 foreach $Path (@FileList)
4559 # Get URL_path
4560 $Path =~ /$::CGI_HOME/;
4561 my $URL_path = $';
4562 # Check whether this file should be visible
4563 next if $::BlockPathAccess && $URL_path =~ m@$::BlockPathAccess@;
4565 my $Title = $URL_path;
4566 my $Caption = "";
4567 # Current file should not be used again
4568 ++$LinkUsed{$URL_path};
4569 # Open HTML doc
4570 unless(open(TextFile, $Path))
4572 print "<$Prefix>$Title <blink>(not found)</blink><br>\n";
4573 next;
4575 while(<TextFile>)
4577 chop $_;
4578 $Line = $_;
4579 # Catch Title
4580 while($Line =~ m@<title>@i)
4582 if($Line =~ m@<title>([^<]*)</title>@i)
4584 $Title = $1;
4585 $Line = $';
4587 else
4589 $Line .= <TextFile>;
4590 chop $Line;
4593 # Catch First Header
4594 while(!$Caption && $Line =~ m@<h1>@i)
4596 if($Line =~ m@</h[1-9]>@i)
4598 $Caption = $`;
4599 $Line = $';
4600 $Caption =~ m@<h1>@i;
4601 $Caption = $';
4602 $Line = $`.$Caption.$Line;
4604 else
4606 $Line .= <TextFile>;
4607 chop $Line;
4610 # Catch and print Links
4611 while($Line =~ m@<a href\=([^>]*)>@i)
4613 $Link = $1;
4614 $Line = $';
4615 # Remove quotes
4616 $Link =~ s/\"//g;
4617 # Remove extras
4618 $Link =~ s/[\#\?].*$//g;
4619 # Remove Servername
4620 if($Link =~ m@(http://|^)@i)
4622 $Link = $';
4623 # Only build tree for current server
4624 next unless $Link =~ m@$::ENV{'SERVER_NAME'}|^/@;
4625 # Remove server name and port
4626 $Link =~ s@^[^\/]*@@g;
4628 # Store the current link
4629 next if $LinkUsed{$Link} || $Link eq $URL_path;
4630 ++$LinkUsed{$Link};
4631 ++$LocalLinks{$Link};
4635 close TextFile;
4636 print "<$Prefix>";
4637 print "<a href=http://";
4638 print "$::ENV{'SERVER_NAME'}\:$::ENV{'SERVER_PORT'}$URL_path>";
4639 print "$Title</a>\n";
4640 print "<br>$Caption\n"
4641 if $Caption && $Caption ne $Title && $ListType =~ /dl/i;
4642 print "<$ListType>\n";
4643 foreach $Link (keys(%LocalLinks))
4645 &HTMLdocTree($Link, $ListType);
4647 print "</$ListType>\n";
4651 ###########################<<<<<<<<<<End Remove
4653 # Make require happy
4656 =head1 NAME
4658 CGIscriptor -
4660 =head1 DESCRIPTION
4662 A flexible HTML 4 compliant script/module for CGI-aware
4663 embeded Perl, shell-scripts, and other scripting languages,
4664 executed at the server side.
4666 =head1 README
4668 Executes embeded Perl code in HTML pages with easy
4669 access to CGI variables. Also processes embeded shell
4670 scripts and scripts in any other language with an
4671 interactive interpreter (e.g., in-line Python, Tcl,
4672 Ruby, Awk, Lisp, Xlispstat, Prolog, M4, R, REBOL, Praat,
4673 sh, bash, csh, ksh).
4675 CGIscriptor is very flexible and hides all the specifics
4676 and idiosyncrasies of correct output and CGI coding and naming.
4677 CGIscriptor complies with the W3C HTML 4.0 recommendations.
4679 This Perl program will run on any WWW server that runs
4680 Perl scripts, just add a line like the following to your
4681 srm.conf file (Apache example):
4683 ScriptAlias /SHTML/ /real-path/CGIscriptor.pl/
4685 URL's that refer to http://www.your.address/SHTML/... will
4686 now be handled by CGIscriptor.pl, which can use a private
4687 directory tree (default is the DOCUMENT_ROOT directory tree,
4688 but it can be anywhere).
4690 =head1 PREREQUISITES
4693 =head1 COREQUISITES
4696 =pod OSNAMES
4698 Linux, *BSD, *nix, MS WinXP
4700 =pod SCRIPT CATEGORIES
4702 Servers
4706 =cut