Work on Login and session tickets
[CGIscriptor.git] / CGIscriptor.pl
blobbe6ffb2c34e09a658551ef22f35d6e691a6c4fdf
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 # 22 May 2012 - Added Access control with Session Tickets linked to
64 # IP Address and PATH_INFO.
65 # 21 May 2012 - Corrected the links generated by CGIscriptor::BrowseDirs
66 # Will link to current base URL when the HTTP server is '.' or '~'
67 # 29 Oct 2009 - Adapted David A. Wheeler's suggestion about filenames:
68 # CGIsafeFileName does not accept filenames starting with '-'
69 # (http://www.dwheeler.com/essays/fixing-unix-linux-filenames.html)
70 # 08 Oct 2009 - Some corrections in the README.txt file, eg, new email address
71 # 28 Jan 2005 - Added a file selector to performTranslation.
72 # Changed %TranslationTable to @TranslationTable
73 # and patterns to lists.
74 # 27 Jan 2005 - Added a %TranslationTable with associated
75 # performTranslation(\$text) function to allow
76 # run changes in the web pages. Say, to translate
77 # legacy pages with <%=...%> delimiters to the new
78 # <SCRIPT TYPE=..></SCRIPT> format.
79 # 27 Jan 2005 - Small bug of extra '\n' in output removed from the
80 # Other Languages Code.
81 # 10 May 2004 - Belated upload of latest version (2.3) to CPAN
82 # 07 Oct 2003 - Corrected error '\s' -> '\\s' in rebol scripting
83 # language call
84 # 07 Oct 2003 - Corrected omitted INS tags in <DIV><INS> handling
85 # 20 May 2003 - Added a --help switch to print the manual.
86 # 06 Mar 2003 - Adapted the blurb at the end of the file.
87 # 03 Mar 2003 - Added a user definable dieHandler function to catch all
88 # "die" calls. Also "enhanced" the STDERR printout.
89 # 10 Feb 2003 - Split off the reading of the POST part of a query
90 # from Initialize_output. This was suggested by Gerd Franke
91 # to allow for the catching of the file_path using a
92 # POST based lookup. That is, he needed the POST part
93 # to change the file_path.
94 # 03 Feb 2003 - %{$name}; => %{$name} = (); in defineCGIvariableHash.
95 # 03 Feb 2003 - \1 better written as $1 in
96 # $directive =~ s/[^\\\$]\#[^\n\f\r]*([\n\f\r])/$1/g
97 # 29 Jan 2003 - This makes "CLASS="ssperl" CSS-compatible Gerd Franke
98 # added:
99 # $ServerScriptContentClass = "ssperl";
100 # changed in ProcessFile():
101 # unless(($CurrentContentType =~
102 # 28 Jan 2003 - Added 'INS' Tag! Gerd Franke
103 # 20 Dec 2002 - Removed useless $Directoryseparator variable.
104 # Update comments and documentation.
105 # 18 Dec 2002 - Corrected bug in Accept/Reject processing.
106 # Files didn't work.
107 # 24 Jul 2002 - Added .htaccess documentation (from Gerd Franke)
108 # Also added a note that RawFilePattern can be a
109 # complete file name.
110 # 19 Mar 2002 - Added SRC pseudo-files PREFIX and POSTFIX. These
111 # switch to prepending or to appending the content
112 # of the SRC attribute. Default is prefixing. You
113 # can add as many of these switches as you like.
114 # 13 Mar 2002 - Do not search for tag content if a tag closes with
115 # />, i.e., <DIV ... /> will be handled the XML/XHTML way.
116 # 25 Jan 2002 - Added 'curl' and 'snarf' to SRC attribute URL handling
117 # (replaces wget).
118 # 25 Jan 2002 - Found a bug in SAFEqx, now executes qx() in a scalar context
119 # (i.o. a list context). This is necessary for binary results.
120 # 24 Jan 2002 - Disambiguated -T $SRCfile to -T "$SRCfile" (and -e) and
121 # changed the order of if/elsif to allow removing these
122 # conditions in systems with broken -T functions.
123 # (I also removed a spurious ')' bracket)
124 # 17 Jan 2002 - Changed DIV tag SRC from <SOURCE> to sysread(SOURCE,...)
125 # to support binary files.
126 # 17 Jan 2002 - Removed WhiteSpace from $FileAllowedCharacters.
127 # 17 Jan 2002 - Allow "file://" prefix in SRC attribute. It is simply
128 # stipped from the path.
129 # 15 Jan 2002 - Version 2.2
130 # 15 Jan 2002 - Debugged and completed URL support (including
131 # CGIscriptor::read_url() function)
132 # 07 Jan 2002 - Added automatic (magic) URL support to the SRC attribute
133 # with the main::GET_URL function. Uses wget -O underlying.
134 # 04 Jan 2002 - Added initialization of $NewDirective in InsertForeignScript
135 # (i.e., my $NewDirective = "";) to clear old output
136 # (this was a realy anoying bug).
137 # 03 Jan 2002 - Added a <DIV CLASS='text/ssperl' ID='varname'></DIV>
138 # tags that assign the body text as-is (literally)
139 # to $varname. Allows standard HTML-tools to handle
140 # Cascading Style Sheet templates. This implements a
141 # design by Gerd Franke (franke@roo.de).
142 # 03 Jan 2002 - I finaly gave in and allowed SRC files to expand ~/.
143 # 12 Oct 2001 - Normalized spelling of "CGIsafFileName" in documentation.
144 # 09 Oct 2001 - Added $ENV{'CGI_BINARY_FILE'} to log files to
145 # detect unwanted indexing of TAR files by webcrawlers.
146 # 10 Sep 2001 - Added $YOUR_SCRIPTS directory to @INC for 'require'.
147 # 22 Aug 2001 - Added .txt (Content-type: text/plain) as a default
148 # processed file type. Was processed via BinaryMapFile.
149 # 31 May 2001 - Changed =~ inside CGIsafeEmailAddress that was buggy.
150 # 29 May 2001 - Updated $CGI_HOME to point to $ENV{DOCUMENT_ROOT} io
151 # the root of PATH_TRANSLATED. DOCUMENT_ROOT can now
152 # be manipulated to achieve a "Sub Root".
153 # NOTE: you can have $YOUR_HTML_FILES != DOCUMENT_ROOT
154 # 28 May 2001 - Changed CGIscriptor::BrowsDirs function for security
155 # and debugging (it now works).
156 # 21 May 2001 - defineCGIvariableHash will ADD values to existing
157 # hashes,instead of replacing existing hashes.
158 # 17 May 2001 - Interjected a '&' when pasting POST to GET data
159 # 24 Apr 2001 - Blocked direct requests for BinaryMapFile.
160 # 16 Aug 2000 - Added hash table extraction for CGI parameters with
161 # CGIparseValueHash (used with structured parameters).
162 # Use: CGI='%<CGI-partial-name>' (fill in your name in <>)
163 # Will collect all <CGI-partial-name><key>=value pairs in
164 # $<CGI-partial-name>{<key>} = value;
165 # 16 Aug 2000 - Adapted SAFEqx to protect @PARAMETER values.
166 # 09 Aug 2000 - Added support for non-filesystem input by way of
167 # the CGI_FILE_CONTENTS and CGI_DATA_ACCESS_CODE
168 # environment variables.
169 # 26 Jul 2000 - On the command-line, file-path '-' indicates STDIN.
170 # This allows CGIscriptor to be used in pipes.
171 # Default, $BLOCK_STDIN_HTTP_REQUEST=1 will block this
172 # in an HTTP request (i.e., in a web server).
173 # 26 Jul 2000 - Blocked 'Content-type: text/html' if the SERVER_PROTOCOL
174 # is not HTTP or another protocol. Changed the default
175 # source directory to DOCUMENT_ROOT (i.o. the incorrect
176 # SERVER_ROOT).
177 # 24 Jul 2000 - -slim Command-line argument added to remove all
178 # comments, security, etc.. Updated documentation.
179 # 05 Jul 2000 - Added IF and UNLESS attributes to make the
180 # execution of all <META> and <SCRIPT> code
181 # conditional.
182 # 05 Jul 2000 - Rewrote and isolated the code for extracting
183 # quoted items from CGI and SRC attributes.
184 # Now all attributes expect the same set of
185 # quotes: '', "", ``, (), {}, [] and the same
186 # preceded by a \, e.g., "\((aap)\)" will be
187 # extracted as "(aap)".
188 # 17 Jun 2000 - Construct @ARGV list directly in CGIexecute
189 # name-space (i.o. by evaluation) from
190 # CGI attributes to prevent interference with
191 # the processing for non perl scripts.
192 # Changed CGIparseValueList to prevent runaway
193 # loops.
194 # 16 Jun 2000 - Added a direct (interpolated) display mode
195 # (text/ssdisplay) and a user log mode
196 # (text/sslogfile).
197 # 06 Jun 2000 - Replace "print $Result" with a syswrite loop to
198 # allow large string output.
199 # 02 Jun 2000 - Corrected shrubCGIparameter($CGI_VALUE) to realy
200 # remove all control characters. Changed Interpreter
201 # initialization to shrub interpolated CGI parameters.
202 # Added 'text/ssmailto' interpreter script.
203 # 22 May 2000 - Changed some of the comments
204 # 09 May 2000 - Added list extraction for CGI parameters with
205 # CGIparseValueList (used with multiple selections).
206 # Use: CGI='@<CGI-parameter>' (fill in your name in <>)
207 # 09 May 2000 - Added a 'Not Present' condition to CGIparseValue.
208 # 27 Apr 2000 - Updated documentation to reflect changes.
209 # 27 Apr 2000 - SRC attribute "cleaned". Supported for external
210 # interpreters.
211 # 27 Apr 2000 - CGI attribute can be used in <SCRIPT> tag.
212 # 27 Apr 2000 - Gprolog, M4 support added.
213 # 26 Apr 2000 - Lisp (rep) support added.
214 # 20 Apr 2000 - Use of external interpreters now functional.
215 # 20 Apr 2000 - Removed bug from extracting Content types (RegExp)
216 # 10 Mar 2000 - Qualified unconditional removal of '#' that preclude
217 # the use of $#foo, i.e., I changed
218 # s/[^\\]\#[^\n\f\r]*([\n\f\r])/\1/g
219 # to
220 # s/[^\\\$]\#[^\n\f\r]*([\n\f\r])/\1/g
221 # 03 Mar 2000 - Added a '$BlockPathAccess' variable to "hide"
222 # things like, e.g., CVS information in CVS subtrees
223 # 10 Feb 2000 - URLencode/URLdecode have been made case-insensitive
224 # 10 Feb 2000 - Added a BrowseDirs function (CGIscriptor package)
225 # 01 Feb 2000 - A BinaryMapFile in the ~/ directory has precedence
226 # over a "burried" BinaryMapFile.
227 # 04 Oct 1999 - Added two functions to check file names and email addresses
228 # (CGIscriptor::CGIsafeFileName and
229 # CGIscriptor::CGIsafeEmailAddress)
230 # 28 Sept 1999 - Corrected bug in sysread call for reading POST method
231 # to allow LONG posts.
232 # 28 Sept 1999 - Changed CGIparseValue to handle multipart/form-data.
233 # 29 July 1999 - Refer to BinaryMapFile from CGIscriptor directory, if
234 # this directory exists.
235 # 07 June 1999 - Limit file-pattern matching to LAST extension
236 # 04 June 1999 - Default text/html content type is printed only once.
237 # 18 May 1999 - Bug in replacement of ~/ and ./ removed.
238 # (Rob van Son, R.J.J.H.vanSon@uva.nl)
239 # 15 May 1999 - Changed the name of the execute package to CGIexecute.
240 # Changed the processing of the Accept and Reject file.
241 # Added a full expression evaluation to Access Control.
242 # (Rob van Son, R.J.J.H.vanSon@uva.nl)
243 # 27 Apr 1999 - Brought CGIscriptor under the GNU GPL. Made CGIscriptor
244 # Version 1.1 a module that can be called with 'require "CGIscriptor.pl"'.
245 # Requests are serviced by "Handle_Request()". CGIscriptor
246 # can still be called as a isolated perl script and a shell
247 # command.
248 # Changed the "factory default setting" so that it will run
249 # from the DOCUMENT_ROOT directory.
250 # (Rob van Son, R.J.J.H.vanSon@uva.nl)
251 # 29 Mar 1999 - Remove second debugging STDERR switch. Moved most code
252 # to subroutines to change CGIscriptor into a module.
253 # Added mapping to process unsupported file types (e.g., binary
254 # pictures). See $BinaryMapFile.
255 # (Rob van Son, R.J.J.H.vanSon@uva.nl)
256 # 24 Sept 1998 - Changed text of license (Rob van Son, R.J.J.H.vanSon@uva.nl)
257 # Removed a double setting of filepatterns and maximum query
258 # size. Changed email address. Removed some typos from the
259 # comments.
260 # 02 June 1998 - Bug fixed in URLdecode. Changing the foreach loop variable
261 # caused quiting CGIscriptor.(Rob van Son, R.J.J.H.vanSon@uva.nl)
262 # 02 June 1998 - $SS_PUB and $SS_SCRIPT inserted an extra /, removed.
263 # (Rob van Son, R.J.J.H.vanSon@uva.nl)
266 # Known Bugs:
268 # 23 Mar 2000
269 # It is not possible to use operators or variables to construct variable names,
270 # e.g., $bar = \@{$foo}; won't work. However, eval('$bar = \@{'.$foo.'};');
271 # will indeed work. If someone could tell me why, I would be obliged.
274 ############################################################################
276 # OBLIGATORY USER CONFIGURATION
278 # Configure the directories where all user files can be found (this
279 # is the equivalent of the server root directory of a WWW-server).
280 # These directories can be located ANYWHERE. For security reasons, it is
281 # better to locate them outside the WWW-tree of your HTTP server, unless
282 # CGIscripter handles ALL requests.
284 # For convenience, the defaults are set to the root of the WWW server.
285 # However, this might not be safe!
287 # ~/ text files
288 # $YOUR_HTML_FILES = "/usr/pub/WWW/SHTML"; # or SS_PUB as environment var
289 # (patch to use the parent directory of CGIscriptor as document root, should be removed)
290 if($ENV{'SCRIPT_FILENAME'}) # && $ENV{'SCRIPT_FILENAME'} !~ /\Q$ENV{'DOCUMENT_ROOT'}\E/)
292 $ENV{'DOCUMENT_ROOT'} = $ENV{'SCRIPT_FILENAME'};
293 $ENV{'DOCUMENT_ROOT'} =~ s@/CGIscriptor.*$@@ig;
296 # Just enter your own directory path here
297 $YOUR_HTML_FILES = $ENV{'DOCUMENT_ROOT'}; # default is the DOCUMENT_ROOT
299 # ./ script files (recommended to be different from the previous)
300 # $YOUR_SCRIPTS = "/usr/pub/WWW/scripts"; # or SS_SCRIPT as environment var
301 $YOUR_SCRIPTS = $YOUR_HTML_FILES; # This might be a SECURITY RISK
303 # End of obligatory user configuration
304 # (note: there is more non-essential user configuration below)
306 ############################################################################
308 # OPTIONAL USER CONFIGURATION (all values are used CASE INSENSITIVE)
310 # Script content-types: TYPE="Content-type" (user defined mime-type)
311 $ServerScriptContentType = "text/ssperl"; # Server Side Perl scripts
312 # CSS require a simple class
313 $ServerScriptContentClass = $ServerScriptContentType =~ m!/! ?
314 $' : "ssperl"; # Server Side Perl CSS classes
316 $ShellScriptContentType = "text/osshell"; # OS shell scripts
317 # # (Server Side perl ``-execution)
319 # Accessible file patterns, block any request that doesn't match.
320 # Matches any file with the extension .(s)htm(l), .txt, or .xmr
321 # (\. is used in regexp)
322 # Note: die unless $PATH_INFO =~ m@($FilePattern)$@is;
323 $FilePattern = ".shtml|.htm|.html|.xml|.xmr|.txt";
325 # The table with the content type MIME types
326 # (allows to differentiate MIME types, if needed)
327 %ContentTypeTable =
329 '.html' => 'text/html',
330 '.shtml' => 'text/html',
331 '.htm' => 'text/html',
332 '.xml' => 'text/xml',
333 '.txt' => 'text/plain'
337 # File pattern post-processing
338 $FilePattern =~ s/([@.])/\\$1/g; # Convert . and @ to \. and \@
340 # SHAsum command needed for Authorization and Login
341 # (note, these have to be accessible in the HTML pages, ie, the CGIexecute environment)
342 $CGIexecute::SHASUMcmd = "shasum-5.12 -b";
343 $CGIexecute::RANDOMHASHcmd = 'dd count=1 if=/dev/urandom 2>/dev/null |'.$CGIexecute::SHASUMcmd.' |cut -f 1 -d" "';
345 # File patterns of files which require a login.
346 %LoginRequiredPatterns = (
347 '^/Private/' => "Private/.Sessions\tPrivate/.Passwords\t/Private/Login.html"
349 # Session Ticket Directory: .Session/
350 # Password Directory: .Password/
351 # Login page: Login.html
353 # Raw files must contain their own Content-type (xmr <- x-multipart-replace).
354 # THIS IS A SUBSET OF THE FILES DEFINED IN $FilePattern
355 $RawFilePattern = ".xmr";
356 # (In principle, this could contain a full file specification, e.g.,
357 # ".xmr|relocated.html")
359 # Raw File pattern post-processing
360 $RawFilePattern =~ s/([@.])/\\$1/g; # Convert . and @ to \. and \@
362 # Server protocols for which "Content-type: text/html\n\n" should be printed
363 # (you should not bother with these, except for HTTP, they are mostly imaginary)
364 $ContentTypeServerProtocols = 'HTTP|MAIL|MIME';
366 # Block access to all (sub-) paths and directories that match the
367 # following (URL) path (is used as:
368 # 'die if $BlockPathAccess && $ENV{'PATH_INFO'} =~ m@$BlockPathAccess@;' )
369 $BlockPathAccess = '/(CVS|\.git)/'; # Protect CVS and .git information
371 # All (blocked) other file-types can be mapped to a single "binary-file"
372 # processor (a kind of pseudo-file path). This can either be an error
373 # message (e.g., "illegal file") or contain a script that serves binary
374 # files.
375 # Note: the real file path wil be stored in $ENV{CGI_BINARY_FILE}.
376 $BinaryMapFile = "/BinaryMapFile.xmr";
377 # Allow for the addition of a CGIscriptor directory
378 # Note that a BinaryMapFile in the root "~/" directory has precedence
379 $BinaryMapFile = "/CGIscriptor".$BinaryMapFile
380 if ! -e "$YOUR_HTML_FILES".$BinaryMapFile
381 && -e "$YOUR_HTML_FILES/CGIscriptor".$BinaryMapFile;
384 # List of all characters that are allowed in file names and paths.
385 # All requests containing illegal characters are blocked. This
386 # blocks most tricks (e.g., adding "\000", "\n", or other control
387 # characters, also blocks URI's using %FF)
388 # THIS IS A SECURITY FEATURE
389 # (this is also used to parse filenames in SRC= features, note the
390 # '-quotes, they are essential)
391 $FileAllowedChars = '\w\.\~\/\:\*\?\-'; # Covers Unix and Mac, but NO spaces
393 # Maximum size of the Query (number of characters clients can send
394 # covers both GET & POST combined)
395 $MaximumQuerySize = 2**20 - 1; # = 2**14 - 1
398 # Embeded URL get function used in SRC attributes and CGIscriptor::read_url
399 # (returns a string with the PERL code to transfer the URL contents, e.g.,
400 # "SAFEqx(\'curl \"http://www.fon.hum.uva.nl\"\')")
401 # "SAFEqx(\'wget --quiet --output-document=- \"http://www.fon.hum.uva.nl\"\')")
402 # Be sure to handle <BASE HREF='URL'> and allow BOTH
403 # direct printing GET_URL($URL [, 0]) and extracting the content of
404 # the $URL for post-processing GET_URL($URL, 1).
405 # You get the WHOLE file, including HTML header.
406 # The shell command Use $URL where the URL should go
407 # ('wget', 'snarf' or 'curl', uncomment the one you would like to use)
408 my $GET_URL_shell_command = 'wget --quiet --output-document=- $URL';
409 #my $GET_URL_shell_command = 'snarf $URL -';
410 #my $GET_URL_shell_command = 'curl $URL';
412 sub GET_URL # ($URL, $ValueNotPrint) -> content_of_url
414 my $URL = shift || return;
415 my $ValueNotPrint = shift || 0;
417 # Check URL for illegal characters
418 return "print '<h1>Illegal URL<h1>'\"\n\";" if $URL =~ /[^$FileAllowedChars\%]/;
420 # Include URL in final command
421 my $CurrentCommand = $GET_URL_shell_command;
422 $CurrentCommand =~ s/\$URL/$URL/g;
424 # Print to STDOUT or return a value
425 my $BlockPrint = "print STDOUT ";
426 $BlockPrint = "" if $ValueNotPrint;
428 my $Commands = <<"GETURLCODE";
429 # Get URL
431 my \$Page = "";
433 # Simple, using shell command
434 \$Page = SAFEqx('$CurrentCommand');
436 # Add a BASE tage to the header
437 \$Page =~ s!\\</head!\\<base href='$URL'\\>\\</head!ig unless \$Page =~ m!\\<base!;
439 # Print the URL value, or return it as a value
440 $BlockPrint\$Page;
442 GETURLCODE
443 return $Commands;
446 # As files can get rather large (and binary), you might want to use
447 # some more intelligent reading procedure, e.g.,
448 # Direct Perl
449 # # open(URLHANDLE, '/usr/bin/wget --quiet --output-document=- "$URL"|') || die "wget: \$!";
450 # #open(URLHANDLE, '/usr/bin/snarf "$URL" -|') || die "snarf: \$!";
451 # open(URLHANDLE, '/usr/bin/curl "$URL"|') || die "curl: \$!";
452 # my \$text = "";
453 # while(sysread(URLHANDLE,\$text, 1024) > 0)
455 # \$Page .= \$text;
456 # };
457 # close(URLHANDLE) || die "\$!";
458 # However, this doesn't work with the CGIexecute->evaluate() function.
459 # You get an error: 'No child processes at (eval 16) line 15, <file0> line 8.'
461 # You can forget the next two variables, they are only needed when
462 # you don't want to use a regular file system (i.e., with open)
463 # but use some kind of database/RAM image for accessing (generating)
464 # the data.
466 # Name of the environment variable that contains the file contents
467 # when reading directly from Database/RAM. When this environment variable,
468 # $ENV{$CGI_FILE_CONTENTS}, is not false, no real file will be read.
469 $CGI_FILE_CONTENTS = 'CGI_FILE_CONTENTS';
470 # Uncomment the following if you want to force the use of the data access code
471 # $ENV{$CGI_FILE_CONTENTS} = '-'; # Force use of $ENV{$CGI_DATA_ACCESS_CODE}
473 # Name of the environment variable that contains the RAM access perl
474 # code needed to read additional "files", i.e.,
475 # $ENV{$CGI_FILE_CONTENTS} = eval("\@_=('$file_path'); do{$ENV{$CGI_DATA_ACCESS_CODE}}");
476 # When $ENV{$CGI_FILE_CONTENTS} eq '-', this code is executed to generate the data.
477 $CGI_DATA_ACCESS_CODE = 'CGI_DATA_ACCESS_CODE';
479 # You can, of course, fill this yourself, e.g.,
480 # $ENV{$CGI_DATA_ACCESS_CODE} =
481 # 'open(INPUT, "<$_[0]"); while(<INPUT>){print;};close(INPUT);'
484 # DEBUGGING
486 # Suppress error messages, this can be changed for debugging or error-logging
487 #open(STDERR, "/dev/null"); # (comment out for use in debugging)
489 # SPECIAL: Remove Comments, security, etc. if the command line is
490 # '>CGIscriptor.pl -slim >slimCGIscriptor.pl'
491 $TrimDownCGIscriptor = 1 if $ARGV[0] =~ /^\-slim/i;
493 # If CGIscriptor is used from the command line, the command line
494 # arguments are interpreted as the file (1st) and the Query String (rest).
495 # Get the arguments
496 $ENV{'PATH_INFO'} = shift(@ARGV) unless exists($ENV{'PATH_INFO'}) || grep(/\-\-help/i, @ARGV);
497 $ENV{'QUERY_STRING'} = join("&", @ARGV) unless exists($ENV{'QUERY_STRING'});
500 # Handle bail-outs in a user definable way.
501 # Catch Die and replace it with your own function.
502 # Ends with a call to "die $_[0];"
504 sub dieHandler # ($ErrorCode, "Message", @_) -> DEAD
506 my $ErrorCode = shift;
507 my $ErrorMessage = shift;
509 # Place your own reporting functions here
511 # Now, kill everything (default)
512 print STDERR "$ErrorCode: $ErrorMessage\n";
513 die $ErrorMessage;
517 # End of optional user configuration
518 # (note: there is more non-essential user configuration below)
520 if(grep(/\-\-help/i, @ARGV))
522 print << 'ENDOFPREHELPTEXT2';
524 ###############################################################################
526 # Author and Copyright (c):
527 # Rob van Son, © 1995,1996,1997,1998,1999,2000,2001,2002-2012
528 # NKI-AVL Amsterdam
529 # r.v.son@nki.nl
530 # Institute of Phonetic Sciences & IFOTT/ACLS
531 # University of Amsterdam
532 # Email: R.J.J.H.vanSon@gmail.com
533 # Email: R.J.J.H.vanSon@uva.nl
534 # WWW : http://www.fon.hum.uva.nl/rob/
536 # License for use and disclaimers
538 # CGIscriptor merges plain ASCII HTML files transparantly
539 # with CGI variables, in-line PERL code, shell commands,
540 # and executable scripts in other scripting languages.
542 # This program is free software; you can redistribute it and/or
543 # modify it under the terms of the GNU General Public License
544 # as published by the Free Software Foundation; either version 2
545 # of the License, or (at your option) any later version.
547 # This program is distributed in the hope that it will be useful,
548 # but WITHOUT ANY WARRANTY; without even the implied warranty of
549 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
550 # GNU General Public License for more details.
552 # You should have received a copy of the GNU General Public License
553 # along with this program; if not, write to the Free Software
554 # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
557 # Contributors:
558 # Rob van Son (R.J.J.H.vanSon@uva.nl)
559 # Gerd Franke franke@roo.de (designed the <DIV> behaviour)
561 #######################################################
562 ENDOFPREHELPTEXT2
564 #######################################################>>>>>>>>>>Start Remove
566 # You can skip the following code, it is an auto-splice
567 # procedure.
569 # Construct a slimmed down version of CGIscriptor
570 # (i.e., CGIscriptor.pl -slim > slimCGIscriptor.pl)
572 if($TrimDownCGIscriptor)
574 open(CGISCRIPTOR, "<CGIscriptor.pl")
575 || dieHandler(1, "<CGIscriptor.pl not slimmed down: $!\n");
576 my $SKIPtext = 0;
577 my $SKIPComments = 0;
579 while(<CGISCRIPTOR>)
581 my $SKIPline = 0;
583 ++$LineCount;
585 # Start of SKIP text
586 $SKIPtext = 1 if /[\>]{10}Start Remove/;
587 $SKIPComments = 1 if $SKIPtext == 1;
589 # Skip this line?
590 $SKIPline = 1 if $SKIPtext || ($SKIPComments && /^\s*\#/);
592 ++$PrintCount unless $SKIPline;
594 print STDOUT $_ unless $SKIPline;
596 # End of SKIP text ?
597 $SKIPtext = 0 if /[\<]{10}End Remove/;
599 # Ready!
600 print STDERR "\# Printed $PrintCount out of $LineCount lines\n";
601 exit;
604 #######################################################
606 if(grep(/\-\-help/i, @ARGV))
608 print << 'ENDOFHELPTEXT';
610 # HYPE
612 # CGIscriptor merges plain ASCII HTML files transparantly and safely
613 # with CGI variables, in-line PERL code, shell commands, and executable
614 # scripts in many languages (on-line and real-time). It combines the
615 # "ease of use" of HTML files with the versatillity of specialized
616 # scripts and PERL programs. It hides all the specifics and
617 # idiosyncrasies of correct output and CGI coding and naming. Scripts
618 # do not have to be aware of HTML, HTTP, or CGI conventions just as HTML
619 # files can be ignorant of scripts and the associated values. CGIscriptor
620 # complies with the W3C HTML 4.0 recommendations.
621 # In addition to its use as a WWW embeded CGI processor, it can
622 # be used as a command-line document preprocessor (text-filter).
624 # THIS IS HOW IT WORKS
626 # The aim of CGIscriptor is to execute "plain" scripts inside a text file
627 # using any required CGIparameters and environment variables. It
628 # is optimized to transparantly process HTML files inside a WWW server.
629 # The native language is Perl, but many other scripting languages
630 # can be used.
632 # CGIscriptor reads text files from the requested input file (i.e., from
633 # $YOUR_HTML_FILES$PATH_INFO) and writes them to <STDOUT> (i.e., the
634 # client requesting the service) preceded by the obligatory
635 # "Content-type: text/html\n\n" or "Content-type: text/plain\n\n" string
636 # (except for "raw" files which supply their own Content-type message
637 # and only if the SERVER_PROTOCOL supports HTTP, MAIL, or MIME).
639 # When CGIscriptor encounters an embedded script, indicated by an HTML4 tag
641 # <SCRIPT TYPE="text/ssperl" [CGI="$VAR='default value'"] [SRC="ScriptSource"]>
642 # PERL script
643 # </SCRIPT>
645 # or
647 # <SCRIPT TYPE="text/osshell" [CGI="$name='default value'"] [SRC="ScriptSource"]>
648 # OS Shell script
649 # </SCRIPT>
651 # construct (anything between []-brackets is optional, other MIME-types
652 # and scripting languages are supported), the embedded script is removed
653 # and both the contents of the source file (i.e., "do 'ScriptSource'")
654 # AND the script are evaluated as a PERL program (i.e., by eval()),
655 # shell script (i.e., by a "safe" version of `Command`, qx) or an external
656 # interpreter. The output of the eval() function takes the place of the
657 # original <SCRIPT></SCRIPT> construct in the output string. Any CGI
658 # parameters declared by the CGI attribute are available as simple perl
659 # variables, and can subsequently be made available as variables to other
660 # scripting languages (e.g., bash, python, or lisp).
662 # Example: printing "Hello World"
663 # <HTML><HEAD><TITLE>Hello World</TITLE>
664 # <BODY>
665 # <H1><SCRIPT TYPE="text/ssperl">"Hello World"</SCRIPT></H1>
666 # </BODY></HTML>
668 # Save this in a file, hello.html, in the directory you indicated with
669 # $YOUR_HTML_FILES and access http://your_server/SHTML/hello.html
670 # (or to whatever name you use as an alias for CGIscriptor.pl).
671 # This is realy ALL you need to do to get going.
673 # You can use any values that are delivered in CGI-compliant form (i.e.,
674 # the "?name=value" type URL additions) transparently as "$name" variables
675 # in your scripts IFF you have declared them in the CGI attribute of
676 # a META or SCRIPT tag before e.g.:
677 # <META CONTENT="text/ssperl; CGI='$name = `default value`'
678 # [SRC='ScriptSource']">
679 # or
680 # <SCRIPT TYPE="text/ssperl" CGI="$name = 'default value'"
681 # [SRC='ScriptSource']>
682 # After such a 'CGI' attribute, you can use $name as an ordinary PERL variable
683 # (the ScriptSource file is immediately evaluated with "do 'ScriptSource'").
684 # The CGIscriptor script allows you to write ordinary HTML files which will
685 # include dynamic CGI aware (run time) features, such as on-line answers
686 # to specific CGI requests, queries, or the results of calculations.
688 # For example, if you wanted to answer questions of clients, you could write
689 # a Perl program called "Answer.pl" with a function "AnswerQuestion()"
690 # that prints out the answer to requests given as arguments. You then write
691 # an HTML page "Respond.html" containing the following fragment:
693 # <center>
694 # The Answer to your question
695 # <META CONTENT="text/ssperl; CGI='$Question'">
696 # <h3><SCRIPT TYPE="text/ssperl">$Question</SCRIPT></h3>
697 # is
698 # <h3><SCRIPT TYPE="text/ssperl" SRC="./PATH/Answer.pl">
699 # AnswerQuestion($Question);
700 # </SCRIPT></h3>
701 # </center>
702 # <FORM ACTION=Respond.html METHOD=GET>
703 # Next question: <INPUT NAME="Question" TYPE=TEXT SIZE=40><br>
704 # <INPUT TYPE=SUBMIT VALUE="Ask">
705 # </FORM>
707 # The output could look like the following (in HTML-speak):
709 # <CENTER>
710 # The Answer to your question
711 # <h3>What is the capital of the Netherlands?</h3>
712 # is
713 # <h3>Amsterdam</h3>
714 # </CENTER>
715 # <FORM ACTION=Respond.html METHOD=GET>
716 # Next question: <INPUT NAME="Question" TYPE=TEXT SIZE=40><br>
717 # <INPUT TYPE=SUBMIT VALUE="Ask">
719 # Note that the function "Answer.pl" does know nothing about CGI or HTML,
720 # it just prints out answers to arguments. Likewise, the text has no
721 # provisions for scripts or CGI like constructs. Also, it is completely
722 # trivial to extend this "program" to use the "Answer" later in the page
723 # to call up other information or pictures/sounds. The final text never
724 # shows any cue as to what the original "source" looked like, i.e.,
725 # where you store your scripts and how they are called.
727 # There are some extra's. The argument of the files called in a SRC= tag
728 # can access the CGI variables declared in the preceding META tag from
729 # the @ARGV array. Executable files are called as:
730 # `file '$ARGV[0]' ... ` (e.g., `Answer.pl \'$Question\'`;)
731 # The files called from SRC can even be (CGIscriptor) html files which are
732 # processed in-line. Furthermore, the SRC= tag can contain a perl block
733 # that is evaluated. That is,
734 # <META CONTENT="text/ssperl; CGI='$Question' SRC='{$Question}'">
735 # will result in the evaluation of "print do {$Question};" and the VALUE
736 # of $Question will be printed. Note that these "SRC-blocks" can be
737 # preceded and followed by other file names, but only a single block is
738 # allowed in a SRC= tag.
740 # One of the major hassles of dynamic WWW pages is the fact that several
741 # mutually incompatible browsers and platforms must be supported. For example,
742 # the way sound is played automatically is different for Netscape and
743 # Internet Explorer, and for each browser it is different again on
744 # Unix, MacOS, and Windows. Realy dangerous is processing user-supplied
745 # (form-) values to construct email addresses, file names, or database
746 # queries. All Apache WWW-server exploits reported in the media are
747 # based on faulty CGI-scripts that didn't check their user-data properly.
749 # There is no panacee for these problems, but a lot of work and problems
750 # can be saved by allowing easy and transparent control over which
751 # <SCRIPT></SCRIPT> blocks are executed on what CGI-data. CGIscriptor
752 # supplies such a method in the form of a pair of attributes:
753 # IF='...condition..' and UNLESS='...condition...'. When added to a
754 # script tag, the whole block (including the SRC attribute) will be
755 # ignored if the condition is false (IF) or true (UNLESS).
756 # For example, the following block will NOT be evaluated if the value
757 # of the CGI variable FILENAME is NOT a valid filename:
759 # <SCRIPT TYPE='text/ssperl' CGI='$FILENAME'
760 # IF='CGIscriptor::CGIsafeFileName($FILENAME)'>
761 # .....
762 # </SCRIPT>
764 # (the function CGIsafeFileName(String) returns an empty string ("")
765 # if the String argument is not a valid filename).
766 # The UNLESS attribute is the mirror image of IF.
768 # A user manual follows the HTML 4 and security paragraphs below.
770 ##########################################################################
772 # HTML 4 compliance
774 # In general, CGIscriptor.pl complies with the HTML 4 recommendations of
775 # the W3C. This means that any software to manage Web sites will be able
776 # to handle CGIscriptor files, as will web agents.
778 # All script code should be placed between <SCRIPT></SCRIPT> tags, the
779 # script type is indicated with TYPE="mime-type", the LANGUAGE
780 # feature is ignored, and a SRC feature is implemented. All CGI specific
781 # features are delegated to the CGI attribute.
783 # However, the behavior deviates from the W3C recommendations at some
784 # points. Most notably:
785 # 0- The scripts are executed at the server side, invissible to the
786 # client (i.e., the browser)
787 # 1- The mime-types are personal and idiosyncratic, but can be adapted.
788 # 2- Code in the body of a <SCRIPT></SCRIPT> tag-pair is still evaluated
789 # when a SRC feature is present.
790 # 3- The SRC attribute reads a list of files.
791 # 4- The files in a SRC attribute are processed according to file type.
792 # 5- The SRC attribute evaluates inline Perl code.
793 # 6- Processed META, DIV, INS tags are removed from the output
794 # document.
795 # 7- All attributes of the processed META tags, except CONTENT, are ignored
796 # (i.e., deleted from the output).
797 # 8- META tags can be placed ANYWHERE in the document.
798 # 9- Through the SRC feature, META tags can have visible output in the
799 # document.
800 # 10- The CGI attribute that declares CGI parameters, can be used
801 # inside the <SCRIPT> tag.
802 # 11- Use of an extended quote set, i.e., '', "", ``, (), {}, []
803 # and their \-slashed combinations: \'\', \"\", \`\`, \(\),
804 # \{\}, \[\].
805 # 12- IF and UNLESS attributes to <SCRIPT>, <META>, <DIV>, <INS> tags.
806 # 13- <DIV> tags cannot be nested, DIV tags are not
807 # rendered with new-lines.
808 # 14- The XML style <TAG .... /> is recognized and handled correctly.
809 # (i.e., no content is processed)
811 # The reasons for these choices are:
812 # You can still write completely HTML4 compliant documents. CGIscriptor
813 # will not force you to write "deviant" code. However, it allows you to
814 # do so (which is, in fact, just as bad). The prime design principle
815 # was to allow users to include plain Perl code. The code itself should
816 # be "enhancement free". Therefore, extra features were needed to
817 # supply easy access to CGI and Web site components. For security
818 # reasons these have to be declared explicitly. The SRC feature
819 # transparently manages access to external files, especially the safe
820 # use of executable files.
821 # The CGI attribute handles the declarations of external (CGI) variables
822 # in the SCRIPT and META tag's.
823 # EVERYTHING THE CGI ATTRIBUTE AND THE META TAG DO CAN BE DONE INSIDE
824 # A <SCRIPT></SCRIPT> TAG CONSTRUCT.
826 # The reason for the IF, UNLESS, and SRC attributes (and their Perl code
827 # evaluation) were build into the META and SCRIPT tags is part laziness,
828 # part security. The SRC blocks allows more compact documents and easier
829 # debugging. The values of the CGI variables can be immediately screened
830 # for security by IF or UNLESS conditions, and even SRC attributes (e.g.,
831 # email addresses and file names), and a few commands can be called
832 # without having to add another Perl TAG pair. This is especially important
833 # for documents that require the use of other (more restricted) "scripting"
834 # languages and facilities that lag transparent control structures.
836 ##########################################################################
838 # SECURITY
840 # Your WWW site is a few keystrokes away from a few hundred million internet
841 # users. A fair percentage of these users knows more about your computer
842 # than you do. And some of these just might have bad intentions.
844 # To ensure uncompromized operation of your server and platform, several
845 # features are incorporated in CGIscriptor.pl to enhance security.
846 # First of all, you should check the source of this program. No security
847 # measures will help you when you download programs from anonymous sources.
848 # If you want to use THIS file, please make sure that it is uncompromized.
849 # The best way to do this is to contact the source and try to determine
850 # whether s/he is reliable (and accountable).
852 # BE AWARE THAT ANY PROGRAMMER CAN CHANGE THIS PROGRAM IN SUCH A WAY THAT
853 # IT WILL SET THE DOORS TO YOUR SYSTEM WIDE OPEN
855 # I would like to ask any user who finds bugs that could compromise
856 # security to report them to me (and any other bug too,
857 # Email: R.J.J.H.vanSon@uva.nl or ifa@hum.uva.nl).
859 # Security features
861 # 1 Invisibility
862 # The inner workings of the HTML source files are completely hidden
863 # from the client. Only the HTTP header and the ever changing content
864 # of the output distinguish it from the output of a plain, fixed HTML
865 # file. Names, structures, and arguments of the "embedded" scripts
866 # are invisible to the client. Error output is suppressed except
867 # during debugging (user configurable).
869 # 2 Separate directory trees
870 # Directories containing Inline text and script files can reside on
871 # separate trees, distinct from those of the HTTP server. This means
872 # that NEITHER the text files, NOR the script files can be read by
873 # clients other than through CGIscriptor.pl, UNLESS they are
874 # EXPLICITELY made available.
876 # 3 Requests are NEVER "evaluated"
877 # All client supplied values are used as literal values (''-quoted).
878 # Client supplied ''-quotes are ALWAYS removed. Therefore, as long as the
879 # embedded scripts do NOT themselves evaluate these values, clients CANNOT
880 # supply executable commands. Be sure to AVOID scripts like:
882 # <META CONTENT="text/ssperl; CGI='$UserValue'">
883 # <SCRIPT TYPE="text/ssperl">$dir = `ls -1 $UserValue`;</SCRIPT>
885 # These are a recipe for disaster. However, the following quoted
886 # form should be save (but is still not adviced):
888 # <SCRIPT TYPE="text/ssperl">$dir = `ls -1 \'$UserValue\'`;</SCRIPT>
890 # A special function, SAFEqx(), will automatically do exactly this,
891 # e.g., SAFEqx('ls -1 $UserValue') will execute `ls -1 \'$UserValue\'`
892 # with $UserValue interpolated. I recommend to use SAFEqx() instead
893 # of backticks whenever you can. The OS shell scripts inside
895 # <SCRIPT TYPE="text/osshell">ls -1 $UserValue</SCRIPT>
897 # are handeld by SAFEqx and automatically ''-quoted.
899 # 4 Logging of requests
900 # All requests can be logged separate from the Host server. The level of
901 # detail is user configurable: Including or excluding the actual queries.
902 # This allows for the inspection of (im-) proper use.
904 # 5 Access control: Clients
905 # The Remote addresses can be checked against a list of authorized
906 # (i.e., accepted) or non-authorized (i.e., rejected) clients. Both
907 # REMOTE_HOST and REMOTE_ADDR are tested so clients without a proper
908 # HOST name can be (in-) excluded by their IP-address. Client patterns
909 # containing all numbers and dots are considered IP-addresses, all others
910 # domain names. No wild-cards or regexp's are allowed, only partial
911 # addresses.
912 # Matching of names is done from the back to the front (domain first,
913 # i.e., $REMOTE_HOST =~ /\Q$pattern\E$/is), so including ".edu" will
914 # accept or reject all clients from the domain EDU. Matching of
915 # IP-addresses is done from the front to the back (domain first, i.e.,
916 # $REMOTE_ADDR =~ /^\Q$pattern\E/is), so including "128." will (in-)
917 # exclude all clients whose IP-address starts with 128.
918 # There are two special symbols: "-" matches HOSTs with no name and "*"
919 # matches ALL HOSTS/clients.
920 # For those needing more expressional power, lines starting with
921 # "-e" are evaluated by the perl eval() function. E.g.,
922 # '-e $REMOTE_HOST =~ /\.edu$/is;' will accept/reject clients from the
923 # domain '.edu'.
925 # 6 Access control: Files
926 # In principle, CGIscriptor could read ANY file in the directory
927 # tree as discussed in 1. However, for security reasons this is
928 # restricted to text files. It can be made more restricted by entering
929 # a global file pattern (e.g., ".html"). This is done by default.
930 # For each client requesting access, the file pattern(s) can be made
931 # more restrictive than the global pattern by entering client specific
932 # file patterns in the Access Control files (see 5).
933 # For example: if the ACCEPT file contained the lines
934 # * DEMO
935 # .hum.uva.nl LET
936 # 145.18.230.
937 # Then all clients could request paths containing "DEMO" or "demo", e.g.
938 # "/my/demo/file.html" ($PATH_INFO =~ /\Q$pattern\E/), Clients from
939 # *.hum.uva.nl could also request paths containing "LET or "let", e.g.
940 # "/my/let/file.html", and clients from the local cluster
941 # 145.18.230.[0-9]+ could access ALL files.
942 # Again, for those needing more expressional power, lines starting with
943 # "-e" are evaluated. For instance:
944 # '-e $REMOTE_HOST =~ /\.edu$/is && $PATH_INFO =~ m@/DEMO/@is;'
945 # will accept/reject requests for files from the directory "/demo/" from
946 # clients from the domain '.edu'.
948 # Access control: Login
949 # Specific paths can be controlled by Session Tickets which must be
950 # present as a SESSIONTICKET CGI variable in the request. These paths are
951 # defined in %LoginRequiredPatterns as pairs of:
952 # ('regexp' => 'SessionPath\tPasswordPath\tLogin.html').
953 # Session Tickets are stored in a separate directory (SessionPath, e.g.,
954 # "Private/.Session") as files with the exact same name of the SESSIONTICKET CGI
955 # Type: SESSION
956 # IPaddress: <127.0.0.1>
957 # AllowedPaths: <^/Private/Name/>
958 # Expires: <3600>
959 # ...
960 # Other content can follow. It is adviced that Session Tickets should be deleted
961 # after some (idle) time. The IP address should be the IP number at login, and
962 # the SESSIONTICKET will be rejected if it is presented from another IP address.
963 # AllowedPaths is a perl regexp. Be careful how they match. Make sure to delimit
964 # the names to prevent access to overlapping names, eg, "^/Private/Rob" will also
965 # match "^/Private/Robert", however, "^/Private/Rob/" will not. Expires is the
966 # time the ticket will remain valid after creation (file ctime). Time can be given
967 # in s[econds] (default), m[inutes], h[hours], or d[ays], eg, "24h" means 24 hours.
968 # None of these need be present, but the Ticket must have a non-zero size.
970 # 7 Query length limiting
971 # The length of the Query string can be limited. If CONTENT_LENGTH is larger
972 # than this limit, the request is rejected. The combined length of the
973 # Query string and the POST input is checked before any processing is done.
974 # This will prevent clients from overloading the scripts.
975 # The actual, combined, Query Size is accessible as a variable through
976 # $CGI_Content_Length.
978 # 8 Illegal filenames, paths, and protected directories
979 # One of the primary security concerns in handling CGI-scripts is the
980 # use of "funny" characters in the requests that con scripts in executing
981 # malicious commands. Examples are inserting ';', null bytes, or <newline>
982 # characters in URL's and filenames, followed by executable commands. A
983 # special variable $FileAllowedChars stores a string of all allowed
984 # characters. Any request that translates to a filename with a character
985 # OUTSIDE this set will be rejected.
986 # In general, all (readable files) in the DocumentRoot tree are accessible.
987 # This might not be what you want. For instance, your DocumentRoot directory
988 # might be the working directory of a CVS project and contain sensitive
989 # information (e.g., the password to get to the repository). You can block
990 # access to these subdirectories by adding the corresponding patterns to
991 # the $BlockPathAccess variable. For instance, $BlockPathAccess = '/CVS/'
992 # will block any request that contains '/CVS/' or:
993 # die if $BlockPathAccess && $ENV{'PATH_INFO'} =~ m@$BlockPathAccess@;
995 # 9 The execution of code blocks can be controlled in a transparent way
996 # by adding IF or UNLESS conditions in the tags themselves. That is,
997 # a simple check of the validity of filenames or email addresses can
998 # be done before any code is executed.
1000 ###############################################################################
1002 # USER MANUAL (sort of)
1004 # CGIscriptor removes embedded scripts, indicated by an HTML 4 type
1005 # <SCRIPT TYPE='text/ssperl'> </SCRIPT> or <SCRIPT TYPE='text/osshell'>
1006 # </SCRIPT> constructs. CGIscriptor also recognizes XML-type
1007 # <SCRIPT TYPE='text/ssperl'/> constructs. These are usefull when
1008 # the necessary code is already available in the TAG itself (e.g.,
1009 # using external files). The contents of the directive are executed by
1010 # the PERL eval() and `` functions (in a separate name space). The
1011 # result of the eval() function replaces the <SCRIPT> </SCRIPT> construct
1012 # in the output file. You can use the values that are delivered in
1013 # CGI-compliant form (i.e., the "?name=value&.." type URL additions)
1014 # transparently as "$name" variables in your directives after they are
1015 # defined in a <META> or <SCRIPT> tag.
1016 # If you define the variable "$CGIscriptorResults" in a CGI attribute, all
1017 # subsequent <SCRIPT> and <META> results (including the defining
1018 # tag) will also be pushed onto a stack: @CGIscriptorResults. This list
1019 # behaves like any other, ordinary list and can be manipulated.
1021 # Both GET and POST requests are accepted. These two methods are treated
1022 # equal. Variables, i.e., those values that are determined when a file is
1023 # processed, are indicated in the CGI attribute by $<name> or $<name>=<default>
1024 # in which <name> is the name of the variable and <default> is the value
1025 # used when there is NO current CGI value for <name> (you can use
1026 # white-spaces in $<name>=<default> but really DO make sure that the
1027 # default value is followed by white space or is quoted). Names can contain
1028 # any alphanumeric characters and _ (i.e., names match /[\w]+/).
1029 # If the Content-type: is 'multipart/*', the input is treated as a
1030 # MIME multipart message and automatically delimited. CGI variables get
1031 # the "raw" (i.e., undecoded) body of the corresponding message part.
1033 # Variables can be CGI variables, i.e., those from the QUERY_STRING,
1034 # environment variables, e.g., REMOTE_USER, REMOTE_HOST, or REMOTE_ADDR,
1035 # or predefined values, e.g., CGI_Decoded_QS (The complete, decoded,
1036 # query string), CGI_Content_Length (the length of the decoded query
1037 # string), CGI_Year, CGI_Month, CGI_Time, and CGI_Hour (the current
1038 # date and time).
1040 # All these are available when defined in a CGI attribute. All environment
1041 # variables are accessible as $ENV{'name'}. So, to access the REMOTE_HOST
1042 # and the REMOTE_USER, use, e.g.:
1044 # <SCRIPT TYPE='text/ssperl'>
1045 # ($ENV{'REMOTE_HOST'}||"-")." $ENV{'REMOTE_USER'}"
1046 # </SCRIPT>
1048 # (This will print a "-" if REMOTE_HOST is not known)
1049 # Another way to do this is:
1051 # <META CONTENT="text/ssperl; CGI='$REMOTE_HOST = - $REMOTE_USER'">
1052 # <SCRIPT TYPE='text/ssperl'>"$REMOTE_HOST $REMOTE_USER"</SCRIPT>
1053 # or
1054 # <META CONTENT='text/ssperl; CGI="$REMOTE_HOST = - $REMOTE_USER"
1055 # SRC={"$REMOTE_HOST $REMOTE_USER\n"}'>
1057 # This is possible because ALL environment variables are available as
1058 # CGI variables. The environment variables take precedence over CGI
1059 # names in case of a "name clash". For instance:
1060 # <META CONTENT="text/ssperl; CGI='$HOME' SRC={$HOME}">
1061 # Will print the current HOME directory (environment) irrespective whether
1062 # there is a CGI variable from the query
1063 # (e.g., Where do you live? <INPUT TYPE="TEXT" NAME="HOME">)
1064 # THIS IS A SECURITY FEATURE. It prevents clients from changing
1065 # the values of defined environment variables (e.g., by supplying
1066 # a bogus $REMOTE_ADDR). Although $ENV{} is not changed by the META tags,
1067 # it would make the use of declared variables insecure. You can still
1068 # access CGI variables after a name clash with
1069 # CGIscriptor::CGIparseValue(<name>).
1071 # Some CGI variables are present several times in the query string
1072 # (e.g., from multiple selections). These should be defined as
1073 # @VARIABLENAME=default in the CGI attribute. The list @VARIABLENAME
1074 # will contain ALL VARIABLENAME values from the query, or a single
1075 # default value. If there is an ENVIRONMENT variable of the
1076 # same name, it will be used instead of the default AND the query
1077 # values. The corresponding function is
1078 # CGIscriptor::CGIparseValueList(<name>)
1080 # CGI variables collected in a @VARIABLENAME list are unordered.
1081 # When more structured variables are needed, a hash table can be used.
1082 # A variable defined as %VARIABLE=default will collect all
1083 # CGI-parameters whose name start with 'VARIABLE' in a hash table with
1084 # the remainder of the name as a key. For instance, %PERSON will
1085 # collect PERSONname='John Doe', PERSONbirthdate='01 Jan 00', and
1086 # PERSONspouse='Alice' into a hash table %PERSON such that $PERSON{'spouse'}
1087 # equals 'Alice'. Any default value or environment value will be stored
1088 # under the "" key. If there is an ENVIRONMENT variable of the same name,
1089 # it will be used instead of the default AND the query values. The
1090 # corresponding function is CGIscriptor::CGIparseValueHash(<name>)
1092 # This method of first declaring your environment and CGI variables
1093 # before being able to use them in the scripts might seem somewhat
1094 # clumsy, but it protects you from inadvertedly printing out the values of
1095 # system environment variables when their names coincide with those used
1096 # in the CGI forms. It also prevents "clients" from supplying CGI
1097 # parameter values for your private variables.
1098 # THIS IS A SECURITY FEATURE!
1101 # NON-HTML CONTENT TYPES
1103 # Normally, CGIscriptor prints the standard "Content-type: text/html\n\n"
1104 # message before anything is printed. This has been extended to include
1105 # plain text (.txt) files, for which the Content-type (MIME type)
1106 # 'text/plain' is printed. In all other respects, text files are treated
1107 # as HTML files (this can be switched off by removing '.txt' from the
1108 # $FilePattern variable) . When the content type should be something else,
1109 # e.g., with multipart files, use the $RawFilePattern (.xmr, see also next
1110 # item). CGIscriptor will not print a Content-type message for this file
1111 # type (which must supply its OWN Content-type message). Raw files must
1112 # still conform to the <SCRIPT></SCRIPT> and <META> tag specifications.
1115 # NON-HTML FILES
1117 # CGIscriptor is intended to process HTML and text files only. You can
1118 # create documents of any mime-type on-the-fly using "raw" text files,
1119 # e.g., with the .xmr extension. However, CGIscriptor will not process
1120 # binary files of any type, e.g., pictures or sounds. Given the sheer
1121 # number of formats, I do not have any intention to do so. However,
1122 # an escape route has been provided. You can construct a genuine raw
1123 # (.xmr) text file that contains the perl code to service any file type
1124 # you want. If the global $BinaryMapFile variable contains the path to
1125 # this file (e.g., /BinaryMapFile.xmr), this file will be called
1126 # whenever an unsupported (non-HTML) file type is requested. The path
1127 # to the requested binary file is stored in $ENV('CGI_BINARY_FILE')
1128 # and can be used like any other CGI-variable. Servicing binary files
1129 # then becomes supplying the correct Content-type (e.g., print
1130 # "Content-type: image/jpeg\n\n";) and reading the file and writing it
1131 # to STDOUT (e.g., using sysread() and syswrite()).
1134 # THE META TAG
1136 # All attributes of a META tag are ignored, except the
1137 # CONTENT='text/ssperl; CGI=" ... " [SRC=" ... "]' attribute. The string
1138 # inside the quotes following the CONTENT= indication (white-space is
1139 # ignored, "" '' `` (){}[]-quote pairs are allowed, plus their \ versions)
1140 # MUST start with any of the CGIscriptor mime-types (e.g.: text/ssperl or
1141 # text/osshell) and a comma or semicolon.
1142 # The quoted string following CGI= contains a white-space separated list
1143 # of declarations of the CGI (and Environment) values and default values
1144 # used when no CGI values are supplied by the query string.
1146 # If the default value is a longer string containing special characters,
1147 # possibly spanning several lines, the string must be enclosed in quotes.
1148 # You may use any pair of quotes or brackets from the list '', "", ``, (),
1149 # [], or {} to distinguish default values (or preceded by \, e.g., \(...\)
1150 # is different from (...)). The outermost pair will always be used and any
1151 # other quotes inside the string are considered to be part of the string
1152 # value, e.g.,
1154 # $Value = {['this'
1155 # "and" (this)]}
1156 # will result in $Value getting the default value: ['this'
1157 # "and" (this)]
1158 # (NOTE that the newline is part of the default value!).
1160 # Internally, for defining and initializing CGI (ENV) values, the META
1161 # and SCRIPT tags use the functions "defineCGIvariable($name, $default)"
1162 # (scalars) and "defineCGIvariableList($name, $default)" (lists).
1163 # These functions can be used inside scripts as
1164 # "CGIscriptor::defineCGIvariable($name, $default)" and
1165 # "CGIscriptor::defineCGIvariableList($name, $default)".
1166 # "CGIscriptor::defineCGIvariableHash($name, $default)".
1168 # The CGI attribute will be processed exactly identical when used inside
1169 # the <SCRIPT> tag. However, this use is not according to the
1170 # HTML 4.0 specifications of the W3C.
1173 # THE DIV/INS TAGS
1175 # There is a problem when constructing html files containing
1176 # server-side perl scripts with standard HTML tools. These
1177 # tools will refuse to process any text between <SCRIPT></SCRIPT>
1178 # tags. This is quite annoying when you want to use large
1179 # HTML templates where you will fill in values.
1181 # For this purpose, CGIscriptor will read the neutral
1182 # <DIV CLASS="ssperl" ID="varname"></DIV> or
1183 # <INS CLASS="ssperl" ID="varname"></INS>
1184 # tag (in Cascading Style Sheet manner) Note that
1185 # "varname" has NO '$' before it, it is a bare name.
1186 # Any text between these <DIV ...></DIV> or
1187 # <INS ...></INS>tags will be assigned to '$varname'
1188 # as is (e.g., as a literal).
1189 # No processing or interpolation will be performed.
1190 # There is also NO nesting possible. Do NOT nest a
1191 # </DIV> inside a <DIV></DIV>! Moreover, neither INS nor
1192 # DIV tags do ensure a block structure in the final
1193 # rendering (i.e., no empty lines).
1195 # Note that <DIV CLASS="ssperl" ID="varname"/>
1196 # is handled the XML way. No content is processed,
1197 # but varname is defined, and any SRC directives are
1198 # processed.
1200 # You can use $varname like any other variable name.
1201 # However, $varname is NOT a CGI variable and will be
1202 # completely internal to your script. There is NO
1203 # interaction between $varname and the outside world.
1205 # To interpolate a DIV derived text, you can use:
1206 # $varname =~ s/([\]])/\\\1/g; # Mark ']'-quotes
1207 # $varname = eval("qq[$varname]"); # Interpolate all values
1209 # The DIV tags will process IF, UNLESS, CGI and
1210 # SRC attributes. The SRC files will be pre-pended to the
1211 # body text of the tag. SRC blocks are NOT executed.
1213 # CONDITIONAL PROCESSING: THE 'IF' AND 'UNLESS' ATTRIBUTES
1215 # It is often necessary to include code-blocks that should be executed
1216 # conditionally, e.g., only for certain browsers or operating system.
1217 # Furthermore, quite often sanity and security checks are necessary
1218 # before user (form) data can be processed, e.g., with respect to
1219 # email addresses and filenames.
1221 # Checks added to the code are often difficult to find, interpret or
1222 # maintain and in general mess up the code flow. This kind of confussion
1223 # is dangerous.
1224 # Also, for many of the supported "foreign" scripting languages, adding
1225 # these checks is cumbersome or even impossible.
1227 # As a uniform method for asserting the correctness of "context", two
1228 # attributes are added to all supported tags: IF and UNLESS.
1229 # They both evaluate their value and block execution when the
1230 # result is <FALSE> (IF) or <TRUE> (UNLESS) in Perl, e.g.,
1231 # UNLESS='$NUMBER \> 100;' blocks execution if $NUMBER <= 100. Note that
1232 # the backslash in the '\>' is removed and only used to differentiate
1233 # this conditional '>' from the tag-closing '>'. For symmetry, the
1234 # backslash in '\<' is also removed. Inside these conditionals,
1235 # ~/ and ./ are expanded to their respective directory root paths.
1237 # For example, the following tag will be ignored when the filename is
1238 # invalid:
1240 # <SCRIPT TYPE='text/ssperl' CGI='$FILENAME'
1241 # IF='CGIscriptor::CGIsafeFileName($FILENAME);'>
1242 # ...
1243 # </SCRIPT>
1245 # The IF and UNLESS values must be quoted. The same quotes are supported
1246 # as with the other attributes. The SRC attribute is ignored when IF and
1247 # UNLESS block execution.
1249 # NOTE: 'IF' and 'UNLESS' always evaluate perl code.
1252 # THE MAGIC SOURCE ATTRIBUTE (SRC=)
1254 # The SRC attribute inside tags accepts a list of filenames and URL's
1255 # separated by "," comma's (or ";" semicolons).
1256 # ALL the variable values defined in the CGI attribute are available
1257 # in @ARGV as if the file or block was executed from the command line,
1258 # in the exact order in which they were declared in the preceding CGI
1259 # attribute.
1261 # First, a SRC={}-block will be evaluated as if the code inside the
1262 # block was part of a <SCRIPT></SCRIPT> construct, i.e.,
1263 # "print do { code };'';" or `code` (i.e., SAFEqx('code)).
1264 # Only a single block is evaluated. Note that this is processed less
1265 # efficiently than <SCRIPT> </SCRIPT> blocks. Type of evaluation
1266 # depends on the content-type: Perl for text/ssperl and OS shell for
1267 # text/osshell. For other mime types (scripting languages), anything in
1268 # the source block is put in front of the code block "inside" the tag.
1270 # Second, executable files (i.e., -x filename != 0) are evaluated as:
1271 # print `filename \'$ARGV[0]\' \'$ARGV[1]\' ...`
1272 # That is, you can actually call executables savely from the SRC tag.
1274 # Third, text files that match the file pattern, used by CGIscriptor to
1275 # check whether files should be processed ($FilePattern), are
1276 # processed in-line (i.e., recursively) by CGIscriptor as if the code
1277 # was inserted in the original source file. Recursions, i.e., calling
1278 # a file inside itself, are blocked. If you need them, you have to code
1279 # them explicitely using "main::ProcessFile($file_path)".
1281 # Fourth, Perl text files (i.e., -T filename != 0) are evaluated as:
1282 # "do FileName;'';".
1284 # Last, URL's (i.e., starting with 'HTTP://', 'FTP://', 'GOPHER://',
1285 # 'TELNET://', 'WHOIS://' etc.) are loaded
1286 # and printed. The loading and handling of <BASE> and document header
1287 # is done by a command generated by main::GET_URL($URL [, 0]). You can enter your
1288 # own code (default is curl, wget, or snarf and some post-processing to add a <BASE> tag).
1290 # There are two pseudo-file names: PREFIX and POSTFIX. These implement
1291 # a switch from prefixing the SRC code/files (PREFIX, default) before the
1292 # content of the tag to appending the code after the content of the tag
1293 # (POSTFIX). The switches are done in the order in which the PREFIX and
1294 # POSTFIX labels are encountered. You can mix PREFIX and POSTFIX labels
1295 # in any order with the SRC files. Note that the ORDER of file execution
1296 # is determined for prefixed and postfixed files seperately.
1298 # File paths can be preceded by the URL protocol prefix "file://". This
1299 # is simply STRIPPED from the name.
1301 # Example:
1302 # The request
1303 # "http://cgi-bin/Action_Forms.pl/Statistics/Sign_Test.html?positive=8&negative=22
1304 # will result in printing "${SS_PUB}/Statistics/Sign_Test.html"
1305 # With QUERY_STRING = "positive=8&negative=22"
1307 # on encountering the lines:
1308 # <META CONTENT="text/osshell; CGI='$positive=11 $negative=3'">
1309 # <b><SCRIPT LANGUAGE=PERL TYPE="text/ssperl" SRC="./Statistics/SignTest.pl">
1310 # </SCRIPT></b><p>"
1312 # This line will be processed as:
1313 # "<b>`${SS_SCRIPT}/Statistics/SignTest.pl '8' '22'`</b><p>"
1315 # In which "${SS_SCRIPT}/Statistics/SignTest.pl" is an executable script,
1316 # This line will end up printed as:
1317 # "<b>p <= 0.0161</b><p>"
1319 # Note that the META tag itself will never be printed, and is invisible to
1320 # the outside world.
1322 # The SRC files in a DIV or INS tag will be added (pre-pended) to the body
1323 # of the <DIV></DIV> tag. Blocks are NOT executed! If you do not
1324 # need any content, you can use the <DIV...../> format.
1327 # THE CGISCRIPTOR ROOT DIRECTORIES ~/ AND ./
1329 # Inside <SCRIPT></SCRIPT> tags, filepaths starting
1330 # with "~/" are replaced by "$YOUR_HTML_FILES/", this way files in the
1331 # public directories can be accessed without direct reference to the
1332 # actual paths. Filepaths starting with "./" are replaced by
1333 # "$YOUR_SCRIPTS/" and this should only be used for scripts.
1335 # Note: this replacement can seriously affect Perl scripts. Watch
1336 # out for constructs like $a =~ s/aap\./noot./g, use
1337 # $a =~ s@aap\.@noot.@g instead.
1339 # CGIscriptor.pl will assign the values of $SS_PUB and $SS_SCRIPT
1340 # (i.e., $YOUR_HTML_FILES and $YOUR_SCRIPTS) to the environment variables
1341 # $SS_PUB and $SS_SCRIPT. These can be accessed by the scripts that are
1342 # executed.
1343 # Values not preceded by $, ~/, or ./ are used as literals
1346 # OS SHELL SCRIPT EVALUATION (CONTENT-TYPE=TEXT/OSSHELL)
1348 # OS scripts are executed by a "safe" version of the `` operator (i.e.,
1349 # SAFEqx(), see also below) and any output is printed. CGIscriptor will
1350 # interpolate the script and replace all user-supplied CGI-variables by
1351 # their ''-quoted values (actually, all variables defined in CGI attributes
1352 # are quoted). Other Perl variables are interpolated in a simple fasion,
1353 # i.e., $scalar by their value, @list by join(' ', @list), and %hash by
1354 # their name=value pairs. Complex references, e.g., @$variable, are all
1355 # evaluated in a scalar context. Quotes should be used with care.
1356 # NOTE: the results of the shell script evaluation will appear in the
1357 # @CGIscriptorResults stack just as any other result.
1358 # All occurrences of $@% that should NOT be interpolated must be
1359 # preceeded by a "\". Interpolation can be switched off completely by
1360 # setting $CGIscriptor::NoShellScriptInterpolation = 1
1361 # (set to 0 or undef to switch interpolation on again)
1362 # i.e.,
1363 # <SCRIPT TYPE="text/ssperl">
1364 # $CGIscriptor::NoShellScriptInterpolation = 1;
1365 # </SCRIPT>
1368 # RUN TIME TRANSLATION OF INPUT FILES
1370 # Allows general and global conversions of files using Regular Expressions.
1371 # Very handy (but costly) to rewrite legacy pages to a new format.
1372 # Select files to use it on with
1373 # my $TranslationPaths = 'filepattern';
1374 # This is costly. For efficiency, define:
1375 # $TranslationPaths = ''; when not using translations.
1376 # Accepts general regular expressions: [$pattern, $replacement]
1378 # Define:
1379 # my $TranslationPaths = 'filepattern'; # Pattern matching PATH_INFO
1381 # push(@TranslationTable, ['pattern', 'replacement']);
1382 # e.g. (for Ruby Rails):
1383 # push(@TranslationTable, ['<%=', '<SCRIPT TYPE="text/ssruby">']);
1384 # push(@TranslationTable, ['%>', '</SCRIPT>']);
1386 # Runs:
1387 # my $currentRegExp;
1388 # foreach $currentRegExp (@TranslationTable)
1390 # my ($pattern, $replacement) = @$currentRegExp;
1391 # $$text =~ s!$pattern!$replacement!msg;
1392 # };
1395 # EVALUATION OF OTHER SCRIPTING LANGUAGES
1397 # Adding a MIME-type and an interpreter command to
1398 # %ScriptingLanguages automatically will catch any other
1399 # scripting language in the standard
1400 # <SCRIPT TYPE="[mime]"></SCRIPT> manner.
1401 # E.g., adding: $ScriptingLanguages{'text/sspython'} = 'python';
1402 # will actually execute the folowing code in an HTML page
1403 # (ignore 'REMOTE_HOST' for the moment):
1404 # <SCRIPT TYPE="text/sspython">
1405 # # A Python script
1406 # x = ["A","real","python","script","Hello","World","and", REMOTE_HOST]
1407 # print x[4:8] # Prints the list ["Hello","World","and", REMOTE_HOST]
1408 # </SCRIPT>
1410 # The script code is NOT interpolated by perl, EXCEPT for those
1411 # interpreters that cannot handle variables themselves.
1412 # Currently, several interpreters are pre-installed:
1414 # Perl test - "text/testperl" => 'perl',
1415 # Python - "text/sspython" => 'python',
1416 # Ruby - "text/ssruby" => 'ruby',
1417 # Tcl - "text/sstcl" => 'tcl',
1418 # Awk - "text/ssawk" => 'awk -f-',
1419 # Gnu Lisp - "text/sslisp" => 'rep | tail +5 '.
1420 # "| egrep -v '> |^rep. |^nil\\\$'",
1421 # XLispstat - "text/xlispstat" => 'xlispstat | tail +7 '.
1422 # "| egrep -v '> \\\$|^NIL'",
1423 # Gnu Prolog- "text/ssprolog" => 'gprolog',
1424 # M4 macro's- "text/ssm4" => 'm4',
1425 # Born shell- "text/sh" => 'sh',
1426 # Bash - "text/bash" => 'bash',
1427 # C-shell - "text/csh" => 'csh',
1428 # Korn shell- "text/ksh" => 'ksh',
1429 # Praat - "text/sspraat" => "praat - | sed 's/Praat > //g'",
1430 # R - "text/ssr" => "R --vanilla --slave | sed 's/^[\[0-9\]*] //g'",
1431 # REBOL - "text/ssrebol" =>
1432 # "rebol --quiet|egrep -v '^[> ]* == '|sed 's/^\s*\[> \]* //g'",
1433 # PostgreSQL- "text/postgresql" => 'psql 2>/dev/null',
1434 # (psql)
1436 # Note that the "value" of $ScriptingLanguages{mime} must be a command
1437 # that reads Standard Input and writes to standard output. Any extra
1438 # output of interactive interpreters (banners, echo's, prompts)
1439 # should be removed by piping the output through 'tail', 'grep',
1440 # 'sed', or even 'awk' or 'perl'.
1442 # For access to CGI variables there is a special hashtable:
1443 # %ScriptingCGIvariables.
1444 # CGI variables can be accessed in three ways.
1445 # 1. If the mime type is not present in %ScriptingCGIvariables,
1446 # nothing is done and the script itself should parse the relevant
1447 # environment variables.
1448 # 2. If the mime type IS present in %ScriptingCGIvariables, but it's
1449 # value is empty, e.g., $ScriptingCGIvariables{"text/sspraat"} = '';,
1450 # the script text is interpolated by perl. That is, all $var, @array,
1451 # %hash, and \-slashes are replaced by their respective values.
1452 # 3. In all other cases, the CGI and environment variables are added
1453 # in front of the script according to the format stored in
1454 # %ScriptingCGIvariables. That is, the following (pseudo-)code is
1455 # executed for each CGI- or Environment variable defined in the CGI-tag:
1456 # printf(INTERPRETER, $ScriptingCGIvariables{$mime}, $CGI_NAME, $CGI_VALUE);
1458 # For instance, "text/testperl" => '$%s = "%s";' defines variable
1459 # definitions for Perl, and "text/sspython" => '%s = "%s"' for Python
1460 # (note that these definitions are not save, the real ones contain '-quotes).
1462 # THIS WILL NOT WORK FOR @VARIABLES, the (empty) $VARIABLES will be used
1463 # instead.
1465 # The $CGI_VALUE parameters are "shrubed" of all control characters
1466 # and quotes (by &shrubCGIparameter($CGI_VALUE)) for the options 2 and 3.
1467 # Control characters are replaced by \0<octal ascii value> (the exception
1468 # is \015, the newline, which is replaced by \n) and quotes
1469 # and backslashes by their HTML character
1470 # value (' -> &#39; ` -> &#96; " -> &quot; \ -> &#92; & -> &amper;).
1471 # For example:
1472 # if a client would supply the string value (in standard perl, e.g.,
1473 # \n means <newline>)
1474 # "/dev/null';\nrm -rf *;\necho '"
1475 # it would be processed as
1476 # '/dev/null&#39;;\nrm -rf *;\necho &#39;'
1477 # (e.g., sh or bash would process the latter more according to your
1478 # intentions).
1479 # If your intepreter requires different protection measures, you will
1480 # have to supply these in %main::SHRUBcharacterTR (string => translation),
1481 # e.g., $SHRUBcharacterTR{"\'"} = "&#39;";
1483 # Currently, the following definitions are used:
1484 # %ScriptingCGIvariables = (
1485 # "text/testperl" => "\$\%s = '\%s';", # Perl $VAR = 'value' (for testing)
1486 # "text/sspython" => "\%s = '\%s'", # Python VAR = 'value'
1487 # "text/ssruby" => '@%s = "%s"', # Ruby @VAR = "value"
1488 # "text/sstcl" => 'set %s "%s"', # TCL set VAR "value"
1489 # "text/ssawk" => '%s = "%s";', # Awk VAR = "value";
1490 # "text/sslisp" => '(setq %s "%s")', # Gnu lisp (rep) (setq VAR "value")
1491 # "text/xlispstat" => '(setq %s "%s")', # Xlispstat (setq VAR "value")
1492 # "text/ssprolog" => '', # Gnu prolog (interpolated)
1493 # "text/ssm4" => "define(`\%s', `\%s')", # M4 macro's define(`VAR', `value')
1494 # "text/sh" => "\%s='\%s';", # Born shell VAR='value';
1495 # "text/bash" => "\%s='\%s';", # Born again shell VAR='value';
1496 # "text/csh" => "\$\%s = '\%s';", # C shell $VAR = 'value';
1497 # "text/ksh" => "\$\%s = '\%s';", # Korn shell $VAR = 'value';
1498 # "text/sspraat" => '', # Praat (interpolation)
1499 # "text/ssr" => '%s <- "%s";', # R VAR <- "value";
1500 # "text/ssrebol" => '%s: copy "%s"', # REBOL VAR: copy "value"
1501 # "text/postgresql" => '', # PostgreSQL (interpolation)
1502 # "" => ""
1503 # );
1505 # Four tables allow fine-tuning of interpreter with code that should be
1506 # added before and after each code block:
1508 # Code added before each script block
1509 # %ScriptingPrefix = (
1510 # "text/testperl" => "\# Prefix Code;", # Perl script testing
1511 # "text/ssm4" => 'divert(0)' # M4 macro's (open STDOUT)
1512 # );
1513 # Code added at the end of each script block
1514 # %ScriptingPostfix = (
1515 # "text/testperl" => "\# Postfix Code;", # Perl script testing
1516 # "text/ssm4" => 'divert(-1)' # M4 macro's (block STDOUT)
1517 # );
1518 # Initialization code, inserted directly after opening (NEVER interpolated)
1519 # %ScriptingInitialization = (
1520 # "text/testperl" => "\# Initialization Code;", # Perl script testing
1521 # "text/ssawk" => 'BEGIN {', # Server Side awk scripts
1522 # "text/sslisp" => '(prog1 nil ', # Lisp (rep)
1523 # "text/xlispstat" => '(prog1 nil ', # xlispstat
1524 # "text/ssm4" => 'divert(-1)' # M4 macro's (block STDOUT)
1525 # );
1526 # Cleanup code, inserted before closing (NEVER interpolated)
1527 # %ScriptingCleanup = (
1528 # "text/testperl" => "\# Cleanup Code;", # Perl script testing
1529 # "text/sspraat" => 'Quit',
1530 # "text/ssawk" => '};', # Server Side awk scripts
1531 # "text/sslisp" => '(princ "\n" standard-output)).' # Closing print to rep
1532 # "text/xlispstat" => '(print "" *standard-output*)).' # Closing print to xlispstat
1533 # "text/postgresql" => '\q',
1534 # );
1537 # The SRC attribute is NOT magical for these interpreters. In short,
1538 # all code inside a source file or {} block is written verbattim
1539 # to the interpreter. No (pre-)processing or executional magic is done.
1541 # A serious shortcomming of the described mechanism for handling other
1542 # (scripting) languages, with respect to standard perl scripts
1543 # (i.e., 'text/ssperl'), is that the code is only executed when
1544 # the pipe to the interpreter is closed. So the pipe has to be
1545 # closed at the end of each block. This means that the state of the
1546 # interpreter (e.g., all variable values) is lost after the closing of
1547 # the next </SCRIPT> tag. The standard 'text/ssperl' scripts retain
1548 # all values and definitions.
1550 # APPLICATION MIME TYPES
1552 # To ease some important auxilliary functions from within the
1553 # html pages I have added them as MIME types. This uses
1554 # the mechanism that is also used for the evaluation of
1555 # other scripting languages, with interpolation of CGI
1556 # parameters (and perl-variables). Actually, these are
1557 # defined exactly like any other "scripting language".
1559 # text/ssdisplay: display some (HTML) text with interpolated
1560 # variables (uses `cat`).
1561 # text/sslogfile: write (append) the interpolated block to the file
1562 # mentioned on the first, non-empty line
1563 # (the filename can be preceded by 'File: ',
1564 # note the space after the ':',
1565 # uses `awk .... >> <filename>`).
1566 # text/ssmailto: send email directly from within the script block.
1567 # The first line of the body must contain
1568 # To:Name@Valid.Email.Address
1569 # (note: NO space between 'To:' and the email adres)
1570 # For other options see the mailto man pages.
1571 # It works by directly sending the (interpolated)
1572 # content of the text block to a pipe into the
1573 # Linux program 'mailto'.
1575 # In these script blocks, all Perl variables will be
1576 # replaced by their values. All CGI variables are cleaned before
1577 # they are used. These CGI variables must be redefined with a
1578 # CGI attribute to restore their original values.
1579 # In general, this will be more secure than constructing
1580 # e.g., your own email command lines. For instance, Mailto will
1581 # not execute any odd (forged) email addres, but just stops
1582 # when the email address is invalid and awk will construct
1583 # any filename you give it (e.g. '<File;rm\\\040-f' would end up
1584 # as a "valid" UNIX filename). Note that it will also gladly
1585 # store this file anywhere (/../../../etc/passwd will work!).
1586 # Use the CGIscriptor::CGIsafeFileName() function to clean the
1587 # filename.
1589 # SHELL SCRIPT PIPING
1591 # If a shell script starts with the UNIX style "#! <shell command> \n"
1592 # line, the rest of the shell script is piped into the indicated command,
1593 # i.e.,
1594 # open(COMMAND, "| command");print COMMAND $RestOfScript;
1596 # In many ways this is equivalent to the MIME-type profiling for
1597 # evaluating other scripting languages as discussed above. The
1598 # difference breaks down to convenience. Shell script piping is a
1599 # "raw" implementation. It allows you to control all aspects of
1600 # execution. Using the MIME-type profiling is easier, but has a
1601 # lot of defaults built in that might get in the way. Another
1602 # difference is that shell script piping uses the SAFEqx() function,
1603 # and MIME-type profiling does not.
1605 # Execution of shell scripts is under the control of the Perl Script blocks
1606 # in the document. The MIME-type triggered execution of <SCRIPT></SCRIPT>
1607 # blocks can be simulated easily. You can switch to a different shell,
1608 # e.g. tcl, completely by executing the following Perl commands inside
1609 # your document:
1611 # <SCRIPT TYPE="text/ssperl">
1612 # $main::ShellScriptContentType = "text/ssTcl"; # Yes, you can do this
1613 # CGIscriptor::RedirectShellScript('/usr/bin/tcl'); # Pipe to Tcl
1614 # $CGIscriptor::NoShellScriptInterpolation = 1;
1615 # </SCRIPT>
1617 # After this script is executed, CGIscriptor will parse scripts of
1618 # TYPE="text/ssTcl" and pipe their contents into '|/usr/bin/tcl'
1619 # WITHOUT interpolation (i.e., NO substitution of Perl variables).
1620 # The crucial function is :
1621 # CGIscriptor::RedirectShellScript('/usr/bin/tcl')
1622 # After executing this function, all shell scripts AND all
1623 # calls to SAFEqx()) are piped into '|/usr/bin/tcl'. If the argument
1624 # of RedirectShellScript is empty, e.g., '', the original (default)
1625 # value is reset.
1627 # The standard output, STDOUT, of any pipe is send to the client.
1628 # Currently, you should be carefull with quotes in such a piped script.
1629 # The results of a pipe is NOT put on the @CGIscriptorResults stack.
1630 # As a result, you do not have access to the output of any piped (#!)
1631 # process! If you want such access, execute
1632 # <SCRIPT TYPE="text/osshell">echo "script"|command</SCRIPT>
1633 # or
1634 # <SCRIPT TYPE="text/ssperl">
1635 # $resultvar = SAFEqx('echo "script"|command');
1636 # </SCRIPT>.
1638 # Safety is never complete. Although SAFEqx() prevents some of the
1639 # most obvious forms of attacks and security slips, it cannot prevent
1640 # them all. Especially, complex combinations of quotes and intricate
1641 # variable references cannot be handled safely by SAFEqx. So be on
1642 # guard.
1645 # PERL CODE EVALUATION (CONTENT-TYPE=TEXT/SSPERL)
1647 # All PERL scripts are evaluated inside a PERL package. This package
1648 # has a separate name space. This isolated name space protects the
1649 # CGIscriptor.pl program against interference from user code. However,
1650 # some variables, e.g., $_, are global and cannot be protected. You are
1651 # advised NOT to use such global variable names. You CAN write
1652 # directives that directly access the variables in the main program.
1653 # You do so at your own risk (there is definitely enough rope available
1654 # to hang yourself). The behavior of CGIscriptor becomes undefined if
1655 # you change its private variables during run time. The PERL code
1656 # directives are used as in:
1657 # $Result = eval($directive); print $Result;'';
1658 # ($directive contains all text between <SCRIPT></SCRIPT>).
1659 # That is, the <directive> is treated as ''-quoted string and
1660 # the result is treated as a scalar. To prevent the VALUE of the code
1661 # block from appearing on the client's screen, end the directive with
1662 # ';""</SCRIPT>'. Evaluated directives return the last value, just as
1663 # eval(), blocks, and subroutines, but only as a scalar.
1665 # IMPORTANT: All PERL variables defined are persistent. Each <SCRIPT>
1666 # </SCRIPT> construct is evaluated as a {}-block with associated scope
1667 # (e.g., for "my $var;" declarations). This means that values assigned
1668 # to a PERL variable can be used throughout the document unless they
1669 # were declared with "my". The following will actually work as intended
1670 # (note that the ``-quotes in this example are NOT evaluated, but used
1671 # as simple quotes):
1673 # <META CONTENT="text/ssperl; CGI=`$String='abcdefg'`">
1674 # anything ...
1675 # <SCRIPT TYPE=text/ssperl>@List = split('', $String);</SCRIPT>
1676 # anything ...
1677 # <SCRIPT TYPE=text/ssperl>join(", ", @List[1..$#List]);</SCRIPT>
1679 # The first <SCRIPT TYPE=text/ssperl></SCRIPT> construct will return the
1680 # value scalar(@List), the second <SCRIPT TYPE=text/ssperl></SCRIPT>
1681 # construct will print the elements of $String separated by commas, leaving
1682 # out the first element, i.e., $List[0].
1684 # Another warning: './' and '~/' are ALWAYS replaced by the values of
1685 # $YOUR_SCRIPTS and $YOUR_HTML_FILES, respectively . This can interfere
1686 # with pattern matching, e.g., $a =~ s/aap\./noot\./g will result in the
1687 # evaluations of $a =~ s/aap\\${YOUR_SCRIPTS}noot\\${YOUR_SCRIPTS}g. Use
1688 # s@<regexp>.@<replacement>.@g instead.
1691 # USER EXTENSIONS
1693 # A CGIscriptor package is attached to the bottom of this file. With
1694 # this package you can personalize your version of CGIscriptor by
1695 # including often used perl routines. These subroutines can be
1696 # accessed by prefixing their names with CGIscriptor::, e.g.,
1697 # <SCRIPT LANGUAGE=PERL TYPE=text/ssperl>
1698 # CGIscriptor::ListDocs("/Books/*") # List all documents in /Books
1699 # </SCRIPT>
1700 # It already contains some useful subroutines for Document Management.
1701 # As it is a separate package, it has its own namespace, isolated from
1702 # both the evaluator and the main program. To access variables from
1703 # the document <SCRIPT></SCRIPT> blocks, use $CGIexecute::<var>.
1705 # Currently, the following functions are implemented
1706 # (precede them with CGIscriptor::, see below for more information)
1707 # - SAFEqx ('String') -> result of qx/"String"/ # Safe application of ``-quotes
1708 # Is used by text/osshell Shell scripts. Protects all CGI
1709 # (client-supplied) values with single quotes before executing the
1710 # commands (one of the few functions that also works WITHOUT CGIscriptor::
1711 # in front)
1712 # - defineCGIvariable ($name[, $default) -> 0/1 (i.e., failure/success)
1713 # Is used by the META tag to define and initialize CGI and ENV
1714 # name/value pairs. Tries to obtain an initializing value from (in order):
1715 # $ENV{$name}
1716 # The Query string
1717 # The default value given (if any)
1718 # (one of the few functions that also works WITHOUT CGIscriptor::
1719 # in front)
1720 # - CGIsafeFileName (FileName) -> FileName or ""
1721 # Check a string against the Allowed File Characters (and ../ /..).
1722 # Returns an empty string for unsafe filenames.
1723 # - CGIsafeEmailAddress (Email) -> Email or ""
1724 # Check a string against correct email address pattern.
1725 # Returns an empty string for unsafe addresses.
1726 # - RedirectShellScript ('CommandString') -> FILEHANDLER or undef
1727 # Open a named PIPE for SAFEqx to receive ALL shell scripts
1728 # - URLdecode (URL encoded string) -> plain string # Decode URL encoded argument
1729 # - URLencode (plain string) -> URL encoded string # Encode argument as URL code
1730 # - CGIparseValue (ValueName [, URL_encoded_QueryString]) -> Decoded value
1731 # Extract the value of a CGI variable from the global or a private
1732 # URL-encoded query (multipart POST raw, NOT decoded)
1733 # - CGIparseValueList (ValueName [, URL_encoded_QueryString])
1734 # -> List of decoded values
1735 # As CGIparseValue, but now assembles ALL values of ValueName into a list.
1736 # - CGIparseHeader (ValueName [, URL_encoded_QueryString]) -> Header
1737 # Extract the header of a multipart CGI variable from the global or a private
1738 # URL-encoded query ("" when not a multipart variable or absent)
1739 # - CGIparseForm ([URL_encoded_QueryString]) -> Decoded Form
1740 # Decode the complete global URL-encoded query or a private
1741 # URL-encoded query
1742 # - read_url(URL) # Returns the page from URL (with added base tag, both FTP and HTTP)
1743 # Uses main::GET_URL(URL, 1) to get at the command to read the URL.
1744 # - BrowseDirs(RootDirectory [, Pattern, Startdir, CGIname]) # print browsable directories
1745 # - ListDocs(Pattern [,ListType]) # Prints a nested HTML directory listing of
1746 # all documents, e.g., ListDocs("/*", "dl");.
1747 # - HTMLdocTree(Pattern [,ListType]) # Prints a nested HTML listing of all
1748 # local links starting from a given document, e.g.,
1749 # HTMLdocTree("/Welcome.html", "dl");
1752 # THE RESULTS STACK: @CGISCRIPTORRESULTS
1754 # If the pseudo-variable "$CGIscriptorResults" has been defined in a
1755 # META tag, all subsequent SCRIPT and META results are pushed
1756 # on the @CGIscriptorResults stack. This list is just another
1757 # Perl variable and can be used and manipulated like any other list.
1758 # $CGIscriptorResults[-1] is always the last result.
1759 # This is only of limited use, e.g., to use the results of an OS shell
1760 # script inside a Perl script. Will NOT contain the results of Pipes
1761 # or code from MIME-profiling.
1764 # USEFULL CGI PREDEFINED VARIABLES (DO NOT ASSIGN TO THESE)
1766 # $CGI_HOME - The DocumentRoot directory
1767 # $CGI_Decoded_QS - The complete decoded Query String
1768 # $CGI_Content_Length - The ACTUAL length of the Query String
1769 # $CGI_Date - Current date and time
1770 # $CGI_Year $CGI_Month $CGI_Day $CGI_WeekDay - Current Date
1771 # $CGI_Time - Current Time
1772 # $CGI_Hour $CGI_Minutes $CGI_Seconds - Current Time, split
1773 # GMT Date/Time:
1774 # $CGI_GMTYear $CGI_GMTMonth $CGI_GMTDay $CGI_GMTWeekDay $CGI_GMTYearDay
1775 # $CGI_GMTHour $CGI_GMTMinutes $CGI_GMTSeconds $CGI_GMTisdst
1778 # USEFULL CGI ENVIRONMENT VARIABLES
1780 # Variables accessible (in APACHE) as $ENV{<name>}
1781 # (see: "http://hoohoo.ncsa.uiuc.edu/cgi/env.html"):
1783 # QUERY_STRING - The query part of URL, that is, everything that follows the
1784 # question mark.
1785 # PATH_INFO - Extra path information given after the script name
1786 # PATH_TRANSLATED - Extra pathinfo translated through the rule system.
1787 # (This doesn't always make sense.)
1788 # REMOTE_USER - If the server supports user authentication, and the script is
1789 # protected, this is the username they have authenticated as.
1790 # REMOTE_HOST - The hostname making the request. If the server does not have
1791 # this information, it should set REMOTE_ADDR and leave this unset
1792 # REMOTE_ADDR - The IP address of the remote host making the request.
1793 # REMOTE_IDENT - If the HTTP server supports RFC 931 identification, then this
1794 # variable will be set to the remote user name retrieved from
1795 # the server. Usage of this variable should be limited to logging
1796 # only.
1797 # AUTH_TYPE - If the server supports user authentication, and the script
1798 # is protected, this is the protocol-specific authentication
1799 # method used to validate the user.
1800 # CONTENT_TYPE - For queries which have attached information, such as HTTP
1801 # POST and PUT, this is the content type of the data.
1802 # CONTENT_LENGTH - The length of the said content as given by the client.
1803 # SERVER_SOFTWARE - The name and version of the information server software
1804 # answering the request (and running the gateway).
1805 # Format: name/version
1806 # SERVER_NAME - The server's hostname, DNS alias, or IP address as it
1807 # would appear in self-referencing URLs
1808 # GATEWAY_INTERFACE - The revision of the CGI specification to which this
1809 # server complies. Format: CGI/revision
1810 # SERVER_PROTOCOL - The name and revision of the information protocol this
1811 # request came in with. Format: protocol/revision
1812 # SERVER_PORT - The port number to which the request was sent.
1813 # REQUEST_METHOD - The method with which the request was made. For HTTP,
1814 # this is "GET", "HEAD", "POST", etc.
1815 # SCRIPT_NAME - A virtual path to the script being executed, used for
1816 # self-referencing URLs.
1817 # HTTP_ACCEPT - The MIME types which the client will accept, as given by
1818 # HTTP headers. Other protocols may need to get this
1819 # information from elsewhere. Each item in this list should
1820 # be separated by commas as per the HTTP spec.
1821 # Format: type/subtype, type/subtype
1822 # HTTP_USER_AGENT - The browser the client is using to send the request.
1823 # General format: software/version library/version.
1826 # INSTRUCTIONS FOR RUNNING CGIscriptor ON UNIX
1828 # CGIscriptor.pl will run on any WWW server that runs Perl scripts, just add
1829 # a line like the following to your srm.conf file (Apache example):
1831 # ScriptAlias /SHTML/ /real-path/CGIscriptor.pl/
1833 # URL's that refer to http://www.your.address/SHTML/... will now be handled
1834 # by CGIscriptor.pl, which can use a private directory tree (default is the
1835 # DOCUMENT_ROOT directory tree, but it can be anywhere, see manual).
1837 # If your hosting ISP won't let you add ScriptAlias lines you can use
1838 # the following "rewrite"-based "scriptalias" in .htaccess
1839 # (from Gerd Franke)
1841 # RewriteEngine On
1842 # RewriteBase /
1843 # RewriteCond %{REQUEST_FILENAME} .html$
1844 # RewriteCond %{SCRIPT_FILENAME} !cgiscriptor.pl$
1845 # RewriteCond %{REQUEST_FILENAME} -f
1846 # RewriteRule ^(.*)$ /cgi-bin/cgiscriptor.pl/$1?&%{QUERY_STRING}
1848 # Everthing with the extension ".html" and not including "cgiscriptor.pl"
1849 # in the url and where the file "path/filename.html" exists is redirected
1850 # to "/cgi.bin/cgiscriptor.pl/path/filename.html?query".
1851 # The user configuration should get the same path-level as the
1852 # .htaccess-file:
1854 # # Just enter your own directory path here
1855 # $YOUR_HTML_FILES = "$ENV{'DOCUMENT_ROOT'}";
1856 # # use DOCUMENT_ROOT only, if .htaccess lies in the root-directory.
1858 # If this .htaccess goes in a specific directory, the path to this
1859 # directory must be added to $ENV{'DOCUMENT_ROOT'}.
1861 # The CGIscriptor file contains all documentation as comments. These
1862 # comments can be removed to speed up loading (e.g., `egrep -v '^#'
1863 # CGIscriptor.pl` > leanScriptor.pl). A bare bones version of
1864 # CGIscriptor.pl, lacking documentation, most comments, access control,
1865 # example functions etc. (but still with the copyright notice and some
1866 # minimal documentation) can be obtained by calling CGIscriptor.pl on the
1867 # command line with the '-slim' command line argument, e.g.,
1869 # >CGIscriptor.pl -slim > slimCGIscriptor.pl
1871 # CGIscriptor.pl can be run from the command line with <path> and <query> as
1872 # arguments, as `CGIscriptor.pl <path> <query>`, inside a perl script
1873 # with 'do CGIscriptor.pl' after setting $ENV{PATH_INFO}
1874 # and $ENV{QUERY_STRING}, or CGIscriptor.pl can be loaded with 'require
1875 # "/real-path/CGIscriptor.pl"'. In the latter case, requests are processed
1876 # by 'Handle_Request();' (again after setting $ENV{PATH_INFO} and
1877 # $ENV{QUERY_STRING}).
1879 # Using the command line execution option, CGIscriptor.pl can be used as a
1880 # document (meta-)preprocessor. If the first argument is '-', STDIN will be read.
1881 # For example:
1883 # > cat MyDynamicDocument.html | CGIscriptor.pl - '[QueryString]' > MyStaticFile.html
1885 # This command line will produce a STATIC file with the DYNAMIC content of
1886 # MyDocument.html "interpolated".
1888 # This option would be very dangerous when available over the internet.
1889 # If someone could sneak a 'http://www.your.domain/-' URL past your
1890 # server, CGIscriptor could EXECUTE any POSTED contend.
1891 # Therefore, for security reasons, STDIN will NOT be read
1892 # if ANY of the HTTP server environment variables is set (e.g.,
1893 # SERVER_PORT, SERVER_PROTOCOL, SERVER_NAME, SERVER_SOFTWARE,
1894 # HTTP_USER_AGENT, REMOTE_ADDR).
1895 # This block on processing STDIN on HTTP requests can be lifted by setting
1896 # $BLOCK_STDIN_HTTP_REQUEST = 0;
1897 # In the security configuration. Butbe carefull when doing this.
1898 # It can be very dangerous.
1900 # Running demo's and more information can be found at
1901 # http://www.fon.hum.uva.nl/~rob/OSS/OSS.html
1903 # A pocket-size HTTP daemon, CGIservlet.pl, is available from my web site or
1904 # CPAN that can use CGIscriptor.pl as the base of a µWWW server and
1905 # demonstrates its use.
1908 # PROCESSING NON-FILESYSTEM DATA
1910 # Normally, HTTP (WWW) requests map onto file that can be accessed
1911 # using the perl open() function. That is, the web server runs on top of
1912 # some directory structure. However, we can envission (and put to good
1913 # use) other systems that do not use a normal file system. The whole CGI
1914 # was developed to make dynamic document generation possible.
1916 # A special case is where we want to have it both: A normal web server
1917 # with normal "file data", but not a normal files system. For instance,
1918 # we want or normal Web Site to run directly from a RAM hash table or
1919 # other database, instead of from disk. But we do NOT want to code the
1920 # whole site structure in CGI.
1922 # CGIscriptor can do this. If the web server fills an environment variable
1923 # $ENV{'CGI_FILE_CONTENT'} with the content of the "file", then the content
1924 # of this variable is processed instead of opening a file. If this environment
1925 # variable has the value '-', the content of another environment variable,
1926 # $ENV{'CGI_DATA_ACCESS_CODE'} is executed as:
1927 # eval("\@_ = ($file_path); do {$ENV{'CGI_DATA_ACCESS_CODE'}};")
1928 # and the result is processed as if it was the content of the requested
1929 # file.
1930 # (actually, the names of the environment variables are user configurable,
1931 # they are stored in the local variables $CGI_FILE_CONTENT and
1932 # $CGI_DATA_ACCESS_CODE)
1934 # When using this mechanism, the SRC attribute mechanism will only partially work.
1935 # Only the "recursive" calls to CGIscriptor (the ProcessFile() function)
1936 # will work, the automagical execution of SRC files won't. (In this case,
1937 # the SRC attribute won't work either for other scripting languages)
1940 # NON-UNIX PLATFORMS
1942 # CGIscriptor.pl was mainly developed and tested on UNIX. However, as I
1943 # coded part of the time on an Apple Macintosh under MacPerl, I made sure
1944 # CGIscriptor did run under MacPerl (with command line options). But only
1945 # as an independend script, not as part of a HTTP server. I have used it
1946 # under Apache in Windows XP.
1948 ENDOFHELPTEXT
1949 exit;
1951 ###############################################################################
1953 # SECURITY CONFIGURATION
1955 # Special configurations related to SECURITY
1956 # (i.e., optional, see also environment variables below)
1958 # LOGGING
1959 # Log Clients and the requested paths (Redundant when loging Queries)
1961 $ClientLog = "./Client.log"; # (uncomment for use)
1963 # Format: Localtime | REMOTE_USER REMOTE_IDENT REMOTE_HOST REMOTE_ADDRESS \
1964 # PATH_INFO CONTENT_LENGTH (actually, the real query+post length)
1966 # Log Clients and the queries, the CGIQUERYDECODE is required if you want
1967 # to log queries. If you log Queries, the loging of Clients is redundant
1968 # (note that queries can be quite long, so this might not be a good idea)
1970 #$QueryLog = "./Query.log"; # (uncomment for use)
1972 # ACCESS CONTROL
1973 # the Access files should contain Hostnames or IP addresses,
1974 # i.e. REMOTE_HOST or REMOTE_ADDR, each on a separate line
1975 # optionally followed by one ore more file patterns, e.g., "edu /DEMO".
1976 # Matching is done "domain first". For example ".edu" matches all
1977 # clients whose "name" ends in ".edu" or ".EDU". The file pattern
1978 # "/DEMO" matches all paths that contain the strings "/DEMO" or "/demo"
1979 # (both matchings are done case-insensitive).
1980 # The name special symbol "-" matches ALL clients who do not supply a
1981 # REMOTE_HOST name, "*" matches all clients.
1982 # Lines starting with '-e' are evaluated. A non-zero return value indicates
1983 # a match. You can use $REMOTE_HOST, $REMOTE_ADDR, and $PATH_INFO. These
1984 # lines are evaluated in the program's own name-space. So DO NOT assign to
1985 # variables.
1987 # Accept the following users (remove comment # and adapt filename)
1988 $CGI_Accept = -s "$YOUR_SCRIPTS/ACCEPT.lis" ? "$YOUR_SCRIPTS/ACCEPT.lis" : ''; # (uncomment for use)
1990 # Reject requests from the following users (remove comment # and
1991 # adapt filename, this is only of limited use)
1992 $CGI_Reject = -s "$YOUR_SCRIPTS/REJECT.lis" ? "$YOUR_SCRIPTS/REJECT.lis" : ''; # (uncomment for use)
1994 # Empty lines or comment lines starting with '#' are ignored in both
1995 # $CGI_Accept and $CGI_Reject.
1997 # Block STDIN (i.e., '-') requests when servicing an HTTP request
1998 # Comment this out if you realy want to use STDIN in an on-line web server
1999 $BLOCK_STDIN_HTTP_REQUEST = 1;
2002 # End of security configuration
2004 ##################################################<<<<<<<<<<End Remove
2006 # PARSING CGI VALUES FROM THE QUERY STRING (USER CONFIGURABLE)
2008 # The CGI parse commands. These commands extract the values of the
2009 # CGI variables from the URL encoded Query String.
2010 # If you want to use your own CGI decoders, you can call them here
2011 # instead, using your own PATH and commenting/uncommenting the
2012 # appropriate lines
2014 # CGI parse command for individual values
2015 # (if $List > 0, returns a list value, if $List < 0, a hash table, this is optional)
2016 sub YOUR_CGIPARSE # ($Name [, $List]) -> Decoded value
2018 my $Name = shift;
2019 my $List = shift || 0;
2020 # Use one of the following by uncommenting
2021 if(!$List) # Simple value
2023 return CGIscriptor::CGIparseValue($Name) ;
2025 elsif($List < 0) # Hash tables
2027 return CGIscriptor::CGIparseValueHash($Name); # Defined in CGIscriptor below
2029 else # Lists
2031 return CGIscriptor::CGIparseValueList($Name); # Defined in CGIscriptor below
2034 # return `/PATH/cgiparse -value $Name`; # Shell commands
2035 # require "/PATH/cgiparse.pl"; return cgivalue($Name); # Library
2037 # Complete queries
2038 sub YOUR_CGIQUERYDECODE
2040 # Use one of the following by uncommenting
2041 return CGIscriptor::CGIparseForm(); # Defined in CGIscriptor below
2042 # return `/PATH/cgiparse -form`; # Shell commands
2043 # require "/PATH/cgiparse.pl"; return cgiform(); # Library
2046 # End of configuration
2048 #######################################################################
2050 # Translating input files.
2051 # Allows general and global conversions of files using Regular Expressions
2052 # Translations are applied in the order of definition.
2054 # Define:
2055 # my $TranslationPaths = 'pattern'; # Pattern matching PATH_INFO
2057 # push(@TranslationTable, ['pattern', 'replacement']);
2058 # e.g. (for Ruby Rails):
2059 # push(@TranslationTable, ['<%=', '<SCRIPT TYPE="text/ssruby">']);
2060 # push(@TranslationTable, ['%>', '</SCRIPT>']);
2062 # Runs:
2063 # my $currentRegExp;
2064 # foreach $currentRegExp (keys(%TranslationTable))
2066 # my $currentRegExp;
2067 # foreach $currentRegExp (@TranslationTable)
2069 # my ($pattern, $replacement) = @$currentRegExp;
2070 # $$text =~ s!$pattern!$replacement!msg;
2071 # };
2072 # };
2074 # Configuration section
2076 #######################################################################
2078 # The file paths on which to apply the translation
2079 my $TranslationPaths = ''; # NO files
2080 #$TranslationPaths = '.'; # ANY file
2081 # $TranslationPaths = '\.html'; # HTML files
2083 my @TranslationTable = ();
2084 # Some legacy code
2085 push(@TranslationTable, ['\<\s*CGI\s+([^\>])*\>', '\<SCRIPT TYPE=\"text/ssperl\"\>$1\<\/SCRIPT>']);
2086 # Ruby Rails?
2087 push(@TranslationTable, ['<%=', '<SCRIPT TYPE="text/ssruby">']);
2088 push(@TranslationTable, ['%>', '</SCRIPT>']);
2090 sub performTranslation # (\$text)
2092 my $text = shift || return;
2093 if(@TranslationTable && $TranslationPaths && $ENV{'PATH_INFO'} =~ m!$TranslationPaths!)
2095 my $currentRegExp;
2096 foreach $currentRegExp (@TranslationTable)
2098 my ($pattern, $replacement) = @$currentRegExp;
2099 $$text =~ s!$pattern!$replacement!msg;
2104 #######################################################################
2106 # Seamless access to other (Scripting) Languages
2107 # TYPE='text/ss<interpreter>'
2109 # Configuration section
2111 #######################################################################
2113 # OTHER SCRIPTING LANGUAGES AT THE SERVER SIDE (MIME => OScommand)
2114 # Yes, it realy is this simple! (unbelievable, isn't it)
2115 # NOTE: Some interpreters require some filtering to obtain "clean" output
2117 %ScriptingLanguages = (
2118 "text/testperl" => 'perl', # Perl for testing
2119 "text/sspython" => 'python', # Python
2120 "text/ssruby" => 'ruby', # Ruby
2121 "text/sstcl" => 'tcl', # TCL
2122 "text/ssawk" => 'awk -f-', # Awk
2123 "text/sslisp" => # lisp (rep, GNU)
2124 'rep | tail +4 '."| egrep -v '> |^rep. |^nil\\\$'",
2125 "text/xlispstat" => # xlispstat
2126 'xlispstat | tail +7 ' ."| egrep -v '> \\\$|^NIL'",
2127 "text/ssprolog" => # Prolog (GNU)
2128 "gprolog | tail +4 | sed 's/^| ?- //'",
2129 "text/ssm4" => 'm4', # M4 macro's
2130 "text/sh" => 'sh', # Born shell
2131 "text/bash" => 'bash', # Born again shell
2132 "text/csh" => 'csh', # C shell
2133 "text/ksh" => 'ksh', # Korn shell
2134 "text/sspraat" => # Praat (sound/speech analysis)
2135 "praat - | sed 's/Praat > //g'",
2136 "text/ssr" => # R
2137 "R --vanilla --slave | sed 's/^[\[0-9\]*] //'",
2138 "text/ssrebol" => # REBOL
2139 "rebol --quiet|egrep -v '^[> ]* == '|sed 's/^\\s*\[> \]* //'",
2140 "text/postgresql" => 'psql 2>/dev/null',
2142 # Not real scripting, but the use of other applications
2143 "text/ssmailto" => "awk 'NF||F{F=1;print \\\$0;}'|mailto >/dev/null", # Send mail from server
2144 "text/ssdisplay" => 'cat', # Display, (interpolation)
2145 "text/sslogfile" => # Log to file, (interpolation)
2146 "awk 'NF||L {if(!L){L=tolower(\\\$1)~/^file:\\\$/ ? \\\$2 : \\\$1;}else{print \\\$0 >> L;};}'",
2148 "" => ""
2151 # To be able to access the CGI variables in your script, they
2152 # should be passed to the scripting language in a readable form
2153 # Here you can enter how they should be printed (the first %s
2154 # is replaced by the NAME of the CGI variable as it apears in the
2155 # META tag, the second by its VALUE).
2156 # For Perl this would be:
2157 # "text/testperl" => '$%s = "%s";',
2158 # which would be executed as
2159 # printf('$%s = "%s";', $CGI_NAME, $CGI_VALUE);
2161 # If the hash table value doesn't exist, nothing is done
2162 # (you have to parse the Environment variables yourself).
2163 # If it DOES exist but is empty (e.g., "text/sspraat" => '',)
2164 # Perl string interpolation of variables (i.e., $var, @array,
2165 # %hash) is performed. This means that $@%\ must be protected
2166 # with a \.
2168 %ScriptingCGIvariables = (
2169 "text/testperl" => "\$\%s = '\%s';", # Perl $VAR = 'value'; (for testing)
2170 "text/sspython" => "\%s = '\%s'", # Python VAR = 'value'
2171 "text/ssruby" => '@%s = "%s"', # Ruby @VAR = 'value'
2172 "text/sstcl" => 'set %s "%s"', # TCL set VAR "value"
2173 "text/ssawk" => '%s = "%s";', # Awk VAR = 'value';
2174 "text/sslisp" => '(setq %s "%s")', # Gnu lisp (rep) (setq VAR "value")
2175 "text/xlispstat" => '(setq %s "%s")', # xlispstat (setq VAR "value")
2176 "text/ssprolog" => '', # Gnu prolog (interpolated)
2177 "text/ssm4" => "define(`\%s', `\%s')", # M4 macro's define(`VAR', `value')
2178 "text/sh" => "\%s='\%s'", # Born shell VAR='value'
2179 "text/bash" => "\%s='\%s'", # Born again shell VAR='value'
2180 "text/csh" => "\$\%s='\%s';", # C shell $VAR = 'value';
2181 "text/ksh" => "\$\%s='\%s';", # Korn shell $VAR = 'value';
2183 "text/ssrebol" => '%s: copy "%s"', # REBOL VAR: copy "value"
2184 "text/sspraat" => '', # Praat (interpolation)
2185 "text/ssr" => '%s <- "%s";', # R VAR <- "value";
2186 "text/postgresql" => '', # PostgreSQL (interpolation)
2188 # Not real scripting, but the use of other applications
2189 "text/ssmailto" => '', # MAILTO, (interpolation)
2190 "text/ssdisplay" => '', # Display, (interpolation)
2191 "text/sslogfile" => '', # Log to file, (interpolation)
2193 "" => ""
2196 # If you want something added in front or at the back of each script
2197 # block as send to the interpreter add it here.
2198 # mime => "string", e.g., "text/sspython" => "python commands"
2199 %ScriptingPrefix = (
2200 "text/testperl" => "\# Prefix Code;", # Perl script testing
2201 "text/ssm4" => 'divert(0)', # M4 macro's (open STDOUT)
2203 "" => ""
2205 # If you want something added at the end of each script block
2206 %ScriptingPostfix = (
2207 "text/testperl" => "\# Postfix Code;", # Perl script testing
2208 "text/ssm4" => 'divert(-1)', # M4 macro's (block STDOUT)
2210 "" => ""
2212 # If you need initialization code, directly after opening
2213 %ScriptingInitialization = (
2214 "text/testperl" => "\# Initialization Code;", # Perl script testing
2215 "text/ssawk" => 'BEGIN {', # Server Side awk scripts (VAR = "value")
2216 "text/sslisp" => '(prog1 nil ', # Lisp (rep)
2217 "text/xlispstat" => '(prog1 nil ', # xlispstat
2218 "text/ssm4" => 'divert(-1)', # M4 macro's (block STDOUT)
2220 "" => ""
2222 # If you need cleanup code before closing
2223 %ScriptingCleanup = (
2224 "text/testperl" => "\# Cleanup Code;", # Perl script testing
2225 "text/sspraat" => 'Quit',
2226 "text/ssawk" => '};', # Server Side awk scripts (VAR = "value")
2227 "text/sslisp" => '(princ "\n" standard-output)).', # Closing print to rep
2228 "text/xlispstat" => '(print ""))', # Closing print to xlispstat
2229 "text/postgresql" => '\q', # quit psql
2230 "text/ssdisplay" => "", # close cat
2232 "" => ""
2235 # End of configuration for foreign scripting languages
2237 ###############################################################################
2239 # Initialization Code
2242 sub Initialize_Request
2244 ###############################################################################
2246 # ENVIRONMENT VARIABLES
2248 # Use environment variables to configure CGIscriptor on a temporary basis.
2249 # If you define any of the configurable variables as environment variables,
2250 # these are used instead of the "hard coded" values above.
2252 $SS_PUB = $ENV{'SS_PUB'} || $YOUR_HTML_FILES;
2253 $SS_SCRIPT = $ENV{'SS_SCRIPT'} || $YOUR_SCRIPTS;
2256 # Substitution strings, these are used internally to handle the
2257 # directory separator strings, e.g., '~/' -> 'SS_PUB:' (Mac)
2258 $HOME_SUB = $SS_PUB;
2259 $SCRIPT_SUB = $SS_SCRIPT;
2262 # Make sure all script are reliably loaded
2263 push(@INC, $SS_SCRIPT);
2266 # Add the directory separator to the "home" directories.
2267 # (This is required for ~/ and ./ substitution)
2268 $HOME_SUB .= '/' if $HOME_SUB;
2269 $SCRIPT_SUB .= '/' if $SCRIPT_SUB;
2271 $CGI_HOME = $ENV{'DOCUMENT_ROOT'};
2272 $ENV{'PATH_TRANSLATED'} =~ /$ENV{'PATH_INFO'}/is;
2273 $CGI_HOME = $` unless $ENV{'DOCUMENT_ROOT'}; # Get the DOCUMENT_ROOT directory
2274 $default_values{'CGI_HOME'} = $CGI_HOME;
2275 $ENV{'HOME'} = $CGI_HOME;
2276 # Set SS_PUB and SS_SCRIPT as Environment variables (make them available
2277 # to the scripts)
2278 $ENV{'SS_PUB'} = $SS_PUB unless $ENV{'SS_PUB'};
2279 $ENV{'SS_SCRIPT'} = $SS_SCRIPT unless $ENV{'SS_SCRIPT'};
2281 $FilePattern = $ENV{'FilePattern'} || $FilePattern;
2282 $MaximumQuerySize = $ENV{'MaximumQuerySize'} || $MaximumQuerySize;
2283 $ClientLog = $ENV{'ClientLog'} || $ClientLog;
2284 $QueryLog = $ENV{'QueryLog'} || $QueryLog;
2285 $CGI_Accept = $ENV{'CGI_Accept'} || $CGI_Accept;
2286 $CGI_Reject = $ENV{'CGI_Reject'} || $CGI_Reject;
2288 # Parse file names
2289 $CGI_Accept =~ s@^\~/@$HOME_SUB@g if $CGI_Accept;
2290 $CGI_Reject =~ s@^\~/@$HOME_SUB@g if $CGI_Reject;
2291 $ClientLog =~ s@^\~/@$HOME_SUB@g if $ClientLog;
2292 $QueryLog =~ s@^\~/@$HOME_SUB@g if $QueryLog;
2294 $CGI_Accept =~ s@^\./@$SCRIPT_SUB@g if $CGI_Accept;
2295 $CGI_Reject =~ s@^\./@$SCRIPT_SUB@g if $CGI_Reject;
2296 $ClientLog =~ s@^\./@$SCRIPT_SUB@g if $ClientLog;
2297 $QueryLog =~ s@^\./@$SCRIPT_SUB@g if $QueryLog;
2299 @CGIscriptorResults = (); # A stack of results
2301 # end of Environment variables
2303 #############################################################################
2305 # Define and Store "standard" values
2307 # BEFORE doing ANYTHING check the size of Query String
2308 length($ENV{'QUERY_STRING'}) <= $MaximumQuerySize || dieHandler(2, "QUERY TOO LONG\n");
2310 # The Translated Query String and the Actual length of the (decoded)
2311 # Query String
2312 if($ENV{'QUERY_STRING'})
2314 # If this can contain '`"-quotes, be carefull to use it QUOTED
2315 $default_values{CGI_Decoded_QS} = YOUR_CGIQUERYDECODE();
2316 $default_values{CGI_Content_Length} = length($default_values{CGI_Decoded_QS});
2319 # Get the current Date and time and store them as default variables
2321 # Get Local Time
2322 $LocalTime = localtime;
2324 # CGI_Year CGI_Month CGI_Day CGI_WeekDay CGI_Time
2325 # CGI_Hour CGI_Minutes CGI_Seconds
2327 $default_values{CGI_Date} = $LocalTime;
2328 ($default_values{CGI_WeekDay},
2329 $default_values{CGI_Month},
2330 $default_values{CGI_Day},
2331 $default_values{CGI_Time},
2332 $default_values{CGI_Year}) = split(' ', $LocalTime);
2333 ($default_values{CGI_Hour},
2334 $default_values{CGI_Minutes},
2335 $default_values{CGI_Seconds}) = split(':', $default_values{CGI_Time});
2337 # GMT:
2338 # CGI_GMTYear CGI_GMTMonth CGI_GMTDay CGI_GMTWeekDay CGI_GMTYearDay
2339 # CGI_GMTHour CGI_GMTMinutes CGI_GMTSeconds CGI_GMTisdst
2341 ($default_values{CGI_GMTSeconds},
2342 $default_values{CGI_GMTMinutes},
2343 $default_values{CGI_GMTHour},
2344 $default_values{CGI_GMTDay},
2345 $default_values{CGI_GMTMonth},
2346 $default_values{CGI_GMTYear},
2347 $default_values{CGI_GMTWeekDay},
2348 $default_values{CGI_GMTYearDay},
2349 $default_values{CGI_GMTisdst}) = gmtime;
2353 # End of Initialize Request
2355 ###################################################################
2357 # SECURITY: ACCESS CONTROL
2359 # Check the credentials of each client (use pattern matching, domain first).
2360 # This subroutine will kill-off (die) the current process whenever access
2361 # is denied.
2363 sub Access_Control
2365 # >>>>>>>>>>Start Remove
2367 # ACCEPTED CLIENTS
2369 # Only accept clients which are authorized, reject all unnamed clients
2370 # if REMOTE_HOST is given.
2371 # If file patterns are given, check whether the user is authorized for
2372 # THIS file.
2373 if($CGI_Accept)
2375 # Use local variables, REMOTE_HOST becomes '-' if undefined
2376 my $REMOTE_HOST = $ENV{REMOTE_HOST} || '-';
2377 my $REMOTE_ADDR = $ENV{REMOTE_ADDR};
2378 my $PATH_INFO = $ENV{'PATH_INFO'};
2380 open(CGI_Accept, "<$CGI_Accept") || dieHandler(3, "$CGI_Accept: $!\n");
2381 $NoAccess = 1;
2382 while(<CGI_Accept>)
2384 next unless /\S/; # Skip empty lines
2385 next if /^\s*\#/; # Skip comments
2387 # Full expressions
2388 if(/^\s*-e\s/is)
2390 my $Accept = $'; # Get the expression
2391 $NoAccess &&= eval($Accept); # evaluate the expresion
2393 else
2395 my ($Accept, @FilePatternList) = split;
2396 if($Accept eq '*' # Always match
2397 ||$REMOTE_HOST =~ /\Q$Accept\E$/is # REMOTE_HOST matches
2398 || (
2399 $Accept =~ /^[0-9\.]+$/
2400 && $REMOTE_ADDR =~ /^\Q$Accept\E/ # IP address matches
2404 if($FilePatternList[0])
2406 foreach $Pattern (@FilePatternList)
2408 # Check whether this patterns is accepted
2409 $NoAccess &&= ($PATH_INFO !~ m@\Q$Pattern\E@is);
2412 else
2414 $NoAccess = 0; # No file patterns -> Accepted
2418 # Blocked
2419 last unless $NoAccess;
2421 close(CGI_Accept);
2422 if($NoAccess){ dieHandler(4, "No Access: $PATH_INFO\n");};
2426 # REJECTED CLIENTS
2428 # Reject named clients, accept all unnamed clients
2429 if($CGI_Reject)
2431 # Use local variables, REMOTE_HOST becomes '-' if undefined
2432 my $REMOTE_HOST = $ENV{'REMOTE_HOST'} || '-';
2433 my $REMOTE_ADDR = $ENV{'REMOTE_ADDR'};
2434 my $PATH_INFO = $ENV{'PATH_INFO'};
2436 open(CGI_Reject, "<$CGI_Reject") || dieHandler(5, "$CGI_Reject: $!\n");
2437 $NoAccess = 0;
2438 while(<CGI_Reject>)
2440 next unless /\S/; # Skip empty lines
2441 next if /^\s*\#/; # Skip comments
2443 # Full expressions
2444 if(/^-e\s/is)
2446 my $Reject = $'; # Get the expression
2447 $NoAccess ||= eval($Reject); # evaluate the expresion
2449 else
2451 my ($Reject, @FilePatternList) = split;
2452 if($Reject eq '*' # Always match
2453 ||$REMOTE_HOST =~ /\Q$Reject\E$/is # REMOTE_HOST matches
2454 ||($Reject =~ /^[0-9\.]+$/
2455 && $REMOTE_ADDR =~ /^\Q$Reject\E/is # IP address matches
2459 if($FilePatternList[0])
2461 foreach $Pattern (@FilePatternList)
2463 $NoAccess ||= ($PATH_INFO =~ m@\Q$Pattern\E@is);
2466 else
2468 $NoAccess = 1; # No file patterns -> Rejected
2472 last if $NoAccess;
2474 close(CGI_Reject);
2475 if($NoAccess){ dieHandler(6, "Request rejected: $PATH_INFO\n");};
2478 ##########################################################<<<<<<<<<<End Remove
2481 # Get the filename
2483 # Does the filename contain any illegal characters (e.g., |, >, or <)
2484 dieHandler(7, "Illegal request: $ENV{'PATH_INFO'}\n") if $ENV{'PATH_INFO'} =~ /[^$FileAllowedChars]/;
2485 # Does the pathname contain an illegal (blocked) "directory"
2486 dieHandler(8, "Illegal request: $ENV{'PATH_INFO'}\n") if $BlockPathAccess && $ENV{'PATH_INFO'} =~ m@$BlockPathAccess@; # Access is blocked
2487 # Does the pathname contain a direct referencer to BinaryMapFile
2488 dieHandler(9, "Illegal request: $ENV{'PATH_INFO'}\n") if $BinaryMapFile && $ENV{'PATH_INFO'} =~ m@\Q$BinaryMapFile\E@; # Access is blocked
2490 # SECURITY: Is PATH_INFO allowed?
2491 if($FilePattern && $ENV{'PATH_INFO'} && $ENV{'PATH_INFO'} ne '-' &&
2492 ($ENV{'PATH_INFO'} !~ m@($FilePattern)$@is))
2494 # Unsupported file types can be processed by a special raw-file
2495 if($BinaryMapFile)
2497 $ENV{'CGI_BINARY_FILE'} = $ENV{'PATH_INFO'};
2498 $ENV{'PATH_INFO'} = $BinaryMapFile;
2500 else
2502 dieHandler(10, "Illegal file\n");
2508 # End of Security Access Control
2511 ############################################################################
2513 # Get the POST part of the query and add it to the QUERY_STRING.
2516 sub Get_POST_part_of_query
2519 # If POST, Read data from stdin to QUERY_STRING
2520 if($ENV{'REQUEST_METHOD'} =~ /POST/is)
2522 # SECURITY: Check size of Query String
2523 $ENV{'CONTENT_LENGTH'} <= $MaximumQuerySize || dieHandler(11, "Query too long: $ENV{'CONTENT_LENGTH'}\n"); # Query too long
2524 my $QueryRead = 0;
2525 my $SystemRead = $ENV{'CONTENT_LENGTH'};
2526 $ENV{'QUERY_STRING'} .= '&' if length($ENV{'QUERY_STRING'}) > 0;
2527 while($SystemRead > 0)
2529 $QueryRead = sysread(STDIN, $Post, $SystemRead); # Limit length
2530 $ENV{'QUERY_STRING'} .= $Post;
2531 $SystemRead -= $QueryRead;
2533 # Update decoded Query String
2534 $default_values{CGI_Decoded_QS} = YOUR_CGIQUERYDECODE();
2535 $default_values{CGI_Content_Length} =
2536 length($default_values{CGI_Decoded_QS});
2540 # End of getting POST part of query
2543 ############################################################################
2545 # Start (HTML) output and logging
2546 # (if there are irregularities, it can kill the current process)
2549 sub Initialize_output
2551 # Construct the REAL file path (except for STDIN on the command line)
2552 my $file_path = $ENV{'PATH_INFO'} ne '-' ? $SS_PUB . $ENV{'PATH_INFO'} : '-';
2553 $file_path =~ s/\?.*$//; # Remove query
2554 # This is only necessary if your server does not catch ../ directives
2555 $file_path !~ m@\.\./@ || dieHandler(12, "Illegal ../ Construct\n"); # SECURITY: Do not allow ../ constructs
2557 # Block STDIN use (-) if CGIscriptor is servicing a HTTP request
2558 if($file_path eq '-')
2560 dieHandler(13, "STDIN request in On Line system\n") if $BLOCK_STDIN_HTTP_REQUEST
2561 && ($ENV{'SERVER_SOFTWARE'}
2562 || $ENV{'SERVER_NAME'}
2563 || $ENV{'GATEWAY_INTERFACE'}
2564 || $ENV{'SERVER_PROTOCOL'}
2565 || $ENV{'SERVER_PORT'}
2566 || $ENV{'REMOTE_ADDR'}
2567 || $ENV{'HTTP_USER_AGENT'});
2572 if($ClientLog)
2574 open(ClientLog, ">>$ClientLog");
2575 print ClientLog "$LocalTime | ",
2576 ($ENV{REMOTE_USER} || "-"), " ",
2577 ($ENV{REMOTE_IDENT} || "-"), " ",
2578 ($ENV{REMOTE_HOST} || "-"), " ",
2579 $ENV{REMOTE_ADDR}, " ",
2580 $ENV{PATH_INFO}, " ",
2581 $ENV{'CGI_BINARY_FILE'}, " ",
2582 ($default_values{CGI_Content_Length} || "-"),
2583 "\n";
2584 close(ClientLog);
2586 if($QueryLog)
2588 open(QueryLog, ">>$QueryLog");
2589 print QueryLog "$LocalTime\n",
2590 ($ENV{REMOTE_USER} || "-"), " ",
2591 ($ENV{REMOTE_IDENT} || "-"), " ",
2592 ($ENV{REMOTE_HOST} || "-"), " ",
2593 $ENV{REMOTE_ADDR}, ": ",
2594 $ENV{PATH_INFO}, " ",
2595 $ENV{'CGI_BINARY_FILE'}, "\n";
2597 # Write Query to Log file
2598 print QueryLog $default_values{CGI_Decoded_QS}, "\n\n";
2599 close(QueryLog);
2602 # Return the file path
2603 return $file_path;
2606 # End of Initialize output
2609 ############################################################################
2611 # Handle login access
2613 # Access is based on a valid session ticket.
2614 # Session tickets should be dependend on user name
2615 # and IP address. The patterns of URLs for which a
2616 # session ticket is needed and the login URL are stored in
2617 # %LoginRequiredPatterns as:
2618 # 'RegEx pattern' -> 'SessionPath\tPasswordPath\tLogin URL'
2621 sub Log_In_Access # () -> 0 = Access Allowed, Login page if access is not allowed
2623 # No patterns, no login
2624 return 0 unless %LoginRequiredPatterns;
2626 # Get and initialize values
2627 my ($SessionPath, $PasswordsPath, $Login, $valid_duration) = ("", "", "", 0);
2628 my $PATH_INFO = $ENV{'PATH_INFO'};
2629 my $REMOTE_ADDR = $ENV{'REMOTE_ADDR'};
2631 CGIexecute::defineCGIvariable('LOGINTICKET', "");
2632 my $LOGINTICKET = ${"CGIexecute::LOGINTICKET"};
2635 CGIexecute::defineCGIvariable('SESSIONTICKET', "");
2636 my $SESSIONTICKET = ${"CGIexecute::SESSIONTICKET"};
2638 foreach my $pattern (keys(%LoginRequiredPatterns))
2640 if($PATH_INFO =~ m#$pattern#)
2642 # Fall through a sieve of requirements
2643 ($SessionPath, $PasswordsPath, $Login) = split(/\t/, $LoginRequiredPatterns{$pattern});
2645 # Is there a login ticket of this name?
2646 if($LOGINTICKET)
2648 goto Login unless (-s "$SessionPath/$LOGINTICKET");
2649 goto Login unless (-s "$PasswordPath/$username");
2650 my $ticket_valid = check_ticket_validity("LOGIN", "$SessionPath/$LOGINTICKET", $REMOTE_ADDR, ".");
2651 goto Login unless $ticket_valid;
2653 # Authorize
2654 CGIexecute::defineCGIvariable('username', "");
2655 my $username = ${"CGIexecute::username"};
2656 CGIexecute::defineCGIvariable('password', "");
2657 my $password = ${"CGIexecute::password"};
2659 $SESSIONTICKET = authorize_login("$SessionPath/$LOGINTICKET", "$PasswordPath/$username", $password);
2660 if($SESSIONTICKET)
2662 create_session_ticket("$SessionPath/$SESSIONTICKET", "$SessionPath/$LOGINTICKET");
2666 # Is there a session ticket of this name?
2667 if($SESSIONTICKET)
2669 goto Login unless $SESSIONTICKET && (-s "$SessionPath/$SESSIONTICKET");
2670 my $ticket_valid = check_ticket_validity("SESSION", "$SessionPath/$SESSIONTICKET", $REMOTE_ADDR, $PATH_INFO);
2671 goto Login unless $ticket_valid;
2673 goto Login;
2675 return 0;
2678 return 0;
2680 Login:
2682 return $Login;
2685 sub authorize_login # ($loginfile, $authorisation, $password) => SESSIONTICKET First two arguments are file paths
2687 my $loginfile = shift || "";
2688 my $authorization = shift || "";
2689 my $password = shift || "";
2691 open(SESSION, "<$loginfile") || die "$loginfile: $!\n";
2692 my @sessionlines = <SESSION>;
2693 close(SESSION);
2695 open(SESSION, "<$authorization") || die "$authorization: $!\n";
2696 my @authorizationlines = <SESSION>;
2697 close(SESSION);
2699 # Get Randomsalt
2700 my @tmp = grep(/^\s*Randomsalt:\s+/, @sessionlines);
2701 my $Randomsalt = $tmp[0];
2702 $Randomsalt =~ s/^\s*Randomsalt:\s+(\S*)/\1/isg;
2704 return "" unless $Randomsalt;
2705 @tmp = grep(/^\s*Password:\s+/, @authorizationlines);
2706 my $storedpassword = $tmp[0];
2707 $storedpassword =~ s/^\s*Password:\s+(\S*)/\1/isg;
2708 return "" unless $storedpassword;
2709 my $Hashedpassword = `echo "$Randomsalt$storedpassword"|cur -f 1 -d" "`;
2710 chomp($Hashedpassword);
2712 # Did login succeed?
2713 return "" unless $password eq $Hashedpassword;
2715 # Extract Session Ticket
2716 @tmp = grep(/^\s*Session:\s+/ig, @sessionlines);
2717 my $sessionfile = $tmp[0];
2718 $sessionfile =~ s/^\s*Session:\s+(\S*)/\1/isg;
2719 $sessionfile = "" if -x "$SessionPath/$sessionfile";
2721 return $sessionfile;
2724 sub create_session_file #($sessionfile, $loginfile) -> Is $loginfile deleted? 0/1
2726 my $sessionfile = shift || "";
2727 my $loginfile = shift || "";
2729 open(LOGIN, "<$loginfile") || die "$loginfile: $!\n";
2730 my @loginlines = <LOGIN>;
2731 close(LOGIN);
2732 my @tmp = grep(/^\s*Randomsalt:\s+/, @sessionlines);
2733 my $IPaddress = $tmp[0];
2734 $IPaddress =~ s/^\s*IPaddress:\s+(\S*)/\1/isg;
2735 my $AllowedPaths = "^/Private/";
2736 my $Expires = "12h";
2738 open(SESSION, ">$sessionfile") || die "$sessionfile: $!\n";
2739 print SESSION << "ENDOFSESSIONTICKET";
2740 Type: SESSION
2741 IPaddress: $IPaddress
2742 AllowedPaths: $AllowedPaths
2743 Expires: $Expires
2744 ENDOFSESSIONTICKET
2745 close(SESSION);
2747 # Login file should now be removed
2748 return unlink($loginfile);
2751 sub check_ticket_validity # ($type, $ticket, $address, $path)
2753 my $type = shift || "SESSION";
2754 my $ticket = shift || "";
2755 my $address = shift || "";
2756 my $path = shift || "";
2758 # Is there a session ticket of this name?
2759 return 0 unless -s "$ticket";
2761 # There is a session ticket, is it linked to this IP address?
2762 my $ticket = read_ticket($ticket);
2764 # Is this the right type of ticket
2765 return unless $ticket->{"Type"}->[0] eq $type;
2767 # Does the IP address match?
2768 $IPmatches = 0;
2769 for my $IPpattern (@{$ticket->{"IPaddress"}})
2771 ++$IPmatches if $address =~ m#^$IPpattern#ig;
2773 return 0 unless !$ticket->{"IPaddress"} || $IPmatches;
2775 # Is the path allowed
2776 my $Pathmatches = 0;
2777 foreach my $Allowedline (@{$ticket->{"AllowedPaths"}})
2779 chomp($Allowedline);
2780 if($Allowedline =~ /^\s*AllowedPaths:\s+(.*)$/)
2782 $Pathpattern = $1;
2783 ++$Pathmatches if $path =~ m#$Pathpattern#ig;
2786 return 0 unless !@{$ticket->{"AllowedPaths"}} || $Pathmatches;
2788 # Is the ticket expired?
2789 my $Expired = 0;
2790 if($ticket->{"Expires"} && @{$ticket->{"Expires"}})
2792 my $CurrentTime = time();
2793 ++$Expired if($CurrentTime > $ticket->{"Expires"}->[0]);
2795 return 0 if $Expired;
2797 return 1;
2801 sub read_ticket # ($ticketfile) -> &%ticket
2803 my $ticketfile = shift || "";
2804 my $ticket = {};
2805 if($ticketfile && -s $ticketfile)
2807 open(TICKETFILE, "<$ticketfile") || die "$ticketfile: $!\n";
2808 my @alllines = <TICKETFILE>;
2809 close(TICKETFILE);
2810 foreach my $currentline (@alllines)
2812 if($currentline =~ /^\s*(\S[^\:]+)\:\s+(.*)\s*$/)
2814 my $Label = $1;
2815 my $Value = $2;
2816 # Recalculate expire date from relative time
2817 if($Label =~ /^Expires$/ig && $Value =~ /^\+/)
2819 # Get SessionTicket file stats
2820 my ($dev,$ino,$mode,$nlink,$uid,$gid,$rdev,$size,$atime,$mtime,$ctime,$blksize,$blocks)
2821 = stat("$ticket");
2822 if($Value =~ /^\+(\d+)\s*d(ays)?\s*$/)
2824 $ExpireTime = 24*3600*$1;
2826 elsif($Value =~ /^\+(\d+)\s*m(inutes)?\s*$/)
2828 $ExpireTime = 60*$1;
2830 elsif($Value =~ /^\+(\d+)\s*h(ours)?\s*$/)
2832 $ExpireTime = 3600*$1;
2834 elsif($Value =~ /^\+(\d+)\s*s(econds)?\s*$/)
2836 $ExpireTime = $1;
2838 elsif($Value =~ /^\+(\d+)\s*$/)
2840 $ExpireTime = $1;
2843 my $ActualExpireTime = $ExpireTime + $ctime;
2844 $Value = $ActualExpireTime;
2846 $ticket->{$Label} = () unless exists($ticket->{$Label});
2847 push(@{$ticket->{$Label}}, $Value);
2851 if(exists($ticket->{Expires}))
2853 @{$ticket->{Expires}} = sort(@{$ticket->{Expires}});
2855 return $ticket;
2858 # End of Handle login access
2861 ############################################################################
2863 # Handle foreign interpreters (i.e., scripting languages)
2865 # Insert perl code to execute scripts in foreign scripting languages.
2866 # Actually, the scripts inside the <SCRIPT></SCRIPT> blocks are piped
2867 # into an interpreter.
2868 # The code presented here is fairly confusing because it
2869 # actually writes perl code code to the output.
2871 # A table with the file handles
2872 %SCRIPTINGINPUT = ();
2874 # A function to clean up Client delivered CGI parameter values
2875 # (i.e., quote all odd characters)
2876 %SHRUBcharacterTR =
2878 "\'" => '&#39;',
2879 "\`" => '&#96;',
2880 "\"" => '&quot;',
2881 '&' => '&amper;',
2882 "\\" => '&#92;'
2885 sub shrubCGIparameter # ($String) -> Cleaned string
2887 my $String = shift || "";
2889 # Change all quotes [`'"] into HTML character entities
2890 my ($Char, $Transcript) = ('&', $SHRUBcharacterTR{'&'});
2892 # Protect &
2893 $String =~ s/\Q$Char\E/$Transcript/isg if $Transcript;
2895 while( ($Char, $Transcript) = each %SHRUBcharacterTR)
2897 next if $Char eq '&';
2898 $String =~ s/\Q$Char\E/$Transcript/isg;
2901 # Replace newlines
2902 $String =~ s/[\n]/\\n/g;
2903 # Replace control characters with their backslashed octal ordinal numbers
2904 $String =~ s/([^\S \t])/(sprintf("\\0%o", ord($1)))/eisg; #
2905 $String =~ s/([\x00-\x08\x0A-\x1F])/(sprintf("\\0%o", ord($1)))/eisg; #
2907 return $String;
2911 # The initial open statements: Open a pipe to the foreign script interpreter
2912 sub OpenForeignScript # ($ContentType) -> $DirectivePrefix
2914 my $ContentType = lc(shift) || return "";
2915 my $NewDirective = "";
2917 return $NewDirective if($SCRIPTINGINPUT{$ContentType});
2919 # Construct a unique file handle name
2920 $SCRIPTINGFILEHANDLE = uc($ContentType);
2921 $SCRIPTINGFILEHANDLE =~ s/\W/\_/isg;
2922 $SCRIPTINGINPUT{$ContentType} = $SCRIPTINGFILEHANDLE
2923 unless $SCRIPTINGINPUT{$ContentType};
2925 # Create the relevant script: Open the pipe to the interpreter
2926 $NewDirective .= <<"BLOCKCGISCRIPTOROPEN";
2927 # Open interpreter for '$ContentType'
2928 # Open pipe to interpreter (if it isn't open already)
2929 open($SCRIPTINGINPUT{$ContentType}, "|$ScriptingLanguages{$ContentType}") || main::dieHandler(14, "$ContentType: \$!\\n");
2930 BLOCKCGISCRIPTOROPEN
2932 # Insert Initialization code and CGI variables
2933 $NewDirective .= InitializeForeignScript($ContentType);
2935 # Ready
2936 return $NewDirective;
2940 # The final closing code to stop the interpreter
2941 sub CloseForeignScript # ($ContentType) -> $DirectivePrefix
2943 my $ContentType = lc(shift) || return "";
2944 my $NewDirective = "";
2946 # Do nothing unless the pipe realy IS open
2947 return "" unless $SCRIPTINGINPUT{$ContentType};
2949 # Initial comment
2950 $NewDirective .= "\# Close interpreter for '$ContentType'\n";
2953 # Write the Postfix code
2954 $NewDirective .= CleanupForeignScript($ContentType);
2956 # Create the relevant script: Close the pipe to the interpreter
2957 $NewDirective .= <<"BLOCKCGISCRIPTORCLOSE";
2958 close($SCRIPTINGINPUT{$ContentType}) || main::dieHandler(15, \"$ContentType: \$!\\n\");
2959 select(STDOUT); \$|=1;
2961 BLOCKCGISCRIPTORCLOSE
2963 # Remove the file handler of the foreign script
2964 delete($SCRIPTINGINPUT{$ContentType});
2966 return $NewDirective;
2970 # The initialization code for the foreign script interpreter
2971 sub InitializeForeignScript # ($ContentType) -> $DirectivePrefix
2973 my $ContentType = lc(shift) || return "";
2974 my $NewDirective = "";
2976 # Add initialization code
2977 if($ScriptingInitialization{$ContentType})
2979 $NewDirective .= <<"BLOCKCGISCRIPTORINIT";
2980 # Initialization Code for '$ContentType'
2981 # Select relevant output filehandle
2982 select($SCRIPTINGINPUT{$ContentType}); \$|=1;
2984 # The Initialization code (if any)
2985 print $SCRIPTINGINPUT{$ContentType} <<'${ContentType}INITIALIZATIONCODE';
2986 $ScriptingInitialization{$ContentType}
2987 ${ContentType}INITIALIZATIONCODE
2989 BLOCKCGISCRIPTORINIT
2992 # Add all CGI variables defined
2993 if(exists($ScriptingCGIvariables{$ContentType}))
2995 # Start writing variable definitions to the Interpreter
2996 if($ScriptingCGIvariables{$ContentType})
2998 $NewDirective .= <<"BLOCKCGISCRIPTORVARDEF";
2999 # CGI variables (from the %default_values table)
3000 print $SCRIPTINGINPUT{$ContentType} << '${ContentType}CGIVARIABLES';
3001 BLOCKCGISCRIPTORVARDEF
3004 my ($N, $V);
3005 foreach $N (keys(%default_values))
3007 # Determine whether the parameter has been defined
3008 # (the eval is a workaround to get at the variable value)
3009 next unless eval("defined(\$CGIexecute::$N)");
3011 # Get the value from the EXECUTION environment
3012 $V = eval("\$CGIexecute::$N");
3013 # protect control characters (i.e., convert them to \0.. form)
3014 $V = shrubCGIparameter($V);
3016 # Protect interpolated variables
3017 eval("\$CGIexecute::$N = '$V';") unless $ScriptingCGIvariables{$ContentType};
3019 # Print the actual declaration for this scripting language
3020 if($ScriptingCGIvariables{$ContentType})
3022 $NewDirective .= sprintf($ScriptingCGIvariables{$ContentType}, $N, $V);
3023 $NewDirective .= "\n";
3027 # Stop writing variable definitions to the Interpreter
3028 if($ScriptingCGIvariables{$ContentType})
3030 $NewDirective .= <<"BLOCKCGISCRIPTORVARDEFEND";
3031 ${ContentType}CGIVARIABLES
3032 BLOCKCGISCRIPTORVARDEFEND
3037 $NewDirective .= << "BLOCKCGISCRIPTOREND";
3039 # Select STDOUT filehandle
3040 select(STDOUT); \$|=1;
3042 BLOCKCGISCRIPTOREND
3044 return $NewDirective;
3048 # The cleanup code for the foreign script interpreter
3049 sub CleanupForeignScript # ($ContentType) -> $DirectivePrefix
3051 my $ContentType = lc(shift) || return "";
3052 my $NewDirective = "";
3054 # Return if not needed
3055 return $NewDirective unless $ScriptingCleanup{$ContentType};
3057 # Create the relevant script: Open the pipe to the interpreter
3058 $NewDirective .= <<"BLOCKCGISCRIPTORSTOP";
3059 # Cleanup Code for '$ContentType'
3060 # Select relevant output filehandle
3061 select($SCRIPTINGINPUT{$ContentType}); \$|=1;
3062 # Print Cleanup code to foreign script
3063 print $SCRIPTINGINPUT{$ContentType} <<'${ContentType}SCRIPTSTOP';
3064 $ScriptingCleanup{$ContentType}
3065 ${ContentType}SCRIPTSTOP
3067 # Select STDOUT filehandle
3068 select(STDOUT); \$|=1;
3069 BLOCKCGISCRIPTORSTOP
3071 return $NewDirective;
3075 # The prefix code for each <script></script> block
3076 sub PrefixForeignScript # ($ContentType) -> $DirectivePrefix
3078 my $ContentType = lc(shift) || return "";
3079 my $NewDirective = "";
3081 # Return if not needed
3082 return $NewDirective unless $ScriptingPrefix{$ContentType};
3084 my $Quote = "\'";
3085 # If the CGIvariables parameter is defined, but empty, interpolate
3086 # code string (i.e., $var .= << "END" i.s.o. $var .= << 'END')
3087 $Quote = '"' if exists($ScriptingCGIvariables{$ContentType}) &&
3088 !$ScriptingCGIvariables{$ContentType};
3090 # Add initialization code
3091 $NewDirective .= <<"BLOCKCGISCRIPTORPREFIX";
3092 # Prefix Code for '$ContentType'
3093 # Select relevant output filehandle
3094 select($SCRIPTINGINPUT{$ContentType}); \$|=1;
3096 # The block Prefix code (if any)
3097 print $SCRIPTINGINPUT{$ContentType} <<$Quote${ContentType}PREFIXCODE$Quote;
3098 $ScriptingPrefix{$ContentType}
3099 ${ContentType}PREFIXCODE
3100 # Select STDOUT filehandle
3101 select(STDOUT); \$|=1;
3102 BLOCKCGISCRIPTORPREFIX
3104 return $NewDirective;
3108 # The postfix code for each <script></script> block
3109 sub PostfixForeignScript # ($ContentType) -> $DirectivePrefix
3111 my $ContentType = lc(shift) || return "";
3112 my $NewDirective = "";
3114 # Return if not needed
3115 return $NewDirective unless $ScriptingPostfix{$ContentType};
3117 my $Quote = "\'";
3118 # If the CGIvariables parameter is defined, but empty, interpolate
3119 # code string (i.e., $var .= << "END" i.s.o. $var .= << 'END')
3120 $Quote = '"' if exists($ScriptingCGIvariables{$ContentType}) &&
3121 !$ScriptingCGIvariables{$ContentType};
3123 # Create the relevant script: Open the pipe to the interpreter
3124 $NewDirective .= <<"BLOCKCGISCRIPTORPOSTFIX";
3125 # Postfix Code for '$ContentType'
3126 # Select filehandle to interpreter
3127 select($SCRIPTINGINPUT{$ContentType}); \$|=1;
3128 # Print postfix code to foreign script
3129 print $SCRIPTINGINPUT{$ContentType} <<$Quote${ContentType}SCRIPTPOSTFIX$Quote;
3130 $ScriptingPostfix{$ContentType}
3131 ${ContentType}SCRIPTPOSTFIX
3132 # Select STDOUT filehandle
3133 select(STDOUT); \$|=1;
3134 BLOCKCGISCRIPTORPOSTFIX
3136 return $NewDirective;
3139 sub InsertForeignScript # ($ContentType, $directive, @SRCfile) -> $NewDirective
3141 my $ContentType = lc(shift) || return "";
3142 my $directive = shift || return "";
3143 my @SRCfile = @_;
3144 my $NewDirective = "";
3146 my $Quote = "\'";
3147 # If the CGIvariables parameter is defined, but empty, interpolate
3148 # code string (i.e., $var .= << "END" i.s.o. $var .= << 'END')
3149 $Quote = '"' if exists($ScriptingCGIvariables{$ContentType}) &&
3150 !$ScriptingCGIvariables{$ContentType};
3152 # Create the relevant script
3153 $NewDirective .= <<"BLOCKCGISCRIPTORINSERT";
3154 # Insert Code for '$ContentType'
3155 # Select filehandle to interpreter
3156 select($SCRIPTINGINPUT{$ContentType}); \$|=1;
3157 BLOCKCGISCRIPTORINSERT
3159 # Use SRC feature files
3160 my $ThisSRCfile;
3161 while($ThisSRCfile = shift(@_))
3163 # Handle blocks
3164 if($ThisSRCfile =~ /^\s*\{\s*/)
3166 my $Block = $';
3167 $Block = $` if $Block =~ /\s*\}\s*$/;
3168 $NewDirective .= <<"BLOCKCGISCRIPTORSRCBLOCK";
3169 print $SCRIPTINGINPUT{$ContentType} <<$Quote${ContentType}SRCBLOCKCODE$Quote;
3170 $Block
3171 ${ContentType}SRCBLOCKCODE
3172 BLOCKCGISCRIPTORSRCBLOCK
3174 next;
3177 # Handle files
3178 $NewDirective .= <<"BLOCKCGISCRIPTORSRCFILES";
3179 # Read $ThisSRCfile
3180 open(SCRIPTINGSOURCE, "<$ThisSRCfile") || main::dieHandler(16, "$ThisSRCfILE: \$!");
3181 while(<SCRIPTINGSOURCE>)
3183 print $SCRIPTINGINPUT{$ContentType} \$_;
3185 close(SCRIPTINGSOURCE);
3187 BLOCKCGISCRIPTORSRCFILES
3191 # Add the directive
3192 if($directive)
3194 $NewDirective .= <<"BLOCKCGISCRIPTORINSERT";
3195 print $SCRIPTINGINPUT{$ContentType} <<$Quote${ContentType}DIRECTIVECODE$Quote;
3196 $directive
3197 ${ContentType}DIRECTIVECODE
3198 BLOCKCGISCRIPTORINSERT
3202 $NewDirective .= <<"BLOCKCGISCRIPTORSELECT";
3203 # Select STDOUT filehandle
3204 select(STDOUT); \$|=1;
3205 BLOCKCGISCRIPTORSELECT
3207 # Ready
3208 return $NewDirective;
3211 sub CloseAllForeignScripts # Call CloseForeignScript on all open scripts
3213 my $ContentType;
3214 foreach $ContentType (keys(%SCRIPTINGINPUT))
3216 my $directive = CloseForeignScript($ContentType);
3217 print STDERR "\nDirective $CGI_Date: ", $directive;
3218 CGIexecute->evaluate($directive);
3223 # End of handling foreign (external) scripting languages.
3225 ############################################################################
3227 # A subroutine to handle "nested" quotes, it cuts off the leading
3228 # item or quoted substring
3229 # E.g.,
3230 # ' A_word and more words' -> @('A_word', ' and more words')
3231 # '"quoted string" The rest' -> @('quoted string', ' The rest')
3232 # (this is needed for parsing the <TAGS> and their attributes)
3233 my $SupportedQuotes = "\'\"\`\(\{\[";
3234 my %QuotePairs = ('('=>')','['=>']','{'=>'}'); # Brackets
3235 sub ExtractQuotedItem # ($String) -> @($QuotedString, $RestOfString)
3237 my @Result = ();
3238 my $String = shift || return @Result;
3240 if($String =~ /^\s*([\w\/\-\.]+)/is)
3242 push(@Result, $1, $');
3244 elsif($String =~ /^\s*(\\?)([\Q$SupportedQuotes\E])/is)
3246 my $BackSlash = $1 || "";
3247 my $OpenQuote = $2;
3248 my $CloseQuote = $OpenQuote;
3249 $CloseQuote = $QuotePairs{$OpenQuote} if $QuotePairs{$OpenQuote};
3251 if($BackSlash)
3253 $String =~ /^\s*\\\Q$OpenQuote\E/i;
3254 my $Onset = $';
3255 $Onset =~ /\\\Q$CloseQuote\E/i;
3256 my $Rest = $';
3257 my $Item = $`;
3258 push(@Result, $Item, $Rest);
3261 else
3263 $String =~ /^\s*\Q$OpenQuote\E([^\Q$CloseQuote\E]*)\Q$CloseQuote\E/i;
3264 push(@Result, $1, $');
3267 else
3269 push(@Result, "", $String);
3271 return @Result;
3274 # Now, start with the real work
3276 # Control the output of the Content-type: text/html\n\n message
3277 my $SupressContentType = 0;
3279 # Process a file
3280 sub ProcessFile # ($file_path)
3282 my $file_path = shift || return 0;
3285 # Generate a unique file handle (for recursions)
3286 my @SRClist = ();
3287 my $FileHandle = "file";
3288 my $n = 0;
3289 while(!eof($FileHandle.$n)) {++$n;};
3290 $FileHandle .= $n;
3292 # Start HTML output
3293 # Use the default Content-type if this is NOT a raw file
3294 unless(($RawFilePattern && $ENV{'PATH_INFO'} =~ m@($RawFilePattern)$@i)
3295 || $SupressContentType)
3297 $ENV{'PATH_INFO'} =~ m@($FilePattern)$@i;
3298 my $ContentType = $ContentTypeTable{$1};
3299 print "Content-type: $ContentType\n";
3300 print "\n";
3301 $SupressContentType = 1; # Content type has been printed
3305 # Get access to the actual data. This can be from RAM (by way of an
3306 # environment variable) or by opening a file.
3308 # Handle the use of RAM images (file-data is stored in the
3309 # $CGI_FILE_CONTENTS environment variable)
3310 # Note that this environment variable will be cleared, i.e., it is strictly for
3311 # single-use only!
3312 if($ENV{$CGI_FILE_CONTENTS})
3314 # File has been read already
3315 $_ = $ENV{$CGI_FILE_CONTENTS};
3316 # Sorry, you have to do the reading yourself (dynamic document creation?)
3317 # NOTE: you must read the whole document at once
3318 if($_ eq '-')
3320 $_ = eval("\@_=('$file_path'); do{$ENV{$CGI_DATA_ACCESS_CODE}}");
3322 else # Clear environment variable
3324 $ENV{$CGI_FILE_CONTENTS} = '-';
3327 # Open Only PLAIN TEXT files (or STDIN) and NO executable files (i.e., scripts).
3328 # THIS IS A SECURITY FEATURE!
3329 elsif($file_path eq '-' || (-e "$file_path" && -r _ && -T _ && -f _ && ! (-x _ || -X _) ))
3331 open($FileHandle, $file_path) || dieHandler(17, "<h2>File not found</h2>\n");
3332 push(@OpenFiles, $file_path);
3333 $_ = <$FileHandle>; # Read first line
3335 else
3337 print "<h2>File not found</h2>\n";
3338 dieHandler(18, "$file_path\n");
3341 $| = 1; # Flush output buffers
3343 # Initialize variables
3344 my $METAarguments = ""; # The CGI arguments from the latest META tag
3345 my @METAvalues = (); # The ''-quoted CGI values from the latest META tag
3346 my $ClosedTag = 0; # <TAG> </TAG> versus <TAG/>
3349 # Send document to output
3350 # Process the requested document.
3351 # Do a loop BEFORE reading input again (this catches the RAM/Database
3352 # type of documents).
3353 do {
3356 # Handle translations if needed
3358 performTranslation(\$_) if $TranslationPaths;
3360 # Catch <SCRIPT LANGUAGE="PERL" TYPE="text/ssperl" > directives in $_
3361 # There can be more than 1 <SCRIPT> or META tags on a line
3362 while(/\<\s*(SCRIPT|META|DIV|INS)\s/is)
3364 my $directive = "";
3365 # Store rest of line
3366 my $Before = $`;
3367 my $ScriptTag = $&;
3368 my $After = $';
3369 my $TagType = uc($1);
3370 # The before part can be send to the output
3371 print $Before;
3373 # Read complete Tag from after and/or file
3374 until($After =~ /([^\\])\>/)
3376 $After .= <$FileHandle>;
3377 performTranslation(\$After) if $TranslationPaths;
3380 if($After =~ /([^\\])\>/)
3382 $ScriptTag .= $`.$&; # Keep the Script Tag intact
3383 $After = $';
3385 else
3387 dieHandler(19, "Closing > not found\n");
3390 # The tag could be closed by />, we handle this in the XML way
3391 # and don't process any content (we ignore whitespace)
3392 $ClosedTag = ($ScriptTag =~ m@[^\\]/\s*\>\s*$@) ? 1 : 0;
3395 # TYPE or CLASS?
3396 my $TypeName = ($TagType =~ /META/is) ? "CONTENT" : "TYPE";
3397 $TypeName = "CLASS" if $TagType eq 'DIV' || $TagType eq 'INS';
3399 # Parse <SCRIPT> or <META> directive
3400 # If NOT (TYPE|CONTENT)="text/ssperl" (i.e., $ServerScriptContentType),
3401 # send the line to the output and go to the next loop
3402 my $CurrentContentType = "";
3403 if($ScriptTag =~ /(^|\s)$TypeName\s*=\s*/is)
3405 my ($Type) = ExtractQuotedItem($');
3406 $Type =~ /^\s*([\w\/\-]+)\s*[\,\;]?/;
3407 $CurrentContentType = lc($1); # Note: mime-types are "case-less"
3408 # CSS classes are aliases of $ServerScriptContentType
3409 if($TypeName eq "CLASS" && $CurrentContentType eq $ServerScriptContentClass)
3411 $CurrentContentType = $ServerScriptContentType;
3416 # Not a known server-side content type, print and continue
3417 unless(($CurrentContentType =~
3418 /$ServerScriptContentType|$ShellScriptContentType/is) ||
3419 $ScriptingLanguages{$CurrentContentType})
3421 print $ScriptTag;
3422 $_ = $After;
3423 next;
3427 # A known server-side content type, evaluate
3429 # First, handle \> and \<
3430 $ScriptTag =~ s/\\\>/\>/isg;
3431 $ScriptTag =~ s/\\\</\</isg;
3433 # Extract the CGI, SRC, ID, IF and UNLESS attributes
3434 my %ScriptTagAttributes = ();
3435 while($ScriptTag =~ /(^|\s)(CGI|IF|UNLESS|SRC|ID)\s*=\s*/is)
3437 my $Attribute = $2;
3438 my $Rest = $';
3439 my $Value = "";
3440 ($Value, $ScriptTag) = ExtractQuotedItem($Rest);
3441 $ScriptTagAttributes{uc($Attribute)} = $Value;
3445 # The attribute used to define the CGI variables
3446 # Extract CGI-variables from
3447 # <META CONTENT="text/ssperl; CGI='' SRC=''">
3448 # <SCRIPT TYPE='text/ssperl' CGI='' SRC=''>
3449 # <DIV CLASS='ssperl' CGI='' SRC='' ID=""> tags
3450 # <INS CLASS='ssperl' CGI='' SRC='' ID=""> tags
3451 if($ScriptTagAttributes{'CGI'})
3453 @ARGV = (); # Reset ARGV
3454 $ARGC = 0;
3455 $METAarguments = ""; # Reset the META CGI arguments
3456 @METAvalues = ();
3457 my $Meta_CGI = $ScriptTagAttributes{'CGI'};
3459 # Process default values of variables ($<name> = 'default value')
3460 # Allowed quotes are '', "", ``, (), [], and {}
3461 while($Meta_CGI =~ /(^\s*|[^\\])([\$\@\%]?)([\w\-]+)\s*/is)
3463 my $varType = $2 || '$'; # Variable or list
3464 my $name = $3; # The Name
3465 my $default = "";
3466 $Meta_CGI = $';
3468 if($Meta_CGI =~ /^\s*\=\s*/is)
3470 # Locate (any) default value
3471 ($default, $Meta_CGI) = ExtractQuotedItem($'); # Cut the parameter from the CGI
3473 $RemainingTag = $Meta_CGI;
3476 # Define CGI (or ENV) variable, initalize it from the
3477 # Query string or the default value
3479 # Also construct the @ARGV and @_ arrays. This allows other (SRC=) Perl
3480 # scripts to access the CGI arguments defined in the META tag
3481 # (Not for CGI inside <SCRIPT> tags)
3482 if($varType eq '$')
3484 CGIexecute::defineCGIvariable($name, $default)
3485 || dieHandler(20, "INVALID CGI name/value pair ($name, $default)\n");
3486 push(@METAvalues, "'".${"CGIexecute::$name"}."'");
3487 # Add value to the @ARGV list
3488 push(@ARGV, ${"CGIexecute::$name"});
3489 ++$ARGC;
3491 elsif($varType eq '@')
3493 CGIexecute::defineCGIvariableList($name, $default)
3494 || dieHandler(21, "INVALID CGI name/value list pair ($name, $default)\n");
3495 push(@METAvalues, "'".join("'", @{"CGIexecute::$name"})."'");
3496 # Add value to the @ARGV list
3497 push(@ARGV, @{"CGIexecute::$name"});
3498 $ARGC = scalar(@CGIexecute::ARGV);
3500 elsif($varType eq '%')
3502 CGIexecute::defineCGIvariableHash($name, $default)
3503 || dieHandler(22, "INVALID CGI name/value hash pair ($name, $default)\n");
3504 my @PairList = map {"$_ => ".${"CGIexecute::$name"}{$_}} keys(%{"CGIexecute::$name"});
3505 push(@METAvalues, "'".join("'", @PairList)."'");
3506 # Add value to the @ARGV list
3507 push(@ARGV, %{"CGIexecute::$name"});
3508 $ARGC = scalar(@CGIexecute::ARGV);
3511 # Store the values for internal and later use
3512 $METAarguments .= "$varType".$name.","; # A string of CGI variable names
3514 push(@METAvalues, "\'".eval("\"$varType\{CGIexecute::$name\}\"")."\'"); # ALWAYS add '-quotes around values
3519 # The IF (conditional execution) Attribute
3520 # Evaluate the condition and stop unless it evaluates to true
3521 if($ScriptTagAttributes{'IF'})
3523 my $IFcondition = $ScriptTagAttributes{'IF'};
3525 # Convert SCRIPT calls, ./<script>
3526 $IFcondition =~ s@([\W]|^)\./([\S])@$1$SCRIPT_SUB$2@g;
3528 # Convert FILE calls, ~/<file>
3529 $IFcondition =~ s@([\W])\~/([\S])@$1$HOME_SUB$2@g;
3531 # Block execution if necessary
3532 unless(CGIexecute->evaluate($IFcondition))
3534 %ScriptTagAttributes = ();
3535 $CurrentContentType = "";
3539 # The UNLESS (conditional execution) Attribute
3540 # Evaluate the condition and stop if it evaluates to true
3541 if($ScriptTagAttributes{'UNLESS'})
3543 my $UNLESScondition = $ScriptTagAttributes{'UNLESS'};
3545 # Convert SCRIPT calls, ./<script>
3546 $UNLESScondition =~ s@([\W]|^)\./([\S])@$1$SCRIPT_SUB$2@g;
3548 # Convert FILE calls, ~/<file>
3549 $UNLESScondition =~ s@([\W])\~/([\S])@$1$HOME_SUB$2@g;
3551 # Block execution if necessary
3552 if(CGIexecute->evaluate($UNLESScondition))
3554 %ScriptTagAttributes = ();
3555 $CurrentContentType = "";
3559 # The SRC (Source File) Attribute
3560 # Extract any source script files and add them in
3561 # front of the directive
3562 # The SRC list should be emptied
3563 @SRClist = ();
3564 my $SRCtag = "";
3565 my $Prefix = 1;
3566 my $PrefixDirective = "";
3567 my $PostfixDirective = "";
3568 # There is a SRC attribute
3569 if($ScriptTagAttributes{'SRC'})
3571 $SRCtag = $ScriptTagAttributes{'SRC'};
3572 # Remove "file://" prefixes
3573 $SRCtag =~ s@([^\w\/\\]|^)file\://([^\s\/\@\=])@$1$2@gis;
3574 # Expand script filenames "./Script"
3575 $SRCtag =~ s@([^\w\/\\]|^)\./([^\s\/\@\=])@$1$SCRIPT_SUB/$2@gis;
3576 # Expand script filenames "~/Script"
3577 $SRCtag =~ s@([^\w\/\\]|^)\~/([^\s\/\@\=])@$1$HOME_SUB/$2@gis;
3580 # File source tags
3581 while($SRCtag =~ /\S/is)
3583 my $SRCdirective = "";
3585 # Pseudo file, just a switch to go from PREFIXING to POSTFIXING
3586 # SRC files
3587 if($SRCtag =~ /^[\s\;\,]*(POSTFIX|PREFIX)([^$FileAllowedChars]|$)/is)
3589 my $InsertionPlace = $1;
3590 $SRCtag = $2.$';
3592 $Prefix = $InsertionPlace =~ /POSTFIX/i ? 0 : 1;
3593 # Go to next round
3594 next;
3596 # {}-blocks are just evaluated by "do"
3597 elsif($SRCtag =~ /^[\s\;\,]*\{/is)
3599 my $SRCblock = $';
3600 if($SRCblock =~ /\}[\s\;\,]*([^\}]*)$/is)
3602 $SRCblock = $`;
3603 $SRCtag = $1.$';
3604 # SAFEqx shell script blocks
3605 if($CurrentContentType =~ /$ShellScriptContentType/is)
3607 # Handle ''-quotes inside the script
3608 $SRCblock =~ s/[\']/\\$&/gis;
3610 $SRCblock = "print do { SAFEqx(\'".$SRCblock."\'); };'';";
3611 $SRCdirective .= $SRCblock."\n";
3613 # do { SRCblocks }
3614 elsif($CurrentContentType =~ /$ServerScriptContentType/is)
3616 $SRCblock = "print do { $SRCblock };'';";
3617 $SRCdirective .= $SRCblock."\n";
3619 else # The interpreter should handle this
3621 push(@SRClist, "{ $SRCblock }");
3625 else
3626 { dieHandler(23, "Closing \} missing\n");};
3628 # Files are processed as Text or Executable files
3629 elsif($SRCtag =~ /[\s\;\,]*([$FileAllowedChars]+)[\;\,\s]*/is)
3631 my $SrcFile = $1;
3632 $SRCtag = $';
3634 # We are handling one of the external interpreters
3635 if($ScriptingLanguages{$CurrentContentType})
3637 push(@SRClist, $SrcFile);
3639 # We are at the start of a DIV tag, just load all SRC files and/or URL's
3640 elsif($TagType eq 'DIV' || $TagType eq 'INS') # All files are prepended in DIV's
3642 # $SrcFile is a URL pointing to an HTTP or FTP server
3643 if($SrcFile =~ m!^([a-z]+)\://!)
3645 my $URLoutput = CGIscriptor::read_url($SrcFile);
3646 $SRCdirective .= $URLoutput;
3648 # SRC file is an existing file
3649 elsif(-e "$SrcFile")
3651 open(DIVSOURCE, "<$SrcFile") || dieHandler(24, "<$SrcFile: $!\n");
3652 my $Content;
3653 while(sysread(DIVSOURCE, $Content, 1024) > 0)
3655 $SRCdirective .= $Content;
3657 close(DIVSOURCE);
3660 # Executable files are executed as
3661 # `$SrcFile 'ARGV[0]' 'ARGV[1]'`
3662 elsif(-x "$SrcFile")
3664 $SRCdirective .= "print \`$SrcFile @METAvalues\`;'';\n";
3666 # Handle 'standard' files, using ProcessFile
3667 elsif((-T "$SrcFile" || $ENV{$CGI_FILE_CONTENTS})
3668 && $SrcFile =~ m@($FilePattern)$@) # A recursion
3671 # Do not process still open files because it can lead
3672 # to endless recursions
3673 if(grep(/^$SrcFile$/, @OpenFiles))
3674 { dieHandler(25, "$SrcFile allready opened (endless recursion)\n")};
3675 # Prepare meta arguments
3676 $SRCdirective .= '@ARGV = (' .$METAarguments.");\n" if $METAarguments;
3677 # Process the file
3678 $SRCdirective .= "main::ProcessFile(\'$SrcFile\');'';\n";
3680 elsif($SrcFile =~ m!^([a-z]+)\://!) # URL's are loaded and printed
3682 $SRCdirective .= GET_URL($SrcFile);
3684 elsif(-T "$SrcFile") # Textfiles are "do"-ed (Perl execution)
3686 $SRCdirective .= '@ARGV = (' .$METAarguments.");\n" if $METAarguments;
3687 $SRCdirective .= "do \'$SrcFile\';'';\n";
3689 else # This one could not be resolved (should be handled by BinaryMapFile)
3691 $SRCdirective .= 'print "'.$SrcFile.' cannot be used"'."\n";
3696 # Postfix or Prefix
3697 if($Prefix)
3699 $PrefixDirective .= $SRCdirective;
3701 else
3703 $PostfixDirective .= $SRCdirective;
3706 # The prefix should be handled immediately
3707 $directive .= $PrefixDirective;
3708 $PrefixDirective = "";
3712 # Handle the content of the <SCRIPT></SCRIPT> tags
3713 # Do not process the content of <SCRIPT/>
3714 if($TagType =~ /SCRIPT/is && !$ClosedTag) # The <SCRIPT> TAG
3716 my $EndScriptTag = "";
3718 # Execute SHELL scripts with SAFEqx()
3719 if($CurrentContentType =~ /$ShellScriptContentType/is)
3721 $directive .= "SAFEqx(\'";
3724 # Extract Program
3725 while($After !~ /\<\s*\/SCRIPT[^\>]*\>/is && !eof($FileHandle))
3727 $After .= <$FileHandle>;
3728 performTranslation(\$After) if $TranslationPaths;
3731 if($After =~ /\<\s*\/SCRIPT[^\>]*\>/is)
3733 $directive .= $`;
3734 $EndScriptTag = $&;
3735 $After = $';
3737 else
3739 dieHandler(26, "Missing </SCRIPT> end tag in $ENV{'PATH_INFO'}\n");
3742 # Process only when content should be executed
3743 if($CurrentContentType)
3746 # Remove all comments from Perl scripts
3747 # (NOT from OS shell scripts)
3748 $directive =~ s/[^\\\$]\#[^\n\f\r]*([\n\f\r])/$1/g
3749 if $CurrentContentType =~ /$ServerScriptContentType/i;
3751 # Convert SCRIPT calls, ./<script>
3752 $directive =~ s@([\W]|^)\./([\S])@$1$SCRIPT_SUB$2@g;
3754 # Convert FILE calls, ~/<file>
3755 $directive =~ s@([\W])\~/([\S])@$1$HOME_SUB$2@g;
3757 # Execute SHELL scripts with SAFEqx(), closing bracket
3758 if($CurrentContentType =~ /$ShellScriptContentType/i)
3760 # Handle ''-quotes inside the script
3761 $directive =~ /SAFEqx\(\'/;
3762 $directive = $`.$&;
3763 my $Executable = $';
3764 $Executable =~ s/[\']/\\$&/gs;
3766 $directive .= $Executable."\');"; # Closing bracket
3769 else
3771 $directive = "";
3774 # Handle the content of the <DIV></DIV> tags
3775 # Do not process the content of <DIV/>
3776 elsif(($TagType eq 'DIV' || $TagType eq 'INS') && !$ClosedTag) # The <DIV> TAGs
3778 my $EndScriptTag = "";
3780 # Extract Text
3781 while($After !~ /\<\s*\/$TagType[^\>]*\>/is && !eof($FileHandle))
3783 $After .= <$FileHandle>;
3784 performTranslation(\$After) if $TranslationPaths;
3787 if($After =~ /\<\s*\/$TagType[^\>]*\>/is)
3789 $directive .= $`;
3790 $EndScriptTag = $&;
3791 $After = $';
3793 else
3795 dieHandler(27, "Missing </$TagType> end tag in $ENV{'PATH_INFO'}\n");
3798 # Add the Postfixed directives (but only when it contains something printable)
3799 $directive .= "\n".$PostfixDirective if $PostfixDirective =~ /\S/;
3800 $PostfixDirective = "";
3803 # Process only when content should be handled
3804 if($CurrentContentType)
3807 # Get the name (ID), and clean it (i.e., remove anything that is NOT part of
3808 # a valid Perl name). Names should not contain $, but we can handle it.
3809 my $name = $ScriptTagAttributes{'ID'};
3810 $name =~ /^\s*[\$\@\%]?([\w\-]+)/;
3811 $name = $1;
3813 # Assign DIV contents to $NAME value OUTSIDE the CGI values!
3814 CGIexecute::defineCGIexecuteVariable($name, $directive);
3815 $directive = "";
3818 # Nothing to execute
3819 $directive = "";
3823 # Handle Foreign scripting languages
3824 if($ScriptingLanguages{$CurrentContentType})
3826 my $newDirective = "";
3827 $newDirective .= OpenForeignScript($CurrentContentType); # Only if not already done
3828 $newDirective .= PrefixForeignScript($CurrentContentType);
3829 $newDirective .= InsertForeignScript($CurrentContentType, $directive, @SRClist);
3830 $newDirective .= PostfixForeignScript($CurrentContentType);
3831 $newDirective .= CloseForeignScript($CurrentContentType); # This shouldn't be necessary
3833 $newDirective .= '"";';
3835 $directive = $newDirective;
3839 # Add the Postfixed directives (but only when it contains something printable)
3840 $directive .= "\n".$PostfixDirective if $PostfixDirective =~ /\S/;
3841 $PostfixDirective = "";
3844 # EXECUTE the script and print the results
3846 # Use this to debug the program
3847 # print STDERR "Directive $CGI_Date: \n", $directive, "\n\n";
3849 my $Result = CGIexecute->evaluate($directive) if $directive; # Evaluate as PERL code
3850 $Result =~ s/\n$//g; # Remove final newline
3852 # Print the Result of evaluating the directive
3853 # (this will handle LARGE, >64 kB output)
3854 my $BytesWritten = 1;
3855 while($Result && $BytesWritten)
3857 $BytesWritten = syswrite(STDOUT, $Result, 64);
3858 $Result = substr($Result, $BytesWritten);
3860 # print $Result; # Could be used instead of above code
3862 # Store result if wanted, i.e., if $CGIscriptorResults has been
3863 # defined in a <META> tag.
3864 push(@CGIexecute::CGIscriptorResults, $Result)
3865 if exists($default_values{'CGIscriptorResults'});
3867 # Process the rest of the input line (this could contain
3868 # another directive)
3869 $_ = $After;
3871 print $_;
3872 } while(<$FileHandle>); # Read and Test AFTER first loop!
3874 close ($FileHandle);
3875 dieHandler(28, "Error in recursion\n") unless pop(@OpenFiles) == $file_path;
3879 ###############################################################################
3881 # Call the whole package
3883 sub Handle_Request
3885 my $file_path = "";
3887 # Initialization Code
3888 Initialize_Request();
3890 # SECURITY: ACCESS CONTROL
3891 Access_Control();
3893 # Read the POST part of the query, if there is one
3894 Get_POST_part_of_query();
3896 # Start (HTML) output and logging
3897 $file_path = Initialize_output();
3899 # Check login access or divert to login procedure
3900 $Use_Login = Log_In_Access();
3901 $file_path = $Use_Login if $Use_Login;
3903 # Record which files are still open (to avoid endless recursions)
3904 my @OpenFiles = ();
3906 # Record whether the default HTML ContentType has already been printed
3907 # but only if the SERVER uses HTTP or some other protocol that might interpret
3908 # a content MIME type.
3910 $SupressContentType = !("$ENV{'SERVER_PROTOCOL'}" =~ /($ContentTypeServerProtocols)/i);
3912 # Process the specified file
3913 ProcessFile($file_path) if $file_path ne $SS_PUB;
3915 # Cleanup all open external (foreign) interpreters
3916 CloseAllForeignScripts();
3919 "" # SUCCESS
3922 # Make a single call to handle an (empty) request
3923 Handle_Request();
3926 # END OF PACKAGE MAIN
3929 ####################################################################################
3931 # The CGIEXECUTE PACKAGE
3933 ####################################################################################
3935 # Isolate the evaluation of directives as PERL code from the rest of the program.
3936 # Remember that each package has its own name space.
3937 # Note that only the FIRST argument of execute->evaluate is actually evaluated,
3938 # all other arguments are accessible inside the first argument as $_[0] to $_[$#_].
3940 package CGIexecute;
3942 sub evaluate
3944 my $self = shift;
3945 my $directive = shift;
3946 $directive = eval($directive);
3947 warn $@ if $@; # Write an error message to STDERR
3948 $directive; # Return value of directive
3952 # defineCGIexecuteVariable($name [, $value]) -> 0/1
3954 # Define and intialize variables inside CGIexecute
3955 # Does no sanity checking, for internal use only
3957 sub defineCGIexecuteVariable # ($name [, $value]) -> 0/1
3959 my $name = shift || return 0; # The Name
3960 my $value = shift || ""; # The value
3962 ${$name} = $value;
3964 return 1;
3967 # defineCGIvariable($name [, $default]) -> 0/1
3969 # Define and intialize CGI variables
3970 # Tries (in order) $ENV{$name}, the Query string and the
3971 # default value.
3972 # Removes all '-quotes etc.
3974 sub defineCGIvariable # ($name [, $default]) -> 0/1
3976 my $name = shift || return 0; # The Name
3977 my $default = shift || ""; # The default value
3979 # Remove \-quoted characters
3980 $default =~ s/\\(.)/$1/g;
3981 # Store default values
3982 $::default_values{$name} = $default if $default;
3984 # Process variables
3985 my $temp = undef;
3986 # If there is a user supplied value, it replaces the
3987 # default value.
3989 # Environment values have precedence
3990 if(exists($ENV{$name}))
3992 $temp = $ENV{$name};
3994 # Get name and its value from the query string
3995 elsif($ENV{QUERY_STRING} =~ /$name/) # $name is in the query string
3997 $temp = ::YOUR_CGIPARSE($name);
3999 # Defined values must exist for security
4000 elsif(!exists($::default_values{$name}))
4002 $::default_values{$name} = undef;
4005 # SECURITY, do not allow '- and `-quotes in
4006 # client values.
4007 # Remove all existing '-quotes
4008 $temp =~ s/([\r\f]+\n)/\n/g; # Only \n is allowed
4009 $temp =~ s/[\']/&#8217;/igs; # Remove all single quotes
4010 $temp =~ s/[\`]/&#8216;/igs; # Remove all backtick quotes
4011 # If $temp is empty, use the default value (if it exists)
4012 unless($temp =~ /\S/ || length($temp) > 0) # I.e., $temp is empty
4014 $temp = $::default_values{$name};
4015 # Remove all existing '-quotes
4016 $temp =~ s/([\r\f]+\n)/\n/g; # Only \n is allowed
4017 $temp =~ s/[\']/&#8217;/igs; # Remove all single quotes
4018 $temp =~ s/[\`]/&#8216;/igs; # Remove all backtick quotes
4020 else # Store current CGI values and remove defaults
4022 $::default_values{$name} = $temp;
4024 # Define the CGI variable and its value (in the execute package)
4025 ${$name} = $temp;
4027 # return SUCCES
4028 return 1;
4031 sub defineCGIvariableList # ($name [, $default]) -> 0/1)
4033 my $name = shift || return 0; # The Name
4034 my $default = shift || ""; # The default value
4036 # Defined values must exist for security
4037 if(!exists($::default_values{$name}))
4039 $::default_values{$name} = $default;
4042 my @temp = ();
4045 # For security:
4046 # Environment values have precedence
4047 if(exists($ENV{$name}))
4049 push(@temp, $ENV{$name});
4051 # Get name and its values from the query string
4052 if($ENV{QUERY_STRING} =~ /$name/) # $name is in the query string
4054 push(@temp, ::YOUR_CGIPARSE($name, 1)); # Extract LIST
4056 else
4058 push(@temp, $::default_values{$name});
4062 # SECURITY, do not allow '- and `-quotes in
4063 # client values.
4064 # Remove all existing '-quotes
4065 @temp = map {s/([\r\f]+\n)/\n/g; $_} @temp; # Only \n is allowed
4066 @temp = map {s/[\']/&#8217;/igs; $_} @temp; # Remove all single quotes
4067 @temp = map {s/[\`]/&#8216;/igs; $_} @temp; # Remove all backtick quotes
4069 # Store current CGI values and remove defaults
4070 $::default_values{$name} = $temp[0];
4072 # Define the CGI variable and its value (in the execute package)
4073 @{$name} = @temp;
4075 # return SUCCES
4076 return 1;
4079 sub defineCGIvariableHash # ($name [, $default]) -> 0/1) Note: '$name{""} = $default';
4081 my $name = shift || return 0; # The Name
4082 my $default = shift || ""; # The default value
4084 # Defined values must exist for security
4085 if(!exists($::default_values{$name}))
4087 $::default_values{$name} = $default;
4090 my %temp = ();
4093 # For security:
4094 # Environment values have precedence
4095 if(exists($ENV{$name}))
4097 $temp{""} = $ENV{$name};
4099 # Get name and its values from the query string
4100 if($ENV{QUERY_STRING} =~ /$name/) # $name is in the query string
4102 %temp = ::YOUR_CGIPARSE($name, -1); # Extract HASH table
4104 elsif($::default_values{$name} ne "")
4106 $temp{""} = $::default_values{$name};
4110 # SECURITY, do not allow '- and `-quotes in
4111 # client values.
4112 # Remove all existing '-quotes
4113 my $Key;
4114 foreach $Key (keys(%temp))
4116 $temp{$Key} =~ s/([\r\f]+\n)/\n/g; # Only \n is allowed
4117 $temp{$Key} =~ s/[\']/&#8217;/igs; # Remove all single quotes
4118 $temp{$Key} =~ s/[\`]/&#8216;/igs; # Remove all backtick quotes
4121 # Store current CGI values and remove defaults
4122 $::default_values{$name} = $temp{""};
4124 # Define the CGI variable and its value (in the execute package)
4125 %{$name} = ();
4126 my $tempKey;
4127 foreach $tempKey (keys(%temp))
4129 ${$name}{$tempKey} = $temp{$tempKey};
4132 # return SUCCES
4133 return 1;
4137 # SAFEqx('CommandString')
4139 # A special function that is a safe alternative to backtick quotes (and qx//)
4140 # with client-supplied CGI values. All CGI variables are surrounded by
4141 # single ''-quotes (except between existing \'\'-quotes, don't try to be
4142 # too smart). All variables are then interpolated. Simple (@) lists are
4143 # expanded with join(' ', @List), and simple (%) hash tables expanded
4144 # as a list of "key=value" pairs. Complex variables, e.g., @$var, are
4145 # evaluated in a scalar context (e.g., as scalar(@$var)). All occurrences of
4146 # $@% that should NOT be interpolated must be preceeded by a "\".
4147 # If the first line of the String starts with "#! interpreter", the
4148 # remainder of the string is piped into interpreter (after interpolation), i.e.,
4149 # open(INTERPRETER, "|interpreter");print INTERPRETER remainder;
4150 # just like in UNIX. There are some problems with quotes. Be carefull in
4151 # using them. You do not have access to the output of any piped (#!)
4152 # process! If you want such access, execute
4153 # <SCRIPT TYPE="text/osshell">echo "script"|interpreter</SCRIPT> or
4154 # <SCRIPT TYPE="text/ssperl">$resultvar = SAFEqx('echo "script"|interpreter');
4155 # </SCRIPT>.
4157 # SAFEqx ONLY WORKS WHEN THE STRING ITSELF IS SURROUNDED BY SINGLE QUOTES
4158 # (SO THAT IT IS NOT INTERPOLATED BEFORE IT CAN BE PROTECTED)
4159 sub SAFEqx # ('String') -> result of executing qx/"String"/
4161 my $CommandString = shift;
4162 my $NewCommandString = "";
4164 # Only interpolate when required (check the On/Off switch)
4165 unless($CGIscriptor::NoShellScriptInterpolation)
4168 # Handle existing single quotes around CGI values
4169 while($CommandString =~ /\'[^\']+\'/s)
4171 my $CurrentQuotedString = $&;
4172 $NewCommandString .= $`;
4173 $CommandString = $'; # The remaining string
4174 # Interpolate CGI variables between quotes
4175 # (e.g., '$CGIscriptorResults[-1]')
4176 $CurrentQuotedString =~
4177 s/(^|[^\\])([\$\@])((\w*)([\{\[][\$\@\%]?[\:\w\-]+[\}\]])*)/if(exists($main::default_values{$4})){
4178 "$1".eval("$2$3")}else{"$&"}/egs;
4180 # Combine result with previous result
4181 $NewCommandString .= $CurrentQuotedString;
4183 $CommandString = $NewCommandString.$CommandString;
4185 # Select known CGI variables and surround them with single quotes,
4186 # then interpolate all variables
4187 $CommandString =~
4188 s/(^|[^\\])([\$\@\%]+)((\w*)([\{\[][\w\:\$\"\-]+[\}\]])*)/
4189 if($2 eq '$' && exists($main::default_values{$4}))
4190 {"$1\'".eval("\$$3")."\'";}
4191 elsif($2 eq '@'){$1.join(' ', @{"$3"});}
4192 elsif($2 eq '%'){my $t=$1;map {$t.=" $_=".${"$3"}{$_}}
4193 keys(%{"$3"});$t}
4194 else{$1.eval("${2}$3");
4195 }/egs;
4197 # Remove backslashed [$@%]
4198 $CommandString =~ s/\\([\$\@\%])/$1/gs;
4201 # Debugging
4202 # return $CommandString;
4204 # Handle UNIX style "#! shell command\n" constructs as
4205 # a pipe into the shell command. The output cannot be tapped.
4206 my $ReturnValue = "";
4207 if($CommandString =~ /^\s*\#\!([^\f\n\r]+)[\f\n\r]/is)
4209 my $ShellScripts = $';
4210 my $ShellCommand = $1;
4211 open(INTERPRETER, "|$ShellCommand") || dieHandler(29, "\'$ShellCommand\' PIPE not opened: &!\n");
4212 select(INTERPRETER);$| = 1;
4213 print INTERPRETER $ShellScripts;
4214 close(INTERPRETER);
4215 select(STDOUT);$| = 1;
4217 # Shell scripts which are redirected to an existing named pipe.
4218 # The output cannot be tapped.
4219 elsif($CGIscriptor::ShellScriptPIPE)
4221 CGIscriptor::printSAFEqxPIPE($CommandString);
4223 else # Plain ``-backtick execution
4225 # Execute the commands
4226 $ReturnValue = qx/$CommandString/;
4228 return $ReturnValue;
4231 ####################################################################################
4233 # The CGIscriptor PACKAGE
4235 ####################################################################################
4237 # Isolate the evaluation of CGIscriptor functions, i.e., those prefixed with
4238 # "CGIscriptor::"
4240 package CGIscriptor;
4243 # The Interpolation On/Off switch
4244 my $NoShellScriptInterpolation = undef;
4245 # The ShellScript redirection pipe
4246 my $ShellScriptPIPE = undef;
4248 # Open a named PIPE for SAFEqx to receive ALL shell scripts
4249 sub RedirectShellScript # ('CommandString')
4251 my $CommandString = shift || undef;
4253 if($CommandString)
4255 $ShellScriptPIPE = "ShellScriptNamedPipe";
4256 open($ShellScriptPIPE, "|$CommandString")
4257 || main::dieHandler(30, "\'|$CommandString\' PIPE open failed: $!\n");
4259 else
4261 close($ShellScriptPIPE);
4262 $ShellScriptPIPE = undef;
4264 return $ShellScriptPIPE;
4267 # Print to redirected shell script pipe
4268 sub printSAFEqxPIPE # ("String") -> print return value
4270 my $String = shift || undef;
4272 select($ShellScriptPIPE); $| = 1;
4273 my $returnvalue = print $ShellScriptPIPE ($String);
4274 select(STDOUT); $| = 1;
4276 return $returnvalue;
4279 # a pointer to CGIexecute::SAFEqx
4280 sub SAFEqx # ('String') -> result of qx/"String"/
4282 my $CommandString = shift;
4283 return CGIexecute::SAFEqx($CommandString);
4287 # a pointer to CGIexecute::defineCGIvariable
4288 sub defineCGIvariable # ($name[, $default]) ->0/1
4290 my $name = shift;
4291 my $default = shift;
4292 return CGIexecute::defineCGIvariable($name, $default);
4296 # Decode URL encoded arguments
4297 sub URLdecode # (URL encoded input) -> string
4299 my $output = "";
4300 my $char;
4301 my $Value;
4302 foreach $Value (@_)
4304 my $EncodedValue = $Value; # Do not change the loop variable
4305 # Convert all "+" to " "
4306 $EncodedValue =~ s/\+/ /g;
4307 # Convert all hexadecimal codes (%FF) to their byte values
4308 while($EncodedValue =~ /\%([0-9A-F]{2})/i)
4310 $output .= $`.chr(hex($1));
4311 $EncodedValue = $';
4313 $output .= $EncodedValue; # The remaining part of $Value
4315 $output;
4318 # Encode arguments as URL codes.
4319 sub URLencode # (input) -> URL encoded string
4321 my $output = "";
4322 my $char;
4323 my $Value;
4324 foreach $Value (@_)
4326 my @CharList = split('', $Value);
4327 foreach $char (@CharList)
4329 if($char =~ /\s/)
4330 { $output .= "+";}
4331 elsif($char =~ /\w\-/)
4332 { $output .= $char;}
4333 else
4335 $output .= uc(sprintf("%%%2.2x", ord($char)));
4339 $output;
4342 # Extract the value of a CGI variable from the URL-encoded $string
4343 # Also extracts the data blocks from a multipart request. Does NOT
4344 # decode the multipart blocks
4345 sub CGIparseValue # (ValueName [, URL_encoded_QueryString [, \$QueryReturnReference]]) -> Decoded value
4347 my $ValueName = shift;
4348 my $QueryString = shift || $main::ENV{'QUERY_STRING'};
4349 my $ReturnReference = shift || undef;
4350 my $output = "";
4352 if($QueryString =~ /(^|\&)$ValueName\=([^\&]*)(\&|$)/)
4354 $output = URLdecode($2);
4355 $$ReturnReference = $' if ref($ReturnReference);
4357 # Get multipart POST or PUT methods
4358 elsif($main::ENV{'CONTENT_TYPE'} =~ m@(multipart/([\w\-]+)\;\s+boundary\=([\S]+))@i)
4360 my $MultipartType = $2;
4361 my $BoundaryString = $3;
4362 # Remove the boundary-string
4363 my $temp = $QueryString;
4364 $temp =~ /^\Q--$BoundaryString\E/m;
4365 $temp = $';
4367 # Identify the newline character(s), this is the first character in $temp
4368 my $NewLine = "\r\n"; # Actually, this IS the correct one
4369 unless($temp =~ /^(\-\-|\r\n)/) # However, you never realy can be sure
4371 # Is this correct??? I have to check.
4372 $NewLine = "\r\n" if $temp =~ /^(\r\n)/; # Double (CRLF, the correct one)
4373 $NewLine = "\n\r" if $temp =~ /^(\n\r)/; # Double
4374 $NewLine = "\n" if $temp =~ /^([\n])/; # Single Line Feed
4375 $NewLine = "\r" if $temp =~ /^([\r])/; # Single Return
4378 # search through all data blocks
4379 while($temp =~ /^\Q--$BoundaryString\E/m)
4381 my $DataBlock = $`;
4382 $temp = $';
4383 # Get the empty line after the header
4384 $DataBlock =~ /$NewLine$NewLine/;
4385 $Header = $`;
4386 $output = $';
4387 my $Header = $`;
4388 $output = $';
4390 # Remove newlines from the header
4391 $Header =~ s/$NewLine/ /g;
4393 # Look whether this block is the one you are looking for
4394 # Require the quotes!
4395 if($Header =~ /name\s*=\s*[\"\']$ValueName[\"\']/m)
4397 my $i;
4398 for($i=length($NewLine); $i; --$i)
4400 chop($output);
4402 # OK, get out
4403 last;
4405 # reinitialize the output
4406 $output = "";
4408 $$ReturnReference = $temp if ref($ReturnReference);
4410 elsif($QueryString !~ /(^|\&)$ValueName\=/) # The value simply isn't there
4412 return undef;
4413 $$ReturnReference = undef if ref($ReturnReference);
4415 else
4417 print "ERROR: $ValueName $main::ENV{'CONTENT_TYPE'}\n";
4419 return $output;
4423 # Get a list of values for the same ValueName. Uses CGIparseValue
4425 sub CGIparseValueList # (ValueName [, URL_encoded_QueryString]) -> List of decoded values
4427 my $ValueName = shift;
4428 my $QueryString = shift || $main::ENV{'QUERY_STRING'};
4429 my @output = ();
4430 my $RestQueryString;
4431 my $Value;
4432 while($QueryString &&
4433 (($Value = CGIparseValue($ValueName, $QueryString, \$RestQueryString))
4434 || defined($Value)))
4436 push(@output, $Value);
4437 $QueryString = $RestQueryString; # QueryString is consumed!
4439 # ready, return list with values
4440 return @output;
4443 sub CGIparseValueHash # (ValueName [, URL_encoded_QueryString]) -> Hash table of decoded values
4445 my $ValueName = shift;
4446 my $QueryString = shift || $main::ENV{'QUERY_STRING'};
4447 my $RestQueryString;
4448 my %output = ();
4449 while($QueryString && $QueryString =~ /(^|\&)$ValueName([\w]*)\=/)
4451 my $Key = $2;
4452 my $Value = CGIparseValue("$ValueName$Key", $QueryString, \$RestQueryString);
4453 $output{$Key} = $Value;
4454 $QueryString = $RestQueryString; # QueryString is consumed!
4456 # ready, return list with values
4457 return %output;
4460 sub CGIparseForm # ([URL_encoded_QueryString]) -> Decoded Form (NO multipart)
4462 my $QueryString = shift || $main::ENV{'QUERY_STRING'};
4463 my $output = "";
4465 $QueryString =~ s/\&/\n/g;
4466 $output = URLdecode($QueryString);
4468 $output;
4471 # Extract the header of a multipart CGI variable from the POST input
4472 sub CGIparseHeader # (ValueName [, URL_encoded_QueryString]) -> Decoded value
4474 my $ValueName = shift;
4475 my $QueryString = shift || $main::ENV{'QUERY_STRING'};
4476 my $output = "";
4478 if($main::ENV{'CONTENT_TYPE'} =~ m@(multipart/([\w\-]+)\;\s+boundary\=([\S]+))@i)
4480 my $MultipartType = $2;
4481 my $BoundaryString = $3;
4482 # Remove the boundary-string
4483 my $temp = $QueryString;
4484 $temp =~ /^\Q--$BoundaryString\E/m;
4485 $temp = $';
4487 # Identify the newline character(s), this is the first character in $temp
4488 my $NewLine = "\r\n"; # Actually, this IS the correct one
4489 unless($temp =~ /^(\-\-|\r\n)/) # However, you never realy can be sure
4491 $NewLine = "\n" if $temp =~ /^([\n])/; # Single Line Feed
4492 $NewLine = "\r" if $temp =~ /^([\r])/; # Single Return
4493 $NewLine = "\r\n" if $temp =~ /^(\r\n)/; # Double (CRLF, the correct one)
4494 $NewLine = "\n\r" if $temp =~ /^(\n\r)/; # Double
4497 # search through all data blocks
4498 while($temp =~ /^\Q--$BoundaryString\E/m)
4500 my $DataBlock = $`;
4501 $temp = $';
4502 # Get the empty line after the header
4503 $DataBlock =~ /$NewLine$NewLine/;
4504 $Header = $`;
4505 my $Header = $`;
4507 # Remove newlines from the header
4508 $Header =~ s/$NewLine/ /g;
4510 # Look whether this block is the one you are looking for
4511 # Require the quotes!
4512 if($Header =~ /name\s*=\s*[\"\']$ValueName[\"\']/m)
4514 $output = $Header;
4515 last;
4517 # reinitialize the output
4518 $output = "";
4521 return $output;
4525 # Checking variables for security (e.g., file names and email addresses)
4526 # File names are tested against the $::FileAllowedChars and $::BlockPathAccess variables
4527 sub CGIsafeFileName # FileName -> FileName or ""
4529 my $FileName = shift || "";
4530 return "" if $FileName =~ m?[^$::FileAllowedChars]?;
4531 return "" if $FileName =~ m!(^|/|\:)\-!;
4532 return "" if $FileName =~ m@\.\.\Q$::DirectorySeparator\E@; # Higher directory not allowed
4533 return "" if $FileName =~ m@\Q$::DirectorySeparator\E\.\.@; # Higher directory not allowed
4534 return "" if $::BlockPathAccess && $FileName =~ m@$::BlockPathAccess@; # Invisible (blocked) file
4536 return $FileName;
4539 sub CGIsafeEmailAddress # email -> email or ""
4541 my $Email = shift || "";
4542 return "" unless $Email =~ m/^[\w\.\-]+[\@][\w\.\-\:]+$/;
4543 return $Email;
4546 # Get a URL from the web. Needs main::GET_URL($URL) function
4547 # (i.e., curl, snarf, or wget)
4548 sub read_url # ($URL) -> page/file
4550 my $URL = shift || return "";
4552 # Get the commands to read the URL, do NOT add a print command
4553 my $URL_command = main::GET_URL($URL, 1);
4554 # execute the commands, i.e., actually read it
4555 my $URLcontent = CGIexecute->evaluate($URL_command);
4557 # Ready, return the content.
4558 return $URLcontent;
4561 ################################################>>>>>>>>>>Start Remove
4563 # BrowseDirs(RootDirectory [, Pattern, Start])
4565 # usage:
4566 # <SCRIPT TYPE='text/ssperl'>
4567 # CGIscriptor::BrowseDirs('Sounds', '\.aifc$', 'Speech', 'DIRECTORY')
4568 # </SCRIPT>
4570 # Allows to browse subdirectories. Start should be relative to the RootDirectory,
4571 # e.g., the full path of the directory 'Speech' is '~/Sounds/Speech'.
4572 # Only files which fit /$Pattern/ and directories are displayed.
4573 # Directories down or up the directory tree are supplied with a
4574 # GET request with the name of the CGI variable in the fourth argument (default
4575 # is 'BROWSEDIRS'). So the correct call for a subdirectory could be:
4576 # CGIscriptor::BrowseDirs('Sounds', '\.aifc$', $DIRECTORY, 'DIRECTORY')
4578 sub BrowseDirs # (RootDirectory [, Pattern, Start, CGIvariable, HTTPserver]) -> Print HTML code
4580 my $RootDirectory = shift; # || return 0;
4581 my $Pattern = shift || '\S';
4582 my $Start = shift || "";
4583 my $CGIvariable = shift || "BROWSEDIRS";
4584 my $HTTPserver = shift || '';
4586 $Start = CGIscriptor::URLdecode($Start); # Sometimes, too much has been encoded
4587 $Start =~ s@//+@/@g;
4588 $Start =~ s@[^/]+/\.\.@@ig;
4589 $Start =~ s@^\.\.@@ig;
4590 $Start =~ s@/\.$@@ig;
4591 $Start =~ s!/+$!!g;
4592 $Start .= "/" if $Start;
4594 my @Directory = glob("$::CGI_HOME/$RootDirectory/$Start");
4595 $CurrentDirectory = shift(@Directory);
4596 $CurrentDirectory = $' if $CurrentDirectory =~ m@(/\.\./)+@;
4597 $CurrentDirectory =~ s@^$::CGI_HOME@@g;
4598 print "<h1>";
4599 print "$CurrentDirectory" if $CurrentDirectory;
4600 print "</h1>\n";
4601 opendir(BROWSE, "$::CGI_HOME/$RootDirectory/$Start") || main::dieHandler(31, "$::CGI_HOME/$RootDirectory/$Start $!");
4602 my @AllFiles = sort grep(!/^([\.]+[^\.]|\~)/, readdir(BROWSE));
4604 # Print directories
4605 my $file;
4606 print "<pre><ul TYPE='NONE'>\n";
4607 foreach $file (@AllFiles)
4609 next unless -d "$::CGI_HOME/$RootDirectory/$Start$file";
4610 # Check whether this file should be visible
4611 next if $::BlockPathAccess &&
4612 "/$RootDirectory/$Start$file/" =~ m@$::BlockPathAccess@;
4614 my $NewURL = $Start ? "$Start$file" : $file;
4615 $NewURL = CGIscriptor::URLencode($NewURL);
4616 print "<dt><a href='";
4617 print "$ENV{SCRIPT_NAME}" if $ENV{SCRIPT_NAME} !~ m@[^\w+\-/]@;
4618 print "$ENV{PATH_INFO}?$CGIvariable=$NewURL'>$file</a></dt>\n";
4620 print "</ul></pre>\n";
4622 # Print files
4623 print "<pre><ul TYPE='CIRCLE'>\n";
4624 my $TotalSize = 0;
4625 foreach $file (@AllFiles)
4627 next if $file =~ /^\./;
4628 next if -d "$::CGI_HOME/$RootDirectory/$Start$file";
4629 next if -l "$::CGI_HOME/$RootDirectory/$Start$file";
4630 # Check whether this file should be visible
4631 next if $::BlockPathAccess &&
4632 "$::CGI_HOME/$RootDirectory/$Start$file" =~ m@$::BlockPathAccess@;
4634 if($file =~ m@$Pattern@)
4636 my $Date = localtime($^T - (-M "$::CGI_HOME/$RootDirectory/$Start$file")*3600*24);
4637 my $Size = -s "$::CGI_HOME/$RootDirectory/$Start$file";
4638 $Size = sprintf("%6.0F kB", $Size/1024);
4639 my $Type = `file $::CGI_HOME/$RootDirectory/$Start$file`;
4640 $Type =~ s@\s*$::CGI_HOME/$RootDirectory/$Start$file\s*\:\s*@@ig;
4641 chomp($Type);
4643 print "<li>";
4644 if($HTTPserver =~ /^\s*[\.\~]\s*$/)
4646 print "<a href='$Start$file'>";
4648 elsif($HTTPserver)
4650 print "<a href='$HTTPserver/$RootDirectory/$Start$file'>";
4652 printf("%-40s", "$file</a>") if $HTTPserver;
4653 printf("%-40s", "$file") unless $HTTPserver;
4654 print "\t$Size\t$Date\t$Type";
4655 print "</li>\n";
4658 print "</ul></pre>";
4660 return 1;
4664 # ListDocs(Pattern [,ListType])
4666 # usage:
4667 # <SCRIPT TYPE=text/ssperl>
4668 # CGIscriptor::ListDocs("/*", "dl");
4669 # </SCRIPT>
4671 # This subroutine is very usefull to manage collections of independent
4672 # documents. The resulting list will display the tree-like directory
4673 # structure. If this routine is too slow for online use, you can
4674 # store the result and use a link to that stored file.
4676 # List HTML and Text files with title and first header (HTML)
4677 # or filename and first meaningfull line (general text files).
4678 # The listing starts at the ServerRoot directory. Directories are
4679 # listed recursively.
4681 # You can change the list type (default is dl).
4682 # e.g.,
4683 # <dt><a href=<file.html>>title</a>
4684 # <dd>First Header
4685 # <dt><a href=<file.txt>>file.txt</a>
4686 # <dd>First meaningfull line of text
4688 sub ListDocs # ($Pattern [, prefix]) e.g., ("/Books/*", [, "dl"])
4690 my $Pattern = shift;
4691 $Pattern =~ /\*/;
4692 my $ListType = shift || "dl";
4693 my $Prefix = lc($ListType) eq "dl" ? "dt" : "li";
4694 my $URL_root = "http://$::ENV{'SERVER_NAME'}\:$::ENV{'SERVER_PORT'}";
4695 my @FileList = glob("$::CGI_HOME$Pattern");
4696 my ($FileName, $Path, $Link);
4698 # Print List markers
4699 print "<$ListType>\n";
4701 # Glob all files
4702 File: foreach $FileName (@FileList)
4704 # Check whether this file should be visible
4705 next if $::BlockPathAccess && $FileName =~ m@$::BlockPathAccess@;
4707 # Recursively list files in all directories
4708 if(-d $FileName)
4710 $FileName =~ m@([^/]*)$@;
4711 my $DirName = $1;
4712 print "<$Prefix>$DirName\n";
4713 $Pattern =~ m@([^/]*)$@;
4714 &ListDocs("$`$DirName/$1", $ListType);
4715 next;
4717 # Use textfiles
4718 elsif(-T "$FileName")
4720 open(TextFile, $FileName) || next;
4722 # Ignore all other file types
4723 else
4724 { next;};
4726 # Get file path for link
4727 $FileName =~ /$::CGI_HOME/;
4728 print "<$Prefix><a href=$URL_root$'>";
4729 # Initialize all variables
4730 my $Line = "";
4731 my $TitleFound = 0;
4732 my $Caption = "";
4733 my $Title = "";
4734 # Read file and step through
4735 while(<TextFile>)
4737 chop $_;
4738 $Line = $_;
4739 # HTML files
4740 if($FileName =~ /\.ht[a-zA-Z]*$/i)
4742 # Catch Title
4743 while(!$Title)
4745 if($Line =~ m@<title>([^<]*)</title>@i)
4747 $Title = $1;
4748 $Line = $';
4750 else
4752 $Line .= <TextFile> || goto Print;
4753 chop $Line;
4756 # Catch First Header
4757 while(!$Caption)
4759 if($Line =~ m@</h1>@i)
4761 $Caption = $`;
4762 $Line = $';
4763 $Caption =~ m@<h1>@i;
4764 $Caption = $';
4765 $Line = $`.$Caption.$Line;
4767 else
4769 $Line .= <TextFile> || goto Print;
4770 chop $Line;
4774 # Other text files
4775 else
4777 # Title equals file name
4778 $FileName =~ /([^\/]+)$/;
4779 $Title = $1;
4780 # Catch equals First Meaningfull line
4781 while(!$Caption)
4783 if($Line =~ /[A-Z]/ &&
4784 ($Line =~ /subject|title/i || $Line =~ /^[\w,\.\s\?\:]+$/)
4785 && $Line !~ /Newsgroup/ && $Line !~ /\:\s*$/)
4787 $Line =~ s/\<[^\>]+\>//g;
4788 $Caption = $Line;
4790 else
4792 $Line = <TextFile> || goto Print;
4796 Print: # Print title and subject
4797 print "$Title</a>\n";
4798 print "<dd>$Caption\n" if $ListType eq "dl";
4799 $TitleFound = 0;
4800 $Caption = "";
4801 close TextFile;
4802 next File;
4805 # Print Closing List Marker
4806 print "</$ListType>\n";
4807 ""; # Empty return value
4811 # HTMLdocTree(Pattern [,ListType])
4813 # usage:
4814 # <SCRIPT TYPE=text/ssperl>
4815 # CGIscriptor::HTMLdocTree("/Welcome.html", "dl");
4816 # </SCRIPT>
4818 # The following subroutine is very usefull for checking large document
4819 # trees. Starting from the root (s), it reads all files and prints out
4820 # a nested list of links to all attached files. Non-existing or misplaced
4821 # files are flagged. This is quite a file-i/o intensive routine
4822 # so you would not like it to be accessible to everyone. If you want to
4823 # use the result, save the whole resulting page to disk and use a link
4824 # to this file.
4826 # HTMLdocTree takes an HTML file or file pattern and constructs nested lists
4827 # with links to *local* files (i.e., only links to the local server are
4828 # followed). The list entries are the document titles.
4829 # If the list type is <dl>, the first <H1> header is used too.
4830 # For each file matching the pattern, a list is made recursively of all
4831 # HTML documents that are linked from it and are stored in the same directory
4832 # or a sub-directory. Warnings are given for missing files.
4833 # The listing starts for the ServerRoot directory.
4834 # You can change the default list type <dl> (<dl>, <ul>, <ol>).
4836 %LinkUsed = ();
4838 sub HTMLdocTree # ($Pattern [, listtype])
4839 # e.g., ("/Welcome.html", [, "ul"])
4841 my $Pattern = shift;
4842 my $ListType = shift || "dl";
4843 my $Prefix = lc($ListType) eq "dl" ? "dt" : "li";
4844 my $URL_root = "http://$::ENV{'SERVER_NAME'}\:$::ENV{'SERVER_PORT'}";
4845 my ($Filename, $Path, $Link);
4846 my %LocalLinks = {};
4848 # Read files (glob them for expansion of wildcards)
4849 my @FileList = glob("$::CGI_HOME$Pattern");
4850 foreach $Path (@FileList)
4852 # Get URL_path
4853 $Path =~ /$::CGI_HOME/;
4854 my $URL_path = $';
4855 # Check whether this file should be visible
4856 next if $::BlockPathAccess && $URL_path =~ m@$::BlockPathAccess@;
4858 my $Title = $URL_path;
4859 my $Caption = "";
4860 # Current file should not be used again
4861 ++$LinkUsed{$URL_path};
4862 # Open HTML doc
4863 unless(open(TextFile, $Path))
4865 print "<$Prefix>$Title <blink>(not found)</blink><br>\n";
4866 next;
4868 while(<TextFile>)
4870 chop $_;
4871 $Line = $_;
4872 # Catch Title
4873 while($Line =~ m@<title>@i)
4875 if($Line =~ m@<title>([^<]*)</title>@i)
4877 $Title = $1;
4878 $Line = $';
4880 else
4882 $Line .= <TextFile>;
4883 chop $Line;
4886 # Catch First Header
4887 while(!$Caption && $Line =~ m@<h1>@i)
4889 if($Line =~ m@</h[1-9]>@i)
4891 $Caption = $`;
4892 $Line = $';
4893 $Caption =~ m@<h1>@i;
4894 $Caption = $';
4895 $Line = $`.$Caption.$Line;
4897 else
4899 $Line .= <TextFile>;
4900 chop $Line;
4903 # Catch and print Links
4904 while($Line =~ m@<a href\=([^>]*)>@i)
4906 $Link = $1;
4907 $Line = $';
4908 # Remove quotes
4909 $Link =~ s/\"//g;
4910 # Remove extras
4911 $Link =~ s/[\#\?].*$//g;
4912 # Remove Servername
4913 if($Link =~ m@(http://|^)@i)
4915 $Link = $';
4916 # Only build tree for current server
4917 next unless $Link =~ m@$::ENV{'SERVER_NAME'}|^/@;
4918 # Remove server name and port
4919 $Link =~ s@^[^\/]*@@g;
4921 # Store the current link
4922 next if $LinkUsed{$Link} || $Link eq $URL_path;
4923 ++$LinkUsed{$Link};
4924 ++$LocalLinks{$Link};
4928 close TextFile;
4929 print "<$Prefix>";
4930 print "<a href=http://";
4931 print "$::ENV{'SERVER_NAME'}\:$::ENV{'SERVER_PORT'}$URL_path>";
4932 print "$Title</a>\n";
4933 print "<br>$Caption\n"
4934 if $Caption && $Caption ne $Title && $ListType =~ /dl/i;
4935 print "<$ListType>\n";
4936 foreach $Link (keys(%LocalLinks))
4938 &HTMLdocTree($Link, $ListType);
4940 print "</$ListType>\n";
4944 ###########################<<<<<<<<<<End Remove
4946 # Make require happy
4949 =head1 NAME
4951 CGIscriptor -
4953 =head1 DESCRIPTION
4955 A flexible HTML 4 compliant script/module for CGI-aware
4956 embeded Perl, shell-scripts, and other scripting languages,
4957 executed at the server side.
4959 =head1 README
4961 Executes embeded Perl code in HTML pages with easy
4962 access to CGI variables. Also processes embeded shell
4963 scripts and scripts in any other language with an
4964 interactive interpreter (e.g., in-line Python, Tcl,
4965 Ruby, Awk, Lisp, Xlispstat, Prolog, M4, R, REBOL, Praat,
4966 sh, bash, csh, ksh).
4968 CGIscriptor is very flexible and hides all the specifics
4969 and idiosyncrasies of correct output and CGI coding and naming.
4970 CGIscriptor complies with the W3C HTML 4.0 recommendations.
4972 This Perl program will run on any WWW server that runs
4973 Perl scripts, just add a line like the following to your
4974 srm.conf file (Apache example):
4976 ScriptAlias /SHTML/ /real-path/CGIscriptor.pl/
4978 URL's that refer to http://www.your.address/SHTML/... will
4979 now be handled by CGIscriptor.pl, which can use a private
4980 directory tree (default is the DOCUMENT_ROOT directory tree,
4981 but it can be anywhere).
4983 =head1 PREREQUISITES
4986 =head1 COREQUISITES
4989 =pod OSNAMES
4991 Linux, *BSD, *nix, MS WinXP
4993 =pod SCRIPT CATEGORIES
4995 Servers
4999 =cut