Generate server specific SALT file if it does not exist, adapt test account
[CGIscriptor.git] / CGIscriptor.pl
blob6d30744aae3cc3635738ce3c46226ff245c07f23
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 # 29 May 2012 - CGIsafeFileName does not accept filenames starting with '.'
64 # 29 May 2012 - Added CGIscriptor::BrowseAllDirs to handle browsing directories
65 # correctly.
66 # 22 May 2012 - Added Access control with Session Tickets linked to
67 # IP Address and PATH_INFO.
68 # 21 May 2012 - Corrected the links generated by CGIscriptor::BrowseDirs
69 # Will link to current base URL when the HTTP server is '.' or '~'
70 # 29 Oct 2009 - Adapted David A. Wheeler's suggestion about filenames:
71 # CGIsafeFileName does not accept filenames starting with '-'
72 # (http://www.dwheeler.com/essays/fixing-unix-linux-filenames.html)
73 # 08 Oct 2009 - Some corrections in the README.txt file, eg, new email address
74 # 28 Jan 2005 - Added a file selector to performTranslation.
75 # Changed %TranslationTable to @TranslationTable
76 # and patterns to lists.
77 # 27 Jan 2005 - Added a %TranslationTable with associated
78 # performTranslation(\$text) function to allow
79 # run changes in the web pages. Say, to translate
80 # legacy pages with <%=...%> delimiters to the new
81 # <SCRIPT TYPE=..></SCRIPT> format.
82 # 27 Jan 2005 - Small bug of extra '\n' in output removed from the
83 # Other Languages Code.
84 # 10 May 2004 - Belated upload of latest version (2.3) to CPAN
85 # 07 Oct 2003 - Corrected error '\s' -> '\\s' in rebol scripting
86 # language call
87 # 07 Oct 2003 - Corrected omitted INS tags in <DIV><INS> handling
88 # 20 May 2003 - Added a --help switch to print the manual.
89 # 06 Mar 2003 - Adapted the blurb at the end of the file.
90 # 03 Mar 2003 - Added a user definable dieHandler function to catch all
91 # "die" calls. Also "enhanced" the STDERR printout.
92 # 10 Feb 2003 - Split off the reading of the POST part of a query
93 # from Initialize_output. This was suggested by Gerd Franke
94 # to allow for the catching of the file_path using a
95 # POST based lookup. That is, he needed the POST part
96 # to change the file_path.
97 # 03 Feb 2003 - %{$name}; => %{$name} = (); in defineCGIvariableHash.
98 # 03 Feb 2003 - \1 better written as $1 in
99 # $directive =~ s/[^\\\$]\#[^\n\f\r]*([\n\f\r])/$1/g
100 # 29 Jan 2003 - This makes "CLASS="ssperl" CSS-compatible Gerd Franke
101 # added:
102 # $ServerScriptContentClass = "ssperl";
103 # changed in ProcessFile():
104 # unless(($CurrentContentType =~
105 # 28 Jan 2003 - Added 'INS' Tag! Gerd Franke
106 # 20 Dec 2002 - Removed useless $Directoryseparator variable.
107 # Update comments and documentation.
108 # 18 Dec 2002 - Corrected bug in Accept/Reject processing.
109 # Files didn't work.
110 # 24 Jul 2002 - Added .htaccess documentation (from Gerd Franke)
111 # Also added a note that RawFilePattern can be a
112 # complete file name.
113 # 19 Mar 2002 - Added SRC pseudo-files PREFIX and POSTFIX. These
114 # switch to prepending or to appending the content
115 # of the SRC attribute. Default is prefixing. You
116 # can add as many of these switches as you like.
117 # 13 Mar 2002 - Do not search for tag content if a tag closes with
118 # />, i.e., <DIV ... /> will be handled the XML/XHTML way.
119 # 25 Jan 2002 - Added 'curl' and 'snarf' to SRC attribute URL handling
120 # (replaces wget).
121 # 25 Jan 2002 - Found a bug in SAFEqx, now executes qx() in a scalar context
122 # (i.o. a list context). This is necessary for binary results.
123 # 24 Jan 2002 - Disambiguated -T $SRCfile to -T "$SRCfile" (and -e) and
124 # changed the order of if/elsif to allow removing these
125 # conditions in systems with broken -T functions.
126 # (I also removed a spurious ')' bracket)
127 # 17 Jan 2002 - Changed DIV tag SRC from <SOURCE> to sysread(SOURCE,...)
128 # to support binary files.
129 # 17 Jan 2002 - Removed WhiteSpace from $FileAllowedCharacters.
130 # 17 Jan 2002 - Allow "file://" prefix in SRC attribute. It is simply
131 # stipped from the path.
132 # 15 Jan 2002 - Version 2.2
133 # 15 Jan 2002 - Debugged and completed URL support (including
134 # CGIscriptor::read_url() function)
135 # 07 Jan 2002 - Added automatic (magic) URL support to the SRC attribute
136 # with the main::GET_URL function. Uses wget -O underlying.
137 # 04 Jan 2002 - Added initialization of $NewDirective in InsertForeignScript
138 # (i.e., my $NewDirective = "";) to clear old output
139 # (this was a realy anoying bug).
140 # 03 Jan 2002 - Added a <DIV CLASS='text/ssperl' ID='varname'></DIV>
141 # tags that assign the body text as-is (literally)
142 # to $varname. Allows standard HTML-tools to handle
143 # Cascading Style Sheet templates. This implements a
144 # design by Gerd Franke (franke@roo.de).
145 # 03 Jan 2002 - I finaly gave in and allowed SRC files to expand ~/.
146 # 12 Oct 2001 - Normalized spelling of "CGIsafFileName" in documentation.
147 # 09 Oct 2001 - Added $ENV{'CGI_BINARY_FILE'} to log files to
148 # detect unwanted indexing of TAR files by webcrawlers.
149 # 10 Sep 2001 - Added $YOUR_SCRIPTS directory to @INC for 'require'.
150 # 22 Aug 2001 - Added .txt (Content-type: text/plain) as a default
151 # processed file type. Was processed via BinaryMapFile.
152 # 31 May 2001 - Changed =~ inside CGIsafeEmailAddress that was buggy.
153 # 29 May 2001 - Updated $CGI_HOME to point to $ENV{DOCUMENT_ROOT} io
154 # the root of PATH_TRANSLATED. DOCUMENT_ROOT can now
155 # be manipulated to achieve a "Sub Root".
156 # NOTE: you can have $YOUR_HTML_FILES != DOCUMENT_ROOT
157 # 28 May 2001 - Changed CGIscriptor::BrowsDirs function for security
158 # and debugging (it now works).
159 # 21 May 2001 - defineCGIvariableHash will ADD values to existing
160 # hashes,instead of replacing existing hashes.
161 # 17 May 2001 - Interjected a '&' when pasting POST to GET data
162 # 24 Apr 2001 - Blocked direct requests for BinaryMapFile.
163 # 16 Aug 2000 - Added hash table extraction for CGI parameters with
164 # CGIparseValueHash (used with structured parameters).
165 # Use: CGI='%<CGI-partial-name>' (fill in your name in <>)
166 # Will collect all <CGI-partial-name><key>=value pairs in
167 # $<CGI-partial-name>{<key>} = value;
168 # 16 Aug 2000 - Adapted SAFEqx to protect @PARAMETER values.
169 # 09 Aug 2000 - Added support for non-filesystem input by way of
170 # the CGI_FILE_CONTENTS and CGI_DATA_ACCESS_CODE
171 # environment variables.
172 # 26 Jul 2000 - On the command-line, file-path '-' indicates STDIN.
173 # This allows CGIscriptor to be used in pipes.
174 # Default, $BLOCK_STDIN_HTTP_REQUEST=1 will block this
175 # in an HTTP request (i.e., in a web server).
176 # 26 Jul 2000 - Blocked 'Content-type: text/html' if the SERVER_PROTOCOL
177 # is not HTTP or another protocol. Changed the default
178 # source directory to DOCUMENT_ROOT (i.o. the incorrect
179 # SERVER_ROOT).
180 # 24 Jul 2000 - -slim Command-line argument added to remove all
181 # comments, security, etc.. Updated documentation.
182 # 05 Jul 2000 - Added IF and UNLESS attributes to make the
183 # execution of all <META> and <SCRIPT> code
184 # conditional.
185 # 05 Jul 2000 - Rewrote and isolated the code for extracting
186 # quoted items from CGI and SRC attributes.
187 # Now all attributes expect the same set of
188 # quotes: '', "", ``, (), {}, [] and the same
189 # preceded by a \, e.g., "\((aap)\)" will be
190 # extracted as "(aap)".
191 # 17 Jun 2000 - Construct @ARGV list directly in CGIexecute
192 # name-space (i.o. by evaluation) from
193 # CGI attributes to prevent interference with
194 # the processing for non perl scripts.
195 # Changed CGIparseValueList to prevent runaway
196 # loops.
197 # 16 Jun 2000 - Added a direct (interpolated) display mode
198 # (text/ssdisplay) and a user log mode
199 # (text/sslogfile).
200 # 06 Jun 2000 - Replace "print $Result" with a syswrite loop to
201 # allow large string output.
202 # 02 Jun 2000 - Corrected shrubCGIparameter($CGI_VALUE) to realy
203 # remove all control characters. Changed Interpreter
204 # initialization to shrub interpolated CGI parameters.
205 # Added 'text/ssmailto' interpreter script.
206 # 22 May 2000 - Changed some of the comments
207 # 09 May 2000 - Added list extraction for CGI parameters with
208 # CGIparseValueList (used with multiple selections).
209 # Use: CGI='@<CGI-parameter>' (fill in your name in <>)
210 # 09 May 2000 - Added a 'Not Present' condition to CGIparseValue.
211 # 27 Apr 2000 - Updated documentation to reflect changes.
212 # 27 Apr 2000 - SRC attribute "cleaned". Supported for external
213 # interpreters.
214 # 27 Apr 2000 - CGI attribute can be used in <SCRIPT> tag.
215 # 27 Apr 2000 - Gprolog, M4 support added.
216 # 26 Apr 2000 - Lisp (rep) support added.
217 # 20 Apr 2000 - Use of external interpreters now functional.
218 # 20 Apr 2000 - Removed bug from extracting Content types (RegExp)
219 # 10 Mar 2000 - Qualified unconditional removal of '#' that preclude
220 # the use of $#foo, i.e., I changed
221 # s/[^\\]\#[^\n\f\r]*([\n\f\r])/\1/g
222 # to
223 # s/[^\\\$]\#[^\n\f\r]*([\n\f\r])/\1/g
224 # 03 Mar 2000 - Added a '$BlockPathAccess' variable to "hide"
225 # things like, e.g., CVS information in CVS subtrees
226 # 10 Feb 2000 - URLencode/URLdecode have been made case-insensitive
227 # 10 Feb 2000 - Added a BrowseDirs function (CGIscriptor package)
228 # 01 Feb 2000 - A BinaryMapFile in the ~/ directory has precedence
229 # over a "burried" BinaryMapFile.
230 # 04 Oct 1999 - Added two functions to check file names and email addresses
231 # (CGIscriptor::CGIsafeFileName and
232 # CGIscriptor::CGIsafeEmailAddress)
233 # 28 Sept 1999 - Corrected bug in sysread call for reading POST method
234 # to allow LONG posts.
235 # 28 Sept 1999 - Changed CGIparseValue to handle multipart/form-data.
236 # 29 July 1999 - Refer to BinaryMapFile from CGIscriptor directory, if
237 # this directory exists.
238 # 07 June 1999 - Limit file-pattern matching to LAST extension
239 # 04 June 1999 - Default text/html content type is printed only once.
240 # 18 May 1999 - Bug in replacement of ~/ and ./ removed.
241 # (Rob van Son, R.J.J.H.vanSon@uva.nl)
242 # 15 May 1999 - Changed the name of the execute package to CGIexecute.
243 # Changed the processing of the Accept and Reject file.
244 # Added a full expression evaluation to Access Control.
245 # (Rob van Son, R.J.J.H.vanSon@uva.nl)
246 # 27 Apr 1999 - Brought CGIscriptor under the GNU GPL. Made CGIscriptor
247 # Version 1.1 a module that can be called with 'require "CGIscriptor.pl"'.
248 # Requests are serviced by "Handle_Request()". CGIscriptor
249 # can still be called as a isolated perl script and a shell
250 # command.
251 # Changed the "factory default setting" so that it will run
252 # from the DOCUMENT_ROOT directory.
253 # (Rob van Son, R.J.J.H.vanSon@uva.nl)
254 # 29 Mar 1999 - Remove second debugging STDERR switch. Moved most code
255 # to subroutines to change CGIscriptor into a module.
256 # Added mapping to process unsupported file types (e.g., binary
257 # pictures). See $BinaryMapFile.
258 # (Rob van Son, R.J.J.H.vanSon@uva.nl)
259 # 24 Sept 1998 - Changed text of license (Rob van Son, R.J.J.H.vanSon@uva.nl)
260 # Removed a double setting of filepatterns and maximum query
261 # size. Changed email address. Removed some typos from the
262 # comments.
263 # 02 June 1998 - Bug fixed in URLdecode. Changing the foreach loop variable
264 # caused quiting CGIscriptor.(Rob van Son, R.J.J.H.vanSon@uva.nl)
265 # 02 June 1998 - $SS_PUB and $SS_SCRIPT inserted an extra /, removed.
266 # (Rob van Son, R.J.J.H.vanSon@uva.nl)
269 # Known Bugs:
271 # 23 Mar 2000
272 # It is not possible to use operators or variables to construct variable names,
273 # e.g., $bar = \@{$foo}; won't work. However, eval('$bar = \@{'.$foo.'};');
274 # will indeed work. If someone could tell me why, I would be obliged.
277 ############################################################################
279 # OBLIGATORY USER CONFIGURATION
281 # Configure the directories where all user files can be found (this
282 # is the equivalent of the server root directory of a WWW-server).
283 # These directories can be located ANYWHERE. For security reasons, it is
284 # better to locate them outside the WWW-tree of your HTTP server, unless
285 # CGIscripter handles ALL requests.
287 # For convenience, the defaults are set to the root of the WWW server.
288 # However, this might not be safe!
290 # ~/ text files
291 # $YOUR_HTML_FILES = "/usr/pub/WWW/SHTML"; # or SS_PUB as environment var
292 # (patch to use the parent directory of CGIscriptor as document root, should be removed)
293 if($ENV{'SCRIPT_FILENAME'}) # && $ENV{'SCRIPT_FILENAME'} !~ /\Q$ENV{'DOCUMENT_ROOT'}\E/)
295 $ENV{'DOCUMENT_ROOT'} = $ENV{'SCRIPT_FILENAME'};
296 $ENV{'DOCUMENT_ROOT'} =~ s@/CGIscriptor.*$@@ig;
299 # Just enter your own directory path here
300 $YOUR_HTML_FILES = $ENV{'DOCUMENT_ROOT'}; # default is the DOCUMENT_ROOT
302 # ./ script files (recommended to be different from the previous)
303 # $YOUR_SCRIPTS = "/usr/pub/WWW/scripts"; # or SS_SCRIPT as environment var
304 $YOUR_SCRIPTS = $YOUR_HTML_FILES; # This might be a SECURITY RISK
306 # End of obligatory user configuration
307 # (note: there is more non-essential user configuration below)
309 ############################################################################
311 # OPTIONAL USER CONFIGURATION (all values are used CASE INSENSITIVE)
313 # Script content-types: TYPE="Content-type" (user defined mime-type)
314 $ServerScriptContentType = "text/ssperl"; # Server Side Perl scripts
315 # CSS require a simple class
316 $ServerScriptContentClass = $ServerScriptContentType =~ m!/! ?
317 $' : "ssperl"; # Server Side Perl CSS classes
319 $ShellScriptContentType = "text/osshell"; # OS shell scripts
320 # # (Server Side perl ``-execution)
322 # Accessible file patterns, block any request that doesn't match.
323 # Matches any file with the extension .(s)htm(l), .txt, or .xmr
324 # (\. is used in regexp)
325 # Note: die unless $PATH_INFO =~ m@($FilePattern)$@is;
326 $FilePattern = ".shtml|.htm|.html|.xml|.xmr|.txt";
328 # The table with the content type MIME types
329 # (allows to differentiate MIME types, if needed)
330 %ContentTypeTable =
332 '.html' => 'text/html',
333 '.shtml' => 'text/html',
334 '.htm' => 'text/html',
335 '.xml' => 'text/xml',
336 '.txt' => 'text/plain'
340 # File pattern post-processing
341 $FilePattern =~ s/([@.])/\\$1/g; # Convert . and @ to \. and \@
343 # SHAsum command needed for Authorization and Login
344 # (note, these have to be accessible in the HTML pages, ie, the CGIexecute environment)
345 $ENV{"SHASUMCMD"} = 'shasum-5.12 |cut -f 1 -d" "';
346 $ENV{"RANDOMHASHCMD"} = 'dd count=1 if=/dev/urandom 2>/dev/null | shasum-5.12 -b |cut -f 1 -d" "';
348 # File patterns of files which require a login.
349 %LoginRequiredPatterns = (
350 '^/Private(/|$)' => "Private/.Sessions\tPrivate/.Passwords\t/Private/Login.html\t+36000"
352 # Session Ticket Directory: .Session/
353 # Password Directory: .Password/
354 # Login page: Login.html
356 # Raw files must contain their own Content-type (xmr <- x-multipart-replace).
357 # THIS IS A SUBSET OF THE FILES DEFINED IN $FilePattern
358 $RawFilePattern = ".xmr";
359 # (In principle, this could contain a full file specification, e.g.,
360 # ".xmr|relocated.html")
362 # Raw File pattern post-processing
363 $RawFilePattern =~ s/([@.])/\\$1/g; # Convert . and @ to \. and \@
365 # Server protocols for which "Content-type: text/html\n\n" should be printed
366 # (you should not bother with these, except for HTTP, they are mostly imaginary)
367 $ContentTypeServerProtocols = 'HTTP|MAIL|MIME';
369 # Block access to all (sub-) paths and directories that match the
370 # following (URL) path (is used as:
371 # 'die if $BlockPathAccess && $ENV{'PATH_INFO'} =~ m@$BlockPathAccess@;' )
372 $BlockPathAccess = '/(CVS|\.git)/'; # Protect CVS and .git information
374 # All (blocked) other file-types can be mapped to a single "binary-file"
375 # processor (a kind of pseudo-file path). This can either be an error
376 # message (e.g., "illegal file") or contain a script that serves binary
377 # files.
378 # Note: the real file path wil be stored in $ENV{CGI_BINARY_FILE}.
379 $BinaryMapFile = "/BinaryMapFile.xmr";
380 # Allow for the addition of a CGIscriptor directory
381 # Note that a BinaryMapFile in the root "~/" directory has precedence
382 $BinaryMapFile = "/CGIscriptor".$BinaryMapFile
383 if ! -e "$YOUR_HTML_FILES".$BinaryMapFile
384 && -e "$YOUR_HTML_FILES/CGIscriptor".$BinaryMapFile;
387 # List of all characters that are allowed in file names and paths.
388 # All requests containing illegal characters are blocked. This
389 # blocks most tricks (e.g., adding "\000", "\n", or other control
390 # characters, also blocks URI's using %FF)
391 # THIS IS A SECURITY FEATURE
392 # (this is also used to parse filenames in SRC= features, note the
393 # '-quotes, they are essential)
394 $FileAllowedChars = '\w\.\~\/\:\*\?\-'; # Covers Unix and Mac, but NO spaces
396 # Maximum size of the Query (number of characters clients can send
397 # covers both GET & POST combined)
398 $MaximumQuerySize = 2**20 - 1; # = 2**14 - 1
401 # Embeded URL get function used in SRC attributes and CGIscriptor::read_url
402 # (returns a string with the PERL code to transfer the URL contents, e.g.,
403 # "SAFEqx(\'curl \"http://www.fon.hum.uva.nl\"\')")
404 # "SAFEqx(\'wget --quiet --output-document=- \"http://www.fon.hum.uva.nl\"\')")
405 # Be sure to handle <BASE HREF='URL'> and allow BOTH
406 # direct printing GET_URL($URL [, 0]) and extracting the content of
407 # the $URL for post-processing GET_URL($URL, 1).
408 # You get the WHOLE file, including HTML header.
409 # The shell command Use $URL where the URL should go
410 # ('wget', 'snarf' or 'curl', uncomment the one you would like to use)
411 my $GET_URL_shell_command = 'wget --quiet --output-document=- $URL';
412 #my $GET_URL_shell_command = 'snarf $URL -';
413 #my $GET_URL_shell_command = 'curl $URL';
415 sub GET_URL # ($URL, $ValueNotPrint) -> content_of_url
417 my $URL = shift || return;
418 my $ValueNotPrint = shift || 0;
420 # Check URL for illegal characters
421 return "print '<h1>Illegal URL<h1>'\"\n\";" if $URL =~ /[^$FileAllowedChars\%]/;
423 # Include URL in final command
424 my $CurrentCommand = $GET_URL_shell_command;
425 $CurrentCommand =~ s/\$URL/$URL/g;
427 # Print to STDOUT or return a value
428 my $BlockPrint = "print STDOUT ";
429 $BlockPrint = "" if $ValueNotPrint;
431 my $Commands = <<"GETURLCODE";
432 # Get URL
434 my \$Page = "";
436 # Simple, using shell command
437 \$Page = SAFEqx('$CurrentCommand');
439 # Add a BASE tage to the header
440 \$Page =~ s!\\</head!\\<base href='$URL'\\>\\</head!ig unless \$Page =~ m!\\<base!;
442 # Print the URL value, or return it as a value
443 $BlockPrint\$Page;
445 GETURLCODE
446 return $Commands;
449 # As files can get rather large (and binary), you might want to use
450 # some more intelligent reading procedure, e.g.,
451 # Direct Perl
452 # # open(URLHANDLE, '/usr/bin/wget --quiet --output-document=- "$URL"|') || die "wget: \$!";
453 # #open(URLHANDLE, '/usr/bin/snarf "$URL" -|') || die "snarf: \$!";
454 # open(URLHANDLE, '/usr/bin/curl "$URL"|') || die "curl: \$!";
455 # my \$text = "";
456 # while(sysread(URLHANDLE,\$text, 1024) > 0)
458 # \$Page .= \$text;
459 # };
460 # close(URLHANDLE) || die "\$!";
461 # However, this doesn't work with the CGIexecute->evaluate() function.
462 # You get an error: 'No child processes at (eval 16) line 15, <file0> line 8.'
464 # You can forget the next two variables, they are only needed when
465 # you don't want to use a regular file system (i.e., with open)
466 # but use some kind of database/RAM image for accessing (generating)
467 # the data.
469 # Name of the environment variable that contains the file contents
470 # when reading directly from Database/RAM. When this environment variable,
471 # $ENV{$CGI_FILE_CONTENTS}, is not false, no real file will be read.
472 $CGI_FILE_CONTENTS = 'CGI_FILE_CONTENTS';
473 # Uncomment the following if you want to force the use of the data access code
474 # $ENV{$CGI_FILE_CONTENTS} = '-'; # Force use of $ENV{$CGI_DATA_ACCESS_CODE}
476 # Name of the environment variable that contains the RAM access perl
477 # code needed to read additional "files", i.e.,
478 # $ENV{$CGI_FILE_CONTENTS} = eval("\@_=('$file_path'); do{$ENV{$CGI_DATA_ACCESS_CODE}}");
479 # When $ENV{$CGI_FILE_CONTENTS} eq '-', this code is executed to generate the data.
480 $CGI_DATA_ACCESS_CODE = 'CGI_DATA_ACCESS_CODE';
482 # You can, of course, fill this yourself, e.g.,
483 # $ENV{$CGI_DATA_ACCESS_CODE} =
484 # 'open(INPUT, "<$_[0]"); while(<INPUT>){print;};close(INPUT);'
487 # DEBUGGING
489 # Suppress error messages, this can be changed for debugging or error-logging
490 #open(STDERR, "/dev/null"); # (comment out for use in debugging)
492 # SPECIAL: Remove Comments, security, etc. if the command line is
493 # '>CGIscriptor.pl -slim >slimCGIscriptor.pl'
494 $TrimDownCGIscriptor = 1 if $ARGV[0] =~ /^\-slim/i;
496 # If CGIscriptor is used from the command line, the command line
497 # arguments are interpreted as the file (1st) and the Query String (rest).
498 # Get the arguments
499 $ENV{'PATH_INFO'} = shift(@ARGV) unless exists($ENV{'PATH_INFO'}) || grep(/\-\-help/i, @ARGV);
500 $ENV{'QUERY_STRING'} = join("&", @ARGV) unless exists($ENV{'QUERY_STRING'});
503 # Handle bail-outs in a user definable way.
504 # Catch Die and replace it with your own function.
505 # Ends with a call to "die $_[0];"
507 sub dieHandler # ($ErrorCode, "Message", @_) -> DEAD
509 my $ErrorCode = shift;
510 my $ErrorMessage = shift;
512 # Place your own reporting functions here
514 # Now, kill everything (default)
515 print STDERR "$ErrorCode: $ErrorMessage\n";
516 die $ErrorMessage;
520 # End of optional user configuration
521 # (note: there is more non-essential user configuration below)
523 if(grep(/\-\-help/i, @ARGV))
525 print << 'ENDOFPREHELPTEXT2';
527 ###############################################################################
529 # Author and Copyright (c):
530 # Rob van Son, © 1995,1996,1997,1998,1999,2000,2001,2002-2012
531 # NKI-AVL Amsterdam
532 # r.v.son@nki.nl
533 # Institute of Phonetic Sciences & IFOTT/ACLS
534 # University of Amsterdam
535 # Email: R.J.J.H.vanSon@gmail.com
536 # Email: R.J.J.H.vanSon@uva.nl
537 # WWW : http://www.fon.hum.uva.nl/rob/
539 # License for use and disclaimers
541 # CGIscriptor merges plain ASCII HTML files transparantly
542 # with CGI variables, in-line PERL code, shell commands,
543 # and executable scripts in other scripting languages.
545 # This program is free software; you can redistribute it and/or
546 # modify it under the terms of the GNU General Public License
547 # as published by the Free Software Foundation; either version 2
548 # of the License, or (at your option) any later version.
550 # This program is distributed in the hope that it will be useful,
551 # but WITHOUT ANY WARRANTY; without even the implied warranty of
552 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
553 # GNU General Public License for more details.
555 # You should have received a copy of the GNU General Public License
556 # along with this program; if not, write to the Free Software
557 # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
560 # Contributors:
561 # Rob van Son (R.J.J.H.vanSon@uva.nl)
562 # Gerd Franke franke@roo.de (designed the <DIV> behaviour)
564 #######################################################
565 ENDOFPREHELPTEXT2
567 #######################################################>>>>>>>>>>Start Remove
569 # You can skip the following code, it is an auto-splice
570 # procedure.
572 # Construct a slimmed down version of CGIscriptor
573 # (i.e., CGIscriptor.pl -slim > slimCGIscriptor.pl)
575 if($TrimDownCGIscriptor)
577 open(CGISCRIPTOR, "<CGIscriptor.pl")
578 || dieHandler(1, "<CGIscriptor.pl not slimmed down: $!\n");
579 my $SKIPtext = 0;
580 my $SKIPComments = 0;
582 while(<CGISCRIPTOR>)
584 my $SKIPline = 0;
586 ++$LineCount;
588 # Start of SKIP text
589 $SKIPtext = 1 if /[\>]{10}Start Remove/;
590 $SKIPComments = 1 if $SKIPtext == 1;
592 # Skip this line?
593 $SKIPline = 1 if $SKIPtext || ($SKIPComments && /^\s*\#/);
595 ++$PrintCount unless $SKIPline;
597 print STDOUT $_ unless $SKIPline;
599 # End of SKIP text ?
600 $SKIPtext = 0 if /[\<]{10}End Remove/;
602 # Ready!
603 print STDERR "\# Printed $PrintCount out of $LineCount lines\n";
604 exit;
607 #######################################################
609 if(grep(/\-\-help/i, @ARGV))
611 print << 'ENDOFHELPTEXT';
613 # HYPE
615 # CGIscriptor merges plain ASCII HTML files transparantly and safely
616 # with CGI variables, in-line PERL code, shell commands, and executable
617 # scripts in many languages (on-line and real-time). It combines the
618 # "ease of use" of HTML files with the versatillity of specialized
619 # scripts and PERL programs. It hides all the specifics and
620 # idiosyncrasies of correct output and CGI coding and naming. Scripts
621 # do not have to be aware of HTML, HTTP, or CGI conventions just as HTML
622 # files can be ignorant of scripts and the associated values. CGIscriptor
623 # complies with the W3C HTML 4.0 recommendations.
624 # In addition to its use as a WWW embeded CGI processor, it can
625 # be used as a command-line document preprocessor (text-filter).
627 # THIS IS HOW IT WORKS
629 # The aim of CGIscriptor is to execute "plain" scripts inside a text file
630 # using any required CGIparameters and environment variables. It
631 # is optimized to transparantly process HTML files inside a WWW server.
632 # The native language is Perl, but many other scripting languages
633 # can be used.
635 # CGIscriptor reads text files from the requested input file (i.e., from
636 # $YOUR_HTML_FILES$PATH_INFO) and writes them to <STDOUT> (i.e., the
637 # client requesting the service) preceded by the obligatory
638 # "Content-type: text/html\n\n" or "Content-type: text/plain\n\n" string
639 # (except for "raw" files which supply their own Content-type message
640 # and only if the SERVER_PROTOCOL supports HTTP, MAIL, or MIME).
642 # When CGIscriptor encounters an embedded script, indicated by an HTML4 tag
644 # <SCRIPT TYPE="text/ssperl" [CGI="$VAR='default value'"] [SRC="ScriptSource"]>
645 # PERL script
646 # </SCRIPT>
648 # or
650 # <SCRIPT TYPE="text/osshell" [CGI="$name='default value'"] [SRC="ScriptSource"]>
651 # OS Shell script
652 # </SCRIPT>
654 # construct (anything between []-brackets is optional, other MIME-types
655 # and scripting languages are supported), the embedded script is removed
656 # and both the contents of the source file (i.e., "do 'ScriptSource'")
657 # AND the script are evaluated as a PERL program (i.e., by eval()),
658 # shell script (i.e., by a "safe" version of `Command`, qx) or an external
659 # interpreter. The output of the eval() function takes the place of the
660 # original <SCRIPT></SCRIPT> construct in the output string. Any CGI
661 # parameters declared by the CGI attribute are available as simple perl
662 # variables, and can subsequently be made available as variables to other
663 # scripting languages (e.g., bash, python, or lisp).
665 # Example: printing "Hello World"
666 # <HTML><HEAD><TITLE>Hello World</TITLE>
667 # <BODY>
668 # <H1><SCRIPT TYPE="text/ssperl">"Hello World"</SCRIPT></H1>
669 # </BODY></HTML>
671 # Save this in a file, hello.html, in the directory you indicated with
672 # $YOUR_HTML_FILES and access http://your_server/SHTML/hello.html
673 # (or to whatever name you use as an alias for CGIscriptor.pl).
674 # This is realy ALL you need to do to get going.
676 # You can use any values that are delivered in CGI-compliant form (i.e.,
677 # the "?name=value" type URL additions) transparently as "$name" variables
678 # in your scripts IFF you have declared them in the CGI attribute of
679 # a META or SCRIPT tag before e.g.:
680 # <META CONTENT="text/ssperl; CGI='$name = `default value`'
681 # [SRC='ScriptSource']">
682 # or
683 # <SCRIPT TYPE="text/ssperl" CGI="$name = 'default value'"
684 # [SRC='ScriptSource']>
685 # After such a 'CGI' attribute, you can use $name as an ordinary PERL variable
686 # (the ScriptSource file is immediately evaluated with "do 'ScriptSource'").
687 # The CGIscriptor script allows you to write ordinary HTML files which will
688 # include dynamic CGI aware (run time) features, such as on-line answers
689 # to specific CGI requests, queries, or the results of calculations.
691 # For example, if you wanted to answer questions of clients, you could write
692 # a Perl program called "Answer.pl" with a function "AnswerQuestion()"
693 # that prints out the answer to requests given as arguments. You then write
694 # an HTML page "Respond.html" containing the following fragment:
696 # <center>
697 # The Answer to your question
698 # <META CONTENT="text/ssperl; CGI='$Question'">
699 # <h3><SCRIPT TYPE="text/ssperl">$Question</SCRIPT></h3>
700 # is
701 # <h3><SCRIPT TYPE="text/ssperl" SRC="./PATH/Answer.pl">
702 # AnswerQuestion($Question);
703 # </SCRIPT></h3>
704 # </center>
705 # <FORM ACTION=Respond.html METHOD=GET>
706 # Next question: <INPUT NAME="Question" TYPE=TEXT SIZE=40><br>
707 # <INPUT TYPE=SUBMIT VALUE="Ask">
708 # </FORM>
710 # The output could look like the following (in HTML-speak):
712 # <CENTER>
713 # The Answer to your question
714 # <h3>What is the capital of the Netherlands?</h3>
715 # is
716 # <h3>Amsterdam</h3>
717 # </CENTER>
718 # <FORM ACTION=Respond.html METHOD=GET>
719 # Next question: <INPUT NAME="Question" TYPE=TEXT SIZE=40><br>
720 # <INPUT TYPE=SUBMIT VALUE="Ask">
722 # Note that the function "Answer.pl" does know nothing about CGI or HTML,
723 # it just prints out answers to arguments. Likewise, the text has no
724 # provisions for scripts or CGI like constructs. Also, it is completely
725 # trivial to extend this "program" to use the "Answer" later in the page
726 # to call up other information or pictures/sounds. The final text never
727 # shows any cue as to what the original "source" looked like, i.e.,
728 # where you store your scripts and how they are called.
730 # There are some extra's. The argument of the files called in a SRC= tag
731 # can access the CGI variables declared in the preceding META tag from
732 # the @ARGV array. Executable files are called as:
733 # `file '$ARGV[0]' ... ` (e.g., `Answer.pl \'$Question\'`;)
734 # The files called from SRC can even be (CGIscriptor) html files which are
735 # processed in-line. Furthermore, the SRC= tag can contain a perl block
736 # that is evaluated. That is,
737 # <META CONTENT="text/ssperl; CGI='$Question' SRC='{$Question}'">
738 # will result in the evaluation of "print do {$Question};" and the VALUE
739 # of $Question will be printed. Note that these "SRC-blocks" can be
740 # preceded and followed by other file names, but only a single block is
741 # allowed in a SRC= tag.
743 # One of the major hassles of dynamic WWW pages is the fact that several
744 # mutually incompatible browsers and platforms must be supported. For example,
745 # the way sound is played automatically is different for Netscape and
746 # Internet Explorer, and for each browser it is different again on
747 # Unix, MacOS, and Windows. Realy dangerous is processing user-supplied
748 # (form-) values to construct email addresses, file names, or database
749 # queries. All Apache WWW-server exploits reported in the media are
750 # based on faulty CGI-scripts that didn't check their user-data properly.
752 # There is no panacee for these problems, but a lot of work and problems
753 # can be saved by allowing easy and transparent control over which
754 # <SCRIPT></SCRIPT> blocks are executed on what CGI-data. CGIscriptor
755 # supplies such a method in the form of a pair of attributes:
756 # IF='...condition..' and UNLESS='...condition...'. When added to a
757 # script tag, the whole block (including the SRC attribute) will be
758 # ignored if the condition is false (IF) or true (UNLESS).
759 # For example, the following block will NOT be evaluated if the value
760 # of the CGI variable FILENAME is NOT a valid filename:
762 # <SCRIPT TYPE='text/ssperl' CGI='$FILENAME'
763 # IF='CGIscriptor::CGIsafeFileName($FILENAME)'>
764 # .....
765 # </SCRIPT>
767 # (the function CGIsafeFileName(String) returns an empty string ("")
768 # if the String argument is not a valid filename).
769 # The UNLESS attribute is the mirror image of IF.
771 # A user manual follows the HTML 4 and security paragraphs below.
773 ##########################################################################
775 # HTML 4 compliance
777 # In general, CGIscriptor.pl complies with the HTML 4 recommendations of
778 # the W3C. This means that any software to manage Web sites will be able
779 # to handle CGIscriptor files, as will web agents.
781 # All script code should be placed between <SCRIPT></SCRIPT> tags, the
782 # script type is indicated with TYPE="mime-type", the LANGUAGE
783 # feature is ignored, and a SRC feature is implemented. All CGI specific
784 # features are delegated to the CGI attribute.
786 # However, the behavior deviates from the W3C recommendations at some
787 # points. Most notably:
788 # 0- The scripts are executed at the server side, invissible to the
789 # client (i.e., the browser)
790 # 1- The mime-types are personal and idiosyncratic, but can be adapted.
791 # 2- Code in the body of a <SCRIPT></SCRIPT> tag-pair is still evaluated
792 # when a SRC feature is present.
793 # 3- The SRC attribute reads a list of files.
794 # 4- The files in a SRC attribute are processed according to file type.
795 # 5- The SRC attribute evaluates inline Perl code.
796 # 6- Processed META, DIV, INS tags are removed from the output
797 # document.
798 # 7- All attributes of the processed META tags, except CONTENT, are ignored
799 # (i.e., deleted from the output).
800 # 8- META tags can be placed ANYWHERE in the document.
801 # 9- Through the SRC feature, META tags can have visible output in the
802 # document.
803 # 10- The CGI attribute that declares CGI parameters, can be used
804 # inside the <SCRIPT> tag.
805 # 11- Use of an extended quote set, i.e., '', "", ``, (), {}, []
806 # and their \-slashed combinations: \'\', \"\", \`\`, \(\),
807 # \{\}, \[\].
808 # 12- IF and UNLESS attributes to <SCRIPT>, <META>, <DIV>, <INS> tags.
809 # 13- <DIV> tags cannot be nested, DIV tags are not
810 # rendered with new-lines.
811 # 14- The XML style <TAG .... /> is recognized and handled correctly.
812 # (i.e., no content is processed)
814 # The reasons for these choices are:
815 # You can still write completely HTML4 compliant documents. CGIscriptor
816 # will not force you to write "deviant" code. However, it allows you to
817 # do so (which is, in fact, just as bad). The prime design principle
818 # was to allow users to include plain Perl code. The code itself should
819 # be "enhancement free". Therefore, extra features were needed to
820 # supply easy access to CGI and Web site components. For security
821 # reasons these have to be declared explicitly. The SRC feature
822 # transparently manages access to external files, especially the safe
823 # use of executable files.
824 # The CGI attribute handles the declarations of external (CGI) variables
825 # in the SCRIPT and META tag's.
826 # EVERYTHING THE CGI ATTRIBUTE AND THE META TAG DO CAN BE DONE INSIDE
827 # A <SCRIPT></SCRIPT> TAG CONSTRUCT.
829 # The reason for the IF, UNLESS, and SRC attributes (and their Perl code
830 # evaluation) were build into the META and SCRIPT tags is part laziness,
831 # part security. The SRC blocks allows more compact documents and easier
832 # debugging. The values of the CGI variables can be immediately screened
833 # for security by IF or UNLESS conditions, and even SRC attributes (e.g.,
834 # email addresses and file names), and a few commands can be called
835 # without having to add another Perl TAG pair. This is especially important
836 # for documents that require the use of other (more restricted) "scripting"
837 # languages and facilities that lag transparent control structures.
839 ##########################################################################
841 # SECURITY
843 # Your WWW site is a few keystrokes away from a few hundred million internet
844 # users. A fair percentage of these users knows more about your computer
845 # than you do. And some of these just might have bad intentions.
847 # To ensure uncompromized operation of your server and platform, several
848 # features are incorporated in CGIscriptor.pl to enhance security.
849 # First of all, you should check the source of this program. No security
850 # measures will help you when you download programs from anonymous sources.
851 # If you want to use THIS file, please make sure that it is uncompromized.
852 # The best way to do this is to contact the source and try to determine
853 # whether s/he is reliable (and accountable).
855 # BE AWARE THAT ANY PROGRAMMER CAN CHANGE THIS PROGRAM IN SUCH A WAY THAT
856 # IT WILL SET THE DOORS TO YOUR SYSTEM WIDE OPEN
858 # I would like to ask any user who finds bugs that could compromise
859 # security to report them to me (and any other bug too,
860 # Email: R.J.J.H.vanSon@uva.nl or ifa@hum.uva.nl).
862 # Security features
864 # 1 Invisibility
865 # The inner workings of the HTML source files are completely hidden
866 # from the client. Only the HTTP header and the ever changing content
867 # of the output distinguish it from the output of a plain, fixed HTML
868 # file. Names, structures, and arguments of the "embedded" scripts
869 # are invisible to the client. Error output is suppressed except
870 # during debugging (user configurable).
872 # 2 Separate directory trees
873 # Directories containing Inline text and script files can reside on
874 # separate trees, distinct from those of the HTTP server. This means
875 # that NEITHER the text files, NOR the script files can be read by
876 # clients other than through CGIscriptor.pl, UNLESS they are
877 # EXPLICITELY made available.
879 # 3 Requests are NEVER "evaluated"
880 # All client supplied values are used as literal values (''-quoted).
881 # Client supplied ''-quotes are ALWAYS removed. Therefore, as long as the
882 # embedded scripts do NOT themselves evaluate these values, clients CANNOT
883 # supply executable commands. Be sure to AVOID scripts like:
885 # <META CONTENT="text/ssperl; CGI='$UserValue'">
886 # <SCRIPT TYPE="text/ssperl">$dir = `ls -1 $UserValue`;</SCRIPT>
888 # These are a recipe for disaster. However, the following quoted
889 # form should be save (but is still not adviced):
891 # <SCRIPT TYPE="text/ssperl">$dir = `ls -1 \'$UserValue\'`;</SCRIPT>
893 # A special function, SAFEqx(), will automatically do exactly this,
894 # e.g., SAFEqx('ls -1 $UserValue') will execute `ls -1 \'$UserValue\'`
895 # with $UserValue interpolated. I recommend to use SAFEqx() instead
896 # of backticks whenever you can. The OS shell scripts inside
898 # <SCRIPT TYPE="text/osshell">ls -1 $UserValue</SCRIPT>
900 # are handeld by SAFEqx and automatically ''-quoted.
902 # 4 Logging of requests
903 # All requests can be logged separate from the Host server. The level of
904 # detail is user configurable: Including or excluding the actual queries.
905 # This allows for the inspection of (im-) proper use.
907 # 5 Access control: Clients
908 # The Remote addresses can be checked against a list of authorized
909 # (i.e., accepted) or non-authorized (i.e., rejected) clients. Both
910 # REMOTE_HOST and REMOTE_ADDR are tested so clients without a proper
911 # HOST name can be (in-) excluded by their IP-address. Client patterns
912 # containing all numbers and dots are considered IP-addresses, all others
913 # domain names. No wild-cards or regexp's are allowed, only partial
914 # addresses.
915 # Matching of names is done from the back to the front (domain first,
916 # i.e., $REMOTE_HOST =~ /\Q$pattern\E$/is), so including ".edu" will
917 # accept or reject all clients from the domain EDU. Matching of
918 # IP-addresses is done from the front to the back (domain first, i.e.,
919 # $REMOTE_ADDR =~ /^\Q$pattern\E/is), so including "128." will (in-)
920 # exclude all clients whose IP-address starts with 128.
921 # There are two special symbols: "-" matches HOSTs with no name and "*"
922 # matches ALL HOSTS/clients.
923 # For those needing more expressional power, lines starting with
924 # "-e" are evaluated by the perl eval() function. E.g.,
925 # '-e $REMOTE_HOST =~ /\.edu$/is;' will accept/reject clients from the
926 # domain '.edu'.
928 # 6 Access control: Files
929 # In principle, CGIscriptor could read ANY file in the directory
930 # tree as discussed in 1. However, for security reasons this is
931 # restricted to text files. It can be made more restricted by entering
932 # a global file pattern (e.g., ".html"). This is done by default.
933 # For each client requesting access, the file pattern(s) can be made
934 # more restrictive than the global pattern by entering client specific
935 # file patterns in the Access Control files (see 5).
936 # For example: if the ACCEPT file contained the lines
937 # * DEMO
938 # .hum.uva.nl LET
939 # 145.18.230.
940 # Then all clients could request paths containing "DEMO" or "demo", e.g.
941 # "/my/demo/file.html" ($PATH_INFO =~ /\Q$pattern\E/), Clients from
942 # *.hum.uva.nl could also request paths containing "LET or "let", e.g.
943 # "/my/let/file.html", and clients from the local cluster
944 # 145.18.230.[0-9]+ could access ALL files.
945 # Again, for those needing more expressional power, lines starting with
946 # "-e" are evaluated. For instance:
947 # '-e $REMOTE_HOST =~ /\.edu$/is && $PATH_INFO =~ m@/DEMO/@is;'
948 # will accept/reject requests for files from the directory "/demo/" from
949 # clients from the domain '.edu'.
951 # Access control: Login
952 # Specific paths can be controlled by Session Tickets which must be
953 # present as a SESSIONTICKET CGI variable in the request. These paths are
954 # defined in %LoginRequiredPatterns as pairs of:
955 # ('regexp' => 'SessionPath\tPasswordPath\tLogin.html').
956 # Session Tickets are stored in a separate directory (SessionPath, e.g.,
957 # "Private/.Session") as files with the exact same name of the SESSIONTICKET CGI
958 # Type: SESSION
959 # IPaddress: <127.0.0.1>
960 # AllowedPaths: <^/Private/Name/>
961 # Expires: <3600>
962 # ...
963 # Other content can follow. It is adviced that Session Tickets should be deleted
964 # after some (idle) time. The IP address should be the IP number at login, and
965 # the SESSIONTICKET will be rejected if it is presented from another IP address.
966 # AllowedPaths is a perl regexp. Be careful how they match. Make sure to delimit
967 # the names to prevent access to overlapping names, eg, "^/Private/Rob" will also
968 # match "^/Private/Robert", however, "^/Private/Rob/" will not. Expires is the
969 # time the ticket will remain valid after creation (file ctime). Time can be given
970 # in s[econds] (default), m[inutes], h[hours], or d[ays], eg, "24h" means 24 hours.
971 # None of these need be present, but the Ticket must have a non-zero size.
973 # 7 Query length limiting
974 # The length of the Query string can be limited. If CONTENT_LENGTH is larger
975 # than this limit, the request is rejected. The combined length of the
976 # Query string and the POST input is checked before any processing is done.
977 # This will prevent clients from overloading the scripts.
978 # The actual, combined, Query Size is accessible as a variable through
979 # $CGI_Content_Length.
981 # 8 Illegal filenames, paths, and protected directories
982 # One of the primary security concerns in handling CGI-scripts is the
983 # use of "funny" characters in the requests that con scripts in executing
984 # malicious commands. Examples are inserting ';', null bytes, or <newline>
985 # characters in URL's and filenames, followed by executable commands. A
986 # special variable $FileAllowedChars stores a string of all allowed
987 # characters. Any request that translates to a filename with a character
988 # OUTSIDE this set will be rejected.
989 # In general, all (readable files) in the DocumentRoot tree are accessible.
990 # This might not be what you want. For instance, your DocumentRoot directory
991 # might be the working directory of a CVS project and contain sensitive
992 # information (e.g., the password to get to the repository). You can block
993 # access to these subdirectories by adding the corresponding patterns to
994 # the $BlockPathAccess variable. For instance, $BlockPathAccess = '/CVS/'
995 # will block any request that contains '/CVS/' or:
996 # die if $BlockPathAccess && $ENV{'PATH_INFO'} =~ m@$BlockPathAccess@;
998 # 9 The execution of code blocks can be controlled in a transparent way
999 # by adding IF or UNLESS conditions in the tags themselves. That is,
1000 # a simple check of the validity of filenames or email addresses can
1001 # be done before any code is executed.
1003 ###############################################################################
1005 # USER MANUAL (sort of)
1007 # CGIscriptor removes embedded scripts, indicated by an HTML 4 type
1008 # <SCRIPT TYPE='text/ssperl'> </SCRIPT> or <SCRIPT TYPE='text/osshell'>
1009 # </SCRIPT> constructs. CGIscriptor also recognizes XML-type
1010 # <SCRIPT TYPE='text/ssperl'/> constructs. These are usefull when
1011 # the necessary code is already available in the TAG itself (e.g.,
1012 # using external files). The contents of the directive are executed by
1013 # the PERL eval() and `` functions (in a separate name space). The
1014 # result of the eval() function replaces the <SCRIPT> </SCRIPT> construct
1015 # in the output file. You can use the values that are delivered in
1016 # CGI-compliant form (i.e., the "?name=value&.." type URL additions)
1017 # transparently as "$name" variables in your directives after they are
1018 # defined in a <META> or <SCRIPT> tag.
1019 # If you define the variable "$CGIscriptorResults" in a CGI attribute, all
1020 # subsequent <SCRIPT> and <META> results (including the defining
1021 # tag) will also be pushed onto a stack: @CGIscriptorResults. This list
1022 # behaves like any other, ordinary list and can be manipulated.
1024 # Both GET and POST requests are accepted. These two methods are treated
1025 # equal. Variables, i.e., those values that are determined when a file is
1026 # processed, are indicated in the CGI attribute by $<name> or $<name>=<default>
1027 # in which <name> is the name of the variable and <default> is the value
1028 # used when there is NO current CGI value for <name> (you can use
1029 # white-spaces in $<name>=<default> but really DO make sure that the
1030 # default value is followed by white space or is quoted). Names can contain
1031 # any alphanumeric characters and _ (i.e., names match /[\w]+/).
1032 # If the Content-type: is 'multipart/*', the input is treated as a
1033 # MIME multipart message and automatically delimited. CGI variables get
1034 # the "raw" (i.e., undecoded) body of the corresponding message part.
1036 # Variables can be CGI variables, i.e., those from the QUERY_STRING,
1037 # environment variables, e.g., REMOTE_USER, REMOTE_HOST, or REMOTE_ADDR,
1038 # or predefined values, e.g., CGI_Decoded_QS (The complete, decoded,
1039 # query string), CGI_Content_Length (the length of the decoded query
1040 # string), CGI_Year, CGI_Month, CGI_Time, and CGI_Hour (the current
1041 # date and time).
1043 # All these are available when defined in a CGI attribute. All environment
1044 # variables are accessible as $ENV{'name'}. So, to access the REMOTE_HOST
1045 # and the REMOTE_USER, use, e.g.:
1047 # <SCRIPT TYPE='text/ssperl'>
1048 # ($ENV{'REMOTE_HOST'}||"-")." $ENV{'REMOTE_USER'}"
1049 # </SCRIPT>
1051 # (This will print a "-" if REMOTE_HOST is not known)
1052 # Another way to do this is:
1054 # <META CONTENT="text/ssperl; CGI='$REMOTE_HOST = - $REMOTE_USER'">
1055 # <SCRIPT TYPE='text/ssperl'>"$REMOTE_HOST $REMOTE_USER"</SCRIPT>
1056 # or
1057 # <META CONTENT='text/ssperl; CGI="$REMOTE_HOST = - $REMOTE_USER"
1058 # SRC={"$REMOTE_HOST $REMOTE_USER\n"}'>
1060 # This is possible because ALL environment variables are available as
1061 # CGI variables. The environment variables take precedence over CGI
1062 # names in case of a "name clash". For instance:
1063 # <META CONTENT="text/ssperl; CGI='$HOME' SRC={$HOME}">
1064 # Will print the current HOME directory (environment) irrespective whether
1065 # there is a CGI variable from the query
1066 # (e.g., Where do you live? <INPUT TYPE="TEXT" NAME="HOME">)
1067 # THIS IS A SECURITY FEATURE. It prevents clients from changing
1068 # the values of defined environment variables (e.g., by supplying
1069 # a bogus $REMOTE_ADDR). Although $ENV{} is not changed by the META tags,
1070 # it would make the use of declared variables insecure. You can still
1071 # access CGI variables after a name clash with
1072 # CGIscriptor::CGIparseValue(<name>).
1074 # Some CGI variables are present several times in the query string
1075 # (e.g., from multiple selections). These should be defined as
1076 # @VARIABLENAME=default in the CGI attribute. The list @VARIABLENAME
1077 # will contain ALL VARIABLENAME values from the query, or a single
1078 # default value. If there is an ENVIRONMENT variable of the
1079 # same name, it will be used instead of the default AND the query
1080 # values. The corresponding function is
1081 # CGIscriptor::CGIparseValueList(<name>)
1083 # CGI variables collected in a @VARIABLENAME list are unordered.
1084 # When more structured variables are needed, a hash table can be used.
1085 # A variable defined as %VARIABLE=default will collect all
1086 # CGI-parameters whose name start with 'VARIABLE' in a hash table with
1087 # the remainder of the name as a key. For instance, %PERSON will
1088 # collect PERSONname='John Doe', PERSONbirthdate='01 Jan 00', and
1089 # PERSONspouse='Alice' into a hash table %PERSON such that $PERSON{'spouse'}
1090 # equals 'Alice'. Any default value or environment value will be stored
1091 # under the "" key. If there is an ENVIRONMENT variable of the same name,
1092 # it will be used instead of the default AND the query values. The
1093 # corresponding function is CGIscriptor::CGIparseValueHash(<name>)
1095 # This method of first declaring your environment and CGI variables
1096 # before being able to use them in the scripts might seem somewhat
1097 # clumsy, but it protects you from inadvertedly printing out the values of
1098 # system environment variables when their names coincide with those used
1099 # in the CGI forms. It also prevents "clients" from supplying CGI
1100 # parameter values for your private variables.
1101 # THIS IS A SECURITY FEATURE!
1104 # NON-HTML CONTENT TYPES
1106 # Normally, CGIscriptor prints the standard "Content-type: text/html\n\n"
1107 # message before anything is printed. This has been extended to include
1108 # plain text (.txt) files, for which the Content-type (MIME type)
1109 # 'text/plain' is printed. In all other respects, text files are treated
1110 # as HTML files (this can be switched off by removing '.txt' from the
1111 # $FilePattern variable) . When the content type should be something else,
1112 # e.g., with multipart files, use the $RawFilePattern (.xmr, see also next
1113 # item). CGIscriptor will not print a Content-type message for this file
1114 # type (which must supply its OWN Content-type message). Raw files must
1115 # still conform to the <SCRIPT></SCRIPT> and <META> tag specifications.
1118 # NON-HTML FILES
1120 # CGIscriptor is intended to process HTML and text files only. You can
1121 # create documents of any mime-type on-the-fly using "raw" text files,
1122 # e.g., with the .xmr extension. However, CGIscriptor will not process
1123 # binary files of any type, e.g., pictures or sounds. Given the sheer
1124 # number of formats, I do not have any intention to do so. However,
1125 # an escape route has been provided. You can construct a genuine raw
1126 # (.xmr) text file that contains the perl code to service any file type
1127 # you want. If the global $BinaryMapFile variable contains the path to
1128 # this file (e.g., /BinaryMapFile.xmr), this file will be called
1129 # whenever an unsupported (non-HTML) file type is requested. The path
1130 # to the requested binary file is stored in $ENV('CGI_BINARY_FILE')
1131 # and can be used like any other CGI-variable. Servicing binary files
1132 # then becomes supplying the correct Content-type (e.g., print
1133 # "Content-type: image/jpeg\n\n";) and reading the file and writing it
1134 # to STDOUT (e.g., using sysread() and syswrite()).
1137 # THE META TAG
1139 # All attributes of a META tag are ignored, except the
1140 # CONTENT='text/ssperl; CGI=" ... " [SRC=" ... "]' attribute. The string
1141 # inside the quotes following the CONTENT= indication (white-space is
1142 # ignored, "" '' `` (){}[]-quote pairs are allowed, plus their \ versions)
1143 # MUST start with any of the CGIscriptor mime-types (e.g.: text/ssperl or
1144 # text/osshell) and a comma or semicolon.
1145 # The quoted string following CGI= contains a white-space separated list
1146 # of declarations of the CGI (and Environment) values and default values
1147 # used when no CGI values are supplied by the query string.
1149 # If the default value is a longer string containing special characters,
1150 # possibly spanning several lines, the string must be enclosed in quotes.
1151 # You may use any pair of quotes or brackets from the list '', "", ``, (),
1152 # [], or {} to distinguish default values (or preceded by \, e.g., \(...\)
1153 # is different from (...)). The outermost pair will always be used and any
1154 # other quotes inside the string are considered to be part of the string
1155 # value, e.g.,
1157 # $Value = {['this'
1158 # "and" (this)]}
1159 # will result in $Value getting the default value: ['this'
1160 # "and" (this)]
1161 # (NOTE that the newline is part of the default value!).
1163 # Internally, for defining and initializing CGI (ENV) values, the META
1164 # and SCRIPT tags use the functions "defineCGIvariable($name, $default)"
1165 # (scalars) and "defineCGIvariableList($name, $default)" (lists).
1166 # These functions can be used inside scripts as
1167 # "CGIscriptor::defineCGIvariable($name, $default)" and
1168 # "CGIscriptor::defineCGIvariableList($name, $default)".
1169 # "CGIscriptor::defineCGIvariableHash($name, $default)".
1171 # The CGI attribute will be processed exactly identical when used inside
1172 # the <SCRIPT> tag. However, this use is not according to the
1173 # HTML 4.0 specifications of the W3C.
1176 # THE DIV/INS TAGS
1178 # There is a problem when constructing html files containing
1179 # server-side perl scripts with standard HTML tools. These
1180 # tools will refuse to process any text between <SCRIPT></SCRIPT>
1181 # tags. This is quite annoying when you want to use large
1182 # HTML templates where you will fill in values.
1184 # For this purpose, CGIscriptor will read the neutral
1185 # <DIV CLASS="ssperl" ID="varname"></DIV> or
1186 # <INS CLASS="ssperl" ID="varname"></INS>
1187 # tag (in Cascading Style Sheet manner) Note that
1188 # "varname" has NO '$' before it, it is a bare name.
1189 # Any text between these <DIV ...></DIV> or
1190 # <INS ...></INS>tags will be assigned to '$varname'
1191 # as is (e.g., as a literal).
1192 # No processing or interpolation will be performed.
1193 # There is also NO nesting possible. Do NOT nest a
1194 # </DIV> inside a <DIV></DIV>! Moreover, neither INS nor
1195 # DIV tags do ensure a block structure in the final
1196 # rendering (i.e., no empty lines).
1198 # Note that <DIV CLASS="ssperl" ID="varname"/>
1199 # is handled the XML way. No content is processed,
1200 # but varname is defined, and any SRC directives are
1201 # processed.
1203 # You can use $varname like any other variable name.
1204 # However, $varname is NOT a CGI variable and will be
1205 # completely internal to your script. There is NO
1206 # interaction between $varname and the outside world.
1208 # To interpolate a DIV derived text, you can use:
1209 # $varname =~ s/([\]])/\\\1/g; # Mark ']'-quotes
1210 # $varname = eval("qq[$varname]"); # Interpolate all values
1212 # The DIV tags will process IF, UNLESS, CGI and
1213 # SRC attributes. The SRC files will be pre-pended to the
1214 # body text of the tag. SRC blocks are NOT executed.
1216 # CONDITIONAL PROCESSING: THE 'IF' AND 'UNLESS' ATTRIBUTES
1218 # It is often necessary to include code-blocks that should be executed
1219 # conditionally, e.g., only for certain browsers or operating system.
1220 # Furthermore, quite often sanity and security checks are necessary
1221 # before user (form) data can be processed, e.g., with respect to
1222 # email addresses and filenames.
1224 # Checks added to the code are often difficult to find, interpret or
1225 # maintain and in general mess up the code flow. This kind of confussion
1226 # is dangerous.
1227 # Also, for many of the supported "foreign" scripting languages, adding
1228 # these checks is cumbersome or even impossible.
1230 # As a uniform method for asserting the correctness of "context", two
1231 # attributes are added to all supported tags: IF and UNLESS.
1232 # They both evaluate their value and block execution when the
1233 # result is <FALSE> (IF) or <TRUE> (UNLESS) in Perl, e.g.,
1234 # UNLESS='$NUMBER \> 100;' blocks execution if $NUMBER <= 100. Note that
1235 # the backslash in the '\>' is removed and only used to differentiate
1236 # this conditional '>' from the tag-closing '>'. For symmetry, the
1237 # backslash in '\<' is also removed. Inside these conditionals,
1238 # ~/ and ./ are expanded to their respective directory root paths.
1240 # For example, the following tag will be ignored when the filename is
1241 # invalid:
1243 # <SCRIPT TYPE='text/ssperl' CGI='$FILENAME'
1244 # IF='CGIscriptor::CGIsafeFileName($FILENAME);'>
1245 # ...
1246 # </SCRIPT>
1248 # The IF and UNLESS values must be quoted. The same quotes are supported
1249 # as with the other attributes. The SRC attribute is ignored when IF and
1250 # UNLESS block execution.
1252 # NOTE: 'IF' and 'UNLESS' always evaluate perl code.
1255 # THE MAGIC SOURCE ATTRIBUTE (SRC=)
1257 # The SRC attribute inside tags accepts a list of filenames and URL's
1258 # separated by "," comma's (or ";" semicolons).
1259 # ALL the variable values defined in the CGI attribute are available
1260 # in @ARGV as if the file or block was executed from the command line,
1261 # in the exact order in which they were declared in the preceding CGI
1262 # attribute.
1264 # First, a SRC={}-block will be evaluated as if the code inside the
1265 # block was part of a <SCRIPT></SCRIPT> construct, i.e.,
1266 # "print do { code };'';" or `code` (i.e., SAFEqx('code)).
1267 # Only a single block is evaluated. Note that this is processed less
1268 # efficiently than <SCRIPT> </SCRIPT> blocks. Type of evaluation
1269 # depends on the content-type: Perl for text/ssperl and OS shell for
1270 # text/osshell. For other mime types (scripting languages), anything in
1271 # the source block is put in front of the code block "inside" the tag.
1273 # Second, executable files (i.e., -x filename != 0) are evaluated as:
1274 # print `filename \'$ARGV[0]\' \'$ARGV[1]\' ...`
1275 # That is, you can actually call executables savely from the SRC tag.
1277 # Third, text files that match the file pattern, used by CGIscriptor to
1278 # check whether files should be processed ($FilePattern), are
1279 # processed in-line (i.e., recursively) by CGIscriptor as if the code
1280 # was inserted in the original source file. Recursions, i.e., calling
1281 # a file inside itself, are blocked. If you need them, you have to code
1282 # them explicitely using "main::ProcessFile($file_path)".
1284 # Fourth, Perl text files (i.e., -T filename != 0) are evaluated as:
1285 # "do FileName;'';".
1287 # Last, URL's (i.e., starting with 'HTTP://', 'FTP://', 'GOPHER://',
1288 # 'TELNET://', 'WHOIS://' etc.) are loaded
1289 # and printed. The loading and handling of <BASE> and document header
1290 # is done by a command generated by main::GET_URL($URL [, 0]). You can enter your
1291 # own code (default is curl, wget, or snarf and some post-processing to add a <BASE> tag).
1293 # There are two pseudo-file names: PREFIX and POSTFIX. These implement
1294 # a switch from prefixing the SRC code/files (PREFIX, default) before the
1295 # content of the tag to appending the code after the content of the tag
1296 # (POSTFIX). The switches are done in the order in which the PREFIX and
1297 # POSTFIX labels are encountered. You can mix PREFIX and POSTFIX labels
1298 # in any order with the SRC files. Note that the ORDER of file execution
1299 # is determined for prefixed and postfixed files seperately.
1301 # File paths can be preceded by the URL protocol prefix "file://". This
1302 # is simply STRIPPED from the name.
1304 # Example:
1305 # The request
1306 # "http://cgi-bin/Action_Forms.pl/Statistics/Sign_Test.html?positive=8&negative=22
1307 # will result in printing "${SS_PUB}/Statistics/Sign_Test.html"
1308 # With QUERY_STRING = "positive=8&negative=22"
1310 # on encountering the lines:
1311 # <META CONTENT="text/osshell; CGI='$positive=11 $negative=3'">
1312 # <b><SCRIPT LANGUAGE=PERL TYPE="text/ssperl" SRC="./Statistics/SignTest.pl">
1313 # </SCRIPT></b><p>"
1315 # This line will be processed as:
1316 # "<b>`${SS_SCRIPT}/Statistics/SignTest.pl '8' '22'`</b><p>"
1318 # In which "${SS_SCRIPT}/Statistics/SignTest.pl" is an executable script,
1319 # This line will end up printed as:
1320 # "<b>p <= 0.0161</b><p>"
1322 # Note that the META tag itself will never be printed, and is invisible to
1323 # the outside world.
1325 # The SRC files in a DIV or INS tag will be added (pre-pended) to the body
1326 # of the <DIV></DIV> tag. Blocks are NOT executed! If you do not
1327 # need any content, you can use the <DIV...../> format.
1330 # THE CGISCRIPTOR ROOT DIRECTORIES ~/ AND ./
1332 # Inside <SCRIPT></SCRIPT> tags, filepaths starting
1333 # with "~/" are replaced by "$YOUR_HTML_FILES/", this way files in the
1334 # public directories can be accessed without direct reference to the
1335 # actual paths. Filepaths starting with "./" are replaced by
1336 # "$YOUR_SCRIPTS/" and this should only be used for scripts.
1338 # Note: this replacement can seriously affect Perl scripts. Watch
1339 # out for constructs like $a =~ s/aap\./noot./g, use
1340 # $a =~ s@aap\.@noot.@g instead.
1342 # CGIscriptor.pl will assign the values of $SS_PUB and $SS_SCRIPT
1343 # (i.e., $YOUR_HTML_FILES and $YOUR_SCRIPTS) to the environment variables
1344 # $SS_PUB and $SS_SCRIPT. These can be accessed by the scripts that are
1345 # executed.
1346 # Values not preceded by $, ~/, or ./ are used as literals
1349 # OS SHELL SCRIPT EVALUATION (CONTENT-TYPE=TEXT/OSSHELL)
1351 # OS scripts are executed by a "safe" version of the `` operator (i.e.,
1352 # SAFEqx(), see also below) and any output is printed. CGIscriptor will
1353 # interpolate the script and replace all user-supplied CGI-variables by
1354 # their ''-quoted values (actually, all variables defined in CGI attributes
1355 # are quoted). Other Perl variables are interpolated in a simple fasion,
1356 # i.e., $scalar by their value, @list by join(' ', @list), and %hash by
1357 # their name=value pairs. Complex references, e.g., @$variable, are all
1358 # evaluated in a scalar context. Quotes should be used with care.
1359 # NOTE: the results of the shell script evaluation will appear in the
1360 # @CGIscriptorResults stack just as any other result.
1361 # All occurrences of $@% that should NOT be interpolated must be
1362 # preceeded by a "\". Interpolation can be switched off completely by
1363 # setting $CGIscriptor::NoShellScriptInterpolation = 1
1364 # (set to 0 or undef to switch interpolation on again)
1365 # i.e.,
1366 # <SCRIPT TYPE="text/ssperl">
1367 # $CGIscriptor::NoShellScriptInterpolation = 1;
1368 # </SCRIPT>
1371 # RUN TIME TRANSLATION OF INPUT FILES
1373 # Allows general and global conversions of files using Regular Expressions.
1374 # Very handy (but costly) to rewrite legacy pages to a new format.
1375 # Select files to use it on with
1376 # my $TranslationPaths = 'filepattern';
1377 # This is costly. For efficiency, define:
1378 # $TranslationPaths = ''; when not using translations.
1379 # Accepts general regular expressions: [$pattern, $replacement]
1381 # Define:
1382 # my $TranslationPaths = 'filepattern'; # Pattern matching PATH_INFO
1384 # push(@TranslationTable, ['pattern', 'replacement']);
1385 # e.g. (for Ruby Rails):
1386 # push(@TranslationTable, ['<%=', '<SCRIPT TYPE="text/ssruby">']);
1387 # push(@TranslationTable, ['%>', '</SCRIPT>']);
1389 # Runs:
1390 # my $currentRegExp;
1391 # foreach $currentRegExp (@TranslationTable)
1393 # my ($pattern, $replacement) = @$currentRegExp;
1394 # $$text =~ s!$pattern!$replacement!msg;
1395 # };
1398 # EVALUATION OF OTHER SCRIPTING LANGUAGES
1400 # Adding a MIME-type and an interpreter command to
1401 # %ScriptingLanguages automatically will catch any other
1402 # scripting language in the standard
1403 # <SCRIPT TYPE="[mime]"></SCRIPT> manner.
1404 # E.g., adding: $ScriptingLanguages{'text/sspython'} = 'python';
1405 # will actually execute the folowing code in an HTML page
1406 # (ignore 'REMOTE_HOST' for the moment):
1407 # <SCRIPT TYPE="text/sspython">
1408 # # A Python script
1409 # x = ["A","real","python","script","Hello","World","and", REMOTE_HOST]
1410 # print x[4:8] # Prints the list ["Hello","World","and", REMOTE_HOST]
1411 # </SCRIPT>
1413 # The script code is NOT interpolated by perl, EXCEPT for those
1414 # interpreters that cannot handle variables themselves.
1415 # Currently, several interpreters are pre-installed:
1417 # Perl test - "text/testperl" => 'perl',
1418 # Python - "text/sspython" => 'python',
1419 # Ruby - "text/ssruby" => 'ruby',
1420 # Tcl - "text/sstcl" => 'tcl',
1421 # Awk - "text/ssawk" => 'awk -f-',
1422 # Gnu Lisp - "text/sslisp" => 'rep | tail +5 '.
1423 # "| egrep -v '> |^rep. |^nil\\\$'",
1424 # XLispstat - "text/xlispstat" => 'xlispstat | tail +7 '.
1425 # "| egrep -v '> \\\$|^NIL'",
1426 # Gnu Prolog- "text/ssprolog" => 'gprolog',
1427 # M4 macro's- "text/ssm4" => 'm4',
1428 # Born shell- "text/sh" => 'sh',
1429 # Bash - "text/bash" => 'bash',
1430 # C-shell - "text/csh" => 'csh',
1431 # Korn shell- "text/ksh" => 'ksh',
1432 # Praat - "text/sspraat" => "praat - | sed 's/Praat > //g'",
1433 # R - "text/ssr" => "R --vanilla --slave | sed 's/^[\[0-9\]*] //g'",
1434 # REBOL - "text/ssrebol" =>
1435 # "rebol --quiet|egrep -v '^[> ]* == '|sed 's/^\s*\[> \]* //g'",
1436 # PostgreSQL- "text/postgresql" => 'psql 2>/dev/null',
1437 # (psql)
1439 # Note that the "value" of $ScriptingLanguages{mime} must be a command
1440 # that reads Standard Input and writes to standard output. Any extra
1441 # output of interactive interpreters (banners, echo's, prompts)
1442 # should be removed by piping the output through 'tail', 'grep',
1443 # 'sed', or even 'awk' or 'perl'.
1445 # For access to CGI variables there is a special hashtable:
1446 # %ScriptingCGIvariables.
1447 # CGI variables can be accessed in three ways.
1448 # 1. If the mime type is not present in %ScriptingCGIvariables,
1449 # nothing is done and the script itself should parse the relevant
1450 # environment variables.
1451 # 2. If the mime type IS present in %ScriptingCGIvariables, but it's
1452 # value is empty, e.g., $ScriptingCGIvariables{"text/sspraat"} = '';,
1453 # the script text is interpolated by perl. That is, all $var, @array,
1454 # %hash, and \-slashes are replaced by their respective values.
1455 # 3. In all other cases, the CGI and environment variables are added
1456 # in front of the script according to the format stored in
1457 # %ScriptingCGIvariables. That is, the following (pseudo-)code is
1458 # executed for each CGI- or Environment variable defined in the CGI-tag:
1459 # printf(INTERPRETER, $ScriptingCGIvariables{$mime}, $CGI_NAME, $CGI_VALUE);
1461 # For instance, "text/testperl" => '$%s = "%s";' defines variable
1462 # definitions for Perl, and "text/sspython" => '%s = "%s"' for Python
1463 # (note that these definitions are not save, the real ones contain '-quotes).
1465 # THIS WILL NOT WORK FOR @VARIABLES, the (empty) $VARIABLES will be used
1466 # instead.
1468 # The $CGI_VALUE parameters are "shrubed" of all control characters
1469 # and quotes (by &shrubCGIparameter($CGI_VALUE)) for the options 2 and 3.
1470 # Control characters are replaced by \0<octal ascii value> (the exception
1471 # is \015, the newline, which is replaced by \n) and quotes
1472 # and backslashes by their HTML character
1473 # value (' -> &#39; ` -> &#96; " -> &quot; \ -> &#92; & -> &amper;).
1474 # For example:
1475 # if a client would supply the string value (in standard perl, e.g.,
1476 # \n means <newline>)
1477 # "/dev/null';\nrm -rf *;\necho '"
1478 # it would be processed as
1479 # '/dev/null&#39;;\nrm -rf *;\necho &#39;'
1480 # (e.g., sh or bash would process the latter more according to your
1481 # intentions).
1482 # If your intepreter requires different protection measures, you will
1483 # have to supply these in %main::SHRUBcharacterTR (string => translation),
1484 # e.g., $SHRUBcharacterTR{"\'"} = "&#39;";
1486 # Currently, the following definitions are used:
1487 # %ScriptingCGIvariables = (
1488 # "text/testperl" => "\$\%s = '\%s';", # Perl $VAR = 'value' (for testing)
1489 # "text/sspython" => "\%s = '\%s'", # Python VAR = 'value'
1490 # "text/ssruby" => '@%s = "%s"', # Ruby @VAR = "value"
1491 # "text/sstcl" => 'set %s "%s"', # TCL set VAR "value"
1492 # "text/ssawk" => '%s = "%s";', # Awk VAR = "value";
1493 # "text/sslisp" => '(setq %s "%s")', # Gnu lisp (rep) (setq VAR "value")
1494 # "text/xlispstat" => '(setq %s "%s")', # Xlispstat (setq VAR "value")
1495 # "text/ssprolog" => '', # Gnu prolog (interpolated)
1496 # "text/ssm4" => "define(`\%s', `\%s')", # M4 macro's define(`VAR', `value')
1497 # "text/sh" => "\%s='\%s';", # Born shell VAR='value';
1498 # "text/bash" => "\%s='\%s';", # Born again shell VAR='value';
1499 # "text/csh" => "\$\%s = '\%s';", # C shell $VAR = 'value';
1500 # "text/ksh" => "\$\%s = '\%s';", # Korn shell $VAR = 'value';
1501 # "text/sspraat" => '', # Praat (interpolation)
1502 # "text/ssr" => '%s <- "%s";', # R VAR <- "value";
1503 # "text/ssrebol" => '%s: copy "%s"', # REBOL VAR: copy "value"
1504 # "text/postgresql" => '', # PostgreSQL (interpolation)
1505 # "" => ""
1506 # );
1508 # Four tables allow fine-tuning of interpreter with code that should be
1509 # added before and after each code block:
1511 # Code added before each script block
1512 # %ScriptingPrefix = (
1513 # "text/testperl" => "\# Prefix Code;", # Perl script testing
1514 # "text/ssm4" => 'divert(0)' # M4 macro's (open STDOUT)
1515 # );
1516 # Code added at the end of each script block
1517 # %ScriptingPostfix = (
1518 # "text/testperl" => "\# Postfix Code;", # Perl script testing
1519 # "text/ssm4" => 'divert(-1)' # M4 macro's (block STDOUT)
1520 # );
1521 # Initialization code, inserted directly after opening (NEVER interpolated)
1522 # %ScriptingInitialization = (
1523 # "text/testperl" => "\# Initialization Code;", # Perl script testing
1524 # "text/ssawk" => 'BEGIN {', # Server Side awk scripts
1525 # "text/sslisp" => '(prog1 nil ', # Lisp (rep)
1526 # "text/xlispstat" => '(prog1 nil ', # xlispstat
1527 # "text/ssm4" => 'divert(-1)' # M4 macro's (block STDOUT)
1528 # );
1529 # Cleanup code, inserted before closing (NEVER interpolated)
1530 # %ScriptingCleanup = (
1531 # "text/testperl" => "\# Cleanup Code;", # Perl script testing
1532 # "text/sspraat" => 'Quit',
1533 # "text/ssawk" => '};', # Server Side awk scripts
1534 # "text/sslisp" => '(princ "\n" standard-output)).' # Closing print to rep
1535 # "text/xlispstat" => '(print "" *standard-output*)).' # Closing print to xlispstat
1536 # "text/postgresql" => '\q',
1537 # );
1540 # The SRC attribute is NOT magical for these interpreters. In short,
1541 # all code inside a source file or {} block is written verbattim
1542 # to the interpreter. No (pre-)processing or executional magic is done.
1544 # A serious shortcomming of the described mechanism for handling other
1545 # (scripting) languages, with respect to standard perl scripts
1546 # (i.e., 'text/ssperl'), is that the code is only executed when
1547 # the pipe to the interpreter is closed. So the pipe has to be
1548 # closed at the end of each block. This means that the state of the
1549 # interpreter (e.g., all variable values) is lost after the closing of
1550 # the next </SCRIPT> tag. The standard 'text/ssperl' scripts retain
1551 # all values and definitions.
1553 # APPLICATION MIME TYPES
1555 # To ease some important auxilliary functions from within the
1556 # html pages I have added them as MIME types. This uses
1557 # the mechanism that is also used for the evaluation of
1558 # other scripting languages, with interpolation of CGI
1559 # parameters (and perl-variables). Actually, these are
1560 # defined exactly like any other "scripting language".
1562 # text/ssdisplay: display some (HTML) text with interpolated
1563 # variables (uses `cat`).
1564 # text/sslogfile: write (append) the interpolated block to the file
1565 # mentioned on the first, non-empty line
1566 # (the filename can be preceded by 'File: ',
1567 # note the space after the ':',
1568 # uses `awk .... >> <filename>`).
1569 # text/ssmailto: send email directly from within the script block.
1570 # The first line of the body must contain
1571 # To:Name@Valid.Email.Address
1572 # (note: NO space between 'To:' and the email adres)
1573 # For other options see the mailto man pages.
1574 # It works by directly sending the (interpolated)
1575 # content of the text block to a pipe into the
1576 # Linux program 'mailto'.
1578 # In these script blocks, all Perl variables will be
1579 # replaced by their values. All CGI variables are cleaned before
1580 # they are used. These CGI variables must be redefined with a
1581 # CGI attribute to restore their original values.
1582 # In general, this will be more secure than constructing
1583 # e.g., your own email command lines. For instance, Mailto will
1584 # not execute any odd (forged) email addres, but just stops
1585 # when the email address is invalid and awk will construct
1586 # any filename you give it (e.g. '<File;rm\\\040-f' would end up
1587 # as a "valid" UNIX filename). Note that it will also gladly
1588 # store this file anywhere (/../../../etc/passwd will work!).
1589 # Use the CGIscriptor::CGIsafeFileName() function to clean the
1590 # filename.
1592 # SHELL SCRIPT PIPING
1594 # If a shell script starts with the UNIX style "#! <shell command> \n"
1595 # line, the rest of the shell script is piped into the indicated command,
1596 # i.e.,
1597 # open(COMMAND, "| command");print COMMAND $RestOfScript;
1599 # In many ways this is equivalent to the MIME-type profiling for
1600 # evaluating other scripting languages as discussed above. The
1601 # difference breaks down to convenience. Shell script piping is a
1602 # "raw" implementation. It allows you to control all aspects of
1603 # execution. Using the MIME-type profiling is easier, but has a
1604 # lot of defaults built in that might get in the way. Another
1605 # difference is that shell script piping uses the SAFEqx() function,
1606 # and MIME-type profiling does not.
1608 # Execution of shell scripts is under the control of the Perl Script blocks
1609 # in the document. The MIME-type triggered execution of <SCRIPT></SCRIPT>
1610 # blocks can be simulated easily. You can switch to a different shell,
1611 # e.g. tcl, completely by executing the following Perl commands inside
1612 # your document:
1614 # <SCRIPT TYPE="text/ssperl">
1615 # $main::ShellScriptContentType = "text/ssTcl"; # Yes, you can do this
1616 # CGIscriptor::RedirectShellScript('/usr/bin/tcl'); # Pipe to Tcl
1617 # $CGIscriptor::NoShellScriptInterpolation = 1;
1618 # </SCRIPT>
1620 # After this script is executed, CGIscriptor will parse scripts of
1621 # TYPE="text/ssTcl" and pipe their contents into '|/usr/bin/tcl'
1622 # WITHOUT interpolation (i.e., NO substitution of Perl variables).
1623 # The crucial function is :
1624 # CGIscriptor::RedirectShellScript('/usr/bin/tcl')
1625 # After executing this function, all shell scripts AND all
1626 # calls to SAFEqx()) are piped into '|/usr/bin/tcl'. If the argument
1627 # of RedirectShellScript is empty, e.g., '', the original (default)
1628 # value is reset.
1630 # The standard output, STDOUT, of any pipe is send to the client.
1631 # Currently, you should be carefull with quotes in such a piped script.
1632 # The results of a pipe is NOT put on the @CGIscriptorResults stack.
1633 # As a result, you do not have access to the output of any piped (#!)
1634 # process! If you want such access, execute
1635 # <SCRIPT TYPE="text/osshell">echo "script"|command</SCRIPT>
1636 # or
1637 # <SCRIPT TYPE="text/ssperl">
1638 # $resultvar = SAFEqx('echo "script"|command');
1639 # </SCRIPT>.
1641 # Safety is never complete. Although SAFEqx() prevents some of the
1642 # most obvious forms of attacks and security slips, it cannot prevent
1643 # them all. Especially, complex combinations of quotes and intricate
1644 # variable references cannot be handled safely by SAFEqx. So be on
1645 # guard.
1648 # PERL CODE EVALUATION (CONTENT-TYPE=TEXT/SSPERL)
1650 # All PERL scripts are evaluated inside a PERL package. This package
1651 # has a separate name space. This isolated name space protects the
1652 # CGIscriptor.pl program against interference from user code. However,
1653 # some variables, e.g., $_, are global and cannot be protected. You are
1654 # advised NOT to use such global variable names. You CAN write
1655 # directives that directly access the variables in the main program.
1656 # You do so at your own risk (there is definitely enough rope available
1657 # to hang yourself). The behavior of CGIscriptor becomes undefined if
1658 # you change its private variables during run time. The PERL code
1659 # directives are used as in:
1660 # $Result = eval($directive); print $Result;'';
1661 # ($directive contains all text between <SCRIPT></SCRIPT>).
1662 # That is, the <directive> is treated as ''-quoted string and
1663 # the result is treated as a scalar. To prevent the VALUE of the code
1664 # block from appearing on the client's screen, end the directive with
1665 # ';""</SCRIPT>'. Evaluated directives return the last value, just as
1666 # eval(), blocks, and subroutines, but only as a scalar.
1668 # IMPORTANT: All PERL variables defined are persistent. Each <SCRIPT>
1669 # </SCRIPT> construct is evaluated as a {}-block with associated scope
1670 # (e.g., for "my $var;" declarations). This means that values assigned
1671 # to a PERL variable can be used throughout the document unless they
1672 # were declared with "my". The following will actually work as intended
1673 # (note that the ``-quotes in this example are NOT evaluated, but used
1674 # as simple quotes):
1676 # <META CONTENT="text/ssperl; CGI=`$String='abcdefg'`">
1677 # anything ...
1678 # <SCRIPT TYPE=text/ssperl>@List = split('', $String);</SCRIPT>
1679 # anything ...
1680 # <SCRIPT TYPE=text/ssperl>join(", ", @List[1..$#List]);</SCRIPT>
1682 # The first <SCRIPT TYPE=text/ssperl></SCRIPT> construct will return the
1683 # value scalar(@List), the second <SCRIPT TYPE=text/ssperl></SCRIPT>
1684 # construct will print the elements of $String separated by commas, leaving
1685 # out the first element, i.e., $List[0].
1687 # Another warning: './' and '~/' are ALWAYS replaced by the values of
1688 # $YOUR_SCRIPTS and $YOUR_HTML_FILES, respectively . This can interfere
1689 # with pattern matching, e.g., $a =~ s/aap\./noot\./g will result in the
1690 # evaluations of $a =~ s/aap\\${YOUR_SCRIPTS}noot\\${YOUR_SCRIPTS}g. Use
1691 # s@<regexp>.@<replacement>.@g instead.
1694 # USER EXTENSIONS
1696 # A CGIscriptor package is attached to the bottom of this file. With
1697 # this package you can personalize your version of CGIscriptor by
1698 # including often used perl routines. These subroutines can be
1699 # accessed by prefixing their names with CGIscriptor::, e.g.,
1700 # <SCRIPT LANGUAGE=PERL TYPE=text/ssperl>
1701 # CGIscriptor::ListDocs("/Books/*") # List all documents in /Books
1702 # </SCRIPT>
1703 # It already contains some useful subroutines for Document Management.
1704 # As it is a separate package, it has its own namespace, isolated from
1705 # both the evaluator and the main program. To access variables from
1706 # the document <SCRIPT></SCRIPT> blocks, use $CGIexecute::<var>.
1708 # Currently, the following functions are implemented
1709 # (precede them with CGIscriptor::, see below for more information)
1710 # - SAFEqx ('String') -> result of qx/"String"/ # Safe application of ``-quotes
1711 # Is used by text/osshell Shell scripts. Protects all CGI
1712 # (client-supplied) values with single quotes before executing the
1713 # commands (one of the few functions that also works WITHOUT CGIscriptor::
1714 # in front)
1715 # - defineCGIvariable ($name[, $default) -> 0/1 (i.e., failure/success)
1716 # Is used by the META tag to define and initialize CGI and ENV
1717 # name/value pairs. Tries to obtain an initializing value from (in order):
1718 # $ENV{$name}
1719 # The Query string
1720 # The default value given (if any)
1721 # (one of the few functions that also works WITHOUT CGIscriptor::
1722 # in front)
1723 # - CGIsafeFileName (FileName) -> FileName or ""
1724 # Check a string against the Allowed File Characters (and ../ /..).
1725 # Returns an empty string for unsafe filenames.
1726 # - CGIsafeEmailAddress (Email) -> Email or ""
1727 # Check a string against correct email address pattern.
1728 # Returns an empty string for unsafe addresses.
1729 # - RedirectShellScript ('CommandString') -> FILEHANDLER or undef
1730 # Open a named PIPE for SAFEqx to receive ALL shell scripts
1731 # - URLdecode (URL encoded string) -> plain string # Decode URL encoded argument
1732 # - URLencode (plain string) -> URL encoded string # Encode argument as URL code
1733 # - CGIparseValue (ValueName [, URL_encoded_QueryString]) -> Decoded value
1734 # Extract the value of a CGI variable from the global or a private
1735 # URL-encoded query (multipart POST raw, NOT decoded)
1736 # - CGIparseValueList (ValueName [, URL_encoded_QueryString])
1737 # -> List of decoded values
1738 # As CGIparseValue, but now assembles ALL values of ValueName into a list.
1739 # - CGIparseHeader (ValueName [, URL_encoded_QueryString]) -> Header
1740 # Extract the header of a multipart CGI variable from the global or a private
1741 # URL-encoded query ("" when not a multipart variable or absent)
1742 # - CGIparseForm ([URL_encoded_QueryString]) -> Decoded Form
1743 # Decode the complete global URL-encoded query or a private
1744 # URL-encoded query
1745 # - read_url(URL) # Returns the page from URL (with added base tag, both FTP and HTTP)
1746 # Uses main::GET_URL(URL, 1) to get at the command to read the URL.
1747 # - BrowseDirs(RootDirectory [, Pattern, Startdir, CGIname]) # print browsable directories
1748 # - ListDocs(Pattern [,ListType]) # Prints a nested HTML directory listing of
1749 # all documents, e.g., ListDocs("/*", "dl");.
1750 # - HTMLdocTree(Pattern [,ListType]) # Prints a nested HTML listing of all
1751 # local links starting from a given document, e.g.,
1752 # HTMLdocTree("/Welcome.html", "dl");
1755 # THE RESULTS STACK: @CGISCRIPTORRESULTS
1757 # If the pseudo-variable "$CGIscriptorResults" has been defined in a
1758 # META tag, all subsequent SCRIPT and META results are pushed
1759 # on the @CGIscriptorResults stack. This list is just another
1760 # Perl variable and can be used and manipulated like any other list.
1761 # $CGIscriptorResults[-1] is always the last result.
1762 # This is only of limited use, e.g., to use the results of an OS shell
1763 # script inside a Perl script. Will NOT contain the results of Pipes
1764 # or code from MIME-profiling.
1767 # USEFULL CGI PREDEFINED VARIABLES (DO NOT ASSIGN TO THESE)
1769 # $CGI_HOME - The DocumentRoot directory
1770 # $CGI_Decoded_QS - The complete decoded Query String
1771 # $CGI_Content_Length - The ACTUAL length of the Query String
1772 # $CGI_Date - Current date and time
1773 # $CGI_Year $CGI_Month $CGI_Day $CGI_WeekDay - Current Date
1774 # $CGI_Time - Current Time
1775 # $CGI_Hour $CGI_Minutes $CGI_Seconds - Current Time, split
1776 # GMT Date/Time:
1777 # $CGI_GMTYear $CGI_GMTMonth $CGI_GMTDay $CGI_GMTWeekDay $CGI_GMTYearDay
1778 # $CGI_GMTHour $CGI_GMTMinutes $CGI_GMTSeconds $CGI_GMTisdst
1781 # USEFULL CGI ENVIRONMENT VARIABLES
1783 # Variables accessible (in APACHE) as $ENV{<name>}
1784 # (see: "http://hoohoo.ncsa.uiuc.edu/cgi/env.html"):
1786 # QUERY_STRING - The query part of URL, that is, everything that follows the
1787 # question mark.
1788 # PATH_INFO - Extra path information given after the script name
1789 # PATH_TRANSLATED - Extra pathinfo translated through the rule system.
1790 # (This doesn't always make sense.)
1791 # REMOTE_USER - If the server supports user authentication, and the script is
1792 # protected, this is the username they have authenticated as.
1793 # REMOTE_HOST - The hostname making the request. If the server does not have
1794 # this information, it should set REMOTE_ADDR and leave this unset
1795 # REMOTE_ADDR - The IP address of the remote host making the request.
1796 # REMOTE_IDENT - If the HTTP server supports RFC 931 identification, then this
1797 # variable will be set to the remote user name retrieved from
1798 # the server. Usage of this variable should be limited to logging
1799 # only.
1800 # AUTH_TYPE - If the server supports user authentication, and the script
1801 # is protected, this is the protocol-specific authentication
1802 # method used to validate the user.
1803 # CONTENT_TYPE - For queries which have attached information, such as HTTP
1804 # POST and PUT, this is the content type of the data.
1805 # CONTENT_LENGTH - The length of the said content as given by the client.
1806 # SERVER_SOFTWARE - The name and version of the information server software
1807 # answering the request (and running the gateway).
1808 # Format: name/version
1809 # SERVER_NAME - The server's hostname, DNS alias, or IP address as it
1810 # would appear in self-referencing URLs
1811 # GATEWAY_INTERFACE - The revision of the CGI specification to which this
1812 # server complies. Format: CGI/revision
1813 # SERVER_PROTOCOL - The name and revision of the information protocol this
1814 # request came in with. Format: protocol/revision
1815 # SERVER_PORT - The port number to which the request was sent.
1816 # REQUEST_METHOD - The method with which the request was made. For HTTP,
1817 # this is "GET", "HEAD", "POST", etc.
1818 # SCRIPT_NAME - A virtual path to the script being executed, used for
1819 # self-referencing URLs.
1820 # HTTP_ACCEPT - The MIME types which the client will accept, as given by
1821 # HTTP headers. Other protocols may need to get this
1822 # information from elsewhere. Each item in this list should
1823 # be separated by commas as per the HTTP spec.
1824 # Format: type/subtype, type/subtype
1825 # HTTP_USER_AGENT - The browser the client is using to send the request.
1826 # General format: software/version library/version.
1829 # INSTRUCTIONS FOR RUNNING CGIscriptor ON UNIX
1831 # CGIscriptor.pl will run on any WWW server that runs Perl scripts, just add
1832 # a line like the following to your srm.conf file (Apache example):
1834 # ScriptAlias /SHTML/ /real-path/CGIscriptor.pl/
1836 # URL's that refer to http://www.your.address/SHTML/... will now be handled
1837 # by CGIscriptor.pl, which can use a private directory tree (default is the
1838 # DOCUMENT_ROOT directory tree, but it can be anywhere, see manual).
1840 # If your hosting ISP won't let you add ScriptAlias lines you can use
1841 # the following "rewrite"-based "scriptalias" in .htaccess
1842 # (from Gerd Franke)
1844 # RewriteEngine On
1845 # RewriteBase /
1846 # RewriteCond %{REQUEST_FILENAME} .html$
1847 # RewriteCond %{SCRIPT_FILENAME} !cgiscriptor.pl$
1848 # RewriteCond %{REQUEST_FILENAME} -f
1849 # RewriteRule ^(.*)$ /cgi-bin/cgiscriptor.pl/$1?&%{QUERY_STRING}
1851 # Everthing with the extension ".html" and not including "cgiscriptor.pl"
1852 # in the url and where the file "path/filename.html" exists is redirected
1853 # to "/cgi.bin/cgiscriptor.pl/path/filename.html?query".
1854 # The user configuration should get the same path-level as the
1855 # .htaccess-file:
1857 # # Just enter your own directory path here
1858 # $YOUR_HTML_FILES = "$ENV{'DOCUMENT_ROOT'}";
1859 # # use DOCUMENT_ROOT only, if .htaccess lies in the root-directory.
1861 # If this .htaccess goes in a specific directory, the path to this
1862 # directory must be added to $ENV{'DOCUMENT_ROOT'}.
1864 # The CGIscriptor file contains all documentation as comments. These
1865 # comments can be removed to speed up loading (e.g., `egrep -v '^#'
1866 # CGIscriptor.pl` > leanScriptor.pl). A bare bones version of
1867 # CGIscriptor.pl, lacking documentation, most comments, access control,
1868 # example functions etc. (but still with the copyright notice and some
1869 # minimal documentation) can be obtained by calling CGIscriptor.pl on the
1870 # command line with the '-slim' command line argument, e.g.,
1872 # >CGIscriptor.pl -slim > slimCGIscriptor.pl
1874 # CGIscriptor.pl can be run from the command line with <path> and <query> as
1875 # arguments, as `CGIscriptor.pl <path> <query>`, inside a perl script
1876 # with 'do CGIscriptor.pl' after setting $ENV{PATH_INFO}
1877 # and $ENV{QUERY_STRING}, or CGIscriptor.pl can be loaded with 'require
1878 # "/real-path/CGIscriptor.pl"'. In the latter case, requests are processed
1879 # by 'Handle_Request();' (again after setting $ENV{PATH_INFO} and
1880 # $ENV{QUERY_STRING}).
1882 # Using the command line execution option, CGIscriptor.pl can be used as a
1883 # document (meta-)preprocessor. If the first argument is '-', STDIN will be read.
1884 # For example:
1886 # > cat MyDynamicDocument.html | CGIscriptor.pl - '[QueryString]' > MyStaticFile.html
1888 # This command line will produce a STATIC file with the DYNAMIC content of
1889 # MyDocument.html "interpolated".
1891 # This option would be very dangerous when available over the internet.
1892 # If someone could sneak a 'http://www.your.domain/-' URL past your
1893 # server, CGIscriptor could EXECUTE any POSTED contend.
1894 # Therefore, for security reasons, STDIN will NOT be read
1895 # if ANY of the HTTP server environment variables is set (e.g.,
1896 # SERVER_PORT, SERVER_PROTOCOL, SERVER_NAME, SERVER_SOFTWARE,
1897 # HTTP_USER_AGENT, REMOTE_ADDR).
1898 # This block on processing STDIN on HTTP requests can be lifted by setting
1899 # $BLOCK_STDIN_HTTP_REQUEST = 0;
1900 # In the security configuration. Butbe carefull when doing this.
1901 # It can be very dangerous.
1903 # Running demo's and more information can be found at
1904 # http://www.fon.hum.uva.nl/~rob/OSS/OSS.html
1906 # A pocket-size HTTP daemon, CGIservlet.pl, is available from my web site or
1907 # CPAN that can use CGIscriptor.pl as the base of a µWWW server and
1908 # demonstrates its use.
1911 # PROCESSING NON-FILESYSTEM DATA
1913 # Normally, HTTP (WWW) requests map onto file that can be accessed
1914 # using the perl open() function. That is, the web server runs on top of
1915 # some directory structure. However, we can envission (and put to good
1916 # use) other systems that do not use a normal file system. The whole CGI
1917 # was developed to make dynamic document generation possible.
1919 # A special case is where we want to have it both: A normal web server
1920 # with normal "file data", but not a normal files system. For instance,
1921 # we want or normal Web Site to run directly from a RAM hash table or
1922 # other database, instead of from disk. But we do NOT want to code the
1923 # whole site structure in CGI.
1925 # CGIscriptor can do this. If the web server fills an environment variable
1926 # $ENV{'CGI_FILE_CONTENT'} with the content of the "file", then the content
1927 # of this variable is processed instead of opening a file. If this environment
1928 # variable has the value '-', the content of another environment variable,
1929 # $ENV{'CGI_DATA_ACCESS_CODE'} is executed as:
1930 # eval("\@_ = ($file_path); do {$ENV{'CGI_DATA_ACCESS_CODE'}};")
1931 # and the result is processed as if it was the content of the requested
1932 # file.
1933 # (actually, the names of the environment variables are user configurable,
1934 # they are stored in the local variables $CGI_FILE_CONTENT and
1935 # $CGI_DATA_ACCESS_CODE)
1937 # When using this mechanism, the SRC attribute mechanism will only partially work.
1938 # Only the "recursive" calls to CGIscriptor (the ProcessFile() function)
1939 # will work, the automagical execution of SRC files won't. (In this case,
1940 # the SRC attribute won't work either for other scripting languages)
1943 # NON-UNIX PLATFORMS
1945 # CGIscriptor.pl was mainly developed and tested on UNIX. However, as I
1946 # coded part of the time on an Apple Macintosh under MacPerl, I made sure
1947 # CGIscriptor did run under MacPerl (with command line options). But only
1948 # as an independend script, not as part of a HTTP server. I have used it
1949 # under Apache in Windows XP.
1951 ENDOFHELPTEXT
1952 exit;
1954 ###############################################################################
1956 # SECURITY CONFIGURATION
1958 # Special configurations related to SECURITY
1959 # (i.e., optional, see also environment variables below)
1961 # LOGGING
1962 # Log Clients and the requested paths (Redundant when loging Queries)
1964 $ClientLog = "./Client.log"; # (uncomment for use)
1966 # Format: Localtime | REMOTE_USER REMOTE_IDENT REMOTE_HOST REMOTE_ADDRESS \
1967 # PATH_INFO CONTENT_LENGTH (actually, the real query+post length)
1969 # Log Clients and the queries, the CGIQUERYDECODE is required if you want
1970 # to log queries. If you log Queries, the loging of Clients is redundant
1971 # (note that queries can be quite long, so this might not be a good idea)
1973 #$QueryLog = "./Query.log"; # (uncomment for use)
1975 # ACCESS CONTROL
1976 # the Access files should contain Hostnames or IP addresses,
1977 # i.e. REMOTE_HOST or REMOTE_ADDR, each on a separate line
1978 # optionally followed by one ore more file patterns, e.g., "edu /DEMO".
1979 # Matching is done "domain first". For example ".edu" matches all
1980 # clients whose "name" ends in ".edu" or ".EDU". The file pattern
1981 # "/DEMO" matches all paths that contain the strings "/DEMO" or "/demo"
1982 # (both matchings are done case-insensitive).
1983 # The name special symbol "-" matches ALL clients who do not supply a
1984 # REMOTE_HOST name, "*" matches all clients.
1985 # Lines starting with '-e' are evaluated. A non-zero return value indicates
1986 # a match. You can use $REMOTE_HOST, $REMOTE_ADDR, and $PATH_INFO. These
1987 # lines are evaluated in the program's own name-space. So DO NOT assign to
1988 # variables.
1990 # Accept the following users (remove comment # and adapt filename)
1991 $CGI_Accept = -s "$YOUR_SCRIPTS/ACCEPT.lis" ? "$YOUR_SCRIPTS/ACCEPT.lis" : ''; # (uncomment for use)
1993 # Reject requests from the following users (remove comment # and
1994 # adapt filename, this is only of limited use)
1995 $CGI_Reject = -s "$YOUR_SCRIPTS/REJECT.lis" ? "$YOUR_SCRIPTS/REJECT.lis" : ''; # (uncomment for use)
1997 # Empty lines or comment lines starting with '#' are ignored in both
1998 # $CGI_Accept and $CGI_Reject.
2000 # Block STDIN (i.e., '-') requests when servicing an HTTP request
2001 # Comment this out if you realy want to use STDIN in an on-line web server
2002 $BLOCK_STDIN_HTTP_REQUEST = 1;
2005 # End of security configuration
2007 ##################################################<<<<<<<<<<End Remove
2009 # PARSING CGI VALUES FROM THE QUERY STRING (USER CONFIGURABLE)
2011 # The CGI parse commands. These commands extract the values of the
2012 # CGI variables from the URL encoded Query String.
2013 # If you want to use your own CGI decoders, you can call them here
2014 # instead, using your own PATH and commenting/uncommenting the
2015 # appropriate lines
2017 # CGI parse command for individual values
2018 # (if $List > 0, returns a list value, if $List < 0, a hash table, this is optional)
2019 sub YOUR_CGIPARSE # ($Name [, $List]) -> Decoded value
2021 my $Name = shift;
2022 my $List = shift || 0;
2023 # Use one of the following by uncommenting
2024 if(!$List) # Simple value
2026 return CGIscriptor::CGIparseValue($Name) ;
2028 elsif($List < 0) # Hash tables
2030 return CGIscriptor::CGIparseValueHash($Name); # Defined in CGIscriptor below
2032 else # Lists
2034 return CGIscriptor::CGIparseValueList($Name); # Defined in CGIscriptor below
2037 # return `/PATH/cgiparse -value $Name`; # Shell commands
2038 # require "/PATH/cgiparse.pl"; return cgivalue($Name); # Library
2040 # Complete queries
2041 sub YOUR_CGIQUERYDECODE
2043 # Use one of the following by uncommenting
2044 return CGIscriptor::CGIparseForm(); # Defined in CGIscriptor below
2045 # return `/PATH/cgiparse -form`; # Shell commands
2046 # require "/PATH/cgiparse.pl"; return cgiform(); # Library
2049 # End of configuration
2051 #######################################################################
2053 # Translating input files.
2054 # Allows general and global conversions of files using Regular Expressions
2055 # Translations are applied in the order of definition.
2057 # Define:
2058 # my $TranslationPaths = 'pattern'; # Pattern matching PATH_INFO
2060 # push(@TranslationTable, ['pattern', 'replacement']);
2061 # e.g. (for Ruby Rails):
2062 # push(@TranslationTable, ['<%=', '<SCRIPT TYPE="text/ssruby">']);
2063 # push(@TranslationTable, ['%>', '</SCRIPT>']);
2065 # Runs:
2066 # my $currentRegExp;
2067 # foreach $currentRegExp (keys(%TranslationTable))
2069 # my $currentRegExp;
2070 # foreach $currentRegExp (@TranslationTable)
2072 # my ($pattern, $replacement) = @$currentRegExp;
2073 # $$text =~ s!$pattern!$replacement!msg;
2074 # };
2075 # };
2077 # Configuration section
2079 #######################################################################
2081 # The file paths on which to apply the translation
2082 my $TranslationPaths = ''; # NO files
2083 #$TranslationPaths = '.'; # ANY file
2084 # $TranslationPaths = '\.html'; # HTML files
2086 my @TranslationTable = ();
2087 # Some legacy code
2088 push(@TranslationTable, ['\<\s*CGI\s+([^\>])*\>', '\<SCRIPT TYPE=\"text/ssperl\"\>$1\<\/SCRIPT>']);
2089 # Ruby Rails?
2090 push(@TranslationTable, ['<%=', '<SCRIPT TYPE="text/ssruby">']);
2091 push(@TranslationTable, ['%>', '</SCRIPT>']);
2093 sub performTranslation # (\$text)
2095 my $text = shift || return;
2096 if(@TranslationTable && $TranslationPaths && $ENV{'PATH_INFO'} =~ m!$TranslationPaths!)
2098 my $currentRegExp;
2099 foreach $currentRegExp (@TranslationTable)
2101 my ($pattern, $replacement) = @$currentRegExp;
2102 $$text =~ s!$pattern!$replacement!msg;
2107 #######################################################################
2109 # Seamless access to other (Scripting) Languages
2110 # TYPE='text/ss<interpreter>'
2112 # Configuration section
2114 #######################################################################
2116 # OTHER SCRIPTING LANGUAGES AT THE SERVER SIDE (MIME => OScommand)
2117 # Yes, it realy is this simple! (unbelievable, isn't it)
2118 # NOTE: Some interpreters require some filtering to obtain "clean" output
2120 %ScriptingLanguages = (
2121 "text/testperl" => 'perl', # Perl for testing
2122 "text/sspython" => 'python', # Python
2123 "text/ssruby" => 'ruby', # Ruby
2124 "text/sstcl" => 'tcl', # TCL
2125 "text/ssawk" => 'awk -f-', # Awk
2126 "text/sslisp" => # lisp (rep, GNU)
2127 'rep | tail +4 '."| egrep -v '> |^rep. |^nil\\\$'",
2128 "text/xlispstat" => # xlispstat
2129 'xlispstat | tail +7 ' ."| egrep -v '> \\\$|^NIL'",
2130 "text/ssprolog" => # Prolog (GNU)
2131 "gprolog | tail +4 | sed 's/^| ?- //'",
2132 "text/ssm4" => 'm4', # M4 macro's
2133 "text/sh" => 'sh', # Born shell
2134 "text/bash" => 'bash', # Born again shell
2135 "text/csh" => 'csh', # C shell
2136 "text/ksh" => 'ksh', # Korn shell
2137 "text/sspraat" => # Praat (sound/speech analysis)
2138 "praat - | sed 's/Praat > //g'",
2139 "text/ssr" => # R
2140 "R --vanilla --slave | sed 's/^[\[0-9\]*] //'",
2141 "text/ssrebol" => # REBOL
2142 "rebol --quiet|egrep -v '^[> ]* == '|sed 's/^\\s*\[> \]* //'",
2143 "text/postgresql" => 'psql 2>/dev/null',
2145 # Not real scripting, but the use of other applications
2146 "text/ssmailto" => "awk 'NF||F{F=1;print \\\$0;}'|mailto >/dev/null", # Send mail from server
2147 "text/ssdisplay" => 'cat', # Display, (interpolation)
2148 "text/sslogfile" => # Log to file, (interpolation)
2149 "awk 'NF||L {if(!L){L=tolower(\\\$1)~/^file:\\\$/ ? \\\$2 : \\\$1;}else{print \\\$0 >> L;};}'",
2151 "" => ""
2154 # To be able to access the CGI variables in your script, they
2155 # should be passed to the scripting language in a readable form
2156 # Here you can enter how they should be printed (the first %s
2157 # is replaced by the NAME of the CGI variable as it apears in the
2158 # META tag, the second by its VALUE).
2159 # For Perl this would be:
2160 # "text/testperl" => '$%s = "%s";',
2161 # which would be executed as
2162 # printf('$%s = "%s";', $CGI_NAME, $CGI_VALUE);
2164 # If the hash table value doesn't exist, nothing is done
2165 # (you have to parse the Environment variables yourself).
2166 # If it DOES exist but is empty (e.g., "text/sspraat" => '',)
2167 # Perl string interpolation of variables (i.e., $var, @array,
2168 # %hash) is performed. This means that $@%\ must be protected
2169 # with a \.
2171 %ScriptingCGIvariables = (
2172 "text/testperl" => "\$\%s = '\%s';", # Perl $VAR = 'value'; (for testing)
2173 "text/sspython" => "\%s = '\%s'", # Python VAR = 'value'
2174 "text/ssruby" => '@%s = "%s"', # Ruby @VAR = 'value'
2175 "text/sstcl" => 'set %s "%s"', # TCL set VAR "value"
2176 "text/ssawk" => '%s = "%s";', # Awk VAR = 'value';
2177 "text/sslisp" => '(setq %s "%s")', # Gnu lisp (rep) (setq VAR "value")
2178 "text/xlispstat" => '(setq %s "%s")', # xlispstat (setq VAR "value")
2179 "text/ssprolog" => '', # Gnu prolog (interpolated)
2180 "text/ssm4" => "define(`\%s', `\%s')", # M4 macro's define(`VAR', `value')
2181 "text/sh" => "\%s='\%s'", # Born shell VAR='value'
2182 "text/bash" => "\%s='\%s'", # Born again shell VAR='value'
2183 "text/csh" => "\$\%s='\%s';", # C shell $VAR = 'value';
2184 "text/ksh" => "\$\%s='\%s';", # Korn shell $VAR = 'value';
2186 "text/ssrebol" => '%s: copy "%s"', # REBOL VAR: copy "value"
2187 "text/sspraat" => '', # Praat (interpolation)
2188 "text/ssr" => '%s <- "%s";', # R VAR <- "value";
2189 "text/postgresql" => '', # PostgreSQL (interpolation)
2191 # Not real scripting, but the use of other applications
2192 "text/ssmailto" => '', # MAILTO, (interpolation)
2193 "text/ssdisplay" => '', # Display, (interpolation)
2194 "text/sslogfile" => '', # Log to file, (interpolation)
2196 "" => ""
2199 # If you want something added in front or at the back of each script
2200 # block as send to the interpreter add it here.
2201 # mime => "string", e.g., "text/sspython" => "python commands"
2202 %ScriptingPrefix = (
2203 "text/testperl" => "\# Prefix Code;", # Perl script testing
2204 "text/ssm4" => 'divert(0)', # M4 macro's (open STDOUT)
2206 "" => ""
2208 # If you want something added at the end of each script block
2209 %ScriptingPostfix = (
2210 "text/testperl" => "\# Postfix Code;", # Perl script testing
2211 "text/ssm4" => 'divert(-1)', # M4 macro's (block STDOUT)
2213 "" => ""
2215 # If you need initialization code, directly after opening
2216 %ScriptingInitialization = (
2217 "text/testperl" => "\# Initialization Code;", # Perl script testing
2218 "text/ssawk" => 'BEGIN {', # Server Side awk scripts (VAR = "value")
2219 "text/sslisp" => '(prog1 nil ', # Lisp (rep)
2220 "text/xlispstat" => '(prog1 nil ', # xlispstat
2221 "text/ssm4" => 'divert(-1)', # M4 macro's (block STDOUT)
2223 "" => ""
2225 # If you need cleanup code before closing
2226 %ScriptingCleanup = (
2227 "text/testperl" => "\# Cleanup Code;", # Perl script testing
2228 "text/sspraat" => 'Quit',
2229 "text/ssawk" => '};', # Server Side awk scripts (VAR = "value")
2230 "text/sslisp" => '(princ "\n" standard-output)).', # Closing print to rep
2231 "text/xlispstat" => '(print ""))', # Closing print to xlispstat
2232 "text/postgresql" => '\q', # quit psql
2233 "text/ssdisplay" => "", # close cat
2235 "" => ""
2238 # End of configuration for foreign scripting languages
2240 ###############################################################################
2242 # Initialization Code
2245 sub Initialize_Request
2247 ###############################################################################
2249 # ENVIRONMENT VARIABLES
2251 # Use environment variables to configure CGIscriptor on a temporary basis.
2252 # If you define any of the configurable variables as environment variables,
2253 # these are used instead of the "hard coded" values above.
2255 $SS_PUB = $ENV{'SS_PUB'} || $YOUR_HTML_FILES;
2256 $SS_SCRIPT = $ENV{'SS_SCRIPT'} || $YOUR_SCRIPTS;
2259 # Substitution strings, these are used internally to handle the
2260 # directory separator strings, e.g., '~/' -> 'SS_PUB:' (Mac)
2261 $HOME_SUB = $SS_PUB;
2262 $SCRIPT_SUB = $SS_SCRIPT;
2265 # Make sure all script are reliably loaded
2266 push(@INC, $SS_SCRIPT);
2269 # Add the directory separator to the "home" directories.
2270 # (This is required for ~/ and ./ substitution)
2271 $HOME_SUB .= '/' if $HOME_SUB;
2272 $SCRIPT_SUB .= '/' if $SCRIPT_SUB;
2274 $CGI_HOME = $ENV{'DOCUMENT_ROOT'};
2275 $ENV{'PATH_TRANSLATED'} =~ /$ENV{'PATH_INFO'}/is;
2276 $CGI_HOME = $` unless $ENV{'DOCUMENT_ROOT'}; # Get the DOCUMENT_ROOT directory
2277 $default_values{'CGI_HOME'} = $CGI_HOME;
2278 $ENV{'HOME'} = $CGI_HOME;
2279 # Set SS_PUB and SS_SCRIPT as Environment variables (make them available
2280 # to the scripts)
2281 $ENV{'SS_PUB'} = $SS_PUB unless $ENV{'SS_PUB'};
2282 $ENV{'SS_SCRIPT'} = $SS_SCRIPT unless $ENV{'SS_SCRIPT'};
2284 $FilePattern = $ENV{'FilePattern'} || $FilePattern;
2285 $MaximumQuerySize = $ENV{'MaximumQuerySize'} || $MaximumQuerySize;
2286 $ClientLog = $ENV{'ClientLog'} || $ClientLog;
2287 $QueryLog = $ENV{'QueryLog'} || $QueryLog;
2288 $CGI_Accept = $ENV{'CGI_Accept'} || $CGI_Accept;
2289 $CGI_Reject = $ENV{'CGI_Reject'} || $CGI_Reject;
2291 # Parse file names
2292 $CGI_Accept =~ s@^\~/@$HOME_SUB@g if $CGI_Accept;
2293 $CGI_Reject =~ s@^\~/@$HOME_SUB@g if $CGI_Reject;
2294 $ClientLog =~ s@^\~/@$HOME_SUB@g if $ClientLog;
2295 $QueryLog =~ s@^\~/@$HOME_SUB@g if $QueryLog;
2297 $CGI_Accept =~ s@^\./@$SCRIPT_SUB@g if $CGI_Accept;
2298 $CGI_Reject =~ s@^\./@$SCRIPT_SUB@g if $CGI_Reject;
2299 $ClientLog =~ s@^\./@$SCRIPT_SUB@g if $ClientLog;
2300 $QueryLog =~ s@^\./@$SCRIPT_SUB@g if $QueryLog;
2302 @CGIscriptorResults = (); # A stack of results
2304 # end of Environment variables
2306 #############################################################################
2308 # Define and Store "standard" values
2310 # BEFORE doing ANYTHING check the size of Query String
2311 length($ENV{'QUERY_STRING'}) <= $MaximumQuerySize || dieHandler(2, "QUERY TOO LONG\n");
2313 # The Translated Query String and the Actual length of the (decoded)
2314 # Query String
2315 if($ENV{'QUERY_STRING'})
2317 # If this can contain '`"-quotes, be carefull to use it QUOTED
2318 $default_values{CGI_Decoded_QS} = YOUR_CGIQUERYDECODE();
2319 $default_values{CGI_Content_Length} = length($default_values{CGI_Decoded_QS});
2322 # Get the current Date and time and store them as default variables
2324 # Get Local Time
2325 $LocalTime = localtime;
2327 # CGI_Year CGI_Month CGI_Day CGI_WeekDay CGI_Time
2328 # CGI_Hour CGI_Minutes CGI_Seconds
2330 $default_values{CGI_Date} = $LocalTime;
2331 ($default_values{CGI_WeekDay},
2332 $default_values{CGI_Month},
2333 $default_values{CGI_Day},
2334 $default_values{CGI_Time},
2335 $default_values{CGI_Year}) = split(' ', $LocalTime);
2336 ($default_values{CGI_Hour},
2337 $default_values{CGI_Minutes},
2338 $default_values{CGI_Seconds}) = split(':', $default_values{CGI_Time});
2340 # GMT:
2341 # CGI_GMTYear CGI_GMTMonth CGI_GMTDay CGI_GMTWeekDay CGI_GMTYearDay
2342 # CGI_GMTHour CGI_GMTMinutes CGI_GMTSeconds CGI_GMTisdst
2344 ($default_values{CGI_GMTSeconds},
2345 $default_values{CGI_GMTMinutes},
2346 $default_values{CGI_GMTHour},
2347 $default_values{CGI_GMTDay},
2348 $default_values{CGI_GMTMonth},
2349 $default_values{CGI_GMTYear},
2350 $default_values{CGI_GMTWeekDay},
2351 $default_values{CGI_GMTYearDay},
2352 $default_values{CGI_GMTisdst}) = gmtime;
2356 # End of Initialize Request
2358 ###################################################################
2360 # SECURITY: ACCESS CONTROL
2362 # Check the credentials of each client (use pattern matching, domain first).
2363 # This subroutine will kill-off (die) the current process whenever access
2364 # is denied.
2366 sub Access_Control
2368 # >>>>>>>>>>Start Remove
2370 # ACCEPTED CLIENTS
2372 # Only accept clients which are authorized, reject all unnamed clients
2373 # if REMOTE_HOST is given.
2374 # If file patterns are given, check whether the user is authorized for
2375 # THIS file.
2376 if($CGI_Accept)
2378 # Use local variables, REMOTE_HOST becomes '-' if undefined
2379 my $REMOTE_HOST = $ENV{REMOTE_HOST} || '-';
2380 my $REMOTE_ADDR = $ENV{REMOTE_ADDR};
2381 my $PATH_INFO = $ENV{'PATH_INFO'};
2383 open(CGI_Accept, "<$CGI_Accept") || dieHandler(3, "$CGI_Accept: $!\n");
2384 $NoAccess = 1;
2385 while(<CGI_Accept>)
2387 next unless /\S/; # Skip empty lines
2388 next if /^\s*\#/; # Skip comments
2390 # Full expressions
2391 if(/^\s*-e\s/is)
2393 my $Accept = $'; # Get the expression
2394 $NoAccess &&= eval($Accept); # evaluate the expresion
2396 else
2398 my ($Accept, @FilePatternList) = split;
2399 if($Accept eq '*' # Always match
2400 ||$REMOTE_HOST =~ /\Q$Accept\E$/is # REMOTE_HOST matches
2401 || (
2402 $Accept =~ /^[0-9\.]+$/
2403 && $REMOTE_ADDR =~ /^\Q$Accept\E/ # IP address matches
2407 if($FilePatternList[0])
2409 foreach $Pattern (@FilePatternList)
2411 # Check whether this patterns is accepted
2412 $NoAccess &&= ($PATH_INFO !~ m@\Q$Pattern\E@is);
2415 else
2417 $NoAccess = 0; # No file patterns -> Accepted
2421 # Blocked
2422 last unless $NoAccess;
2424 close(CGI_Accept);
2425 if($NoAccess){ dieHandler(4, "No Access: $PATH_INFO\n");};
2429 # REJECTED CLIENTS
2431 # Reject named clients, accept all unnamed clients
2432 if($CGI_Reject)
2434 # Use local variables, REMOTE_HOST becomes '-' if undefined
2435 my $REMOTE_HOST = $ENV{'REMOTE_HOST'} || '-';
2436 my $REMOTE_ADDR = $ENV{'REMOTE_ADDR'};
2437 my $PATH_INFO = $ENV{'PATH_INFO'};
2439 open(CGI_Reject, "<$CGI_Reject") || dieHandler(5, "$CGI_Reject: $!\n");
2440 $NoAccess = 0;
2441 while(<CGI_Reject>)
2443 next unless /\S/; # Skip empty lines
2444 next if /^\s*\#/; # Skip comments
2446 # Full expressions
2447 if(/^-e\s/is)
2449 my $Reject = $'; # Get the expression
2450 $NoAccess ||= eval($Reject); # evaluate the expresion
2452 else
2454 my ($Reject, @FilePatternList) = split;
2455 if($Reject eq '*' # Always match
2456 ||$REMOTE_HOST =~ /\Q$Reject\E$/is # REMOTE_HOST matches
2457 ||($Reject =~ /^[0-9\.]+$/
2458 && $REMOTE_ADDR =~ /^\Q$Reject\E/is # IP address matches
2462 if($FilePatternList[0])
2464 foreach $Pattern (@FilePatternList)
2466 $NoAccess ||= ($PATH_INFO =~ m@\Q$Pattern\E@is);
2469 else
2471 $NoAccess = 1; # No file patterns -> Rejected
2475 last if $NoAccess;
2477 close(CGI_Reject);
2478 if($NoAccess){ dieHandler(6, "Request rejected: $PATH_INFO\n");};
2481 ##########################################################<<<<<<<<<<End Remove
2484 # Get the filename
2486 # Does the filename contain any illegal characters (e.g., |, >, or <)
2487 dieHandler(7, "Illegal request: $ENV{'PATH_INFO'}\n") if $ENV{'PATH_INFO'} =~ /[^$FileAllowedChars]/;
2488 # Does the pathname contain an illegal (blocked) "directory"
2489 dieHandler(8, "Illegal request: $ENV{'PATH_INFO'}\n") if $BlockPathAccess && $ENV{'PATH_INFO'} =~ m@$BlockPathAccess@; # Access is blocked
2490 # Does the pathname contain a direct referencer to BinaryMapFile
2491 dieHandler(9, "Illegal request: $ENV{'PATH_INFO'}\n") if $BinaryMapFile && $ENV{'PATH_INFO'} =~ m@\Q$BinaryMapFile\E@; # Access is blocked
2493 # SECURITY: Is PATH_INFO allowed?
2494 if($FilePattern && $ENV{'PATH_INFO'} && $ENV{'PATH_INFO'} ne '-' &&
2495 ($ENV{'PATH_INFO'} !~ m@($FilePattern)$@is))
2497 # Unsupported file types can be processed by a special raw-file
2498 if($BinaryMapFile)
2500 $ENV{'CGI_BINARY_FILE'} = $ENV{'PATH_INFO'};
2501 $ENV{'PATH_INFO'} = $BinaryMapFile;
2503 else
2505 dieHandler(10, "Illegal file\n");
2511 # End of Security Access Control
2514 ############################################################################
2516 # Get the POST part of the query and add it to the QUERY_STRING.
2519 sub Get_POST_part_of_query
2522 # If POST, Read data from stdin to QUERY_STRING
2523 if($ENV{'REQUEST_METHOD'} =~ /POST/is)
2525 # SECURITY: Check size of Query String
2526 $ENV{'CONTENT_LENGTH'} <= $MaximumQuerySize || dieHandler(11, "Query too long: $ENV{'CONTENT_LENGTH'}\n"); # Query too long
2527 my $QueryRead = 0;
2528 my $SystemRead = $ENV{'CONTENT_LENGTH'};
2529 $ENV{'QUERY_STRING'} .= '&' if length($ENV{'QUERY_STRING'}) > 0;
2530 while($SystemRead > 0)
2532 $QueryRead = sysread(STDIN, $Post, $SystemRead); # Limit length
2533 $ENV{'QUERY_STRING'} .= $Post;
2534 $SystemRead -= $QueryRead;
2536 # Update decoded Query String
2537 $default_values{CGI_Decoded_QS} = YOUR_CGIQUERYDECODE();
2538 $default_values{CGI_Content_Length} =
2539 length($default_values{CGI_Decoded_QS});
2543 # End of getting POST part of query
2546 ############################################################################
2548 # Start (HTML) output and logging
2549 # (if there are irregularities, it can kill the current process)
2552 sub Initialize_output
2554 # Construct the REAL file path (except for STDIN on the command line)
2555 my $file_path = $ENV{'PATH_INFO'} ne '-' ? $SS_PUB . $ENV{'PATH_INFO'} : '-';
2556 $file_path =~ s/\?.*$//; # Remove query
2557 # This is only necessary if your server does not catch ../ directives
2558 $file_path !~ m@\.\./@ || dieHandler(12, "Illegal ../ Construct\n"); # SECURITY: Do not allow ../ constructs
2560 # Block STDIN use (-) if CGIscriptor is servicing a HTTP request
2561 if($file_path eq '-')
2563 dieHandler(13, "STDIN request in On Line system\n") if $BLOCK_STDIN_HTTP_REQUEST
2564 && ($ENV{'SERVER_SOFTWARE'}
2565 || $ENV{'SERVER_NAME'}
2566 || $ENV{'GATEWAY_INTERFACE'}
2567 || $ENV{'SERVER_PROTOCOL'}
2568 || $ENV{'SERVER_PORT'}
2569 || $ENV{'REMOTE_ADDR'}
2570 || $ENV{'HTTP_USER_AGENT'});
2575 if($ClientLog)
2577 open(ClientLog, ">>$ClientLog");
2578 print ClientLog "$LocalTime | ",
2579 ($ENV{REMOTE_USER} || "-"), " ",
2580 ($ENV{REMOTE_IDENT} || "-"), " ",
2581 ($ENV{REMOTE_HOST} || "-"), " ",
2582 $ENV{REMOTE_ADDR}, " ",
2583 $ENV{PATH_INFO}, " ",
2584 $ENV{'CGI_BINARY_FILE'}, " ",
2585 ($default_values{CGI_Content_Length} || "-"),
2586 "\n";
2587 close(ClientLog);
2589 if($QueryLog)
2591 open(QueryLog, ">>$QueryLog");
2592 print QueryLog "$LocalTime\n",
2593 ($ENV{REMOTE_USER} || "-"), " ",
2594 ($ENV{REMOTE_IDENT} || "-"), " ",
2595 ($ENV{REMOTE_HOST} || "-"), " ",
2596 $ENV{REMOTE_ADDR}, ": ",
2597 $ENV{PATH_INFO}, " ",
2598 $ENV{'CGI_BINARY_FILE'}, "\n";
2600 # Write Query to Log file
2601 print QueryLog $default_values{CGI_Decoded_QS}, "\n\n";
2602 close(QueryLog);
2605 # Return the file path
2606 return $file_path;
2609 # End of Initialize output
2612 ############################################################################
2614 # Handle login access
2616 # Access is based on a valid session ticket.
2617 # Session tickets should be dependend on user name
2618 # and IP address. The patterns of URLs for which a
2619 # session ticket is needed and the login URL are stored in
2620 # %LoginRequiredPatterns as:
2621 # 'RegEx pattern' -> 'SessionPath\tPasswordPath\tLogin URL'
2624 sub Log_In_Access # () -> 0 = Access Allowed, Login page if access is not allowed
2626 # No patterns, no login
2627 return 0 unless %LoginRequiredPatterns;
2629 # Get and initialize values (watch out for stuff processed by BinaryMap files)
2630 my ($SessionPath, $PasswordsPath, $Login, $valid_duration) = ("", "", "", 0);
2631 my $PATH_INFO = $ENV{'CGI_BINARY_FILE'} ? $ENV{'CGI_BINARY_FILE'} : $ENV{'PATH_INFO'};
2632 my $REMOTE_ADDR = $ENV{'REMOTE_ADDR'};
2634 # Get and check the tickets. Tickets are restricted to word-characters (alphanumeric+_)
2635 CGIexecute::defineCGIvariable('LOGINTICKET', "");
2636 my $LOGINTICKET = ${"CGIexecute::LOGINTICKET"};
2637 return 0 if ($LOGINTICKET && $LOGINTICKET =~ /[^\w]/isg);
2638 CGIexecute::defineCGIvariable('SESSIONTICKET', "");
2639 my $SESSIONTICKET = ${"CGIexecute::SESSIONTICKET"};
2640 return 0 if ($SESSIONTICKET && $SESSIONTICKET =~ /[^\w]/isg);
2642 # Username and password
2643 CGIexecute::defineCGIvariable('USERNAME', "");
2644 my $username = ${"CGIexecute::USERNAME"};
2645 return 0 if $username =~ m!^[^\w]!isg || $username =~ m![^\w \-]!isg;
2646 my $userfile = lc($username);
2647 $userfile =~ s/[^\w]/_/isg;
2648 CGIexecute::defineCGIvariable('PASSWORD', "");
2649 my $password = ${"CGIexecute::PASSWORD"};
2652 foreach my $pattern (keys(%LoginRequiredPatterns))
2654 if($PATH_INFO =~ m#$pattern#)
2656 # Fall through a sieve of requirements
2657 ($SessionPath, $PasswordsPath, $Login, $validtime) = split(/\t/, $LoginRequiredPatterns{$pattern});
2659 # Is there a login ticket of this name?
2660 if($LOGINTICKET)
2662 my $tickets_removed = remove_expired_tickets($SessionPath);
2663 goto Login unless (-s "$SessionPath/$LOGINTICKET");
2664 goto Login unless (-s "$PasswordsPath/$userfile");
2665 my $ticket_valid = check_ticket_validity("LOGIN", "$SessionPath/$LOGINTICKET", $REMOTE_ADDR, ".");
2666 goto Login unless $ticket_valid;
2668 # Authorize
2669 $SESSIONTICKET = authorize_login("$SessionPath/$LOGINTICKET", "$PasswordsPath/$userfile", $password);
2670 if($SESSIONTICKET)
2672 create_session_file("$SessionPath/$SESSIONTICKET", "$SessionPath/$LOGINTICKET", "$PasswordsPath/$userfile", $PATH_INFO);
2676 # Is there a session ticket of this name?
2677 if($SESSIONTICKET)
2679 goto Login unless $SESSIONTICKET && (-s "$SessionPath/$SESSIONTICKET");
2680 my $ticket_valid = check_ticket_validity("SESSION", "$SessionPath/$SESSIONTICKET", $REMOTE_ADDR, $PATH_INFO);
2681 goto Login unless $ticket_valid;
2682 return 0;
2685 goto Login;
2686 return 0;
2689 return 0;
2691 Login:
2693 return "$YOUR_HTML_FILES/$Login";
2696 sub authorize_login # ($loginfile, $authorizationfile, $password, $SessionPath) => SESSIONTICKET First two arguments are file paths
2698 my $loginfile = shift || "";
2699 my $authorizationfile = shift || "";
2700 my $password = shift || "";
2701 my $SessionPath = shift || "";
2703 # Get Login session ticket
2704 my $loginticket = read_ticket($loginfile);
2705 # Get User credentials for authorization
2706 my $authorization = read_ticket($authorizationfile);
2708 # Get Randomsalt
2709 my $Randomsalt = $loginticket->{'Randomsalt'}->[0];
2711 return "" unless $Randomsalt;
2713 my $storedpassword = $authorization->{'Password'}->[0];
2714 return "" unless $storedpassword;
2715 # Without the "bash -c", the 'echo -n' could use sh, which does not recognize the -n option
2716 my $Hashedpassword = `bash -c 'echo -n $Randomsalt$storedpassword| $ENV{"SHASUMCMD"}'`;
2717 chomp($Hashedpassword);
2719 return "" unless $password eq $Hashedpassword;
2721 # Extract Session Ticket
2722 my $sessionticket = $loginticket->{'Session'}->[0];
2723 $sessionticket = "" if -x "$SessionPath/$sessionticket";
2725 return $sessionticket;
2728 sub create_session_file #($sessionfile, $loginfile, $authorizationfile, $path) -> Is $loginfile deleted? 0/1
2730 my $sessionfile = shift || "";
2731 my $loginfile = shift || "";
2732 my $authorizationfile = shift || "";
2733 my $path = shift || "";
2735 # Get Login session ticket
2736 my $loginticket = read_ticket($loginfile);
2737 # Get Authorization (user) session file
2738 my $authorization = read_ticket($authorizationfile);
2740 my @IPaddress = @{$loginticket->{'IPaddress'}};
2741 my @AllowedPaths = @{$authorization->{'AllowedPaths'}};;
2742 my @Expires = ();
2743 foreach my $pattern (keys(%LoginRequiredPatterns))
2745 if($path =~ m#$pattern#)
2747 my ($SessionPath, $PasswordsPath, $Login, $validtime) = split(/\t/, $LoginRequiredPatterns{$pattern});
2748 push(@Expires, $validtime);
2751 # Write Session Ticket
2752 open(SESSION, ">$sessionfile") || die "$sessionfile: $!\n";
2753 print SESSION "Type: SESSION\n";
2754 foreach my $address (@IPaddress)
2756 print SESSION "IPaddress: $address\n";
2758 foreach my $path (@AllowedPaths)
2760 print SESSION "AllowedPaths: $path\n";
2762 foreach my $validtime (@Expires)
2764 print SESSION "Expires: $validtime\n";
2766 print SESSION "Username: ", $authorization->{'Username'}->[0], "\n";
2767 close(SESSION);
2769 # Login file should now be removed
2770 return unlink($loginfile);
2773 sub check_ticket_validity # ($type, $ticketfile, $address, $path)
2775 my $type = shift || "SESSION";
2776 my $ticketfile = shift || "";
2777 my $address = shift || "";
2778 my $path = shift || "";
2780 # Is there a session ticket of this name?
2781 return 0 unless -s "$ticketfile";
2783 # There is a session ticket, is it linked to this IP address?
2784 my $ticket = read_ticket($ticketfile);
2786 # Is this the right type of ticket
2787 return unless $ticket->{"Type"}->[0] eq $type;
2789 # Does the IP address match?
2790 $IPmatches = 0;
2791 for my $IPpattern (@{$ticket->{"IPaddress"}})
2793 ++$IPmatches if $address =~ m#^$IPpattern#ig;
2795 return 0 unless !$ticket->{"IPaddress"} || $IPmatches;
2797 # Is the path allowed
2798 my $Pathmatches = 0;
2799 foreach my $Pathpattern (@{$ticket->{"AllowedPaths"}})
2801 ++$Pathmatches if $path =~ m#$Pathpattern#ig;
2803 return 0 unless !@{$ticket->{"AllowedPaths"}} || $Pathmatches;
2805 # Is the ticket expired?
2806 my $Expired = 0;
2807 if($ticket->{"Expires"} && @{$ticket->{"Expires"}})
2809 my $CurrentTime = time();
2810 ++$Expired if($CurrentTime > $ticket->{"Expires"}->[0]);
2812 return 0 if $Expired;
2814 return 1;
2818 sub remove_expired_tickets # ($path) -> number of tickets removed
2820 my $path = shift || "";
2821 return 0 unless $path;
2822 $path =~ s!/+$!!g;
2823 my $removed_tickets = 0;
2824 my @ticketlist = glob("$path/*");
2825 foreach my $ticketfile (@ticketlist)
2827 my $ticket = read_ticket($ticketfile);
2828 if(@{$ticket->{'Expires'}} && $ticket->{'Expires'}->[0] < time)
2830 unlink $ticketfile;
2831 ++$removed_tickets;
2834 return $removed_tickets;
2837 sub read_ticket # ($ticketfile) -> &%ticket
2839 my $ticketfile = shift || "";
2840 my $ticket = {};
2841 if($ticketfile && -s $ticketfile)
2843 open(TICKETFILE, "<$ticketfile") || die "$ticketfile: $!\n";
2844 my @alllines = <TICKETFILE>;
2845 close(TICKETFILE);
2846 foreach my $currentline (@alllines)
2848 if($currentline =~ /^\s*(\S[^\:]+)\:\s+(.*)\s*$/)
2850 my $Label = $1;
2851 my $Value = $2;
2852 # Recalculate expire date from relative time
2853 if($Label =~ /^Expires$/ig && $Value =~ /^\+/)
2855 # Get SessionTicket file stats
2856 my ($dev,$ino,$mode,$nlink,$uid,$gid,$rdev,$size,$atime,$mtime,$ctime,$blksize,$blocks)
2857 = stat("$ticketfile");
2858 if($Value =~ /^\+(\d+)\s*d(ays)?\s*$/)
2860 $ExpireTime = 24*3600*$1;
2862 elsif($Value =~ /^\+(\d+)\s*m(inutes)?\s*$/)
2864 $ExpireTime = 60*$1;
2866 elsif($Value =~ /^\+(\d+)\s*h(ours)?\s*$/)
2868 $ExpireTime = 3600*$1;
2870 elsif($Value =~ /^\+(\d+)\s*s(econds)?\s*$/)
2872 $ExpireTime = $1;
2874 elsif($Value =~ /^\+(\d+)\s*$/)
2876 $ExpireTime = $1;
2879 my $ActualExpireTime = $ExpireTime + $ctime;
2880 $Value = $ActualExpireTime;
2882 $ticket->{$Label} = () unless exists($ticket->{$Label});
2883 push(@{$ticket->{$Label}}, $Value);
2887 if(exists($ticket->{Expires}))
2889 @{$ticket->{Expires}} = sort(@{$ticket->{Expires}});
2891 return $ticket;
2894 # End of Handle login access
2897 ############################################################################
2899 # Handle foreign interpreters (i.e., scripting languages)
2901 # Insert perl code to execute scripts in foreign scripting languages.
2902 # Actually, the scripts inside the <SCRIPT></SCRIPT> blocks are piped
2903 # into an interpreter.
2904 # The code presented here is fairly confusing because it
2905 # actually writes perl code code to the output.
2907 # A table with the file handles
2908 %SCRIPTINGINPUT = ();
2910 # A function to clean up Client delivered CGI parameter values
2911 # (i.e., quote all odd characters)
2912 %SHRUBcharacterTR =
2914 "\'" => '&#39;',
2915 "\`" => '&#96;',
2916 "\"" => '&quot;',
2917 '&' => '&amper;',
2918 "\\" => '&#92;'
2921 sub shrubCGIparameter # ($String) -> Cleaned string
2923 my $String = shift || "";
2925 # Change all quotes [`'"] into HTML character entities
2926 my ($Char, $Transcript) = ('&', $SHRUBcharacterTR{'&'});
2928 # Protect &
2929 $String =~ s/\Q$Char\E/$Transcript/isg if $Transcript;
2931 while( ($Char, $Transcript) = each %SHRUBcharacterTR)
2933 next if $Char eq '&';
2934 $String =~ s/\Q$Char\E/$Transcript/isg;
2937 # Replace newlines
2938 $String =~ s/[\n]/\\n/g;
2939 # Replace control characters with their backslashed octal ordinal numbers
2940 $String =~ s/([^\S \t])/(sprintf("\\0%o", ord($1)))/eisg; #
2941 $String =~ s/([\x00-\x08\x0A-\x1F])/(sprintf("\\0%o", ord($1)))/eisg; #
2943 return $String;
2947 # The initial open statements: Open a pipe to the foreign script interpreter
2948 sub OpenForeignScript # ($ContentType) -> $DirectivePrefix
2950 my $ContentType = lc(shift) || return "";
2951 my $NewDirective = "";
2953 return $NewDirective if($SCRIPTINGINPUT{$ContentType});
2955 # Construct a unique file handle name
2956 $SCRIPTINGFILEHANDLE = uc($ContentType);
2957 $SCRIPTINGFILEHANDLE =~ s/\W/\_/isg;
2958 $SCRIPTINGINPUT{$ContentType} = $SCRIPTINGFILEHANDLE
2959 unless $SCRIPTINGINPUT{$ContentType};
2961 # Create the relevant script: Open the pipe to the interpreter
2962 $NewDirective .= <<"BLOCKCGISCRIPTOROPEN";
2963 # Open interpreter for '$ContentType'
2964 # Open pipe to interpreter (if it isn't open already)
2965 open($SCRIPTINGINPUT{$ContentType}, "|$ScriptingLanguages{$ContentType}") || main::dieHandler(14, "$ContentType: \$!\\n");
2966 BLOCKCGISCRIPTOROPEN
2968 # Insert Initialization code and CGI variables
2969 $NewDirective .= InitializeForeignScript($ContentType);
2971 # Ready
2972 return $NewDirective;
2976 # The final closing code to stop the interpreter
2977 sub CloseForeignScript # ($ContentType) -> $DirectivePrefix
2979 my $ContentType = lc(shift) || return "";
2980 my $NewDirective = "";
2982 # Do nothing unless the pipe realy IS open
2983 return "" unless $SCRIPTINGINPUT{$ContentType};
2985 # Initial comment
2986 $NewDirective .= "\# Close interpreter for '$ContentType'\n";
2989 # Write the Postfix code
2990 $NewDirective .= CleanupForeignScript($ContentType);
2992 # Create the relevant script: Close the pipe to the interpreter
2993 $NewDirective .= <<"BLOCKCGISCRIPTORCLOSE";
2994 close($SCRIPTINGINPUT{$ContentType}) || main::dieHandler(15, \"$ContentType: \$!\\n\");
2995 select(STDOUT); \$|=1;
2997 BLOCKCGISCRIPTORCLOSE
2999 # Remove the file handler of the foreign script
3000 delete($SCRIPTINGINPUT{$ContentType});
3002 return $NewDirective;
3006 # The initialization code for the foreign script interpreter
3007 sub InitializeForeignScript # ($ContentType) -> $DirectivePrefix
3009 my $ContentType = lc(shift) || return "";
3010 my $NewDirective = "";
3012 # Add initialization code
3013 if($ScriptingInitialization{$ContentType})
3015 $NewDirective .= <<"BLOCKCGISCRIPTORINIT";
3016 # Initialization Code for '$ContentType'
3017 # Select relevant output filehandle
3018 select($SCRIPTINGINPUT{$ContentType}); \$|=1;
3020 # The Initialization code (if any)
3021 print $SCRIPTINGINPUT{$ContentType} <<'${ContentType}INITIALIZATIONCODE';
3022 $ScriptingInitialization{$ContentType}
3023 ${ContentType}INITIALIZATIONCODE
3025 BLOCKCGISCRIPTORINIT
3028 # Add all CGI variables defined
3029 if(exists($ScriptingCGIvariables{$ContentType}))
3031 # Start writing variable definitions to the Interpreter
3032 if($ScriptingCGIvariables{$ContentType})
3034 $NewDirective .= <<"BLOCKCGISCRIPTORVARDEF";
3035 # CGI variables (from the %default_values table)
3036 print $SCRIPTINGINPUT{$ContentType} << '${ContentType}CGIVARIABLES';
3037 BLOCKCGISCRIPTORVARDEF
3040 my ($N, $V);
3041 foreach $N (keys(%default_values))
3043 # Determine whether the parameter has been defined
3044 # (the eval is a workaround to get at the variable value)
3045 next unless eval("defined(\$CGIexecute::$N)");
3047 # Get the value from the EXECUTION environment
3048 $V = eval("\$CGIexecute::$N");
3049 # protect control characters (i.e., convert them to \0.. form)
3050 $V = shrubCGIparameter($V);
3052 # Protect interpolated variables
3053 eval("\$CGIexecute::$N = '$V';") unless $ScriptingCGIvariables{$ContentType};
3055 # Print the actual declaration for this scripting language
3056 if($ScriptingCGIvariables{$ContentType})
3058 $NewDirective .= sprintf($ScriptingCGIvariables{$ContentType}, $N, $V);
3059 $NewDirective .= "\n";
3063 # Stop writing variable definitions to the Interpreter
3064 if($ScriptingCGIvariables{$ContentType})
3066 $NewDirective .= <<"BLOCKCGISCRIPTORVARDEFEND";
3067 ${ContentType}CGIVARIABLES
3068 BLOCKCGISCRIPTORVARDEFEND
3073 $NewDirective .= << "BLOCKCGISCRIPTOREND";
3075 # Select STDOUT filehandle
3076 select(STDOUT); \$|=1;
3078 BLOCKCGISCRIPTOREND
3080 return $NewDirective;
3084 # The cleanup code for the foreign script interpreter
3085 sub CleanupForeignScript # ($ContentType) -> $DirectivePrefix
3087 my $ContentType = lc(shift) || return "";
3088 my $NewDirective = "";
3090 # Return if not needed
3091 return $NewDirective unless $ScriptingCleanup{$ContentType};
3093 # Create the relevant script: Open the pipe to the interpreter
3094 $NewDirective .= <<"BLOCKCGISCRIPTORSTOP";
3095 # Cleanup Code for '$ContentType'
3096 # Select relevant output filehandle
3097 select($SCRIPTINGINPUT{$ContentType}); \$|=1;
3098 # Print Cleanup code to foreign script
3099 print $SCRIPTINGINPUT{$ContentType} <<'${ContentType}SCRIPTSTOP';
3100 $ScriptingCleanup{$ContentType}
3101 ${ContentType}SCRIPTSTOP
3103 # Select STDOUT filehandle
3104 select(STDOUT); \$|=1;
3105 BLOCKCGISCRIPTORSTOP
3107 return $NewDirective;
3111 # The prefix code for each <script></script> block
3112 sub PrefixForeignScript # ($ContentType) -> $DirectivePrefix
3114 my $ContentType = lc(shift) || return "";
3115 my $NewDirective = "";
3117 # Return if not needed
3118 return $NewDirective unless $ScriptingPrefix{$ContentType};
3120 my $Quote = "\'";
3121 # If the CGIvariables parameter is defined, but empty, interpolate
3122 # code string (i.e., $var .= << "END" i.s.o. $var .= << 'END')
3123 $Quote = '"' if exists($ScriptingCGIvariables{$ContentType}) &&
3124 !$ScriptingCGIvariables{$ContentType};
3126 # Add initialization code
3127 $NewDirective .= <<"BLOCKCGISCRIPTORPREFIX";
3128 # Prefix Code for '$ContentType'
3129 # Select relevant output filehandle
3130 select($SCRIPTINGINPUT{$ContentType}); \$|=1;
3132 # The block Prefix code (if any)
3133 print $SCRIPTINGINPUT{$ContentType} <<$Quote${ContentType}PREFIXCODE$Quote;
3134 $ScriptingPrefix{$ContentType}
3135 ${ContentType}PREFIXCODE
3136 # Select STDOUT filehandle
3137 select(STDOUT); \$|=1;
3138 BLOCKCGISCRIPTORPREFIX
3140 return $NewDirective;
3144 # The postfix code for each <script></script> block
3145 sub PostfixForeignScript # ($ContentType) -> $DirectivePrefix
3147 my $ContentType = lc(shift) || return "";
3148 my $NewDirective = "";
3150 # Return if not needed
3151 return $NewDirective unless $ScriptingPostfix{$ContentType};
3153 my $Quote = "\'";
3154 # If the CGIvariables parameter is defined, but empty, interpolate
3155 # code string (i.e., $var .= << "END" i.s.o. $var .= << 'END')
3156 $Quote = '"' if exists($ScriptingCGIvariables{$ContentType}) &&
3157 !$ScriptingCGIvariables{$ContentType};
3159 # Create the relevant script: Open the pipe to the interpreter
3160 $NewDirective .= <<"BLOCKCGISCRIPTORPOSTFIX";
3161 # Postfix Code for '$ContentType'
3162 # Select filehandle to interpreter
3163 select($SCRIPTINGINPUT{$ContentType}); \$|=1;
3164 # Print postfix code to foreign script
3165 print $SCRIPTINGINPUT{$ContentType} <<$Quote${ContentType}SCRIPTPOSTFIX$Quote;
3166 $ScriptingPostfix{$ContentType}
3167 ${ContentType}SCRIPTPOSTFIX
3168 # Select STDOUT filehandle
3169 select(STDOUT); \$|=1;
3170 BLOCKCGISCRIPTORPOSTFIX
3172 return $NewDirective;
3175 sub InsertForeignScript # ($ContentType, $directive, @SRCfile) -> $NewDirective
3177 my $ContentType = lc(shift) || return "";
3178 my $directive = shift || return "";
3179 my @SRCfile = @_;
3180 my $NewDirective = "";
3182 my $Quote = "\'";
3183 # If the CGIvariables parameter is defined, but empty, interpolate
3184 # code string (i.e., $var .= << "END" i.s.o. $var .= << 'END')
3185 $Quote = '"' if exists($ScriptingCGIvariables{$ContentType}) &&
3186 !$ScriptingCGIvariables{$ContentType};
3188 # Create the relevant script
3189 $NewDirective .= <<"BLOCKCGISCRIPTORINSERT";
3190 # Insert Code for '$ContentType'
3191 # Select filehandle to interpreter
3192 select($SCRIPTINGINPUT{$ContentType}); \$|=1;
3193 BLOCKCGISCRIPTORINSERT
3195 # Use SRC feature files
3196 my $ThisSRCfile;
3197 while($ThisSRCfile = shift(@_))
3199 # Handle blocks
3200 if($ThisSRCfile =~ /^\s*\{\s*/)
3202 my $Block = $';
3203 $Block = $` if $Block =~ /\s*\}\s*$/;
3204 $NewDirective .= <<"BLOCKCGISCRIPTORSRCBLOCK";
3205 print $SCRIPTINGINPUT{$ContentType} <<$Quote${ContentType}SRCBLOCKCODE$Quote;
3206 $Block
3207 ${ContentType}SRCBLOCKCODE
3208 BLOCKCGISCRIPTORSRCBLOCK
3210 next;
3213 # Handle files
3214 $NewDirective .= <<"BLOCKCGISCRIPTORSRCFILES";
3215 # Read $ThisSRCfile
3216 open(SCRIPTINGSOURCE, "<$ThisSRCfile") || main::dieHandler(16, "$ThisSRCfILE: \$!");
3217 while(<SCRIPTINGSOURCE>)
3219 print $SCRIPTINGINPUT{$ContentType} \$_;
3221 close(SCRIPTINGSOURCE);
3223 BLOCKCGISCRIPTORSRCFILES
3227 # Add the directive
3228 if($directive)
3230 $NewDirective .= <<"BLOCKCGISCRIPTORINSERT";
3231 print $SCRIPTINGINPUT{$ContentType} <<$Quote${ContentType}DIRECTIVECODE$Quote;
3232 $directive
3233 ${ContentType}DIRECTIVECODE
3234 BLOCKCGISCRIPTORINSERT
3238 $NewDirective .= <<"BLOCKCGISCRIPTORSELECT";
3239 # Select STDOUT filehandle
3240 select(STDOUT); \$|=1;
3241 BLOCKCGISCRIPTORSELECT
3243 # Ready
3244 return $NewDirective;
3247 sub CloseAllForeignScripts # Call CloseForeignScript on all open scripts
3249 my $ContentType;
3250 foreach $ContentType (keys(%SCRIPTINGINPUT))
3252 my $directive = CloseForeignScript($ContentType);
3253 print STDERR "\nDirective $CGI_Date: ", $directive;
3254 CGIexecute->evaluate($directive);
3259 # End of handling foreign (external) scripting languages.
3261 ############################################################################
3263 # A subroutine to handle "nested" quotes, it cuts off the leading
3264 # item or quoted substring
3265 # E.g.,
3266 # ' A_word and more words' -> @('A_word', ' and more words')
3267 # '"quoted string" The rest' -> @('quoted string', ' The rest')
3268 # (this is needed for parsing the <TAGS> and their attributes)
3269 my $SupportedQuotes = "\'\"\`\(\{\[";
3270 my %QuotePairs = ('('=>')','['=>']','{'=>'}'); # Brackets
3271 sub ExtractQuotedItem # ($String) -> @($QuotedString, $RestOfString)
3273 my @Result = ();
3274 my $String = shift || return @Result;
3276 if($String =~ /^\s*([\w\/\-\.]+)/is)
3278 push(@Result, $1, $');
3280 elsif($String =~ /^\s*(\\?)([\Q$SupportedQuotes\E])/is)
3282 my $BackSlash = $1 || "";
3283 my $OpenQuote = $2;
3284 my $CloseQuote = $OpenQuote;
3285 $CloseQuote = $QuotePairs{$OpenQuote} if $QuotePairs{$OpenQuote};
3287 if($BackSlash)
3289 $String =~ /^\s*\\\Q$OpenQuote\E/i;
3290 my $Onset = $';
3291 $Onset =~ /\\\Q$CloseQuote\E/i;
3292 my $Rest = $';
3293 my $Item = $`;
3294 push(@Result, $Item, $Rest);
3297 else
3299 $String =~ /^\s*\Q$OpenQuote\E([^\Q$CloseQuote\E]*)\Q$CloseQuote\E/i;
3300 push(@Result, $1, $');
3303 else
3305 push(@Result, "", $String);
3307 return @Result;
3310 # Now, start with the real work
3312 # Control the output of the Content-type: text/html\n\n message
3313 my $SupressContentType = 0;
3315 # Process a file
3316 sub ProcessFile # ($file_path)
3318 my $file_path = shift || return 0;
3321 # Generate a unique file handle (for recursions)
3322 my @SRClist = ();
3323 my $FileHandle = "file";
3324 my $n = 0;
3325 while(!eof($FileHandle.$n)) {++$n;};
3326 $FileHandle .= $n;
3328 # Start HTML output
3329 # Use the default Content-type if this is NOT a raw file
3330 unless(($RawFilePattern && $ENV{'PATH_INFO'} =~ m@($RawFilePattern)$@i)
3331 || $SupressContentType)
3333 $ENV{'PATH_INFO'} =~ m@($FilePattern)$@i;
3334 my $ContentType = $ContentTypeTable{$1};
3335 print "Content-type: $ContentType\n";
3336 print "\n";
3337 $SupressContentType = 1; # Content type has been printed
3341 # Get access to the actual data. This can be from RAM (by way of an
3342 # environment variable) or by opening a file.
3344 # Handle the use of RAM images (file-data is stored in the
3345 # $CGI_FILE_CONTENTS environment variable)
3346 # Note that this environment variable will be cleared, i.e., it is strictly for
3347 # single-use only!
3348 if($ENV{$CGI_FILE_CONTENTS})
3350 # File has been read already
3351 $_ = $ENV{$CGI_FILE_CONTENTS};
3352 # Sorry, you have to do the reading yourself (dynamic document creation?)
3353 # NOTE: you must read the whole document at once
3354 if($_ eq '-')
3356 $_ = eval("\@_=('$file_path'); do{$ENV{$CGI_DATA_ACCESS_CODE}}");
3358 else # Clear environment variable
3360 $ENV{$CGI_FILE_CONTENTS} = '-';
3363 # Open Only PLAIN TEXT files (or STDIN) and NO executable files (i.e., scripts).
3364 # THIS IS A SECURITY FEATURE!
3365 elsif($file_path eq '-' || (-e "$file_path" && -r _ && -T _ && -f _ && ! (-x _ || -X _) ))
3367 open($FileHandle, $file_path) || dieHandler(17, "<h2>File not found</h2>\n");
3368 push(@OpenFiles, $file_path);
3369 $_ = <$FileHandle>; # Read first line
3371 else
3373 print "<h2>File not found</h2>\n";
3374 dieHandler(18, "$file_path\n");
3377 $| = 1; # Flush output buffers
3379 # Initialize variables
3380 my $METAarguments = ""; # The CGI arguments from the latest META tag
3381 my @METAvalues = (); # The ''-quoted CGI values from the latest META tag
3382 my $ClosedTag = 0; # <TAG> </TAG> versus <TAG/>
3385 # Send document to output
3386 # Process the requested document.
3387 # Do a loop BEFORE reading input again (this catches the RAM/Database
3388 # type of documents).
3389 do {
3392 # Handle translations if needed
3394 performTranslation(\$_) if $TranslationPaths;
3396 # Catch <SCRIPT LANGUAGE="PERL" TYPE="text/ssperl" > directives in $_
3397 # There can be more than 1 <SCRIPT> or META tags on a line
3398 while(/\<\s*(SCRIPT|META|DIV|INS)\s/is)
3400 my $directive = "";
3401 # Store rest of line
3402 my $Before = $`;
3403 my $ScriptTag = $&;
3404 my $After = $';
3405 my $TagType = uc($1);
3406 # The before part can be send to the output
3407 print $Before;
3409 # Read complete Tag from after and/or file
3410 until($After =~ /([^\\])\>/)
3412 $After .= <$FileHandle>;
3413 performTranslation(\$After) if $TranslationPaths;
3416 if($After =~ /([^\\])\>/)
3418 $ScriptTag .= $`.$&; # Keep the Script Tag intact
3419 $After = $';
3421 else
3423 dieHandler(19, "Closing > not found\n");
3426 # The tag could be closed by />, we handle this in the XML way
3427 # and don't process any content (we ignore whitespace)
3428 $ClosedTag = ($ScriptTag =~ m@[^\\]/\s*\>\s*$@) ? 1 : 0;
3431 # TYPE or CLASS?
3432 my $TypeName = ($TagType =~ /META/is) ? "CONTENT" : "TYPE";
3433 $TypeName = "CLASS" if $TagType eq 'DIV' || $TagType eq 'INS';
3435 # Parse <SCRIPT> or <META> directive
3436 # If NOT (TYPE|CONTENT)="text/ssperl" (i.e., $ServerScriptContentType),
3437 # send the line to the output and go to the next loop
3438 my $CurrentContentType = "";
3439 if($ScriptTag =~ /(^|\s)$TypeName\s*=\s*/is)
3441 my ($Type) = ExtractQuotedItem($');
3442 $Type =~ /^\s*([\w\/\-]+)\s*[\,\;]?/;
3443 $CurrentContentType = lc($1); # Note: mime-types are "case-less"
3444 # CSS classes are aliases of $ServerScriptContentType
3445 if($TypeName eq "CLASS" && $CurrentContentType eq $ServerScriptContentClass)
3447 $CurrentContentType = $ServerScriptContentType;
3452 # Not a known server-side content type, print and continue
3453 unless(($CurrentContentType =~
3454 /$ServerScriptContentType|$ShellScriptContentType/is) ||
3455 $ScriptingLanguages{$CurrentContentType})
3457 print $ScriptTag;
3458 $_ = $After;
3459 next;
3463 # A known server-side content type, evaluate
3465 # First, handle \> and \<
3466 $ScriptTag =~ s/\\\>/\>/isg;
3467 $ScriptTag =~ s/\\\</\</isg;
3469 # Extract the CGI, SRC, ID, IF and UNLESS attributes
3470 my %ScriptTagAttributes = ();
3471 while($ScriptTag =~ /(^|\s)(CGI|IF|UNLESS|SRC|ID)\s*=\s*/is)
3473 my $Attribute = $2;
3474 my $Rest = $';
3475 my $Value = "";
3476 ($Value, $ScriptTag) = ExtractQuotedItem($Rest);
3477 $ScriptTagAttributes{uc($Attribute)} = $Value;
3481 # The attribute used to define the CGI variables
3482 # Extract CGI-variables from
3483 # <META CONTENT="text/ssperl; CGI='' SRC=''">
3484 # <SCRIPT TYPE='text/ssperl' CGI='' SRC=''>
3485 # <DIV CLASS='ssperl' CGI='' SRC='' ID=""> tags
3486 # <INS CLASS='ssperl' CGI='' SRC='' ID=""> tags
3487 if($ScriptTagAttributes{'CGI'})
3489 @ARGV = (); # Reset ARGV
3490 $ARGC = 0;
3491 $METAarguments = ""; # Reset the META CGI arguments
3492 @METAvalues = ();
3493 my $Meta_CGI = $ScriptTagAttributes{'CGI'};
3495 # Process default values of variables ($<name> = 'default value')
3496 # Allowed quotes are '', "", ``, (), [], and {}
3497 while($Meta_CGI =~ /(^\s*|[^\\])([\$\@\%]?)([\w\-]+)\s*/is)
3499 my $varType = $2 || '$'; # Variable or list
3500 my $name = $3; # The Name
3501 my $default = "";
3502 $Meta_CGI = $';
3504 if($Meta_CGI =~ /^\s*\=\s*/is)
3506 # Locate (any) default value
3507 ($default, $Meta_CGI) = ExtractQuotedItem($'); # Cut the parameter from the CGI
3509 $RemainingTag = $Meta_CGI;
3512 # Define CGI (or ENV) variable, initalize it from the
3513 # Query string or the default value
3515 # Also construct the @ARGV and @_ arrays. This allows other (SRC=) Perl
3516 # scripts to access the CGI arguments defined in the META tag
3517 # (Not for CGI inside <SCRIPT> tags)
3518 if($varType eq '$')
3520 CGIexecute::defineCGIvariable($name, $default)
3521 || dieHandler(20, "INVALID CGI name/value pair ($name, $default)\n");
3522 push(@METAvalues, "'".${"CGIexecute::$name"}."'");
3523 # Add value to the @ARGV list
3524 push(@ARGV, ${"CGIexecute::$name"});
3525 ++$ARGC;
3527 elsif($varType eq '@')
3529 CGIexecute::defineCGIvariableList($name, $default)
3530 || dieHandler(21, "INVALID CGI name/value list pair ($name, $default)\n");
3531 push(@METAvalues, "'".join("'", @{"CGIexecute::$name"})."'");
3532 # Add value to the @ARGV list
3533 push(@ARGV, @{"CGIexecute::$name"});
3534 $ARGC = scalar(@CGIexecute::ARGV);
3536 elsif($varType eq '%')
3538 CGIexecute::defineCGIvariableHash($name, $default)
3539 || dieHandler(22, "INVALID CGI name/value hash pair ($name, $default)\n");
3540 my @PairList = map {"$_ => ".${"CGIexecute::$name"}{$_}} keys(%{"CGIexecute::$name"});
3541 push(@METAvalues, "'".join("'", @PairList)."'");
3542 # Add value to the @ARGV list
3543 push(@ARGV, %{"CGIexecute::$name"});
3544 $ARGC = scalar(@CGIexecute::ARGV);
3547 # Store the values for internal and later use
3548 $METAarguments .= "$varType".$name.","; # A string of CGI variable names
3550 push(@METAvalues, "\'".eval("\"$varType\{CGIexecute::$name\}\"")."\'"); # ALWAYS add '-quotes around values
3555 # The IF (conditional execution) Attribute
3556 # Evaluate the condition and stop unless it evaluates to true
3557 if($ScriptTagAttributes{'IF'})
3559 my $IFcondition = $ScriptTagAttributes{'IF'};
3561 # Convert SCRIPT calls, ./<script>
3562 $IFcondition =~ s@([\W]|^)\./([\S])@$1$SCRIPT_SUB$2@g;
3564 # Convert FILE calls, ~/<file>
3565 $IFcondition =~ s@([\W])\~/([\S])@$1$HOME_SUB$2@g;
3567 # Block execution if necessary
3568 unless(CGIexecute->evaluate($IFcondition))
3570 %ScriptTagAttributes = ();
3571 $CurrentContentType = "";
3575 # The UNLESS (conditional execution) Attribute
3576 # Evaluate the condition and stop if it evaluates to true
3577 if($ScriptTagAttributes{'UNLESS'})
3579 my $UNLESScondition = $ScriptTagAttributes{'UNLESS'};
3581 # Convert SCRIPT calls, ./<script>
3582 $UNLESScondition =~ s@([\W]|^)\./([\S])@$1$SCRIPT_SUB$2@g;
3584 # Convert FILE calls, ~/<file>
3585 $UNLESScondition =~ s@([\W])\~/([\S])@$1$HOME_SUB$2@g;
3587 # Block execution if necessary
3588 if(CGIexecute->evaluate($UNLESScondition))
3590 %ScriptTagAttributes = ();
3591 $CurrentContentType = "";
3595 # The SRC (Source File) Attribute
3596 # Extract any source script files and add them in
3597 # front of the directive
3598 # The SRC list should be emptied
3599 @SRClist = ();
3600 my $SRCtag = "";
3601 my $Prefix = 1;
3602 my $PrefixDirective = "";
3603 my $PostfixDirective = "";
3604 # There is a SRC attribute
3605 if($ScriptTagAttributes{'SRC'})
3607 $SRCtag = $ScriptTagAttributes{'SRC'};
3608 # Remove "file://" prefixes
3609 $SRCtag =~ s@([^\w\/\\]|^)file\://([^\s\/\@\=])@$1$2@gis;
3610 # Expand script filenames "./Script"
3611 $SRCtag =~ s@([^\w\/\\]|^)\./([^\s\/\@\=])@$1$SCRIPT_SUB/$2@gis;
3612 # Expand script filenames "~/Script"
3613 $SRCtag =~ s@([^\w\/\\]|^)\~/([^\s\/\@\=])@$1$HOME_SUB/$2@gis;
3616 # File source tags
3617 while($SRCtag =~ /\S/is)
3619 my $SRCdirective = "";
3621 # Pseudo file, just a switch to go from PREFIXING to POSTFIXING
3622 # SRC files
3623 if($SRCtag =~ /^[\s\;\,]*(POSTFIX|PREFIX)([^$FileAllowedChars]|$)/is)
3625 my $InsertionPlace = $1;
3626 $SRCtag = $2.$';
3628 $Prefix = $InsertionPlace =~ /POSTFIX/i ? 0 : 1;
3629 # Go to next round
3630 next;
3632 # {}-blocks are just evaluated by "do"
3633 elsif($SRCtag =~ /^[\s\;\,]*\{/is)
3635 my $SRCblock = $';
3636 if($SRCblock =~ /\}[\s\;\,]*([^\}]*)$/is)
3638 $SRCblock = $`;
3639 $SRCtag = $1.$';
3640 # SAFEqx shell script blocks
3641 if($CurrentContentType =~ /$ShellScriptContentType/is)
3643 # Handle ''-quotes inside the script
3644 $SRCblock =~ s/[\']/\\$&/gis;
3646 $SRCblock = "print do { SAFEqx(\'".$SRCblock."\'); };'';";
3647 $SRCdirective .= $SRCblock."\n";
3649 # do { SRCblocks }
3650 elsif($CurrentContentType =~ /$ServerScriptContentType/is)
3652 $SRCblock = "print do { $SRCblock };'';";
3653 $SRCdirective .= $SRCblock."\n";
3655 else # The interpreter should handle this
3657 push(@SRClist, "{ $SRCblock }");
3661 else
3662 { dieHandler(23, "Closing \} missing\n");};
3664 # Files are processed as Text or Executable files
3665 elsif($SRCtag =~ /[\s\;\,]*([$FileAllowedChars]+)[\;\,\s]*/is)
3667 my $SrcFile = $1;
3668 $SRCtag = $';
3670 # We are handling one of the external interpreters
3671 if($ScriptingLanguages{$CurrentContentType})
3673 push(@SRClist, $SrcFile);
3675 # We are at the start of a DIV tag, just load all SRC files and/or URL's
3676 elsif($TagType eq 'DIV' || $TagType eq 'INS') # All files are prepended in DIV's
3678 # $SrcFile is a URL pointing to an HTTP or FTP server
3679 if($SrcFile =~ m!^([a-z]+)\://!)
3681 my $URLoutput = CGIscriptor::read_url($SrcFile);
3682 $SRCdirective .= $URLoutput;
3684 # SRC file is an existing file
3685 elsif(-e "$SrcFile")
3687 open(DIVSOURCE, "<$SrcFile") || dieHandler(24, "<$SrcFile: $!\n");
3688 my $Content;
3689 while(sysread(DIVSOURCE, $Content, 1024) > 0)
3691 $SRCdirective .= $Content;
3693 close(DIVSOURCE);
3696 # Executable files are executed as
3697 # `$SrcFile 'ARGV[0]' 'ARGV[1]'`
3698 elsif(-x "$SrcFile")
3700 $SRCdirective .= "print \`$SrcFile @METAvalues\`;'';\n";
3702 # Handle 'standard' files, using ProcessFile
3703 elsif((-T "$SrcFile" || $ENV{$CGI_FILE_CONTENTS})
3704 && $SrcFile =~ m@($FilePattern)$@) # A recursion
3707 # Do not process still open files because it can lead
3708 # to endless recursions
3709 if(grep(/^$SrcFile$/, @OpenFiles))
3710 { dieHandler(25, "$SrcFile allready opened (endless recursion)\n")};
3711 # Prepare meta arguments
3712 $SRCdirective .= '@ARGV = (' .$METAarguments.");\n" if $METAarguments;
3713 # Process the file
3714 $SRCdirective .= "main::ProcessFile(\'$SrcFile\');'';\n";
3716 elsif($SrcFile =~ m!^([a-z]+)\://!) # URL's are loaded and printed
3718 $SRCdirective .= GET_URL($SrcFile);
3720 elsif(-T "$SrcFile") # Textfiles are "do"-ed (Perl execution)
3722 $SRCdirective .= '@ARGV = (' .$METAarguments.");\n" if $METAarguments;
3723 $SRCdirective .= "do \'$SrcFile\';'';\n";
3725 else # This one could not be resolved (should be handled by BinaryMapFile)
3727 $SRCdirective .= 'print "'.$SrcFile.' cannot be used"'."\n";
3732 # Postfix or Prefix
3733 if($Prefix)
3735 $PrefixDirective .= $SRCdirective;
3737 else
3739 $PostfixDirective .= $SRCdirective;
3742 # The prefix should be handled immediately
3743 $directive .= $PrefixDirective;
3744 $PrefixDirective = "";
3748 # Handle the content of the <SCRIPT></SCRIPT> tags
3749 # Do not process the content of <SCRIPT/>
3750 if($TagType =~ /SCRIPT/is && !$ClosedTag) # The <SCRIPT> TAG
3752 my $EndScriptTag = "";
3754 # Execute SHELL scripts with SAFEqx()
3755 if($CurrentContentType =~ /$ShellScriptContentType/is)
3757 $directive .= "SAFEqx(\'";
3760 # Extract Program
3761 while($After !~ /\<\s*\/SCRIPT[^\>]*\>/is && !eof($FileHandle))
3763 $After .= <$FileHandle>;
3764 performTranslation(\$After) if $TranslationPaths;
3767 if($After =~ /\<\s*\/SCRIPT[^\>]*\>/is)
3769 $directive .= $`;
3770 $EndScriptTag = $&;
3771 $After = $';
3773 else
3775 dieHandler(26, "Missing </SCRIPT> end tag in $ENV{'PATH_INFO'}\n");
3778 # Process only when content should be executed
3779 if($CurrentContentType)
3782 # Remove all comments from Perl scripts
3783 # (NOT from OS shell scripts)
3784 $directive =~ s/[^\\\$]\#[^\n\f\r]*([\n\f\r])/$1/g
3785 if $CurrentContentType =~ /$ServerScriptContentType/i;
3787 # Convert SCRIPT calls, ./<script>
3788 $directive =~ s@([\W]|^)\./([\S])@$1$SCRIPT_SUB$2@g;
3790 # Convert FILE calls, ~/<file>
3791 $directive =~ s@([\W])\~/([\S])@$1$HOME_SUB$2@g;
3793 # Execute SHELL scripts with SAFEqx(), closing bracket
3794 if($CurrentContentType =~ /$ShellScriptContentType/i)
3796 # Handle ''-quotes inside the script
3797 $directive =~ /SAFEqx\(\'/;
3798 $directive = $`.$&;
3799 my $Executable = $';
3800 $Executable =~ s/[\']/\\$&/gs;
3802 $directive .= $Executable."\');"; # Closing bracket
3805 else
3807 $directive = "";
3810 # Handle the content of the <DIV></DIV> tags
3811 # Do not process the content of <DIV/>
3812 elsif(($TagType eq 'DIV' || $TagType eq 'INS') && !$ClosedTag) # The <DIV> TAGs
3814 my $EndScriptTag = "";
3816 # Extract Text
3817 while($After !~ /\<\s*\/$TagType[^\>]*\>/is && !eof($FileHandle))
3819 $After .= <$FileHandle>;
3820 performTranslation(\$After) if $TranslationPaths;
3823 if($After =~ /\<\s*\/$TagType[^\>]*\>/is)
3825 $directive .= $`;
3826 $EndScriptTag = $&;
3827 $After = $';
3829 else
3831 dieHandler(27, "Missing </$TagType> end tag in $ENV{'PATH_INFO'}\n");
3834 # Add the Postfixed directives (but only when it contains something printable)
3835 $directive .= "\n".$PostfixDirective if $PostfixDirective =~ /\S/;
3836 $PostfixDirective = "";
3839 # Process only when content should be handled
3840 if($CurrentContentType)
3843 # Get the name (ID), and clean it (i.e., remove anything that is NOT part of
3844 # a valid Perl name). Names should not contain $, but we can handle it.
3845 my $name = $ScriptTagAttributes{'ID'};
3846 $name =~ /^\s*[\$\@\%]?([\w\-]+)/;
3847 $name = $1;
3849 # Assign DIV contents to $NAME value OUTSIDE the CGI values!
3850 CGIexecute::defineCGIexecuteVariable($name, $directive);
3851 $directive = "";
3854 # Nothing to execute
3855 $directive = "";
3859 # Handle Foreign scripting languages
3860 if($ScriptingLanguages{$CurrentContentType})
3862 my $newDirective = "";
3863 $newDirective .= OpenForeignScript($CurrentContentType); # Only if not already done
3864 $newDirective .= PrefixForeignScript($CurrentContentType);
3865 $newDirective .= InsertForeignScript($CurrentContentType, $directive, @SRClist);
3866 $newDirective .= PostfixForeignScript($CurrentContentType);
3867 $newDirective .= CloseForeignScript($CurrentContentType); # This shouldn't be necessary
3869 $newDirective .= '"";';
3871 $directive = $newDirective;
3875 # Add the Postfixed directives (but only when it contains something printable)
3876 $directive .= "\n".$PostfixDirective if $PostfixDirective =~ /\S/;
3877 $PostfixDirective = "";
3880 # EXECUTE the script and print the results
3882 # Use this to debug the program
3883 # print STDERR "Directive $CGI_Date: \n", $directive, "\n\n";
3885 my $Result = CGIexecute->evaluate($directive) if $directive; # Evaluate as PERL code
3886 $Result =~ s/\n$//g; # Remove final newline
3888 # Print the Result of evaluating the directive
3889 # (this will handle LARGE, >64 kB output)
3890 my $BytesWritten = 1;
3891 while($Result && $BytesWritten)
3893 $BytesWritten = syswrite(STDOUT, $Result, 64);
3894 $Result = substr($Result, $BytesWritten);
3896 # print $Result; # Could be used instead of above code
3898 # Store result if wanted, i.e., if $CGIscriptorResults has been
3899 # defined in a <META> tag.
3900 push(@CGIexecute::CGIscriptorResults, $Result)
3901 if exists($default_values{'CGIscriptorResults'});
3903 # Process the rest of the input line (this could contain
3904 # another directive)
3905 $_ = $After;
3907 print $_;
3908 } while(<$FileHandle>); # Read and Test AFTER first loop!
3910 close ($FileHandle);
3911 dieHandler(28, "Error in recursion\n") unless pop(@OpenFiles) == $file_path;
3915 ###############################################################################
3917 # Call the whole package
3919 sub Handle_Request
3921 my $file_path = "";
3923 # Initialization Code
3924 Initialize_Request();
3926 # SECURITY: ACCESS CONTROL
3927 Access_Control();
3929 # Read the POST part of the query, if there is one
3930 Get_POST_part_of_query();
3932 # Start (HTML) output and logging
3933 $file_path = Initialize_output();
3935 # Check login access or divert to login procedure
3936 $Use_Login = Log_In_Access();
3937 $file_path = $Use_Login if $Use_Login;
3939 # Record which files are still open (to avoid endless recursions)
3940 my @OpenFiles = ();
3942 # Record whether the default HTML ContentType has already been printed
3943 # but only if the SERVER uses HTTP or some other protocol that might interpret
3944 # a content MIME type.
3946 $SupressContentType = !("$ENV{'SERVER_PROTOCOL'}" =~ /($ContentTypeServerProtocols)/i);
3948 # Process the specified file
3949 ProcessFile($file_path) if $file_path ne $SS_PUB;
3951 # Cleanup all open external (foreign) interpreters
3952 CloseAllForeignScripts();
3955 "" # SUCCESS
3958 # Make a single call to handle an (empty) request
3959 Handle_Request();
3962 # END OF PACKAGE MAIN
3965 ####################################################################################
3967 # The CGIEXECUTE PACKAGE
3969 ####################################################################################
3971 # Isolate the evaluation of directives as PERL code from the rest of the program.
3972 # Remember that each package has its own name space.
3973 # Note that only the FIRST argument of execute->evaluate is actually evaluated,
3974 # all other arguments are accessible inside the first argument as $_[0] to $_[$#_].
3976 package CGIexecute;
3978 sub evaluate
3980 my $self = shift;
3981 my $directive = shift;
3982 $directive = eval($directive);
3983 warn $@ if $@; # Write an error message to STDERR
3984 $directive; # Return value of directive
3988 # defineCGIexecuteVariable($name [, $value]) -> 0/1
3990 # Define and intialize variables inside CGIexecute
3991 # Does no sanity checking, for internal use only
3993 sub defineCGIexecuteVariable # ($name [, $value]) -> 0/1
3995 my $name = shift || return 0; # The Name
3996 my $value = shift || ""; # The value
3998 ${$name} = $value;
4000 return 1;
4003 # defineCGIvariable($name [, $default]) -> 0/1
4005 # Define and intialize CGI variables
4006 # Tries (in order) $ENV{$name}, the Query string and the
4007 # default value.
4008 # Removes all '-quotes etc.
4010 sub defineCGIvariable # ($name [, $default]) -> 0/1
4012 my $name = shift || return 0; # The Name
4013 my $default = shift || ""; # The default value
4015 # Remove \-quoted characters
4016 $default =~ s/\\(.)/$1/g;
4017 # Store default values
4018 $::default_values{$name} = $default if $default;
4020 # Process variables
4021 my $temp = undef;
4022 # If there is a user supplied value, it replaces the
4023 # default value.
4025 # Environment values have precedence
4026 if(exists($ENV{$name}))
4028 $temp = $ENV{$name};
4030 # Get name and its value from the query string
4031 elsif($ENV{QUERY_STRING} =~ /$name/) # $name is in the query string
4033 $temp = ::YOUR_CGIPARSE($name);
4035 # Defined values must exist for security
4036 elsif(!exists($::default_values{$name}))
4038 $::default_values{$name} = undef;
4041 # SECURITY, do not allow '- and `-quotes in
4042 # client values.
4043 # Remove all existing '-quotes
4044 $temp =~ s/([\r\f]+\n)/\n/g; # Only \n is allowed
4045 $temp =~ s/[\']/&#8217;/igs; # Remove all single quotes
4046 $temp =~ s/[\`]/&#8216;/igs; # Remove all backtick quotes
4047 # If $temp is empty, use the default value (if it exists)
4048 unless($temp =~ /\S/ || length($temp) > 0) # I.e., $temp is empty
4050 $temp = $::default_values{$name};
4051 # Remove all existing '-quotes
4052 $temp =~ s/([\r\f]+\n)/\n/g; # Only \n is allowed
4053 $temp =~ s/[\']/&#8217;/igs; # Remove all single quotes
4054 $temp =~ s/[\`]/&#8216;/igs; # Remove all backtick quotes
4056 else # Store current CGI values and remove defaults
4058 $::default_values{$name} = $temp;
4060 # Define the CGI variable and its value (in the execute package)
4061 ${$name} = $temp;
4063 # return SUCCES
4064 return 1;
4067 sub defineCGIvariableList # ($name [, $default]) -> 0/1)
4069 my $name = shift || return 0; # The Name
4070 my $default = shift || ""; # The default value
4072 # Defined values must exist for security
4073 if(!exists($::default_values{$name}))
4075 $::default_values{$name} = $default;
4078 my @temp = ();
4081 # For security:
4082 # Environment values have precedence
4083 if(exists($ENV{$name}))
4085 push(@temp, $ENV{$name});
4087 # Get name and its values from the query string
4088 if($ENV{QUERY_STRING} =~ /$name/) # $name is in the query string
4090 push(@temp, ::YOUR_CGIPARSE($name, 1)); # Extract LIST
4092 else
4094 push(@temp, $::default_values{$name});
4098 # SECURITY, do not allow '- and `-quotes in
4099 # client values.
4100 # Remove all existing '-quotes
4101 @temp = map {s/([\r\f]+\n)/\n/g; $_} @temp; # Only \n is allowed
4102 @temp = map {s/[\']/&#8217;/igs; $_} @temp; # Remove all single quotes
4103 @temp = map {s/[\`]/&#8216;/igs; $_} @temp; # Remove all backtick quotes
4105 # Store current CGI values and remove defaults
4106 $::default_values{$name} = $temp[0];
4108 # Define the CGI variable and its value (in the execute package)
4109 @{$name} = @temp;
4111 # return SUCCES
4112 return 1;
4115 sub defineCGIvariableHash # ($name [, $default]) -> 0/1) Note: '$name{""} = $default';
4117 my $name = shift || return 0; # The Name
4118 my $default = shift || ""; # The default value
4120 # Defined values must exist for security
4121 if(!exists($::default_values{$name}))
4123 $::default_values{$name} = $default;
4126 my %temp = ();
4129 # For security:
4130 # Environment values have precedence
4131 if(exists($ENV{$name}))
4133 $temp{""} = $ENV{$name};
4135 # Get name and its values from the query string
4136 if($ENV{QUERY_STRING} =~ /$name/) # $name is in the query string
4138 %temp = ::YOUR_CGIPARSE($name, -1); # Extract HASH table
4140 elsif($::default_values{$name} ne "")
4142 $temp{""} = $::default_values{$name};
4146 # SECURITY, do not allow '- and `-quotes in
4147 # client values.
4148 # Remove all existing '-quotes
4149 my $Key;
4150 foreach $Key (keys(%temp))
4152 $temp{$Key} =~ s/([\r\f]+\n)/\n/g; # Only \n is allowed
4153 $temp{$Key} =~ s/[\']/&#8217;/igs; # Remove all single quotes
4154 $temp{$Key} =~ s/[\`]/&#8216;/igs; # Remove all backtick quotes
4157 # Store current CGI values and remove defaults
4158 $::default_values{$name} = $temp{""};
4160 # Define the CGI variable and its value (in the execute package)
4161 %{$name} = ();
4162 my $tempKey;
4163 foreach $tempKey (keys(%temp))
4165 ${$name}{$tempKey} = $temp{$tempKey};
4168 # return SUCCES
4169 return 1;
4173 # SAFEqx('CommandString')
4175 # A special function that is a safe alternative to backtick quotes (and qx//)
4176 # with client-supplied CGI values. All CGI variables are surrounded by
4177 # single ''-quotes (except between existing \'\'-quotes, don't try to be
4178 # too smart). All variables are then interpolated. Simple (@) lists are
4179 # expanded with join(' ', @List), and simple (%) hash tables expanded
4180 # as a list of "key=value" pairs. Complex variables, e.g., @$var, are
4181 # evaluated in a scalar context (e.g., as scalar(@$var)). All occurrences of
4182 # $@% that should NOT be interpolated must be preceeded by a "\".
4183 # If the first line of the String starts with "#! interpreter", the
4184 # remainder of the string is piped into interpreter (after interpolation), i.e.,
4185 # open(INTERPRETER, "|interpreter");print INTERPRETER remainder;
4186 # just like in UNIX. There are some problems with quotes. Be carefull in
4187 # using them. You do not have access to the output of any piped (#!)
4188 # process! If you want such access, execute
4189 # <SCRIPT TYPE="text/osshell">echo "script"|interpreter</SCRIPT> or
4190 # <SCRIPT TYPE="text/ssperl">$resultvar = SAFEqx('echo "script"|interpreter');
4191 # </SCRIPT>.
4193 # SAFEqx ONLY WORKS WHEN THE STRING ITSELF IS SURROUNDED BY SINGLE QUOTES
4194 # (SO THAT IT IS NOT INTERPOLATED BEFORE IT CAN BE PROTECTED)
4195 sub SAFEqx # ('String') -> result of executing qx/"String"/
4197 my $CommandString = shift;
4198 my $NewCommandString = "";
4200 # Only interpolate when required (check the On/Off switch)
4201 unless($CGIscriptor::NoShellScriptInterpolation)
4204 # Handle existing single quotes around CGI values
4205 while($CommandString =~ /\'[^\']+\'/s)
4207 my $CurrentQuotedString = $&;
4208 $NewCommandString .= $`;
4209 $CommandString = $'; # The remaining string
4210 # Interpolate CGI variables between quotes
4211 # (e.g., '$CGIscriptorResults[-1]')
4212 $CurrentQuotedString =~
4213 s/(^|[^\\])([\$\@])((\w*)([\{\[][\$\@\%]?[\:\w\-]+[\}\]])*)/if(exists($main::default_values{$4})){
4214 "$1".eval("$2$3")}else{"$&"}/egs;
4216 # Combine result with previous result
4217 $NewCommandString .= $CurrentQuotedString;
4219 $CommandString = $NewCommandString.$CommandString;
4221 # Select known CGI variables and surround them with single quotes,
4222 # then interpolate all variables
4223 $CommandString =~
4224 s/(^|[^\\])([\$\@\%]+)((\w*)([\{\[][\w\:\$\"\-]+[\}\]])*)/
4225 if($2 eq '$' && exists($main::default_values{$4}))
4226 {"$1\'".eval("\$$3")."\'";}
4227 elsif($2 eq '@'){$1.join(' ', @{"$3"});}
4228 elsif($2 eq '%'){my $t=$1;map {$t.=" $_=".${"$3"}{$_}}
4229 keys(%{"$3"});$t}
4230 else{$1.eval("${2}$3");
4231 }/egs;
4233 # Remove backslashed [$@%]
4234 $CommandString =~ s/\\([\$\@\%])/$1/gs;
4237 # Debugging
4238 # return $CommandString;
4240 # Handle UNIX style "#! shell command\n" constructs as
4241 # a pipe into the shell command. The output cannot be tapped.
4242 my $ReturnValue = "";
4243 if($CommandString =~ /^\s*\#\!([^\f\n\r]+)[\f\n\r]/is)
4245 my $ShellScripts = $';
4246 my $ShellCommand = $1;
4247 open(INTERPRETER, "|$ShellCommand") || dieHandler(29, "\'$ShellCommand\' PIPE not opened: &!\n");
4248 select(INTERPRETER);$| = 1;
4249 print INTERPRETER $ShellScripts;
4250 close(INTERPRETER);
4251 select(STDOUT);$| = 1;
4253 # Shell scripts which are redirected to an existing named pipe.
4254 # The output cannot be tapped.
4255 elsif($CGIscriptor::ShellScriptPIPE)
4257 CGIscriptor::printSAFEqxPIPE($CommandString);
4259 else # Plain ``-backtick execution
4261 # Execute the commands
4262 $ReturnValue = qx/$CommandString/;
4264 return $ReturnValue;
4267 ####################################################################################
4269 # The CGIscriptor PACKAGE
4271 ####################################################################################
4273 # Isolate the evaluation of CGIscriptor functions, i.e., those prefixed with
4274 # "CGIscriptor::"
4276 package CGIscriptor;
4279 # The Interpolation On/Off switch
4280 my $NoShellScriptInterpolation = undef;
4281 # The ShellScript redirection pipe
4282 my $ShellScriptPIPE = undef;
4284 # Open a named PIPE for SAFEqx to receive ALL shell scripts
4285 sub RedirectShellScript # ('CommandString')
4287 my $CommandString = shift || undef;
4289 if($CommandString)
4291 $ShellScriptPIPE = "ShellScriptNamedPipe";
4292 open($ShellScriptPIPE, "|$CommandString")
4293 || main::dieHandler(30, "\'|$CommandString\' PIPE open failed: $!\n");
4295 else
4297 close($ShellScriptPIPE);
4298 $ShellScriptPIPE = undef;
4300 return $ShellScriptPIPE;
4303 # Print to redirected shell script pipe
4304 sub printSAFEqxPIPE # ("String") -> print return value
4306 my $String = shift || undef;
4308 select($ShellScriptPIPE); $| = 1;
4309 my $returnvalue = print $ShellScriptPIPE ($String);
4310 select(STDOUT); $| = 1;
4312 return $returnvalue;
4315 # a pointer to CGIexecute::SAFEqx
4316 sub SAFEqx # ('String') -> result of qx/"String"/
4318 my $CommandString = shift;
4319 return CGIexecute::SAFEqx($CommandString);
4323 # a pointer to CGIexecute::defineCGIvariable
4324 sub defineCGIvariable # ($name[, $default]) ->0/1
4326 my $name = shift;
4327 my $default = shift;
4328 return CGIexecute::defineCGIvariable($name, $default);
4332 # Decode URL encoded arguments
4333 sub URLdecode # (URL encoded input) -> string
4335 my $output = "";
4336 my $char;
4337 my $Value;
4338 foreach $Value (@_)
4340 my $EncodedValue = $Value; # Do not change the loop variable
4341 # Convert all "+" to " "
4342 $EncodedValue =~ s/\+/ /g;
4343 # Convert all hexadecimal codes (%FF) to their byte values
4344 while($EncodedValue =~ /\%([0-9A-F]{2})/i)
4346 $output .= $`.chr(hex($1));
4347 $EncodedValue = $';
4349 $output .= $EncodedValue; # The remaining part of $Value
4351 $output;
4354 # Encode arguments as URL codes.
4355 sub URLencode # (input) -> URL encoded string
4357 my $output = "";
4358 my $char;
4359 my $Value;
4360 foreach $Value (@_)
4362 my @CharList = split('', $Value);
4363 foreach $char (@CharList)
4365 if($char =~ /\s/)
4366 { $output .= "+";}
4367 elsif($char =~ /\w\-/)
4368 { $output .= $char;}
4369 else
4371 $output .= uc(sprintf("%%%2.2x", ord($char)));
4375 $output;
4378 # Extract the value of a CGI variable from the URL-encoded $string
4379 # Also extracts the data blocks from a multipart request. Does NOT
4380 # decode the multipart blocks
4381 sub CGIparseValue # (ValueName [, URL_encoded_QueryString [, \$QueryReturnReference]]) -> Decoded value
4383 my $ValueName = shift;
4384 my $QueryString = shift || $main::ENV{'QUERY_STRING'};
4385 my $ReturnReference = shift || undef;
4386 my $output = "";
4388 if($QueryString =~ /(^|\&)$ValueName\=([^\&]*)(\&|$)/)
4390 $output = URLdecode($2);
4391 $$ReturnReference = $' if ref($ReturnReference);
4393 # Get multipart POST or PUT methods
4394 elsif($main::ENV{'CONTENT_TYPE'} =~ m@(multipart/([\w\-]+)\;\s+boundary\=([\S]+))@i)
4396 my $MultipartType = $2;
4397 my $BoundaryString = $3;
4398 # Remove the boundary-string
4399 my $temp = $QueryString;
4400 $temp =~ /^\Q--$BoundaryString\E/m;
4401 $temp = $';
4403 # Identify the newline character(s), this is the first character in $temp
4404 my $NewLine = "\r\n"; # Actually, this IS the correct one
4405 unless($temp =~ /^(\-\-|\r\n)/) # However, you never realy can be sure
4407 # Is this correct??? I have to check.
4408 $NewLine = "\r\n" if $temp =~ /^(\r\n)/; # Double (CRLF, the correct one)
4409 $NewLine = "\n\r" if $temp =~ /^(\n\r)/; # Double
4410 $NewLine = "\n" if $temp =~ /^([\n])/; # Single Line Feed
4411 $NewLine = "\r" if $temp =~ /^([\r])/; # Single Return
4414 # search through all data blocks
4415 while($temp =~ /^\Q--$BoundaryString\E/m)
4417 my $DataBlock = $`;
4418 $temp = $';
4419 # Get the empty line after the header
4420 $DataBlock =~ /$NewLine$NewLine/;
4421 $Header = $`;
4422 $output = $';
4423 my $Header = $`;
4424 $output = $';
4426 # Remove newlines from the header
4427 $Header =~ s/$NewLine/ /g;
4429 # Look whether this block is the one you are looking for
4430 # Require the quotes!
4431 if($Header =~ /name\s*=\s*[\"\']$ValueName[\"\']/m)
4433 my $i;
4434 for($i=length($NewLine); $i; --$i)
4436 chop($output);
4438 # OK, get out
4439 last;
4441 # reinitialize the output
4442 $output = "";
4444 $$ReturnReference = $temp if ref($ReturnReference);
4446 elsif($QueryString !~ /(^|\&)$ValueName\=/) # The value simply isn't there
4448 return undef;
4449 $$ReturnReference = undef if ref($ReturnReference);
4451 else
4453 print "ERROR: $ValueName $main::ENV{'CONTENT_TYPE'}\n";
4455 return $output;
4459 # Get a list of values for the same ValueName. Uses CGIparseValue
4461 sub CGIparseValueList # (ValueName [, URL_encoded_QueryString]) -> List of decoded values
4463 my $ValueName = shift;
4464 my $QueryString = shift || $main::ENV{'QUERY_STRING'};
4465 my @output = ();
4466 my $RestQueryString;
4467 my $Value;
4468 while($QueryString &&
4469 (($Value = CGIparseValue($ValueName, $QueryString, \$RestQueryString))
4470 || defined($Value)))
4472 push(@output, $Value);
4473 $QueryString = $RestQueryString; # QueryString is consumed!
4475 # ready, return list with values
4476 return @output;
4479 sub CGIparseValueHash # (ValueName [, URL_encoded_QueryString]) -> Hash table of decoded values
4481 my $ValueName = shift;
4482 my $QueryString = shift || $main::ENV{'QUERY_STRING'};
4483 my $RestQueryString;
4484 my %output = ();
4485 while($QueryString && $QueryString =~ /(^|\&)$ValueName([\w]*)\=/)
4487 my $Key = $2;
4488 my $Value = CGIparseValue("$ValueName$Key", $QueryString, \$RestQueryString);
4489 $output{$Key} = $Value;
4490 $QueryString = $RestQueryString; # QueryString is consumed!
4492 # ready, return list with values
4493 return %output;
4496 sub CGIparseForm # ([URL_encoded_QueryString]) -> Decoded Form (NO multipart)
4498 my $QueryString = shift || $main::ENV{'QUERY_STRING'};
4499 my $output = "";
4501 $QueryString =~ s/\&/\n/g;
4502 $output = URLdecode($QueryString);
4504 $output;
4507 # Extract the header of a multipart CGI variable from the POST input
4508 sub CGIparseHeader # (ValueName [, URL_encoded_QueryString]) -> Decoded value
4510 my $ValueName = shift;
4511 my $QueryString = shift || $main::ENV{'QUERY_STRING'};
4512 my $output = "";
4514 if($main::ENV{'CONTENT_TYPE'} =~ m@(multipart/([\w\-]+)\;\s+boundary\=([\S]+))@i)
4516 my $MultipartType = $2;
4517 my $BoundaryString = $3;
4518 # Remove the boundary-string
4519 my $temp = $QueryString;
4520 $temp =~ /^\Q--$BoundaryString\E/m;
4521 $temp = $';
4523 # Identify the newline character(s), this is the first character in $temp
4524 my $NewLine = "\r\n"; # Actually, this IS the correct one
4525 unless($temp =~ /^(\-\-|\r\n)/) # However, you never realy can be sure
4527 $NewLine = "\n" if $temp =~ /^([\n])/; # Single Line Feed
4528 $NewLine = "\r" if $temp =~ /^([\r])/; # Single Return
4529 $NewLine = "\r\n" if $temp =~ /^(\r\n)/; # Double (CRLF, the correct one)
4530 $NewLine = "\n\r" if $temp =~ /^(\n\r)/; # Double
4533 # search through all data blocks
4534 while($temp =~ /^\Q--$BoundaryString\E/m)
4536 my $DataBlock = $`;
4537 $temp = $';
4538 # Get the empty line after the header
4539 $DataBlock =~ /$NewLine$NewLine/;
4540 $Header = $`;
4541 my $Header = $`;
4543 # Remove newlines from the header
4544 $Header =~ s/$NewLine/ /g;
4546 # Look whether this block is the one you are looking for
4547 # Require the quotes!
4548 if($Header =~ /name\s*=\s*[\"\']$ValueName[\"\']/m)
4550 $output = $Header;
4551 last;
4553 # reinitialize the output
4554 $output = "";
4557 return $output;
4561 # Checking variables for security (e.g., file names and email addresses)
4562 # File names are tested against the $::FileAllowedChars and $::BlockPathAccess variables
4563 sub CGIsafeFileName # FileName -> FileName or ""
4565 my $FileName = shift || "";
4566 return "" if $FileName =~ m?[^$::FileAllowedChars]?;
4567 return "" if $FileName =~ m!(^|/|\:)[\-\.]!;
4568 return "" if $FileName =~ m@\.\.\Q$::DirectorySeparator\E@; # Higher directory not allowed
4569 return "" if $FileName =~ m@\Q$::DirectorySeparator\E\.\.@; # Higher directory not allowed
4570 return "" if $::BlockPathAccess && $FileName =~ m@$::BlockPathAccess@; # Invisible (blocked) file
4572 return $FileName;
4575 sub CGIsafeEmailAddress # email -> email or ""
4577 my $Email = shift || "";
4578 return "" unless $Email =~ m/^[\w\.\-]+[\@][\w\.\-\:]+$/;
4579 return $Email;
4582 # Get a URL from the web. Needs main::GET_URL($URL) function
4583 # (i.e., curl, snarf, or wget)
4584 sub read_url # ($URL) -> page/file
4586 my $URL = shift || return "";
4588 # Get the commands to read the URL, do NOT add a print command
4589 my $URL_command = main::GET_URL($URL, 1);
4590 # execute the commands, i.e., actually read it
4591 my $URLcontent = CGIexecute->evaluate($URL_command);
4593 # Ready, return the content.
4594 return $URLcontent;
4597 ################################################>>>>>>>>>>Start Remove
4599 # BrowseAllDirs(Directory, indexfile)
4601 # usage:
4602 # <SCRIPT TYPE='text/ssperl'>
4603 # CGIscriptor::BrowseAllDirs('Sounds', 'index.html', '\.wav$')
4604 # </SCRIPT>
4606 # Allows to browse all directories. Stops at '/'. If the directory contains
4607 # an indexfile, eg, index.html, that file will be used instead. Files must match
4608 # the $Pattern, if it is given. Default is
4609 # CGIscriptor::BrowseAllDirs('/', 'index.html', '')
4611 sub BrowseAllDirs # (Directory, indexfile, $Pattern) -> Print HTML code
4613 my $Directory = shift || '/';
4614 my $indexfile = shift || 'index.html';
4615 my $Pattern = shift || '';
4616 $Directory =~ s!/$!!g;
4618 # If the index directory exists, use that one
4619 if(-s "$::CGI_HOME$Directory/$indexfile")
4621 return main::ProcessFile("$::CGI_HOME$Directory/$indexfile");
4624 # No indexfile, continue
4625 my @DirectoryList = glob("$::CGI_HOME$Directory");
4626 $CurrentDirectory = shift(@DirectoryList);
4627 $CurrentDirectory = $' if $CurrentDirectory =~ m@(/\.\./)+@;
4628 $CurrentDirectory =~ s@^$::CGI_HOME@@g;
4629 print "<h1>";
4630 print "$CurrentDirectory" if $CurrentDirectory;
4631 print "</h1>\n";
4633 opendir(BROWSE, "$::CGI_HOME$Directory") || main::dieHandler(31, "$::CGI_HOME$Directory $!");
4634 my @AllFiles = sort grep(!/^([\.]+[^\.]|\~)/, readdir(BROWSE));
4636 # Print directories
4637 my $file;
4638 print "<pre><ul TYPE='NONE'>\n";
4639 foreach $file (@AllFiles)
4641 next unless -d "$::CGI_HOME$Directory/$file";
4642 # Check whether this file should be visible
4643 next if $::BlockPathAccess &&
4644 "$Directory/$file/" =~ m@$::BlockPathAccess@;
4645 print "<dt><a href='$Directory/$file'>$file</a></dt>\n";
4647 print "</ul></pre>\n";
4649 # Print files
4650 print "<pre><ul TYPE='CIRCLE'>\n";
4651 my $TotalSize = 0;
4652 foreach $file (@AllFiles)
4654 next if $file =~ /^\./;
4655 next if -d "$::CGI_HOME$Directory/$file";
4656 next if -l "$::CGI_HOME$Directory/$file";
4657 # Check whether this file should be visible
4658 next if $::BlockPathAccess &&
4659 "$Directory/$file" =~ m@$::BlockPathAccess@;
4661 if(!$Pattern || $file =~ m@$Pattern@)
4663 my $Date = localtime($^T - (-M "$::CGI_HOME$Directory/$file")*3600*24);
4664 my $Size = -s "$::CGI_HOME$Directory/$file";
4665 $Size = sprintf("%6.0F kB", $Size/1024);
4666 my $Type = `file $::CGI_HOME$Directory/$file`;
4667 $Type =~ s@\s*$::CGI_HOME$Directory/$file\s*\:\s*@@ig;
4668 chomp($Type);
4670 print "<li>";
4671 print "<a href='$Directory/$file'>";
4672 printf("%-40s", "$file</a>");
4673 print "\t$Size\t$Date\t$Type";
4674 print "</li>\n";
4677 print "</ul></pre>";
4679 return 1;
4683 ################################################
4685 # BrowseDirs(RootDirectory [, Pattern, Start])
4687 # usage:
4688 # <SCRIPT TYPE='text/ssperl'>
4689 # CGIscriptor::BrowseDirs('Sounds', '\.aifc$', 'Speech', 'DIRECTORY')
4690 # </SCRIPT>
4692 # Allows to browse subdirectories. Start should be relative to the RootDirectory,
4693 # e.g., the full path of the directory 'Speech' is '~/Sounds/Speech'.
4694 # Only files which fit /$Pattern/ and directories are displayed.
4695 # Directories down or up the directory tree are supplied with a
4696 # GET request with the name of the CGI variable in the fourth argument (default
4697 # is 'BROWSEDIRS'). So the correct call for a subdirectory could be:
4698 # CGIscriptor::BrowseDirs('Sounds', '\.aifc$', $DIRECTORY, 'DIRECTORY')
4700 sub BrowseDirs # (RootDirectory [, Pattern, Start, CGIvariable, HTTPserver]) -> Print HTML code
4702 my $RootDirectory = shift; # || return 0;
4703 my $Pattern = shift || '\S';
4704 my $Start = shift || "";
4705 my $CGIvariable = shift || "BROWSEDIRS";
4706 my $HTTPserver = shift || '';
4708 $Start = CGIscriptor::URLdecode($Start); # Sometimes, too much has been encoded
4709 $Start =~ s@//+@/@g;
4710 $Start =~ s@[^/]+/\.\.@@ig;
4711 $Start =~ s@^\.\.@@ig;
4712 $Start =~ s@/\.$@@ig;
4713 $Start =~ s!/+$!!g;
4714 $Start .= "/" if $Start;
4716 my @Directory = glob("$::CGI_HOME/$RootDirectory/$Start");
4717 $CurrentDirectory = shift(@Directory);
4718 $CurrentDirectory = $' if $CurrentDirectory =~ m@(/\.\./)+@;
4719 $CurrentDirectory =~ s@^$::CGI_HOME@@g;
4720 print "<h1>";
4721 print "$CurrentDirectory" if $CurrentDirectory;
4722 print "</h1>\n";
4723 opendir(BROWSE, "$::CGI_HOME/$RootDirectory/$Start") || main::dieHandler(31, "$::CGI_HOME/$RootDirectory/$Start $!");
4724 my @AllFiles = sort grep(!/^([\.]+[^\.]|\~)/, readdir(BROWSE));
4726 # Print directories
4727 my $file;
4728 print "<pre><ul TYPE='NONE'>\n";
4729 foreach $file (@AllFiles)
4731 next unless -d "$::CGI_HOME/$RootDirectory/$Start$file";
4732 # Check whether this file should be visible
4733 next if $::BlockPathAccess &&
4734 "/$RootDirectory/$Start$file/" =~ m@$::BlockPathAccess@;
4736 my $NewURL = $Start ? "$Start$file" : $file;
4737 $NewURL = CGIscriptor::URLencode($NewURL);
4738 print "<dt><a href='";
4739 print "$ENV{SCRIPT_NAME}" if $ENV{SCRIPT_NAME} !~ m@[^\w+\-/]@;
4740 print "$ENV{PATH_INFO}?$CGIvariable=$NewURL'>$file</a></dt>\n";
4742 print "</ul></pre>\n";
4744 # Print files
4745 print "<pre><ul TYPE='CIRCLE'>\n";
4746 my $TotalSize = 0;
4747 foreach $file (@AllFiles)
4749 next if $file =~ /^\./;
4750 next if -d "$::CGI_HOME/$RootDirectory/$Start$file";
4751 next if -l "$::CGI_HOME/$RootDirectory/$Start$file";
4752 # Check whether this file should be visible
4753 next if $::BlockPathAccess &&
4754 "$::CGI_HOME/$RootDirectory/$Start$file" =~ m@$::BlockPathAccess@;
4756 if($file =~ m@$Pattern@)
4758 my $Date = localtime($^T - (-M "$::CGI_HOME/$RootDirectory/$Start$file")*3600*24);
4759 my $Size = -s "$::CGI_HOME/$RootDirectory/$Start$file";
4760 $Size = sprintf("%6.0F kB", $Size/1024);
4761 my $Type = `file $::CGI_HOME/$RootDirectory/$Start$file`;
4762 $Type =~ s@\s*$::CGI_HOME/$RootDirectory/$Start$file\s*\:\s*@@ig;
4763 chomp($Type);
4765 print "<li>";
4766 if($HTTPserver =~ /^\s*[\.\~]\s*$/)
4768 print "<a href='$RootDirectory/$Start$file'>";
4770 elsif($HTTPserver)
4772 print "<a href='$HTTPserver/$RootDirectory/$Start$file'>";
4774 printf("%-40s", "$file</a>") if $HTTPserver;
4775 printf("%-40s", "$file") unless $HTTPserver;
4776 print "\t$Size\t$Date\t$Type";
4777 print "</li>\n";
4780 print "</ul></pre>";
4782 return 1;
4786 # ListDocs(Pattern [,ListType])
4788 # usage:
4789 # <SCRIPT TYPE=text/ssperl>
4790 # CGIscriptor::ListDocs("/*", "dl");
4791 # </SCRIPT>
4793 # This subroutine is very usefull to manage collections of independent
4794 # documents. The resulting list will display the tree-like directory
4795 # structure. If this routine is too slow for online use, you can
4796 # store the result and use a link to that stored file.
4798 # List HTML and Text files with title and first header (HTML)
4799 # or filename and first meaningfull line (general text files).
4800 # The listing starts at the ServerRoot directory. Directories are
4801 # listed recursively.
4803 # You can change the list type (default is dl).
4804 # e.g.,
4805 # <dt><a href=<file.html>>title</a>
4806 # <dd>First Header
4807 # <dt><a href=<file.txt>>file.txt</a>
4808 # <dd>First meaningfull line of text
4810 sub ListDocs # ($Pattern [, prefix]) e.g., ("/Books/*", [, "dl"])
4812 my $Pattern = shift;
4813 $Pattern =~ /\*/;
4814 my $ListType = shift || "dl";
4815 my $Prefix = lc($ListType) eq "dl" ? "dt" : "li";
4816 my $URL_root = "http://$::ENV{'SERVER_NAME'}\:$::ENV{'SERVER_PORT'}";
4817 my @FileList = glob("$::CGI_HOME$Pattern");
4818 my ($FileName, $Path, $Link);
4820 # Print List markers
4821 print "<$ListType>\n";
4823 # Glob all files
4824 File: foreach $FileName (@FileList)
4826 # Check whether this file should be visible
4827 next if $::BlockPathAccess && $FileName =~ m@$::BlockPathAccess@;
4829 # Recursively list files in all directories
4830 if(-d $FileName)
4832 $FileName =~ m@([^/]*)$@;
4833 my $DirName = $1;
4834 print "<$Prefix>$DirName\n";
4835 $Pattern =~ m@([^/]*)$@;
4836 &ListDocs("$`$DirName/$1", $ListType);
4837 next;
4839 # Use textfiles
4840 elsif(-T "$FileName")
4842 open(TextFile, $FileName) || next;
4844 # Ignore all other file types
4845 else
4846 { next;};
4848 # Get file path for link
4849 $FileName =~ /$::CGI_HOME/;
4850 print "<$Prefix><a href=$URL_root$'>";
4851 # Initialize all variables
4852 my $Line = "";
4853 my $TitleFound = 0;
4854 my $Caption = "";
4855 my $Title = "";
4856 # Read file and step through
4857 while(<TextFile>)
4859 chop $_;
4860 $Line = $_;
4861 # HTML files
4862 if($FileName =~ /\.ht[a-zA-Z]*$/i)
4864 # Catch Title
4865 while(!$Title)
4867 if($Line =~ m@<title>([^<]*)</title>@i)
4869 $Title = $1;
4870 $Line = $';
4872 else
4874 $Line .= <TextFile> || goto Print;
4875 chop $Line;
4878 # Catch First Header
4879 while(!$Caption)
4881 if($Line =~ m@</h1>@i)
4883 $Caption = $`;
4884 $Line = $';
4885 $Caption =~ m@<h1>@i;
4886 $Caption = $';
4887 $Line = $`.$Caption.$Line;
4889 else
4891 $Line .= <TextFile> || goto Print;
4892 chop $Line;
4896 # Other text files
4897 else
4899 # Title equals file name
4900 $FileName =~ /([^\/]+)$/;
4901 $Title = $1;
4902 # Catch equals First Meaningfull line
4903 while(!$Caption)
4905 if($Line =~ /[A-Z]/ &&
4906 ($Line =~ /subject|title/i || $Line =~ /^[\w,\.\s\?\:]+$/)
4907 && $Line !~ /Newsgroup/ && $Line !~ /\:\s*$/)
4909 $Line =~ s/\<[^\>]+\>//g;
4910 $Caption = $Line;
4912 else
4914 $Line = <TextFile> || goto Print;
4918 Print: # Print title and subject
4919 print "$Title</a>\n";
4920 print "<dd>$Caption\n" if $ListType eq "dl";
4921 $TitleFound = 0;
4922 $Caption = "";
4923 close TextFile;
4924 next File;
4927 # Print Closing List Marker
4928 print "</$ListType>\n";
4929 ""; # Empty return value
4933 # HTMLdocTree(Pattern [,ListType])
4935 # usage:
4936 # <SCRIPT TYPE=text/ssperl>
4937 # CGIscriptor::HTMLdocTree("/Welcome.html", "dl");
4938 # </SCRIPT>
4940 # The following subroutine is very usefull for checking large document
4941 # trees. Starting from the root (s), it reads all files and prints out
4942 # a nested list of links to all attached files. Non-existing or misplaced
4943 # files are flagged. This is quite a file-i/o intensive routine
4944 # so you would not like it to be accessible to everyone. If you want to
4945 # use the result, save the whole resulting page to disk and use a link
4946 # to this file.
4948 # HTMLdocTree takes an HTML file or file pattern and constructs nested lists
4949 # with links to *local* files (i.e., only links to the local server are
4950 # followed). The list entries are the document titles.
4951 # If the list type is <dl>, the first <H1> header is used too.
4952 # For each file matching the pattern, a list is made recursively of all
4953 # HTML documents that are linked from it and are stored in the same directory
4954 # or a sub-directory. Warnings are given for missing files.
4955 # The listing starts for the ServerRoot directory.
4956 # You can change the default list type <dl> (<dl>, <ul>, <ol>).
4958 %LinkUsed = ();
4960 sub HTMLdocTree # ($Pattern [, listtype])
4961 # e.g., ("/Welcome.html", [, "ul"])
4963 my $Pattern = shift;
4964 my $ListType = shift || "dl";
4965 my $Prefix = lc($ListType) eq "dl" ? "dt" : "li";
4966 my $URL_root = "http://$::ENV{'SERVER_NAME'}\:$::ENV{'SERVER_PORT'}";
4967 my ($Filename, $Path, $Link);
4968 my %LocalLinks = {};
4970 # Read files (glob them for expansion of wildcards)
4971 my @FileList = glob("$::CGI_HOME$Pattern");
4972 foreach $Path (@FileList)
4974 # Get URL_path
4975 $Path =~ /$::CGI_HOME/;
4976 my $URL_path = $';
4977 # Check whether this file should be visible
4978 next if $::BlockPathAccess && $URL_path =~ m@$::BlockPathAccess@;
4980 my $Title = $URL_path;
4981 my $Caption = "";
4982 # Current file should not be used again
4983 ++$LinkUsed{$URL_path};
4984 # Open HTML doc
4985 unless(open(TextFile, $Path))
4987 print "<$Prefix>$Title <blink>(not found)</blink><br>\n";
4988 next;
4990 while(<TextFile>)
4992 chop $_;
4993 $Line = $_;
4994 # Catch Title
4995 while($Line =~ m@<title>@i)
4997 if($Line =~ m@<title>([^<]*)</title>@i)
4999 $Title = $1;
5000 $Line = $';
5002 else
5004 $Line .= <TextFile>;
5005 chop $Line;
5008 # Catch First Header
5009 while(!$Caption && $Line =~ m@<h1>@i)
5011 if($Line =~ m@</h[1-9]>@i)
5013 $Caption = $`;
5014 $Line = $';
5015 $Caption =~ m@<h1>@i;
5016 $Caption = $';
5017 $Line = $`.$Caption.$Line;
5019 else
5021 $Line .= <TextFile>;
5022 chop $Line;
5025 # Catch and print Links
5026 while($Line =~ m@<a href\=([^>]*)>@i)
5028 $Link = $1;
5029 $Line = $';
5030 # Remove quotes
5031 $Link =~ s/\"//g;
5032 # Remove extras
5033 $Link =~ s/[\#\?].*$//g;
5034 # Remove Servername
5035 if($Link =~ m@(http://|^)@i)
5037 $Link = $';
5038 # Only build tree for current server
5039 next unless $Link =~ m@$::ENV{'SERVER_NAME'}|^/@;
5040 # Remove server name and port
5041 $Link =~ s@^[^\/]*@@g;
5043 # Store the current link
5044 next if $LinkUsed{$Link} || $Link eq $URL_path;
5045 ++$LinkUsed{$Link};
5046 ++$LocalLinks{$Link};
5050 close TextFile;
5051 print "<$Prefix>";
5052 print "<a href=http://";
5053 print "$::ENV{'SERVER_NAME'}\:$::ENV{'SERVER_PORT'}$URL_path>";
5054 print "$Title</a>\n";
5055 print "<br>$Caption\n"
5056 if $Caption && $Caption ne $Title && $ListType =~ /dl/i;
5057 print "<$ListType>\n";
5058 foreach $Link (keys(%LocalLinks))
5060 &HTMLdocTree($Link, $ListType);
5062 print "</$ListType>\n";
5066 ###########################<<<<<<<<<<End Remove
5068 # Make require happy
5071 =head1 NAME
5073 CGIscriptor -
5075 =head1 DESCRIPTION
5077 A flexible HTML 4 compliant script/module for CGI-aware
5078 embeded Perl, shell-scripts, and other scripting languages,
5079 executed at the server side.
5081 =head1 README
5083 Executes embeded Perl code in HTML pages with easy
5084 access to CGI variables. Also processes embeded shell
5085 scripts and scripts in any other language with an
5086 interactive interpreter (e.g., in-line Python, Tcl,
5087 Ruby, Awk, Lisp, Xlispstat, Prolog, M4, R, REBOL, Praat,
5088 sh, bash, csh, ksh).
5090 CGIscriptor is very flexible and hides all the specifics
5091 and idiosyncrasies of correct output and CGI coding and naming.
5092 CGIscriptor complies with the W3C HTML 4.0 recommendations.
5094 This Perl program will run on any WWW server that runs
5095 Perl scripts, just add a line like the following to your
5096 srm.conf file (Apache example):
5098 ScriptAlias /SHTML/ /real-path/CGIscriptor.pl/
5100 URL's that refer to http://www.your.address/SHTML/... will
5101 now be handled by CGIscriptor.pl, which can use a private
5102 directory tree (default is the DOCUMENT_ROOT directory tree,
5103 but it can be anywhere).
5105 =head1 PREREQUISITES
5108 =head1 COREQUISITES
5111 =pod OSNAMES
5113 Linux, *BSD, *nix, MS WinXP
5115 =pod SCRIPT CATEGORIES
5117 Servers
5121 =cut