Codebase list libcgi-ssi-perl / 601c23e
[svn-inject] Installing original source of libcgi-ssi-perl Damyan Ivanov 15 years ago
7 changed file(s) with 1560 addition(s) and 0 deletion(s). Raw diff Collapse all Expand all
0 Revision history for Perl extension CGI::SSI.
1
2 0.92 Wed Jul 08 2007
3 - newlines OK in SSI directives
4
5 0.91 Mon May 28 2007
6 - fixed bad LWP::UserAgent arg
7 - added documentation to make the scope of SSI variables more clear.
8 - timefmt applied to LAST_MODIFIED
9
10 0.90 Tue Apr 24 2007
11 - fixed bad HTTP::Cookies arg
12
13 0.89 Fri Apr 20 2007
14 - nested if errors fixed (with thanks to Colin Fine)
15
16 0.88 Fri Sept 2 2005
17 - warn on errors
18 - https URIs
19
20 0.87 Fri Sept 2 2005
21 - CLOSE
22
23 0.86 Fri Jun 17 2005
24 - virtual file w/ params bugfix
25 - added default include virtual host 'localhost'
26
27 0.85 Sat May 07 2005
28 - minor cookie fix
29
30 0.84 Thu May 05 2005
31 - added cookie support, w/ test
32
33 0.83 Thu May 05 2005
34 - added support for high recursion, w/ test
35
36 0.82 Tue Feb 15 2005
37 - config timefmt fix for DATE_LOCAL and DATE_GMT
38
39 0.81 Sun Feb 09 2005
40 - minor but crucial test.pl fix
41
42 0.80 Sun Jan 30 23:59:23 2005
43 - improved test suite
44
45 0.53 Wed Mar 14 10:07:43 2001
46 - added printf support for tied filehandles
47
48 0.52 Mon Feb 26 11:03:24 2001
49 - fixed $HTML::SimpleParse::FIX_CASE issue
50
51 0.51 Mon Feb 05 23:28:34 2001
52 - corrected the test suite
53
54 0.50 Mon Dec 18 15:02:37 2000
55 - include test suite
56 - remove cumbersome features (see 0.14)
57 - better documentation
58
59 0.14 Fri Aug 18 11:55:23 2000
60 - allow $ENV{QUERY_STRING} and
61 $ENV{QUERY_STRING_UNESCAPED} to be
62 visible from virtually-included
63 documents. The query string is passed
64 in as usual : "/file?query".
65
66 0.13 Tue Aug 08 15:46:25 2000
67 - allow for upper-case arguments, require
68 Date::Parse instead of Date::Format,
69 process all included files, add
70 documentation for the fact that
71 DOCUMENT_ROOT is a valid parameter
72 to import() or new().
73
74 0.11 Tue Jun 06 01:05:48 2000
75 - name required pm files in Makefile.PL to
76 allow for easy install via CPAN.pm
77
78 0.10 Thu May 18 12:01:02 2000
79 - various bug fixes; a total overhaul, as a
80 matter of fact.
81
82 0.01 Fri Jan 14 21:45:28 2000
83 - original version; created by h2xs 1.19
84
0 Changes
1 MANIFEST
2 Makefile.PL
3 README
4 SSI.pm
5 test.pl
6 META.yml Module meta-data (added by MakeMaker)
0 # http://module-build.sourceforge.net/META-spec.html
1 #XXXXXXX This is a prototype!!! It will change in the future!!! XXXXX#
2 name: CGI-SSI
3 version: 0.92
4 version_from: SSI.pm
5 installdirs: site
6 requires:
7 Date::Format: 0
8 File::Spec: 0
9 HTML::SimpleParse: 0
10 HTTP::Cookies: 0
11 HTTP::Response: 0
12 LWP::UserAgent: 0
13 URI: 0
14
15 distribution_type: module
16 generated_by: ExtUtils::MakeMaker version 6.17
0 use ExtUtils::MakeMaker;
1 # See lib/ExtUtils/MakeMaker.pm for details of how to influence
2 # the contents of the Makefile that is written.
3 WriteMakefile(
4 'NAME' => 'CGI::SSI',
5 'VERSION_FROM' => 'SSI.pm', # finds $VERSION
6 'PREREQ_PM' => {
7 HTML::SimpleParse => 0,
8 File::Spec => 0,
9 LWP::UserAgent => 0,
10 HTTP::Response => 0,
11 HTTP::Cookies => 0,
12 URI => 0,
13 Date::Format => 0,
14 },
15 );
0 NAME
1 CGI::SSI - Use SSI from CGI scripts
2
3 SYNOPSIS
4 # autotie STDOUT or any other open filehandle
5
6 use CGI::SSI (autotie => 'STDOUT');
7
8 print $shtml; # browser sees resulting HTML
9
10 # or tie it yourself to any open filehandle
11
12 use CGI::SSI;
13
14 open(FILE,'+>'.$html_file) or die $!;
15 $ssi = tie(*FILE, 'CGI::SSI', filehandle => 'FILE');
16 print FILE $shtml; # HTML arrives in the file
17
18 # or use the object-oriented interface
19
20 use CGI::SSI;
21
22 $ssi = CGI::SSI->new();
23
24 $ssi->if('"$varname" =~ /^foo/');
25 $html .= $ssi->process($shtml);
26 $ssi->else();
27 $html .= $ssi->include(file => $filename);
28 $ssi->endif();
29
30 print $ssi->exec(cgi => $url);
31 print $ssi->flastmod(file => $filename);
32
33 #
34 # or roll your own favorite flavor of SSI
35 #
36
37 package CGI::SSI::MySSI;
38 use CGI::SSI;
39 @CGI::SSI::MySSI::ISA = qw(CGI::SSI);
40
41 sub include {
42 my($self,$type,$file_or_url) = @_;
43 # my idea of include goes something like this...
44 return $html;
45 }
46 1;
47 __END__
48
49 #
50 # or use .htaccess to include all files in a dir
51 #
52
53 # in .htaccess
54 Action cgi-ssi /cgi-bin/ssi/process.cgi
55 <FilesMatch "\.shtml">
56 SetHandler cgi-ssi
57 </FilesMatch>
58
59 # in /cgi-bin/ssi/process.cgi
60 #!/usr/local/bin/perl
61 use CGI::SSI;
62 CGI::SSI->handler();
63 __END__
64
65 DESCRIPTION
66 CGI::SSI is meant to be used as an easy way to filter shtml through CGI
67 scripts in a loose imitation of Apache's mod_include. If you're using
68 Apache, you may want to use either mod_include or the Apache::SSI module
69 instead of CGI::SSI. Limitations in a CGI script's knowledge of how the
70 server behaves make some SSI directives impossible to imitate from a CGI
71 script.
72
73 Most of the time, you'll simply want to filter shtml through STDOUT or
74 some other open filehandle. "autotie" is available for STDOUT, but in
75 general, you'll want to tie other filehandles yourself:
76
77 $ssi = tie(*FH, 'CGI::SSI', filehandle => 'FH');
78 print FH $shtml;
79
80 Note that you'll need to pass the name of the filehandle to "tie()" as a
81 named parameter. Other named parameters are possible, as detailed below.
82 These parameters are the same as those passed to the "new()" method.
83 However, "new()" will not tie a filehandle for you.
84
85 CGI::SSI has it's own flavor of SSI. Test expressions are Perlish. You
86 may create and use multiple CGI::SSI objects; they will not step on each
87 others' variables.
88
89 Object-Oriented methods use the same general format so as to imitate SSI
90 directives:
91
92 <!--#include virtual="/foo/bar.footer" -->
93
94 would be
95
96 $ssi->include(virtual => '/foo/bar.footer');
97
98 likewise,
99
100 <!--#exec cgi="/cgi-bin/foo.cgi" -->
101
102 would be
103
104 $ssi->exec(cgi => '/cgi-bin/foo.cgi');
105
106 Usually, if there's no chance for ambiguity, the first argument may be
107 left out:
108
109 <!--#echo var="var_name" -->
110
111 could be either
112
113 $ssi->echo(var => 'var_name');
114
115 or
116
117 $ssi->echo('var_name');
118
119 Likewise,
120
121 $ssi->set(var => $varname, value => $value)
122
123 is the same as
124
125 $ssi->set($varname => $value)
126
127 $ssi->new([%args])
128 Creates a new CGI::SSI object. The following are valid (optional)
129 arguments:
130
131 DOCUMENT_URI => $doc_uri,
132 DOCUMENT_NAME => $doc_name,
133 DOCUMENT_ROOT => $doc_root,
134 errmsg => $oops,
135 sizefmt => ('bytes' || 'abbrev'),
136 timefmt => $time_fmt,
137 MAX_RECURSIONS => $default_100, # when to stop infinite loops w/ error msg
138 COOKIE_JAR => HTTP::Cookies->new,
139
140 $ssi->config($type, $arg)
141 $type is either 'sizefmt', 'timefmt', or 'errmsg'. $arg is similar
142 to those of the SSI "spec", referenced below.
143
144 $ssi->set($varname => $value)
145 Sets variables internal to the CGI::SSI object. (Not to be confused
146 with the normal variables your script uses!) These variables may be
147 used in test expressions, and retreived using $ssi->echo($varname).
148 These variables also will not be available in external, included
149 resources.
150
151 $ssi->echo($varname)
152 Returns the value of the variable named $varname. Such variables may
153 be set manually using the "set()" method. There are also several
154 built-in variables:
155
156 DOCUMENT_URI - the URI of this document
157 DOCUMENT_NAME - the name of the current document
158 DATE_GMT - the same as 'gmtime'
159 DATE_LOCAL - the same as 'localtime'
160 LAST_MODIFIED - the last time this script was modified
161
162 $ssi->exec($type, $arg)
163 $type is either 'cmd' or 'cgi'. $arg is similar to the SSI "spec"
164 (see below).
165
166 $ssi->include($type, $arg)
167 Similar to "exec", but "virtual" and "file" are the two valid types.
168 SSI variables will not be available outside of your CGI::SSI object,
169 regardless of whether the virtual resource is on the local system or
170 a remote system.
171
172 $ssi->flastmod($type, $filename)
173 Similar to "include".
174
175 $ssi->fsize($type, $filename)
176 Same as "flastmod".
177
178 $ssi->printenv
179 Returns the environment similar to Apache's mod_include.
180
181 $ssi->cookie_jar([$jar])
182 Returns the currently-used HTTP::Cookies object. You may optionally
183 pass in a new HTTP::Cookies object. The jar is used for web requests
184 in exec cgi and include virtual directives.
185
186 FLOW-CONTROL METHODS
187 The following methods may be used to test expressions. During a "block"
188 where the test $expr is false, nothing will be returned (or printed, if
189 tied).
190
191 $ssi->if($expr)
192 The expr can be anything Perl, but care should be taken. This causes
193 problems:
194
195 $ssi->set(varname => "foo");
196 <!--#if expr="'\$varname' =~ /^foo$/" -->ok<!--#endif -->
197
198 The $varname is expanded as you would expect. (We escape it so as to
199 use the $varname within the CGI::SSI object, instead of that within
200 our progam.) But the $/ inside the regex is also expanded. This is
201 fixed by escaping the "$":
202
203 <!--#if expr="'\$varname' =~ /^value\$/" -->ok<!--#endif -->
204
205 The expressions used in if and elif tags/calls are tricky due to the
206 number of escapes required. In some cases, you'll need to write
207 "\\\\" to mean "\".
208
209 $ssi->elif($expr)
210 $ssi->else
211 $ssi->endif
212 SEE ALSO
213 "Apache::SSI" and the SSI "spec" at
214 http://www.apache.org/docs/mod/mod_include.html
215
216 AUTHOR
217 (c) 2000-2007 James Tolley <james@bitperfect.com> All Rights Reserved.
218
219 This is free software. You may copy and/or modify it under the same
220 terms as perl itself.
221
222 CREDITS
223 Many Thanks to Corey Wilson for a bug report and fix.
224
0 package CGI::SSI;
1 use strict;
2
3 use HTML::SimpleParse;
4 use File::Spec::Functions; # catfile()
5 use FindBin;
6 use LWP::UserAgent;
7 use HTTP::Response;
8 use HTTP::Cookies;
9 use URI;
10 use Date::Format;
11
12 our $VERSION = '0.92';
13
14 our $DEBUG = 0;
15
16 sub import {
17 my($class,%args) = @_;
18 return unless exists $args{'autotie'};
19 $args{'filehandle'} = $args{'autotie'} =~ /::/ ? $args{'autotie'} : caller().'::'.$args{'autotie'};
20 no strict 'refs';
21 my $self = tie(*{$args{'filehandle'}},$class,%args);
22 return $self;
23 }
24
25 my($gmt,$loc,$lmod);
26
27 sub new {
28 my($class,%args) = @_;
29 my $self = bless {}, $class;
30
31 $self->{'_handle'} = undef;
32
33 my $script_name = '';
34 if(exists $ENV{'SCRIPT_NAME'}) {
35 ($script_name) = $ENV{'SCRIPT_NAME'} =~ /([^\/]+)$/;
36 }
37
38 tie $gmt, 'CGI::SSI::Gmt', $self;
39 tie $loc, 'CGI::SSI::Local', $self;
40 tie $lmod, 'CGI::SSI::LMOD', $self;
41
42 $ENV{'DOCUMENT_ROOT'} ||= '';
43 $self->{'_variables'} = {
44 DOCUMENT_URI => ($args{'DOCUMENT_URI'} || $ENV{'SCRIPT_NAME'}),
45 DATE_GMT => $gmt,
46 DATE_LOCAL => $loc,
47 LAST_MODIFIED => $lmod,
48 DOCUMENT_NAME => ($args{'DOCUMENT_NAME'} || $script_name),
49 DOCUMENT_ROOT => ($args{'DOCUMENT_ROOT'} || $ENV{DOCUMENT_ROOT}),
50 };
51
52 $self->{'_config'} = {
53 errmsg => ($args{'errmsg'} || '[an error occurred while processing this directive]'),
54 sizefmt => ($args{'sizefmt'} || 'abbrev'),
55 timefmt => ($args{'timefmt'} || undef),
56 };
57
58 $self->{_max_recursions} = $args{MAX_RECURSIONS} || 100; # no "infinite" loops
59 $self->{_recursions} = {};
60
61 $self->{_cookie_jar} = $args{COOKIE_JAR} || HTTP::Cookies->new();
62
63 $self->{'_in_if'} = 0;
64 $self->{'_suspend'} = [0];
65 $self->{'_seen_true'} = [1];
66
67 return $self;
68 }
69
70 sub TIEHANDLE {
71 my($class,%args) = @_;
72 my $self = $class->new(%args);
73 $self->{'_handle'} = do { local *STDOUT };
74 my $handle_to_tie = '';
75 if($args{'filehandle'} !~ /::/) {
76 $handle_to_tie = caller().'::'.$args{'filehandle'};
77 } else {
78 $handle_to_tie = $args{'filehandle'};
79 }
80 open($self->{'_handle'},'>&'.$handle_to_tie) or die "Failed to copy the filehandle ($handle_to_tie): $!";
81 return $self;
82 }
83
84 sub PRINT {
85 my $self = shift;
86 print {$self->{'_handle'}} map { $self->process($_) } @_;
87 }
88
89 sub PRINTF {
90 my $self = shift;
91 my $fmt = shift;
92 printf {$self->{'_handle'}} $fmt, map { $self->process($_) } @_;
93 }
94
95 sub CLOSE {
96 my($self) = @_;
97 close $self->{'_handle'};
98 }
99
100 sub process {
101 my($self,@shtml) = @_;
102 my $processed = '';
103 @shtml = split(/(<!--#.+?-->)/s,join '',@shtml);
104 local($HTML::SimpleParse::FIX_CASE) = 0; # prevent var => value from becoming VAR => value
105 for my $token (@shtml) {
106 # next unless(defined $token and length $token);
107 if($token =~ /^<!--#(.+?)\s*-->$/s) {
108 $processed .= $self->_process_ssi_text($self->_interp_vars($1));
109 } else {
110 next if $self->_suspended;
111 $processed .= $token;
112 }
113 }
114 return $processed;
115 }
116
117 sub _process_ssi_text {
118 my($self,$text) = @_;
119
120 # are we suspended?
121 return '' if($self->_suspended and $text !~ /^(?:if|else|elif|endif)\b/);
122
123 # what's the first \S+?
124 if($text !~ s/^(\S+)\s*//) {
125 warn ref($self)." error: failed to find method name at beginning of string: '$text'.\n";
126 return $self->{'_config'}->{'errmsg'};
127 }
128 my $method = $1;
129 return $self->$method( HTML::SimpleParse->parse_args($text) );
130 }
131
132 # many thanks to Apache::SSI
133 sub _interp_vars {
134 local $^W = 0;
135 my($self,$text) = @_;
136 my($a,$b,$c) = ('','','');
137 $text =~ s{ (^|[^\\]) (\\\\)* \$(?:\{)?(\w+)(?:\})? }
138 {($a,$b,$c)=($1,$2,$3); $a . substr($b,length($b)/2) . $self->_echo($c) }exg;
139 return $text;
140 }
141
142 # for internal use only - returns the thing passed in if it's not defined. echo() returns '' in that case.
143 sub _echo {
144 my($self,$key,$var) = @_;
145 $var = $key if @_ == 2;
146
147 if($var eq 'DATE_LOCAL') {
148 return $loc;
149 } elsif($var eq 'DATE_GMT') {
150 return $gmt;
151 } elsif($var eq 'LAST_MODIFIED') {
152 return $lmod;
153 }
154
155 return $self->{'_variables'}->{$var} if exists $self->{'_variables'}->{$var};
156 return $ENV{$var} if exists $ENV{$var};
157 return $var;
158 }
159
160 #
161 # ssi directive methods
162 #
163
164 sub config {
165 my($self,$type,$value) = @_;
166 if($type =~ /^timefmt$/i) {
167 $self->{'_config'}->{'timefmt'} = $value;
168 } elsif($type =~ /^sizefmt$/i) {
169 if(lc $value eq 'abbrev') {
170 $self->{'_config'}->{'sizefmt'} = 'abbrev';
171 } elsif(lc $value eq 'bytes') {
172 $self->{'_config'}->{'sizefmt'} = 'bytes';
173 } else {
174 warn ref($self)." error: value for sizefmt is '$value'. It must be 'abbrev' or 'bytes'.\n";
175 return $self->{'_config'}->{'errmsg'};
176 }
177 } elsif($type =~ /^errmsg$/i) {
178 $self->{'_config'}->{'errmsg'} = $value;
179 } else {
180 warn ref($self)." error: arg to config is '$type'. It must be one of: 'timefmt', 'sizefmt', or 'errmsg'.\n";
181 return $self->{'_config'}->{'errmsg'};
182 }
183 return '';
184 }
185
186 sub set {
187 my($self,%args) = @_;
188 if(scalar keys %args > 1) {
189 $self->{'_variables'}->{$args{'var'}} = $args{'value'};
190 } else { # var => value notation
191 my($var,$value) = %args;
192 $self->{'_variables'}->{$var} = $value;
193 }
194 return '';
195 }
196
197 sub echo {
198 my($self,$key,$var) = @_;
199 $var = $key if @_ == 2;
200
201 if($var eq 'DATE_LOCAL') {
202 return $loc;
203 } elsif($var eq 'DATE_GMT') {
204 return $gmt;
205 } elsif($var eq 'LAST_MODIFIED') {
206 return $lmod;
207 }
208
209 return $self->{'_variables'}->{$var} if exists $self->{'_variables'}->{$var};
210 return $ENV{$var} if exists $ENV{$var};
211 return '';
212 }
213
214 sub printenv {
215 #my $self = shift;
216 return join "\n",map {"$_=$ENV{$_}"} keys %ENV;
217 }
218
219 sub include {
220 $DEBUG and do { local $" = "','"; warn "DEBUG: include('@_')\n" };
221 my($self,$type,$filename) = @_;
222 if(lc $type eq 'file') {
223 return $self->_include_file($filename);
224 } elsif(lc $type eq 'virtual') {
225 return $self->_include_virtual($filename);
226 } else {
227 warn ref($self)." error: arg to include is '$type'. It must be one of: 'file' or 'virtual'.\n";
228 return $self->{'_config'}->{'errmsg'};
229 }
230 }
231
232 sub _include_file {
233 $DEBUG and do { local $" = "','"; warn "DEBUG: _include_file('@_')\n" };
234 my($self,$filename) = @_;
235
236 # get the filename to open
237 $filename = catfile($FindBin::Bin,$filename) unless -e $filename;
238
239 # if we've reached MAX_RECURSIONS for this filename, warn and return the error
240 if(++$self->{_recursions}->{$filename} >= $self->{_max_recursions}) {
241 warn ref($self)." error: the maximum number of 'include file' recursions has been exceeded for '$filename'.\n";
242 return $self->{'_config'}->{'errmsg'};
243 }
244
245 # open the file, or warn and return an error
246 my $fh = do { local *STDIN };
247 open($fh,$filename) or do {
248 warn ref($self)." error: failed to open file ($filename): $!\n";
249 return $self->{'_config'}->{'errmsg'};
250 };
251
252 # process the included file and return the result
253 return $self->process(join '',<$fh>);
254 }
255
256 sub _include_virtual {
257 $DEBUG and do { local $" = "','"; warn "DEBUG: _include_virtual('@_')\n" };
258 my($self,$filename) = @_;
259
260 # if this is a local file that we can just read, let's do that instead of getting it virtually
261 if($filename =~ m|^/(.+)|) { # could be on the local server: absolute filename, relative to ., relative to $ENV{DOCUMENT_ROOT}
262 my $file = $1;
263 if(-e '/'.$file) { # back to the original
264 $file = '/'.$file;
265 } elsif(-e catfile($self->{'_variables'}->{'DOCUMENT_ROOT'},$file)) {
266 $file = catfile($self->{'_variables'}->{'DOCUMENT_ROOT'},$file);
267 } elsif(-e catfile($FindBin::Bin,$file)) {
268 $file = atfile($FindBin::Bin,$file);
269 }
270 return $self->_include_file($file) if -e $file;
271 }
272
273 # create the URI to get(), or warn and return the error
274 my $uri = eval {
275 my $uri = URI->new($filename);
276 $uri->scheme($uri->scheme || ($ENV{HTTPS} ? 'https' : 'http')); # ??
277 $uri->host($uri->host || $ENV{'HTTP_HOST'} || $ENV{'SERVER_NAME'} || 'localhost');
278 $uri;
279 } or do {
280 warn ref($self)." error: failed to create a URI based on '$filename'.\n";
281 return $self->{'_config'}->{'errmsg'};
282 };
283 if($@) {
284 warn ref($self)." error: failed to create a URI based on '$filename'.\n";
285 return $self->{'_config'}->{'errmsg'} if $@;
286 }
287
288 # get the content of the request
289 $self->{_ua} ||= $self->_get_ua();
290 my $url = $uri->canonical;
291
292 # have we reached MAX_RECURSIONS?
293 if(++$self->{_recursions}->{$url} >= $self->{_max_recursions}) {
294 warn ref($self)." error: the maximum number of 'include virtual' recursions has been exceeded for '$url'.\n";
295 return $self->{'_config'}->{'errmsg'};
296 }
297
298 my $response = $self->{_ua}->get($url);
299
300 # is it a success?
301 unless($response->is_success) {
302 warn ref($self)." error: failed to get('$url'): ".$response->status_line.".\n";
303 return $self->{_config}->{errmsg};
304 }
305
306 # process the included content and return the result
307 return $self->process($response->content);
308 }
309
310 sub _get_ua {
311 my $self = shift;
312 my %conf = ();
313 $conf{agent} = $ENV{HTTP_USER_AGENT} if $ENV{HTTP_USER_AGENT};
314 my $ua = LWP::UserAgent->new(%conf);
315 $ua->cookie_jar($self->{_cookie_jar});
316 return $ua;
317 }
318
319 sub cookie_jar {
320 my $self = shift;
321 if(my $jar = shift) {
322 $self->{_cookie_jar} = $jar;
323 }
324 return $self->{_cookie_jar};
325 }
326
327 sub exec {
328 my($self,$type,$filename) = @_;
329 if(lc $type eq 'cmd') {
330 return $self->_exec_cmd($filename);
331 } elsif(lc $type eq 'cgi') {
332 return $self->_exec_cgi($filename);
333 } else {
334 warn ref($self)." error: arg to exec() is '$type'. It must be one of: 'cmd' or 'cgi'.\n";
335 return $self->{'_config'}->{'errmsg'};
336 }
337 }
338
339 sub _exec_cmd {
340 my($self,$filename) = @_;
341
342 # have we reached MAX_RECURSIONS?
343 if(++$self->{_recursions}->{$filename} >= $self->{_max_recursions}) {
344 warn ref($self)." error: the maximum number of 'exec cmd' recursions has been exceeded for '$filename'.\n";
345 return $self->{'_config'}->{'errmsg'};
346 }
347
348 my $output = `$filename`; # security here is mighty bad.
349
350 # was the command a success?
351 if($?) {
352 warn ref($self)." error: `$filename` was not successful.\n";
353 return $self->{'_config'}->{'errmsg'};
354 }
355
356 # process the output, and return the result
357 return $self->process($output);
358 }
359
360 sub _exec_cgi { # no relative $filename allowed.
361 my($self,$filename) = @_;
362
363 # have we reached MAX_RECURSIONS?
364 if(++$self->{_recursions}->{$filename} >= $self->{_max_recursions}) {
365 warn ref($self)." error: the maximum number of 'exec cgi' recursions has been exceeded for '$filename'.\n";
366 return $self->{'_config'}->{'errmsg'};
367 }
368
369 # create the URI from the filename
370 my $uri = eval {
371 my $uri = URI->new($filename);
372 $uri->scheme($uri->scheme || ($ENV{HTTPS} ? 'https' : 'http')); # ??
373 $uri->host($uri->host || $ENV{'HTTP_HOST'} || $ENV{'SERVER_NAME'});
374 $uri->query($uri->query || $ENV{'QUERY_STRING'});
375 $uri;
376 } or do {
377 warn ref($self)." error: failed to create a URI from '$filename'.\n";
378 return $self->{'_config'}->{'errmsg'};
379 };
380 if($@) {
381 warn ref($self)." error: failed to create a URI from '$filename'.\n";
382 return $self->{'_config'}->{'errmsg'} if $@;
383 }
384
385 # get the content
386 $self->{_ua} ||= $self->_get_ua();
387 my $url = $uri->canonical;
388 my $response = $self->{_ua}->get($url);
389
390 # success?
391 unless($response->is_success) {
392 warn ref($self)." error: failed to get('$filename').\n";
393 return $self->{_config}->{errmsg};
394 }
395
396 # process the content and return the result
397 return $self->process($response->content);
398 }
399
400 sub flastmod {
401 my($self,$type,$filename) = @_;
402
403 if(lc $type eq 'file') {
404 $filename = catfile($FindBin::Bin,$filename) unless -e $filename;
405 } elsif(lc $type eq 'virtual') {
406 $filename = catfile($self->{'_variables'}->{'DOCUMENT_ROOT'},$filename)
407 unless $filename =~ /$self->{'_variables'}->{'DOCUMENT_ROOT'}/;
408 } else {
409 warn ref($self)." error: the first argument to flastmod is '$type'. It must be one of: 'file' or 'virtual'.\n";
410 return $self->{'_config'}->{'errmsg'};
411 }
412 unless(-e $filename) {
413 warn ref($self)." error: flastmod failed to find '$filename'.\n";
414 return $self->{'_config'}->{'errmsg'};
415 }
416
417 my $flastmod = (stat $filename)[9];
418
419 if($self->{'_config'}->{'timefmt'}) {
420 my @localtime = localtime($flastmod); # need this??
421 return Date::Format::strftime($self->{'_config'}->{'timefmt'},@localtime);
422 } else {
423 return scalar localtime($flastmod);
424 }
425 }
426
427 sub fsize {
428 my($self,$type,$filename) = @_;
429
430 if(lc $type eq 'file') {
431 $filename = catfile($FindBin::Bin,$filename) unless -e $filename;
432 } elsif(lc $type eq 'virtual') {
433 $filename = catfile($ENV{'DOCUMENT_ROOT'},$filename) unless $filename =~ /$ENV{'DOCUMENT_ROOT'}/;
434 } else {
435 warn ref($self)." error: the first argument to fsize is '$type'. It must be one of: 'file' or 'virtual'.\n";
436 return $self->{'_config'}->{'errmsg'};
437 }
438 unless(-e $filename) {
439 warn ref($self)." error: fsize failed to find '$filename'.\n";
440 return $self->{'_config'}->{'errmsg'};
441 }
442
443 my $fsize = (stat $filename)[7];
444
445 if(lc $self->{'_config'}->{'sizefmt'} eq 'bytes') {
446 1 while $fsize =~ s/^(\d+)(\d{3})/$1,$2/g;
447 return $fsize;
448 } else { # abbrev
449 # gratefully lifted from Apache::SSI
450 return " 0k" unless $fsize;
451 return " 1k" if $fsize < 1024;
452 return sprintf("%4dk", ($fsize + 512)/1024) if $fsize < 1048576;
453 return sprintf("%4.1fM", $fsize/1048576.0) if $fsize < 103809024;
454 return sprintf("%4dM", ($fsize + 524288)/1048576) if $fsize < 1048576;
455 }
456 }
457
458 #
459 # if/elsif/else/endif and related methods
460 #
461
462 sub _test {
463 my($self,$test) = @_;
464 my $retval = eval($test);
465 return undef if $@;
466 return defined $retval ? $retval : 0;
467 }
468
469 sub _entering_if {
470 my $self = shift;
471 $self->{'_in_if'}++;
472 $self->{'_suspend'}->[$self->{'_in_if'}] = $self->{'_suspend'}->[$self->{'_in_if'} - 1];
473 $self->{'_seen_true'}->[$self->{'_in_if'}] = 0;
474 }
475
476 sub _seen_true {
477 my $self = shift;
478 return $self->{'_seen_true'}->[$self->{'_in_if'}];
479 }
480
481 sub _suspended {
482 my $self = shift;
483 return $self->{'_suspend'}->[$self->{'_in_if'}];
484 }
485
486 sub _leaving_if {
487 my $self = shift;
488 $self->{'_in_if'}-- if $self->{'_in_if'};
489 }
490
491 sub _true {
492 my $self = shift;
493 return $self->{'_seen_true'}->[$self->{'_in_if'}]++;
494 }
495
496 sub _suspend {
497 my $self = shift;
498 $self->{'_suspend'}->[$self->{'_in_if'}]++;
499 }
500
501 sub _resume {
502 my $self = shift;
503 $self->{'_suspend'}->[$self->{'_in_if'}]--
504 if $self->{'_suspend'}->[$self->{'_in_if'}];
505 }
506
507 sub _in_if {
508 my $self = shift;
509 return $self->{'_in_if'};
510 }
511
512 sub if {
513 my($self,$expr,$test) = @_;
514 $expr = $test if @_ == 3;
515 $self->_entering_if();
516 if($self->_test($expr)) {
517 $self->_true();
518 } else {
519 $self->_suspend();
520 }
521 return '';
522 }
523
524 sub elif {
525 my($self,$expr,$test) = @_;
526 die "Incorrect use of elif ssi directive: no preceeding 'if'." unless $self->_in_if();
527 $expr = $test if @_ == 3;
528 if(! $self->_seen_true() and $self->_test($expr)) {
529 $self->_true();
530 $self->_resume();
531 } else {
532 $self->_suspend() unless $self->_suspended();
533 }
534 return '';
535 }
536
537 sub else {
538 my $self = shift;
539 die "Incorrect use of else ssi directive: no preceeding 'if'." unless $self->_in_if();
540 unless($self->_seen_true()) {
541 $self->_resume();
542 } else {
543 $self->_suspend();
544 }
545 return '';
546 }
547
548 sub endif {
549 my $self = shift;
550 die "Incorrect use of endif ssi directive: no preceeding 'if'." unless $self->_in_if();
551 $self->_leaving_if();
552 # $self->_resume() if $self->_suspended();
553 return '';
554 }
555
556 #
557 # if we're called like this, it means that we're to handle a CGI request ourselves.
558 # that means that we're to open the file and process the content, sending it to STDOUT
559 # along with a standard HTTP content header
560 #
561 unless(caller) {
562 goto &handler;
563 }
564
565 sub handler {
566 eval "use CGI qw(:standard);";
567 print header();
568
569 unless(UNIVERSAL::isa(tied(*STDOUT),'CGI::SSI')) {
570 tie *STDOUT, 'CGI::SSI', filehandle => 'main::STDOUT';
571 }
572
573 my $filename = "$ENV{DOCUMENT_ROOT}$ENV{REQUEST_URI}";
574 if(-f $filename) {
575 open my $fh, '<', $filename or die "Failed to open file ($filename): $!";
576 print <$fh>;
577 } else {
578 print "Failed to find file ($filename).";
579 }
580
581 exit;
582 }
583
584 #
585 # packages for tie()
586 #
587
588 package CGI::SSI::Gmt;
589
590 sub TIESCALAR { bless [@_], shift() }
591 sub FETCH {
592 my $self = shift;
593 if($self->[-1]->{'_config'}->{'timefmt'}) {
594 my @gt = gmtime;
595 return Date::Format::strftime($self->[-1]->{'_config'}->{'timefmt'},@gt);
596 } else {
597 return scalar gmtime;
598 }
599 }
600
601 package CGI::SSI::Local;
602
603 sub TIESCALAR { bless [@_], shift() }
604 sub FETCH {
605 my $self = shift;
606 if($self->[-1]->{'_config'}->{'timefmt'}) {
607 my @lt = localtime;
608 return Date::Format::strftime($self->[-1]->{'_config'}->{'timefmt'},@lt);
609 } else {
610 return scalar localtime;
611 }
612 }
613
614 package CGI::SSI::LMOD;
615
616 sub TIESCALAR { bless [@_], shift() }
617 sub FETCH {
618 my $self = shift;
619 return $self->[-1]->flastmod('file', $ENV{'SCRIPT_FILENAME'} || $ENV{'PATH_TRANSLATED'} || '');
620 }
621
622 1;
623 __END__
624
625
626 =head1 NAME
627
628 CGI::SSI - Use SSI from CGI scripts
629
630 =head1 SYNOPSIS
631
632 # autotie STDOUT or any other open filehandle
633
634 use CGI::SSI (autotie => 'STDOUT');
635
636 print $shtml; # browser sees resulting HTML
637
638 # or tie it yourself to any open filehandle
639
640 use CGI::SSI;
641
642 open(FILE,'+>'.$html_file) or die $!;
643 $ssi = tie(*FILE, 'CGI::SSI', filehandle => 'FILE');
644 print FILE $shtml; # HTML arrives in the file
645
646 # or use the object-oriented interface
647
648 use CGI::SSI;
649
650 $ssi = CGI::SSI->new();
651
652 $ssi->if('"$varname" =~ /^foo/');
653 $html .= $ssi->process($shtml);
654 $ssi->else();
655 $html .= $ssi->include(file => $filename);
656 $ssi->endif();
657
658 print $ssi->exec(cgi => $url);
659 print $ssi->flastmod(file => $filename);
660
661 #
662 # or roll your own favorite flavor of SSI
663 #
664
665 package CGI::SSI::MySSI;
666 use CGI::SSI;
667 @CGI::SSI::MySSI::ISA = qw(CGI::SSI);
668
669 sub include {
670 my($self,$type,$file_or_url) = @_;
671 # my idea of include goes something like this...
672 return $html;
673 }
674 1;
675 __END__
676
677 #
678 # or use .htaccess to include all files in a dir
679 #
680
681 # in .htaccess
682 Action cgi-ssi /cgi-bin/ssi/process.cgi
683 <FilesMatch "\.shtml">
684 SetHandler cgi-ssi
685 </FilesMatch>
686
687 # in /cgi-bin/ssi/process.cgi
688 #!/usr/local/bin/perl
689 use CGI::SSI;
690 CGI::SSI->handler();
691 __END__
692
693 =head1 DESCRIPTION
694
695 CGI::SSI is meant to be used as an easy way to filter shtml
696 through CGI scripts in a loose imitation of Apache's mod_include.
697 If you're using Apache, you may want to use either mod_include or
698 the Apache::SSI module instead of CGI::SSI. Limitations in a CGI
699 script's knowledge of how the server behaves make some SSI
700 directives impossible to imitate from a CGI script.
701
702 Most of the time, you'll simply want to filter shtml through STDOUT
703 or some other open filehandle. C<autotie> is available for STDOUT,
704 but in general, you'll want to tie other filehandles yourself:
705
706 $ssi = tie(*FH, 'CGI::SSI', filehandle => 'FH');
707 print FH $shtml;
708
709 Note that you'll need to pass the name of the filehandle to C<tie()> as
710 a named parameter. Other named parameters are possible, as detailed
711 below. These parameters are the same as those passed to the C<new()>
712 method. However, C<new()> will not tie a filehandle for you.
713
714 CGI::SSI has it's own flavor of SSI. Test expressions are Perlish.
715 You may create and use multiple CGI::SSI objects; they will not
716 step on each others' variables.
717
718 Object-Oriented methods use the same general format so as to imitate
719 SSI directives:
720
721 <!--#include virtual="/foo/bar.footer" -->
722
723 would be
724
725 $ssi->include(virtual => '/foo/bar.footer');
726
727 likewise,
728
729 <!--#exec cgi="/cgi-bin/foo.cgi" -->
730
731 would be
732
733 $ssi->exec(cgi => '/cgi-bin/foo.cgi');
734
735 Usually, if there's no chance for ambiguity, the first argument may
736 be left out:
737
738 <!--#echo var="var_name" -->
739
740 could be either
741
742 $ssi->echo(var => 'var_name');
743
744 or
745
746 $ssi->echo('var_name');
747
748 Likewise,
749
750 $ssi->set(var => $varname, value => $value)
751
752 is the same as
753
754 $ssi->set($varname => $value)
755
756 =over 4
757
758 =item $ssi->new([%args])
759
760 Creates a new CGI::SSI object. The following are valid (optional) arguments:
761
762 DOCUMENT_URI => $doc_uri,
763 DOCUMENT_NAME => $doc_name,
764 DOCUMENT_ROOT => $doc_root,
765 errmsg => $oops,
766 sizefmt => ('bytes' || 'abbrev'),
767 timefmt => $time_fmt,
768 MAX_RECURSIONS => $default_100, # when to stop infinite loops w/ error msg
769 COOKIE_JAR => HTTP::Cookies->new,
770
771 =item $ssi->config($type, $arg)
772
773 $type is either 'sizefmt', 'timefmt', or 'errmsg'. $arg is similar to
774 those of the SSI C<spec>, referenced below.
775
776 =item $ssi->set($varname => $value)
777
778 Sets variables internal to the CGI::SSI object. (Not to be confused
779 with the normal variables your script uses!) These variables may be used
780 in test expressions, and retreived using $ssi->echo($varname). These
781 variables also will not be available in external, included resources.
782
783 =item $ssi->echo($varname)
784
785 Returns the value of the variable named $varname. Such variables may
786 be set manually using the C<set()> method. There are also several built-in
787 variables:
788
789 DOCUMENT_URI - the URI of this document
790 DOCUMENT_NAME - the name of the current document
791 DATE_GMT - the same as 'gmtime'
792 DATE_LOCAL - the same as 'localtime'
793 LAST_MODIFIED - the last time this script was modified
794
795 =item $ssi->exec($type, $arg)
796
797 $type is either 'cmd' or 'cgi'. $arg is similar to the SSI C<spec>
798 (see below).
799
800 =item $ssi->include($type, $arg)
801
802 Similar to C<exec>, but C<virtual> and C<file> are the two valid types.
803 SSI variables will not be available outside of your CGI::SSI object,
804 regardless of whether the virtual resource is on the local system or
805 a remote system.
806
807 =item $ssi->flastmod($type, $filename)
808
809 Similar to C<include>.
810
811 =item $ssi->fsize($type, $filename)
812
813 Same as C<flastmod>.
814
815 =item $ssi->printenv
816
817 Returns the environment similar to Apache's mod_include.
818
819 =item $ssi->cookie_jar([$jar])
820
821 Returns the currently-used HTTP::Cookies object. You may optionally
822 pass in a new HTTP::Cookies object. The jar is used for web requests
823 in exec cgi and include virtual directives.
824
825 =back
826
827 =head2 FLOW-CONTROL METHODS
828
829 The following methods may be used to test expressions. During a C<block>
830 where the test $expr is false, nothing will be returned (or printed,
831 if tied).
832
833 =over 4
834
835 =item $ssi->if($expr)
836
837 The expr can be anything Perl, but care should be taken. This causes
838 problems:
839
840 $ssi->set(varname => "foo");
841 <!--#if expr="'\$varname' =~ /^foo$/" -->ok<!--#endif -->
842
843 The $varname is expanded as you would expect. (We escape it so as to use
844 the C<$varname> within the CGI::SSI object, instead of that within our
845 progam.) But the C<$/> inside the regex is also expanded. This is fixed
846 by escaping the C<$>:
847
848 <!--#if expr="'\$varname' =~ /^value\$/" -->ok<!--#endif -->
849
850 The expressions used in if and elif tags/calls are tricky due to
851 the number of escapes required. In some cases, you'll need to
852 write C<\\\\> to mean C<\>.
853
854 =item $ssi->elif($expr)
855
856 =item $ssi->else
857
858 =item $ssi->endif
859
860
861 =back
862
863 =head1 SEE ALSO
864
865 C<Apache::SSI> and the SSI C<spec> at
866 http://www.apache.org/docs/mod/mod_include.html
867
868 =head1 AUTHOR
869
870 (c) 2000-2005 James Tolley <james@bitperfect.com> All Rights Reserved.
871
872 This is free software. You may copy and/or modify it under
873 the same terms as perl itself.
874
875 =head1 CREDITS
876
877 Many Thanks to Corey Wilson and Fitz Elliot for bug reports and fixes.
0 use strict;
1 use warnings FATAL => 'all';
2
3 use Test::More tests => 29;
4
5 use List::Util qw(sum);
6 use File::Temp qw(tempfile tempdir);
7
8 use_ok('CGI::SSI');
9
10 # set and echo
11
12 {
13 my $ssi = CGI::SSI->new();
14 $ssi->set(var => 'value');
15 my $value = $ssi->echo('var');
16 ok($value eq 'value','set/echo 1');
17 }
18
19 # other ways to call set and echo
20
21 {
22 my $ssi = CGI::SSI->new();
23 $ssi->set(var => "var2", value => "value2");
24 my $value = $ssi->echo(var => 'var2');
25 ok($value eq 'value2','set/echo 2');
26 }
27
28 # objects don't crush each other's vars.
29
30 {
31 my $ssi = CGI::SSI->new();
32 my $ssi2 = CGI::SSI->new();
33
34 $ssi->set(var => "value");
35 $ssi2->set(var => "value2");
36
37 my $value = $ssi->echo("var");
38 my $value2 = $ssi2->echo("var");
39
40 ok($value eq "value" && $value2 eq "value2",'data encapsulation');
41 }
42
43 # args to new()
44
45 {
46 my $ssi = CGI::SSI->new(
47 DOCUMENT_URI => "doc_uri",
48 DOCUMENT_NAME => "doc_name",
49 DOCUMENT_ROOT => "/",
50 errmsg => "[ERROR!]",
51 sizefmt => "bytes",
52 timefmt => "%B",
53 );
54 ok( ($ssi->echo("DOCUMENT_URI") eq "doc_uri"
55 and $ssi->echo("DOCUMENT_NAME") eq "doc_name"
56 and $ssi->echo("DOCUMENT_ROOT") eq "/"),'new()');
57 }
58
59 # config
60
61 {
62 my %months = map { ($_,1) } qw(January February March April May June
63 July August September October November December);
64
65 # create a tmp file for testing.
66 use IO::File;
67 use POSIX qw(tmpnam);
68
69 my($filename,$fh); # Thanks, Perl Cookbook!
70 do { $filename = tmpnam() } until $fh = IO::File->new($filename, O_RDWR|O_CREAT|O_EXCL);
71 # select( ( select($fh), $| = 1 )[0] );
72 print $fh ' ' x 10;
73 close $fh;
74
75 my $ssi = CGI::SSI->new();
76 $ssi->config(timefmt => "%B");
77 ok($months{ $ssi->flastmod(file => $filename) },'config 1');
78
79 $ssi->config(sizefmt => "bytes"); # TODO: combine these calls to config.
80
81 my $size = $ssi->fsize(file => $filename);
82 ok($size eq int $size,'config 2');
83
84 $ssi->config(errmsg => "error"); # TODO combine config calls
85
86 # close STDERR for this test
87 open COPY,'>&STDERR' or die "no copy of STDERR: $!";
88 close STDERR;
89
90 # perform the test
91 ok($ssi->flastmod("") eq "error",'config 3');
92
93 # re-open STDERR and continue
94 open STDERR,">&COPY" or die "no reassign to STDERR: $!";
95
96 unlink $filename;
97 }
98
99 # tough to do these well, without more info...
100 # include file - with many types of input
101 # include virtual - with different types of input
102
103 {
104 my $ssi = CGI::SSI->new();
105 my $html = $ssi->process(q[<!--#include virtual="http://www.yahoo.com" -->]);
106 ok($html =~ /yahoo/i && $html =~ /mail/i,'include virtual 1');
107 }
108
109 # tough to do these well, without more info...
110 # include file - with many types of input
111 # include virtual - with different types of input
112
113 {
114 my $ssi = CGI::SSI->new(DOCUMENT_ROOT => '.');
115 my $html = $ssi->process(q[<!--#include virtual="/MANIFEST" -->]);
116 ok($html =~ /Changes/i,'include virtual 2');
117 }
118
119 # exec cgi - with different input
120
121 {
122 my $ssi = CGI::SSI->new();
123 my $html = $ssi->process(q[<!--#exec cgi="http://www.yahoo.com/" -->]);
124 ok($html =~ /yahoo/i,'exec cgi');
125 }
126
127 # exec cmd - with different input
128
129 {
130 my $ssi = CGI::SSI->new();
131 my $perl = $^X;
132 $perl =~ s|\\|/|g;
133 my $html = $ssi->process(qq[<!--#exec cmd="$perl -v" -->]);
134 ok($html =~ /perl/i,'exec cmd');
135 }
136
137 # flastmod - different input
138 # fsize - different input
139
140 # if/else
141
142 {
143 my $ssi = CGI::SSI->new();
144 $ssi->set(varname => "test");
145 my $html = $ssi->process(qq[<!--#if expr="'\$varname' =~ /^TEST\$/i" -->if<!--#else -->else<!--#endif --->]);
146 ok($html eq "if",'if/else');
147 }
148
149 # if/elif
150
151 {
152 my $ssi = CGI::SSI->new();
153 my $html = $ssi->process(q[<!--#if expr="my \$i = 2; \$i eq 3;" -->if<!--#elif expr="my \$j = 4; \$j == 4" -->elif<!--#endif -->]);
154 ok($html eq "elif",'if/elif');
155 }
156
157 # if/elif/else
158
159 {
160 my $ssi = CGI::SSI->new();
161 my $html = $ssi->process(q[<!--#if expr="0" -->if<!--#elif expr="'$DATE_LOCAL' !~ /\\\\S/" -->elif<!--#else -->else<!--#endif -->]);
162 ok($html eq "else",'if/elif/else');
163 }
164
165 ## nested ifs:
166
167 # if false -> if true/else
168
169 {
170 my $ssi = CGI::SSI->new();
171 my $html = $ssi->process(q[<!--#if expr="0" -->if1<!--#if expr="1" -->if2<!--#else -->else<!--#endif --><!--#endif -->]);
172 ok(!$html,'if 1');
173 }
174
175
176 # if true -> if false/else
177
178 {
179 my $ssi = CGI::SSI->new();
180 my $html = $ssi->process(q[<!--#if expr="1" -->if1<!--#if expr="0" -->if2<!--#else -->else<!--#endif --><!--#endif -->]);
181 ok($html eq "if1else",'if 2');
182 }
183
184 # if true -> if true/else
185
186 {
187 my $ssi = CGI::SSI->new();
188 my $html = $ssi->process(q[<!--#if expr="1" -->if1<!--#if expr="1" -->if2<!--#else -->else<!--#endif --><!--#endif -->]);
189 ok($html eq "if1if2",'if 3');
190 }
191
192 # one bigger test: if true -> if false/elif true/else -> if false/*elif true*/else
193
194 {
195 my $ssi = CGI::SSI->new();
196 my $html = $ssi->process(q[<!--#if expr="1" -->if1<!--#if expr="0" -->if2<!--#elif expr="1" -->elif1<!--#if expr="0" -->if3<!--#elif expr="1" -->elif2<!--#else -->else1<!--#endif --><!--#else -->else2<!--#endif --><!--#endif -->]);
197 ok($html eq "if1elif1elif2",'if/elif/else');
198 }
199
200 # derive a class, and do something simple (empty class)
201
202 {
203 package CGI::SSI::Empty;
204 @CGI::SSI::Empty::ISA = qw(CGI::SSI);
205
206 package main;
207
208 my $empty = CGI::SSI::Empty->new();
209 my $html = $empty->process(q[<!--#set var="varname" value="foo" --><!--#echo var="varname" -->]);
210 ok($html eq "foo",'inherit 1');
211 }
212
213 # derive a class, and do something simple (altered class)
214
215 {
216 package CGI::SSI::UCEcho;
217 @CGI::SSI::UCEcho::ISA = qw(CGI::SSI);
218
219 sub echo {
220 return uc shift->SUPER::echo(@_);
221 }
222
223 package main;
224
225 my $echo = CGI::SSI::UCEcho->new();
226 my $html = $echo->process(q[<!--#set var="varname" value="foo" --><!--#echo var="varname" -->]);
227 ok($html eq "FOO",'inherit 2');
228 }
229
230 # DATE_LOCAL/DATE_GMT with config{timefmt}
231 {
232 my $ssi = new CGI::SSI (timefmt => '%Y');
233 ok($ssi->echo('DATE_LOCAL') =~ /^\d{4}$/,'config{timefmt}');
234 }
235
236 # check recursion test
237 SKIP: {
238 # create tempfile to hold include directive
239 my($fh,$filename) = tempfile();
240 skip("Failed to create tempfile: $!",1) unless $filename;
241 $filename =~ s/\\/\\\\/g;
242 print $fh qq[<!--#include file="$filename"-->];
243 close $fh;
244
245 my $ssi = CGI::SSI->new(MAX_RECURSIONS => 42);
246
247 # close STDERR for this test
248 open COPY,'>&STDERR' or die "no copy of STDERR: $!";
249 close STDERR;
250
251 # perform the test
252 my $html = $ssi->include(file => $filename);
253 ok($html eq $ssi->{_config}->{errmsg}
254 && (
255 sum(values %{$ssi->{_recursions}}) == 43
256 ||
257 sum(values %{$ssi->{_recursions}}) == 42
258 )
259 , "recursion check");
260
261 # re-open STDERR and continue
262 open STDERR,">&COPY" or die "no reassign to STDERR: $!";
263
264
265 }
266
267 # test cookie support
268 SKIP: {
269 eval "use HTTP::Cookies; 1" or skip("HTTP::Cookies not installed", 1);
270 my $jar = HTTP::Cookies->new({});
271 $jar->set_cookie(1,'mycookie','COOKIEVAL','/','www.bitperfect.com',80,0,0,100);
272
273 my $ssi = CGI::SSI->new(COOKIE_JAR => $jar);
274 my $html = $ssi->process(qq[<!--#include virtual="http://www.bitperfect.com/cgi-bin/cgi-ssi/cookietest.cgi"-->]);
275 ok($html =~ m'COOKIEVAL', "cookie support");
276 }
277
278 SKIP: {
279 # tie by hand & close
280 my($dir) = tempdir();
281 # print $dir,"\n";
282 open FH, "+>$dir/AfDCSd43.tmp" or skip('failed to open tempfile',2);
283 my $ssi = tie *FH, 'CGI::SSI', filehandle => 'FH';
284 isa_ok(tied(*FH),'CGI::SSI','tied object');
285
286 print FH "this is the first test\n";
287
288 close FH;
289 eval { print FH "this is the second test\n" or die "FH is closed" };
290 ok($@ =~ /^FH is closed/,'close()');
291 }
292
293 {
294 # nested ifs
295 my $ssi = CGI::SSI->new();
296 my $html = $ssi->process(<<"");
297 <!--#if expr="0" -->
298 <!--#if expr="1" -->
299 yes
300 <!--#else -->
301 no
302 <!--#endif -->
303 no
304 <!--#else -->
305 yes
306 <!--#endif -->
307
308 ok($html =~ /^\s*yes\s*$/s,'nested ifs');
309 }
310
311 {
312 # timefmt applied to LAST_MODIFIED
313 my $ssi = CGI::SSI->new();
314 my $html = $ssi->process('<!--#config timefmt="%m/%d/%Y %X" --><!--#echo var="LAST_MODIFIED" -->');
315
316 like($html, qr{^\d\d/\d\d/\d{4} \d\d:\d\d:\d\d$}, 'timefmt applied to LAST_MODIFIED');
317 }
318
319 {
320 # newlines in directives
321 my $ssi = CGI::SSI->new();
322 my $html = $ssi->process('<!--#config
323 timefmt="%m/%d/%Y %X" --><!--#echo var="LAST_MODIFIED" -->');
324
325 like($html, qr{^\d\d/\d\d/\d{4} \d\d:\d\d:\d\d$}, 'newlines in directives');
326 }
327
328 # autotie ?
329
330
331 __END__