| line | stmt | bran | cond | sub | pod | time | code | 
| 1 |  |  |  |  |  |  | package CGI::apacheSSI; | 
| 2 | 2 |  |  | 2 |  | 27245 | use strict; | 
|  | 2 |  |  |  |  | 5 |  | 
|  | 2 |  |  |  |  | 84 |  | 
| 3 |  |  |  |  |  |  |  | 
| 4 |  |  |  |  |  |  | # use HTML::SimpleParse; | 
| 5 | 2 |  |  | 2 |  | 1916 | use File::Spec::Functions; # catfile() | 
|  | 2 |  |  |  |  | 1781 |  | 
|  | 2 |  |  |  |  | 273 |  | 
| 6 | 2 |  |  | 2 |  | 3111 | use FindBin; | 
|  | 2 |  |  |  |  | 2659 |  | 
|  | 2 |  |  |  |  | 89 |  | 
| 7 | 2 |  |  | 2 |  | 4294 | use LWP::UserAgent; | 
|  | 2 |  |  |  |  | 131489 |  | 
|  | 2 |  |  |  |  | 79 |  | 
| 8 | 2 |  |  | 2 |  | 23 | use HTTP::Response; | 
|  | 2 |  |  |  |  | 3 |  | 
|  | 2 |  |  |  |  | 48 |  | 
| 9 | 2 |  |  | 2 |  | 4944 | use HTTP::Cookies; | 
|  | 2 |  |  |  |  | 22171 |  | 
|  | 2 |  |  |  |  | 134 |  | 
| 10 | 2 |  |  | 2 |  | 17 | use URI; | 
|  | 2 |  |  |  |  | 4 |  | 
|  | 2 |  |  |  |  | 52 |  | 
| 11 | 2 |  |  | 2 |  | 848 | use Date::Format; | 
|  | 0 |  |  |  |  |  |  | 
|  | 0 |  |  |  |  |  |  | 
| 12 |  |  |  |  |  |  |  | 
| 13 |  |  |  |  |  |  | our $VERSION = '0.93'; | 
| 14 |  |  |  |  |  |  |  | 
| 15 |  |  |  |  |  |  | our $DEBUG = 0; | 
| 16 |  |  |  |  |  |  |  | 
| 17 |  |  |  |  |  |  | sub import { | 
| 18 |  |  |  |  |  |  | my($class,%args) = @_; | 
| 19 |  |  |  |  |  |  | return unless exists $args{'autotie'}; | 
| 20 |  |  |  |  |  |  | $args{'filehandle'} = $args{'autotie'} =~ /::/ ? $args{'autotie'} : caller().'::'.$args{'autotie'}; | 
| 21 |  |  |  |  |  |  | no strict 'refs'; | 
| 22 |  |  |  |  |  |  | my $self = tie(*{$args{'filehandle'}},$class,%args); | 
| 23 |  |  |  |  |  |  | return $self; | 
| 24 |  |  |  |  |  |  | } | 
| 25 |  |  |  |  |  |  |  | 
| 26 |  |  |  |  |  |  | my($gmt,$loc,$lmod); | 
| 27 |  |  |  |  |  |  |  | 
| 28 |  |  |  |  |  |  | # NOTE: check for escaped \( or \), what should it do? | 
| 29 |  |  |  |  |  |  | our $L; # used to return the brackets count | 
| 30 |  |  |  |  |  |  | our $RE_parens_2C = qr/ | 
| 31 |  |  |  |  |  |  | (	  # g1, everything inside the brackets, incl brackets | 
| 32 |  |  |  |  |  |  | \( | 
| 33 |  |  |  |  |  |  | ( (?:	  # g2, everything inside the brackets | 
| 34 |  |  |  |  |  |  | (?{ $L = 1 })	  #  $L counts ('s inside pattern | 
| 35 |  |  |  |  |  |  | (?: | 
| 36 |  |  |  |  |  |  | (?:"[^"\\]*  (?: \\.[^"\\]* )* ") | 
| 37 |  |  |  |  |  |  | | (?:'[^'\\]*  (?: \\.[^'\\]* )* ') | 
| 38 |  |  |  |  |  |  | | (?:`[^`\\]*  (?: \\.[^`\\]* )* `) | 
| 39 |  |  |  |  |  |  | | (?:[^"'`)(]) | 
| 40 |  |  |  |  |  |  | | (?:  \( | 
| 41 |  |  |  |  |  |  | (?{ local  $L=$L+1; })	  # new set of nested parens | 
| 42 |  |  |  |  |  |  | ) | 
| 43 |  |  |  |  |  |  | | (?:  \) | 
| 44 |  |  |  |  |  |  | (?{ local  $L=$L-1; })	  # close a set of nested parens | 
| 45 |  |  |  |  |  |  | (?(?{ $L==0 })(?!))	  #  ...if there was no matching open paren... | 
| 46 |  |  |  |  |  |  | ) | 
| 47 |  |  |  |  |  |  | )* | 
| 48 |  |  |  |  |  |  | )* )   # end g2 | 
| 49 |  |  |  |  |  |  | \) | 
| 50 |  |  |  |  |  |  | )   # end g1 | 
| 51 |  |  |  |  |  |  | /x; | 
| 52 |  |  |  |  |  |  |  | 
| 53 |  |  |  |  |  |  | our $RE_quote_dbl_NC		  = qr/(?:"[^"\\]*  (?: \\.[^"\\]* )* ")/x; | 
| 54 |  |  |  |  |  |  | our $RE_quote_single_NC		  = qr/(?:'[^'\\]*  (?: \\.[^'\\]* )* ')/x; | 
| 55 |  |  |  |  |  |  | our $RE_quote_backtick_NC	  = qr/(?:`[^`\\]*  (?: \\.[^`\\]* )* `)/x; | 
| 56 |  |  |  |  |  |  | our $RE_all_quote_NC		  = qr/$RE_quote_dbl_NC|$RE_quote_single_NC|$RE_quote_backtick_NC/; | 
| 57 |  |  |  |  |  |  | our $RE_all_no_quote_NC		  = qr/$RE_all_quote_NC|[^'"`]/; | 
| 58 |  |  |  |  |  |  | our $RE_all_no_paren_NC		  = qr/$RE_all_quote_NC|[^()'"`]/; | 
| 59 |  |  |  |  |  |  | our $RE_all_no_paren_noop_NC	  = qr/$RE_all_quote_NC | [^()'"`&\|] | &[^&] | \|[^\|]/x; | 
| 60 |  |  |  |  |  |  | our $RE_single_quote_false_NC	  = qr/^ (?:\s*'')+\s* [']* $ | 
| 61 |  |  |  |  |  |  | |^ '? (?:\\')* $/x; | 
| 62 |  |  |  |  |  |  | # empty, or 1+ unspaced single quotes,  trivially false | 
| 63 |  |  |  |  |  |  | # pairs of empty single quotes,  false | 
| 64 |  |  |  |  |  |  | # alternating backslash-single quotes,  false | 
| 65 |  |  |  |  |  |  |  | 
| 66 |  |  |  |  |  |  |  | 
| 67 |  |  |  |  |  |  | # apache's own, special way of quoting strings | 
| 68 |  |  |  |  |  |  | our $RE_apache_expr_quote	  = qr/ (?:"(?:[^"\\]|[\\]+[^\\])*?") | 
| 69 |  |  |  |  |  |  | |(?:'(?:[^'\\]|[\\]+[^\\])*?') | 
| 70 |  |  |  |  |  |  | |(?:`(?:[^`\\]|[\\]+[^\\])*?`) | 
| 71 |  |  |  |  |  |  | /x; | 
| 72 |  |  |  |  |  |  |  | 
| 73 |  |  |  |  |  |  | # NOTE: quotes that would be openers which are immediately preceeded by \w are treated as \w | 
| 74 |  |  |  |  |  |  | # NOTE: needs to be preceeded by \s or =, otherwise becomes part of token (parsing oddity with apache 2.2.22) | 
| 75 |  |  |  |  |  |  | our $RE_apache_expr_quote_all	  = qr/  $RE_apache_expr_quote | [^'"`\s]/x; | 
| 76 |  |  |  |  |  |  | our $RE_runaway = qr/ \s+  \w+['"`]\S*\s+[^'"`]+['"`]+  /x; | 
| 77 |  |  |  |  |  |  | our $RE_token_NC  =  qr{[[:alpha:]]\S+? (?:\s+ $RE_apache_expr_quote_all*? )*?   $RE_runaway? }x; | 
| 78 |  |  |  |  |  |  |  | 
| 79 |  |  |  |  |  |  |  | 
| 80 |  |  |  |  |  |  | sub new { | 
| 81 |  |  |  |  |  |  | my($class,%args) = @_; | 
| 82 |  |  |  |  |  |  | my $self = bless {}, $class; | 
| 83 |  |  |  |  |  |  |  | 
| 84 |  |  |  |  |  |  | $self->{'_handle'}        = undef; | 
| 85 |  |  |  |  |  |  | my $script_name = ''; | 
| 86 |  |  |  |  |  |  | if(exists $ENV{'SCRIPT_NAME'}) { | 
| 87 |  |  |  |  |  |  | ($script_name) = $ENV{'SCRIPT_NAME'} =~ /([^\/]+)$/; | 
| 88 |  |  |  |  |  |  | } | 
| 89 |  |  |  |  |  |  |  | 
| 90 |  |  |  |  |  |  | tie $gmt, 'CGI::apacheSSI::Gmt', $self; | 
| 91 |  |  |  |  |  |  | tie $loc, 'CGI::apacheSSI::Local', $self; | 
| 92 |  |  |  |  |  |  | tie $lmod, 'CGI::apacheSSI::LMOD', $self; | 
| 93 |  |  |  |  |  |  |  | 
| 94 |  |  |  |  |  |  | $ENV{'DOCUMENT_ROOT'} ||= ''; | 
| 95 |  |  |  |  |  |  | $self->{'_variables'}     = { | 
| 96 |  |  |  |  |  |  | DOCUMENT_URI    =>  ($args{'DOCUMENT_URI'} || $ENV{'SCRIPT_NAME'}), | 
| 97 |  |  |  |  |  |  | DATE_GMT        =>  $gmt, | 
| 98 |  |  |  |  |  |  | DATE_LOCAL      =>  $loc, | 
| 99 |  |  |  |  |  |  | LAST_MODIFIED   =>  $lmod, | 
| 100 |  |  |  |  |  |  | DOCUMENT_NAME   =>  ($args{'DOCUMENT_NAME'} || $script_name), | 
| 101 |  |  |  |  |  |  | DOCUMENT_ROOT   =>  ($args{'DOCUMENT_ROOT'} || $ENV{DOCUMENT_ROOT}), | 
| 102 |  |  |  |  |  |  | }; | 
| 103 |  |  |  |  |  |  |  | 
| 104 |  |  |  |  |  |  | $self->{'_config'}        = {  # NOTE: TODO: get these from apache config | 
| 105 |  |  |  |  |  |  | errmsg  =>  ($args{'errmsg'}  || '[an error occurred while processing this directive]'), | 
| 106 |  |  |  |  |  |  | sizefmt =>  ($args{'sizefmt'} || 'abbrev'), | 
| 107 |  |  |  |  |  |  | timefmt =>  ($args{'timefmt'} ||  undef), | 
| 108 |  |  |  |  |  |  | SSIUndefinedEcho =>  ($args{'SSIUndefinedEcho'} ||  '(none)'), | 
| 109 |  |  |  |  |  |  | _verbose_errors  =>  ($args{'_verbose_errors'}  ||  0)  # NOTE: TODO: document this option | 
| 110 |  |  |  |  |  |  | }; | 
| 111 |  |  |  |  |  |  |  | 
| 112 |  |  |  |  |  |  | $self->{_max_recursions} = $args{MAX_RECURSIONS} || 100; # no "infinite" loops | 
| 113 |  |  |  |  |  |  | $self->{_recursions} = {}; | 
| 114 |  |  |  |  |  |  |  | 
| 115 |  |  |  |  |  |  | $self->{_cookie_jar}  = $args{COOKIE_JAR} || HTTP::Cookies->new(); | 
| 116 |  |  |  |  |  |  |  | 
| 117 |  |  |  |  |  |  | $self->{'_in_if'}     = 0; | 
| 118 |  |  |  |  |  |  | $self->{'_suspend'}   = [0]; | 
| 119 |  |  |  |  |  |  | $self->{'_seen_true'} = [1]; | 
| 120 |  |  |  |  |  |  |  | 
| 121 |  |  |  |  |  |  | return $self; | 
| 122 |  |  |  |  |  |  | } | 
| 123 |  |  |  |  |  |  |  | 
| 124 |  |  |  |  |  |  | sub TIEHANDLE { | 
| 125 |  |  |  |  |  |  | my($class,%args) = @_; | 
| 126 |  |  |  |  |  |  | my $self = $class->new(%args); | 
| 127 |  |  |  |  |  |  | $self->{'_handle'} = do { local *STDOUT }; | 
| 128 |  |  |  |  |  |  | my $handle_to_tie = ''; | 
| 129 |  |  |  |  |  |  | if($args{'filehandle'} !~ /::/) { | 
| 130 |  |  |  |  |  |  | $handle_to_tie = caller().'::'.$args{'filehandle'}; | 
| 131 |  |  |  |  |  |  | } else { | 
| 132 |  |  |  |  |  |  | $handle_to_tie = $args{'filehandle'}; | 
| 133 |  |  |  |  |  |  | } | 
| 134 |  |  |  |  |  |  | open($self->{'_handle'},'>&'.$handle_to_tie) or die "Failed to copy the filehandle ($handle_to_tie): $!"; | 
| 135 |  |  |  |  |  |  | return $self; | 
| 136 |  |  |  |  |  |  | } | 
| 137 |  |  |  |  |  |  |  | 
| 138 |  |  |  |  |  |  | sub PRINT { | 
| 139 |  |  |  |  |  |  | my $self = shift; | 
| 140 |  |  |  |  |  |  | print {$self->{'_handle'}} map { $self->process($_) } @_; | 
| 141 |  |  |  |  |  |  | } | 
| 142 |  |  |  |  |  |  |  | 
| 143 |  |  |  |  |  |  | sub PRINTF { | 
| 144 |  |  |  |  |  |  | my $self = shift; | 
| 145 |  |  |  |  |  |  | my $fmt  = shift; | 
| 146 |  |  |  |  |  |  | printf {$self->{'_handle'}} $fmt, map { $self->process($_) } @_; | 
| 147 |  |  |  |  |  |  | } | 
| 148 |  |  |  |  |  |  |  | 
| 149 |  |  |  |  |  |  | sub CLOSE { | 
| 150 |  |  |  |  |  |  | my($self) = @_; | 
| 151 |  |  |  |  |  |  | close $self->{'_handle'}; | 
| 152 |  |  |  |  |  |  | } | 
| 153 |  |  |  |  |  |  |  | 
| 154 |  |  |  |  |  |  | sub SSI_WARN { | 
| 155 |  |  |  |  |  |  | my($self,$msg) = @_; | 
| 156 |  |  |  |  |  |  | warn ref($self)." warn: $msg\n"; | 
| 157 |  |  |  |  |  |  | } | 
| 158 |  |  |  |  |  |  |  | 
| 159 |  |  |  |  |  |  | sub SSI_ERROR { | 
| 160 |  |  |  |  |  |  | (my $self, $@) = @_; | 
| 161 |  |  |  |  |  |  | warn ref($self)." error: $@\n"; | 
| 162 |  |  |  |  |  |  | return;	# returning false here allows us to do one line error returns. | 
| 163 |  |  |  |  |  |  | } | 
| 164 |  |  |  |  |  |  |  | 
| 165 |  |  |  |  |  |  | sub SSI_ERROR_FLUSH { | 
| 166 |  |  |  |  |  |  | my($self,$msg) = @_; | 
| 167 |  |  |  |  |  |  | if ($msg) {$self->SSI_ERROR($msg);} | 
| 168 |  |  |  |  |  |  | $msg=$@;					# NOTE: DEBUG ONLY! | 
| 169 |  |  |  |  |  |  | undef $@; | 
| 170 |  |  |  |  |  |  | return "[SSI ERROR=[$msg]]" if $self->{'_config'}->{'_verbose_errors'}; # NOTE: DEBUG ONLY! | 
| 171 |  |  |  |  |  |  | return $self->{'_config'}->{'errmsg'}; | 
| 172 |  |  |  |  |  |  | } | 
| 173 |  |  |  |  |  |  |  | 
| 174 |  |  |  |  |  |  |  | 
| 175 |  |  |  |  |  |  |  | 
| 176 |  |  |  |  |  |  |  | 
| 177 |  |  |  |  |  |  | # NOTE: "if" allows expr="myexpr1" expr="myexpr2" where myexpr2 overwrites myexpr1. | 
| 178 |  |  |  |  |  |  |  | 
| 179 |  |  |  |  |  |  | sub process {		# NOTE: -- FIXME -- this fails if we comment out the tokens.. ie | 
| 180 |  |  |  |  |  |  | # NOTE: -- FIXME -- this should fail if we have any open quotes (ie, the --> doesnt magically close the tag.. in apache 2.2 at least) | 
| 181 |  |  |  |  |  |  | my($self,@shtml) = @_; | 
| 182 |  |  |  |  |  |  | my $processed = ''; | 
| 183 |  |  |  |  |  |  |  | 
| 184 |  |  |  |  |  |  | # NOTE: FIXME: would this be easier with a global replace  s///ge ? | 
| 185 |  |  |  |  |  |  | @shtml = split(m/()/sx, join '',@shtml); # this will slurp up anything inside quotes, single or double | 
| 186 |  |  |  |  |  |  |  | 
| 187 |  |  |  |  |  |  | my $count=0; | 
| 188 |  |  |  |  |  |  | for my $token (@shtml) { | 
| 189 |  |  |  |  |  |  | if($token =~ /^$/sx) { | 
| 190 |  |  |  |  |  |  | $processed .= $self->_process_ssi_text($1); | 
| 191 |  |  |  |  |  |  | } else { | 
| 192 |  |  |  |  |  |  | next if $self->_suspended; | 
| 193 |  |  |  |  |  |  | $processed .= $token; | 
| 194 |  |  |  |  |  |  | } | 
| 195 |  |  |  |  |  |  | } | 
| 196 |  |  |  |  |  |  | return $processed; | 
| 197 |  |  |  |  |  |  | } | 
| 198 |  |  |  |  |  |  |  | 
| 199 |  |  |  |  |  |  |  | 
| 200 |  |  |  |  |  |  |  | 
| 201 |  |  |  |  |  |  | sub _process_ssi_text { | 
| 202 |  |  |  |  |  |  | my($self,$text) = @_; | 
| 203 |  |  |  |  |  |  |  | 
| 204 |  |  |  |  |  |  | # what's the first \S+? | 
| 205 |  |  |  |  |  |  | if($text !~ s/^(\S+)\s*//) | 
| 206 |  |  |  |  |  |  | { return $self->SSI_ERROR_FLUSH("failed to find method name at beginning of string: '$text'."); } | 
| 207 |  |  |  |  |  |  |  | 
| 208 |  |  |  |  |  |  | my $method = $1; | 
| 209 |  |  |  |  |  |  | if (! $self->can($method) ) | 
| 210 |  |  |  |  |  |  | { return $self->SSI_ERROR_FLUSH("unknown directive \"$method\" in parsed doc."); } | 
| 211 |  |  |  |  |  |  |  | 
| 212 |  |  |  |  |  |  | # are we suspended? | 
| 213 |  |  |  |  |  |  | return '' if($self->_suspended and $method !~ /^(?:if|else|elif|endif)\b/); | 
| 214 |  |  |  |  |  |  |  | 
| 215 |  |  |  |  |  |  | my $res = $self->$method( $self->parse_args($text, $method) ); | 
| 216 |  |  |  |  |  |  | if ($@) { return $self->SSI_ERROR_FLUSH();} | 
| 217 |  |  |  |  |  |  | return $res; | 
| 218 |  |  |  |  |  |  | } | 
| 219 |  |  |  |  |  |  |  | 
| 220 |  |  |  |  |  |  |  | 
| 221 |  |  |  |  |  |  |  | 
| 222 |  |  |  |  |  |  | # many thanks to HTML::SimpleParse, with a couple of modifications | 
| 223 |  |  |  |  |  |  | sub parse_args { | 
| 224 |  |  |  |  |  |  | my ($self, $str, $method) = @_; | 
| 225 |  |  |  |  |  |  | my @returns; | 
| 226 |  |  |  |  |  |  |  | 
| 227 |  |  |  |  |  |  | # Make sure we start searching at the beginning of the string | 
| 228 |  |  |  |  |  |  | pos($str) = 0; | 
| 229 |  |  |  |  |  |  |  | 
| 230 |  |  |  |  |  |  | while (1) { | 
| 231 |  |  |  |  |  |  | next if $str =~ m/\G\s+/gc;  # Get rid of leading whitespace | 
| 232 |  |  |  |  |  |  |  | 
| 233 |  |  |  |  |  |  | if ( $str =~ m/\G | 
| 234 |  |  |  |  |  |  | ([\w.-]+)\s*=\s*			 # the key | 
| 235 |  |  |  |  |  |  | (?: | 
| 236 |  |  |  |  |  |  | # ($RE_all_quote_NC) \s*		 # anything in quotes | 
| 237 |  |  |  |  |  |  | ($RE_apache_expr_quote_all) \s*   # anything in quotes | 
| 238 |  |  |  |  |  |  | |				 #  or | 
| 239 |  |  |  |  |  |  | ([^\s>]*) \s*			 # anything else, without whitespace or > | 
| 240 |  |  |  |  |  |  | )/gcx ) { | 
| 241 |  |  |  |  |  |  | my ($key, $val) = ($1, $+); | 
| 242 |  |  |  |  |  |  | # ----- NOTE: if $key is not "expr" trim the quotes.. | 
| 243 |  |  |  |  |  |  | # ----- (apache evaluates differently depending on the type of quotes) | 
| 244 |  |  |  |  |  |  | if ($key ne "expr") {$val =~ s/^['"`]?(.*?)['"`]?$/$1/;} | 
| 245 |  |  |  |  |  |  | push @returns,  $key, $val; | 
| 246 |  |  |  |  |  |  | } elsif ( $str =~ m,\G/?([\w.-]+)\s*,gc ) { | 
| 247 |  |  |  |  |  |  | push @returns,  $1  , undef; | 
| 248 |  |  |  |  |  |  | } else { | 
| 249 |  |  |  |  |  |  | if ($str =~ m/\G(.+)/gc)  # anything left over?? | 
| 250 |  |  |  |  |  |  | { | 
| 251 |  |  |  |  |  |  | $self->SSI_ERROR("missing argument name for value to tag \"$method\" in"); | 
| 252 |  |  |  |  |  |  | # NOTE: notice this is NOT a "return".. we want processing to continue normally | 
| 253 |  |  |  |  |  |  | } | 
| 254 |  |  |  |  |  |  | last; | 
| 255 |  |  |  |  |  |  | } | 
| 256 |  |  |  |  |  |  | } | 
| 257 |  |  |  |  |  |  |  | 
| 258 |  |  |  |  |  |  | # too many arguments for if element in | 
| 259 |  |  |  |  |  |  | # else/endif/printenv directive does not take tags in | 
| 260 |  |  |  |  |  |  | my %allowed_tag_count;			# NOTE: this needs to be moved up | 
| 261 |  |  |  |  |  |  | $allowed_tag_count{'if'}=["expr"]; | 
| 262 |  |  |  |  |  |  | $allowed_tag_count{'else'}=[]; | 
| 263 |  |  |  |  |  |  |  | 
| 264 |  |  |  |  |  |  | if (defined $allowed_tag_count{$method}) | 
| 265 |  |  |  |  |  |  | { | 
| 266 |  |  |  |  |  |  | if (@returns > 2 * @{ $allowed_tag_count{$method} }) | 
| 267 |  |  |  |  |  |  | { | 
| 268 |  |  |  |  |  |  | if (@{ $allowed_tag_count{$method} } == 0) | 
| 269 |  |  |  |  |  |  | { $self->SSI_ERROR("\"$method\" directive does not take tags in");} | 
| 270 |  |  |  |  |  |  | else | 
| 271 |  |  |  |  |  |  | { $self->SSI_ERROR("too many arguments for \"$method\" element in");} | 
| 272 |  |  |  |  |  |  | } | 
| 273 |  |  |  |  |  |  | elsif (@returns < 2 * @{ $allowed_tag_count{$method} }) | 
| 274 |  |  |  |  |  |  | { $self->SSI_ERROR("missing arguments for directive \"$method\"");} # NOTE: fix this error message | 
| 275 |  |  |  |  |  |  | } | 
| 276 |  |  |  |  |  |  |  | 
| 277 |  |  |  |  |  |  | return @returns; | 
| 278 |  |  |  |  |  |  | } | 
| 279 |  |  |  |  |  |  |  | 
| 280 |  |  |  |  |  |  |  | 
| 281 |  |  |  |  |  |  | sub _interp_vars { | 
| 282 |  |  |  |  |  |  | local $^W = 0; | 
| 283 |  |  |  |  |  |  | my($self,$text,$setcmd) = @_; | 
| 284 |  |  |  |  |  |  |  | 
| 285 |  |  |  |  |  |  | # NOTE: var name in ${} MUST start with at least one \w | 
| 286 |  |  |  |  |  |  | $text =~ s{ ((\\*) ((\\)|(\$)) (\{)?(\w (?(6)(.*)\}|(\w*)) )) } | 
| 287 |  |  |  |  |  |  | { | 
| 288 |  |  |  |  |  |  | my ($all,$slashes, $slash,$dollar, $lbrak,$var)=($1,$2, $4,$5, $6,$7); | 
| 289 |  |  |  |  |  |  | $slashes .= $slash;							   #  NOTE: this can be improved | 
| 290 |  |  |  |  |  |  | if ($lbrak) {chop $var}; | 
| 291 |  |  |  |  |  |  |  | 
| 292 |  |  |  |  |  |  | if (! $setcmd) | 
| 293 |  |  |  |  |  |  | { chop($slashes); } | 
| 294 |  |  |  |  |  |  |  | 
| 295 |  |  |  |  |  |  | if ($dollar && ! $slashes) | 
| 296 |  |  |  |  |  |  | { $var = $self->_echo($var); } | 
| 297 |  |  |  |  |  |  | else | 
| 298 |  |  |  |  |  |  | { | 
| 299 |  |  |  |  |  |  | $var = "{$var}" if ($lbrak) ; | 
| 300 |  |  |  |  |  |  | $var = $dollar.$var; | 
| 301 |  |  |  |  |  |  | } | 
| 302 |  |  |  |  |  |  | $slashes.$var | 
| 303 |  |  |  |  |  |  | }exg; | 
| 304 |  |  |  |  |  |  |  | 
| 305 |  |  |  |  |  |  | return $text; | 
| 306 |  |  |  |  |  |  | } | 
| 307 |  |  |  |  |  |  |  | 
| 308 |  |  |  |  |  |  |  | 
| 309 |  |  |  |  |  |  |  | 
| 310 |  |  |  |  |  |  | # for internal use only - returns the thing passed in if it's not defined. echo() returns '' in that case. | 
| 311 |  |  |  |  |  |  | sub _echo { | 
| 312 |  |  |  |  |  |  | my($self,$key,$var) = @_; | 
| 313 |  |  |  |  |  |  | $var = $key if @_ == 2; | 
| 314 |  |  |  |  |  |  |  | 
| 315 |  |  |  |  |  |  | if($var eq 'DATE_LOCAL') { | 
| 316 |  |  |  |  |  |  | return $loc; | 
| 317 |  |  |  |  |  |  | } elsif($var eq 'DATE_GMT') { | 
| 318 |  |  |  |  |  |  | return $gmt; | 
| 319 |  |  |  |  |  |  | } elsif($var eq 'LAST_MODIFIED') { | 
| 320 |  |  |  |  |  |  | return $lmod; | 
| 321 |  |  |  |  |  |  | } | 
| 322 |  |  |  |  |  |  |  | 
| 323 |  |  |  |  |  |  | return $self->{'_variables'}->{$var} if exists $self->{'_variables'}->{$var}; | 
| 324 |  |  |  |  |  |  | return $ENV{$var} if exists $ENV{$var}; | 
| 325 |  |  |  |  |  |  | return ''; | 
| 326 |  |  |  |  |  |  | } | 
| 327 |  |  |  |  |  |  |  | 
| 328 |  |  |  |  |  |  | # | 
| 329 |  |  |  |  |  |  | # ssi directive methods | 
| 330 |  |  |  |  |  |  | # | 
| 331 |  |  |  |  |  |  |  | 
| 332 |  |  |  |  |  |  | sub config { | 
| 333 |  |  |  |  |  |  | my($self,$type,$value) = @_; | 
| 334 |  |  |  |  |  |  | if($type =~ /^timefmt$/i) { | 
| 335 |  |  |  |  |  |  | $self->{'_config'}->{'timefmt'} = $value; | 
| 336 |  |  |  |  |  |  | } elsif($type =~ /^sizefmt$/i) { | 
| 337 |  |  |  |  |  |  | if(lc $value eq 'abbrev') { | 
| 338 |  |  |  |  |  |  | $self->{'_config'}->{'sizefmt'} = 'abbrev'; | 
| 339 |  |  |  |  |  |  | } elsif(lc $value eq 'bytes') { | 
| 340 |  |  |  |  |  |  | $self->{'_config'}->{'sizefmt'} = 'bytes'; | 
| 341 |  |  |  |  |  |  | } else { | 
| 342 |  |  |  |  |  |  | return $self->SSI_ERROR_FLUSH("value for sizefmt is '$value'. It must be 'abbrev' or 'bytes'."); | 
| 343 |  |  |  |  |  |  | } | 
| 344 |  |  |  |  |  |  | } elsif($type =~ /^errmsg$/i) { | 
| 345 |  |  |  |  |  |  | $self->{'_config'}->{'errmsg'} = $value; | 
| 346 |  |  |  |  |  |  | } elsif($type =~ /^_verbose_errors/i) { | 
| 347 |  |  |  |  |  |  | $self->{'_config'}->{'_verbose_errors'} = $value; | 
| 348 |  |  |  |  |  |  | } else { | 
| 349 |  |  |  |  |  |  | return $self->SSI_ERROR_FLUSH("arg to config is '$type'. It must be one of: 'timefmt', 'sizefmt', or 'errmsg'."); | 
| 350 |  |  |  |  |  |  | } | 
| 351 |  |  |  |  |  |  | return ''; | 
| 352 |  |  |  |  |  |  | } | 
| 353 |  |  |  |  |  |  |  | 
| 354 |  |  |  |  |  |  | sub set { | 
| 355 |  |  |  |  |  |  | my($self,%args) = @_; | 
| 356 |  |  |  |  |  |  | if(scalar keys %args > 1) { | 
| 357 |  |  |  |  |  |  | $self->{'_variables'}->{$args{'var'}} = $self->_interp_vars($args{'value'}, 1); | 
| 358 |  |  |  |  |  |  | } else { # var => value notation | 
| 359 |  |  |  |  |  |  | my($var,$value) = %args; | 
| 360 |  |  |  |  |  |  | $self->{'_variables'}->{$var} = $self->_interp_vars($value, 1); | 
| 361 |  |  |  |  |  |  | } | 
| 362 |  |  |  |  |  |  | return ''; | 
| 363 |  |  |  |  |  |  | } | 
| 364 |  |  |  |  |  |  |  | 
| 365 |  |  |  |  |  |  | sub escaped { | 
| 366 |  |  |  |  |  |  | my ($t)=@_; | 
| 367 |  |  |  |  |  |  | $t =~ s/\\\$/\$/g; | 
| 368 |  |  |  |  |  |  | return $t ; | 
| 369 |  |  |  |  |  |  | } | 
| 370 |  |  |  |  |  |  |  | 
| 371 |  |  |  |  |  |  | sub echo { | 
| 372 |  |  |  |  |  |  | my($self,$key,$var) = @_; | 
| 373 |  |  |  |  |  |  | $var = $key if @_ == 2; | 
| 374 |  |  |  |  |  |  | my $encoding; | 
| 375 |  |  |  |  |  |  | if ($key eq 'encoding') { | 
| 376 |  |  |  |  |  |  | $encoding = $var;		 # NOTE: TODO: handle encoding. | 
| 377 |  |  |  |  |  |  | ($key,$var) = @_[3,4]; | 
| 378 |  |  |  |  |  |  | $var = $key if (!$var); | 
| 379 |  |  |  |  |  |  | } | 
| 380 |  |  |  |  |  |  |  | 
| 381 |  |  |  |  |  |  | if($var eq 'DATE_LOCAL') { | 
| 382 |  |  |  |  |  |  | return $loc; | 
| 383 |  |  |  |  |  |  | } elsif($var eq 'DATE_GMT') { | 
| 384 |  |  |  |  |  |  | return $gmt; | 
| 385 |  |  |  |  |  |  | } elsif($var eq 'LAST_MODIFIED') { | 
| 386 |  |  |  |  |  |  | return $lmod; | 
| 387 |  |  |  |  |  |  | } | 
| 388 |  |  |  |  |  |  | # it seems apache's "echo" command escapes out instances of "\$" to display just "$" | 
| 389 |  |  |  |  |  |  | return &escaped($self->{'_variables'}->{$var}) if exists $self->{'_variables'}->{$var}; | 
| 390 |  |  |  |  |  |  | return &escaped($ENV{$var}) if exists $ENV{$var}; | 
| 391 |  |  |  |  |  |  | return $self->{'_config'}->{'SSIUndefinedEcho'}; | 
| 392 |  |  |  |  |  |  | } | 
| 393 |  |  |  |  |  |  |  | 
| 394 |  |  |  |  |  |  | sub printenv { | 
| 395 |  |  |  |  |  |  | return join "\n",map {"$_=$ENV{$_}"} keys %ENV; | 
| 396 |  |  |  |  |  |  | } | 
| 397 |  |  |  |  |  |  |  | 
| 398 |  |  |  |  |  |  | sub include { | 
| 399 |  |  |  |  |  |  | $DEBUG and do { local $" = "','"; warn "DEBUG: include('@_')\n" }; | 
| 400 |  |  |  |  |  |  | my($self,$type,$filename) = @_; | 
| 401 |  |  |  |  |  |  | if(lc $type eq 'file') { | 
| 402 |  |  |  |  |  |  | return $self->_include_file($filename); | 
| 403 |  |  |  |  |  |  | } elsif(lc $type eq 'virtual') { | 
| 404 |  |  |  |  |  |  | return $self->_include_virtual($filename); | 
| 405 |  |  |  |  |  |  | } else { | 
| 406 |  |  |  |  |  |  | return $self->SSI_ERROR_FLUSH("arg to include is '$type'. It must be one of: 'file' or 'virtual'."); | 
| 407 |  |  |  |  |  |  | } | 
| 408 |  |  |  |  |  |  | } | 
| 409 |  |  |  |  |  |  |  | 
| 410 |  |  |  |  |  |  | sub _include_file { | 
| 411 |  |  |  |  |  |  | $DEBUG and do { local $" = "','"; warn "DEBUG: _include_file('@_')\n" }; | 
| 412 |  |  |  |  |  |  | my($self,$filename) = @_; | 
| 413 |  |  |  |  |  |  |  | 
| 414 |  |  |  |  |  |  | # get the filename to open | 
| 415 |  |  |  |  |  |  | $filename = catfile($FindBin::Bin,$filename) unless -e $filename; | 
| 416 |  |  |  |  |  |  |  | 
| 417 |  |  |  |  |  |  | # if we've reached MAX_RECURSIONS for this filename, warn and return the error | 
| 418 |  |  |  |  |  |  | if(++$self->{_recursions}->{$filename} >= $self->{_max_recursions}) { | 
| 419 |  |  |  |  |  |  | return $self->SSI_ERROR_FLUSH("the maximum number of 'include file' recursions has been exceeded for '$filename'."); | 
| 420 |  |  |  |  |  |  | } | 
| 421 |  |  |  |  |  |  |  | 
| 422 |  |  |  |  |  |  | # open the file, or warn and return an error | 
| 423 |  |  |  |  |  |  | my $fh = do { local *STDIN }; | 
| 424 |  |  |  |  |  |  | open($fh,$filename) or do { | 
| 425 |  |  |  |  |  |  | return $self->SSI_ERROR_FLUSH("failed to open file ($filename): $!"); | 
| 426 |  |  |  |  |  |  | }; | 
| 427 |  |  |  |  |  |  |  | 
| 428 |  |  |  |  |  |  | # process the included file and return the result | 
| 429 |  |  |  |  |  |  | return $self->process(join '',<$fh>); | 
| 430 |  |  |  |  |  |  | } | 
| 431 |  |  |  |  |  |  |  | 
| 432 |  |  |  |  |  |  | sub _include_virtual { | 
| 433 |  |  |  |  |  |  | $DEBUG and do { local $" = "','"; warn "DEBUG: _include_virtual('@_')\n" }; | 
| 434 |  |  |  |  |  |  | my($self,$filename) = @_; | 
| 435 |  |  |  |  |  |  |  | 
| 436 |  |  |  |  |  |  | # if this is a local file that we can just read, let's do that instead of getting it virtually | 
| 437 |  |  |  |  |  |  | if($filename =~ m|^/(.+)|) { # could be on the local server: absolute filename, relative to ., relative to $ENV{DOCUMENT_ROOT} | 
| 438 |  |  |  |  |  |  | my $file = $1; | 
| 439 |  |  |  |  |  |  | if(-e '/'.$file) { # back to the original | 
| 440 |  |  |  |  |  |  | $file = '/'.$file; | 
| 441 |  |  |  |  |  |  | } elsif(-e catfile($self->{'_variables'}->{'DOCUMENT_ROOT'},$file)) { | 
| 442 |  |  |  |  |  |  | $file = catfile($self->{'_variables'}->{'DOCUMENT_ROOT'},$file); | 
| 443 |  |  |  |  |  |  | } elsif(-e catfile($FindBin::Bin,$file)) { | 
| 444 |  |  |  |  |  |  | # $file = atfile($FindBin::Bin,$file);		 # <----- NOTE: is this a typo here?? | 
| 445 |  |  |  |  |  |  | $file = catfile($FindBin::Bin,$file);		 # fixing it just in case | 
| 446 |  |  |  |  |  |  | } | 
| 447 |  |  |  |  |  |  | return $self->_include_file($file) if -e $file; | 
| 448 |  |  |  |  |  |  | } | 
| 449 |  |  |  |  |  |  |  | 
| 450 |  |  |  |  |  |  | # create the URI to get(), or warn and return the error | 
| 451 |  |  |  |  |  |  | my $uri = eval { | 
| 452 |  |  |  |  |  |  | my $uri = URI->new($filename); | 
| 453 |  |  |  |  |  |  | $uri->scheme($uri->scheme || ($ENV{HTTPS} ? 'https' : 'http')); # ?? | 
| 454 |  |  |  |  |  |  | $uri->host($uri->host || $ENV{'HTTP_HOST'} || $ENV{'SERVER_NAME'} || 'localhost'); | 
| 455 |  |  |  |  |  |  | $uri; | 
| 456 |  |  |  |  |  |  | } or do { | 
| 457 |  |  |  |  |  |  | return $self->SSI_ERROR_FLUSH("failed to create a URI based on '$filename'."); | 
| 458 |  |  |  |  |  |  | }; | 
| 459 |  |  |  |  |  |  | if($@) { | 
| 460 |  |  |  |  |  |  | return $self->SSI_ERROR_FLUSH("failed to create a URI based on '$filename'."); | 
| 461 |  |  |  |  |  |  | } | 
| 462 |  |  |  |  |  |  |  | 
| 463 |  |  |  |  |  |  | # get the content of the request | 
| 464 |  |  |  |  |  |  | $self->{_ua} ||= $self->_get_ua(); | 
| 465 |  |  |  |  |  |  | my $url = $uri->canonical; | 
| 466 |  |  |  |  |  |  |  | 
| 467 |  |  |  |  |  |  | # have we reached MAX_RECURSIONS? | 
| 468 |  |  |  |  |  |  | if(++$self->{_recursions}->{$url} >= $self->{_max_recursions}) { | 
| 469 |  |  |  |  |  |  | return $self->SSI_ERROR_FLUSH("the maximum number of 'include virtual' recursions has been exceeded for '$url'."); | 
| 470 |  |  |  |  |  |  | } | 
| 471 |  |  |  |  |  |  |  | 
| 472 |  |  |  |  |  |  | my $response = $self->{_ua}->get($url); | 
| 473 |  |  |  |  |  |  |  | 
| 474 |  |  |  |  |  |  | # is it a success? | 
| 475 |  |  |  |  |  |  | unless($response->is_success) { | 
| 476 |  |  |  |  |  |  | return $self->SSI_ERROR_FLUSH("failed to get('$url'): ".$response->status_line."."); | 
| 477 |  |  |  |  |  |  | } | 
| 478 |  |  |  |  |  |  | # process the included content and return the result | 
| 479 |  |  |  |  |  |  | return $self->process($response->content); | 
| 480 |  |  |  |  |  |  | } | 
| 481 |  |  |  |  |  |  |  | 
| 482 |  |  |  |  |  |  | sub _get_ua { | 
| 483 |  |  |  |  |  |  | my $self = shift; | 
| 484 |  |  |  |  |  |  | my %conf = (); | 
| 485 |  |  |  |  |  |  | $conf{agent} = $ENV{HTTP_USER_AGENT} if $ENV{HTTP_USER_AGENT}; | 
| 486 |  |  |  |  |  |  | my $ua = LWP::UserAgent->new(%conf); | 
| 487 |  |  |  |  |  |  | $ua->cookie_jar($self->{_cookie_jar}); | 
| 488 |  |  |  |  |  |  | return $ua; | 
| 489 |  |  |  |  |  |  | } | 
| 490 |  |  |  |  |  |  |  | 
| 491 |  |  |  |  |  |  | sub cookie_jar { | 
| 492 |  |  |  |  |  |  | my $self = shift; | 
| 493 |  |  |  |  |  |  | if(my $jar = shift) { | 
| 494 |  |  |  |  |  |  | $self->{_cookie_jar} = $jar; | 
| 495 |  |  |  |  |  |  | } | 
| 496 |  |  |  |  |  |  | return $self->{_cookie_jar}; | 
| 497 |  |  |  |  |  |  | } | 
| 498 |  |  |  |  |  |  |  | 
| 499 |  |  |  |  |  |  | sub exec { | 
| 500 |  |  |  |  |  |  | my($self,$type,$filename) = @_; | 
| 501 |  |  |  |  |  |  | if(lc $type eq 'cmd') { | 
| 502 |  |  |  |  |  |  | return $self->_exec_cmd($filename); | 
| 503 |  |  |  |  |  |  | } elsif(lc $type eq 'cgi') { | 
| 504 |  |  |  |  |  |  | return $self->_exec_cgi($filename); | 
| 505 |  |  |  |  |  |  | } else { | 
| 506 |  |  |  |  |  |  | return $self->SSI_ERROR_FLUSH("arg to exec() is '$type'. It must be one of: 'cmd' or 'cgi'."); | 
| 507 |  |  |  |  |  |  | } | 
| 508 |  |  |  |  |  |  | } | 
| 509 |  |  |  |  |  |  |  | 
| 510 |  |  |  |  |  |  | sub _exec_cmd { | 
| 511 |  |  |  |  |  |  | my($self,$filename) = @_; | 
| 512 |  |  |  |  |  |  |  | 
| 513 |  |  |  |  |  |  | # have we reached MAX_RECURSIONS? | 
| 514 |  |  |  |  |  |  | if(++$self->{_recursions}->{$filename} >= $self->{_max_recursions}) { | 
| 515 |  |  |  |  |  |  | return $self->SSI_ERROR_FLUSH("the maximum number of 'exec cmd' recursions has been exceeded for '$filename'."); | 
| 516 |  |  |  |  |  |  | } | 
| 517 |  |  |  |  |  |  |  | 
| 518 |  |  |  |  |  |  | my $output = `$filename`; # security here is mighty bad. | 
| 519 |  |  |  |  |  |  |  | 
| 520 |  |  |  |  |  |  | # was the command a success? | 
| 521 |  |  |  |  |  |  | if($?) { | 
| 522 |  |  |  |  |  |  | return $self->SSI_ERROR_FLUSH("`$filename` was not successful."); | 
| 523 |  |  |  |  |  |  | } | 
| 524 |  |  |  |  |  |  |  | 
| 525 |  |  |  |  |  |  | # process the output, and return the result | 
| 526 |  |  |  |  |  |  | return $self->process($output); | 
| 527 |  |  |  |  |  |  | } | 
| 528 |  |  |  |  |  |  |  | 
| 529 |  |  |  |  |  |  | sub _exec_cgi { # no relative $filename allowed. | 
| 530 |  |  |  |  |  |  | my($self,$filename) = @_; | 
| 531 |  |  |  |  |  |  |  | 
| 532 |  |  |  |  |  |  | # have we reached MAX_RECURSIONS? | 
| 533 |  |  |  |  |  |  | if(++$self->{_recursions}->{$filename} >= $self->{_max_recursions}) { | 
| 534 |  |  |  |  |  |  | return $self->SSI_ERROR_FLUSH("the maximum number of 'exec cgi' recursions has been exceeded for '$filename'."); | 
| 535 |  |  |  |  |  |  | } | 
| 536 |  |  |  |  |  |  |  | 
| 537 |  |  |  |  |  |  | # create the URI from the filename | 
| 538 |  |  |  |  |  |  | my $uri = eval { | 
| 539 |  |  |  |  |  |  | my $uri = URI->new($filename); | 
| 540 |  |  |  |  |  |  | $uri->scheme($uri->scheme || ($ENV{HTTPS} ? 'https' : 'http')); # ?? | 
| 541 |  |  |  |  |  |  | $uri->host($uri->host || $ENV{'HTTP_HOST'} || $ENV{'SERVER_NAME'}); | 
| 542 |  |  |  |  |  |  | $uri->query($uri->query || $ENV{'QUERY_STRING'}); | 
| 543 |  |  |  |  |  |  | $uri; | 
| 544 |  |  |  |  |  |  | } or do { | 
| 545 |  |  |  |  |  |  | return $self->SSI_ERROR_FLUSH("failed to create a URI from '$filename'."); | 
| 546 |  |  |  |  |  |  | }; | 
| 547 |  |  |  |  |  |  | if($@) { | 
| 548 |  |  |  |  |  |  | return $self->SSI_ERROR_FLUSH("failed to create a URI from '$filename'."); | 
| 549 |  |  |  |  |  |  | } | 
| 550 |  |  |  |  |  |  |  | 
| 551 |  |  |  |  |  |  | # get the content | 
| 552 |  |  |  |  |  |  | $self->{_ua} ||= $self->_get_ua(); | 
| 553 |  |  |  |  |  |  | my $url = $uri->canonical; | 
| 554 |  |  |  |  |  |  | my $response = $self->{_ua}->get($url); | 
| 555 |  |  |  |  |  |  |  | 
| 556 |  |  |  |  |  |  | # success? | 
| 557 |  |  |  |  |  |  | unless($response->is_success) { | 
| 558 |  |  |  |  |  |  | return $self->SSI_ERROR_FLUSH("failed to get('$filename')."); | 
| 559 |  |  |  |  |  |  | } | 
| 560 |  |  |  |  |  |  |  | 
| 561 |  |  |  |  |  |  | # process the content and return the result | 
| 562 |  |  |  |  |  |  | return $self->process($response->content); | 
| 563 |  |  |  |  |  |  | } | 
| 564 |  |  |  |  |  |  |  | 
| 565 |  |  |  |  |  |  | sub flastmod { | 
| 566 |  |  |  |  |  |  | my($self,$type,$filename) = @_; | 
| 567 |  |  |  |  |  |  |  | 
| 568 |  |  |  |  |  |  | if(lc $type eq 'file') { | 
| 569 |  |  |  |  |  |  | $filename = catfile($FindBin::Bin,$filename) unless -e $filename; | 
| 570 |  |  |  |  |  |  | } elsif(lc $type eq 'virtual') { | 
| 571 |  |  |  |  |  |  | $filename = catfile($self->{'_variables'}->{'DOCUMENT_ROOT'},$filename) | 
| 572 |  |  |  |  |  |  | unless $filename =~ /$self->{'_variables'}->{'DOCUMENT_ROOT'}/; | 
| 573 |  |  |  |  |  |  | } else { | 
| 574 |  |  |  |  |  |  | return $self->SSI_ERROR_FLUSH("the first argument to flastmod is '$type'. It must be one of: 'file' or 'virtual'."); | 
| 575 |  |  |  |  |  |  | } | 
| 576 |  |  |  |  |  |  |  | 
| 577 |  |  |  |  |  |  | unless(-e $filename) { | 
| 578 |  |  |  |  |  |  | return $self->SSI_ERROR_FLUSH("flastmod failed to find '$filename'."); | 
| 579 |  |  |  |  |  |  | } | 
| 580 |  |  |  |  |  |  |  | 
| 581 |  |  |  |  |  |  | my $flastmod = (stat $filename)[9]; | 
| 582 |  |  |  |  |  |  |  | 
| 583 |  |  |  |  |  |  | if($self->{'_config'}->{'timefmt'}) { | 
| 584 |  |  |  |  |  |  | my @localtime = localtime($flastmod); # need this?? | 
| 585 |  |  |  |  |  |  | return Date::Format::strftime($self->{'_config'}->{'timefmt'},@localtime); | 
| 586 |  |  |  |  |  |  | } else { | 
| 587 |  |  |  |  |  |  | return scalar localtime($flastmod); | 
| 588 |  |  |  |  |  |  | } | 
| 589 |  |  |  |  |  |  | } | 
| 590 |  |  |  |  |  |  |  | 
| 591 |  |  |  |  |  |  | sub fsize { | 
| 592 |  |  |  |  |  |  | my($self,$type,$filename) = @_; | 
| 593 |  |  |  |  |  |  |  | 
| 594 |  |  |  |  |  |  | if(lc $type eq 'file') { | 
| 595 |  |  |  |  |  |  | $filename = catfile($FindBin::Bin,$filename) unless -e $filename; | 
| 596 |  |  |  |  |  |  | } elsif(lc $type eq 'virtual') { | 
| 597 |  |  |  |  |  |  | $filename = catfile($ENV{'DOCUMENT_ROOT'},$filename) unless $filename =~ /$ENV{'DOCUMENT_ROOT'}/; | 
| 598 |  |  |  |  |  |  | } else { | 
| 599 |  |  |  |  |  |  | return $self->SSI_ERROR_FLUSH("the first argument to fsize is '$type'. It must be one of: 'file' or 'virtual'."); | 
| 600 |  |  |  |  |  |  | } | 
| 601 |  |  |  |  |  |  | unless(-e $filename) { | 
| 602 |  |  |  |  |  |  | return $self->SSI_ERROR_FLUSH("fsize failed to find '$filename'."); | 
| 603 |  |  |  |  |  |  | } | 
| 604 |  |  |  |  |  |  |  | 
| 605 |  |  |  |  |  |  | my $fsize = (stat $filename)[7]; | 
| 606 |  |  |  |  |  |  |  | 
| 607 |  |  |  |  |  |  | if(lc $self->{'_config'}->{'sizefmt'} eq 'bytes') { | 
| 608 |  |  |  |  |  |  | 1 while $fsize =~ s/^(\d+)(\d{3})/$1,$2/g; | 
| 609 |  |  |  |  |  |  | return $fsize; | 
| 610 |  |  |  |  |  |  | } else { # abbrev | 
| 611 |  |  |  |  |  |  | # gratefully lifted from Apache::SSI | 
| 612 |  |  |  |  |  |  | return "   0k" unless $fsize; | 
| 613 |  |  |  |  |  |  | return "   1k" if $fsize < 1024; | 
| 614 |  |  |  |  |  |  | return sprintf("%4dk", ($fsize + 512)/1024) if $fsize < 1048576; | 
| 615 |  |  |  |  |  |  | return sprintf("%4.1fM", $fsize/1048576.0) if $fsize < 103809024; | 
| 616 |  |  |  |  |  |  | return sprintf("%4dM", ($fsize + 524288)/1048576) if $fsize < 1048576; | 
| 617 |  |  |  |  |  |  | } | 
| 618 |  |  |  |  |  |  | } | 
| 619 |  |  |  |  |  |  |  | 
| 620 |  |  |  |  |  |  | # | 
| 621 |  |  |  |  |  |  | # if/elsif/else/endif and related methods | 
| 622 |  |  |  |  |  |  | # | 
| 623 |  |  |  |  |  |  | # NOTE: anything calling _test should check $@ | 
| 624 |  |  |  |  |  |  | sub _test { | 
| 625 |  |  |  |  |  |  | my($self,$test) = @_; | 
| 626 |  |  |  |  |  |  | my $quote; | 
| 627 |  |  |  |  |  |  | my ($pound, $pounds); | 
| 628 |  |  |  |  |  |  |  | 
| 629 |  |  |  |  |  |  | $test =~ s/^(['"`])\s*(.*?)\s*(\1)$/$2/; # trim off surrounding (matching) quotes, and whitespace | 
| 630 |  |  |  |  |  |  | $quote= $1; | 
| 631 |  |  |  |  |  |  |  | 
| 632 |  |  |  |  |  |  | # trivial test returns: | 
| 633 |  |  |  |  |  |  | return 0 if $test =~ /$RE_single_quote_false_NC/; | 
| 634 |  |  |  |  |  |  | return 1 if $test =~ /^["`]+$/;  # 1+ double quotes or backticks, trivially true | 
| 635 |  |  |  |  |  |  | return 1 if $test =~ /^[\s`'"]*?([`'"])?[\s]+?\1$/; # whitespace inside second set of quotes, trivially true | 
| 636 |  |  |  |  |  |  | return 1 if $test =~ /^[\w]+$/; # bareword (alphanum) trivially true | 
| 637 |  |  |  |  |  |  |  | 
| 638 |  |  |  |  |  |  | if (1) # ($test =~ m{^\(}) | 
| 639 |  |  |  |  |  |  | { # need to do this otherwise it creates infinite loop for some reason | 
| 640 |  |  |  |  |  |  | if ($test =~ m{ | 
| 641 |  |  |  |  |  |  | ((?:\!\s*)*) \s*	  # $1 | 
| 642 |  |  |  |  |  |  | (	  		  # $2 | 
| 643 |  |  |  |  |  |  | $RE_parens_2C	  # ($3, $4) has 2 capture groups | 
| 644 |  |  |  |  |  |  | | | 
| 645 |  |  |  |  |  |  | (?:$RE_all_no_paren_noop_NC)* | 
| 646 |  |  |  |  |  |  | ) \s* | 
| 647 |  |  |  |  |  |  | (?: | 
| 648 |  |  |  |  |  |  | (\&\& | \|\| )? \s*   # $5 | 
| 649 |  |  |  |  |  |  | (.*)	 	    # $6 | 
| 650 |  |  |  |  |  |  | )? \s* | 
| 651 |  |  |  |  |  |  | }x) | 
| 652 |  |  |  |  |  |  | { | 
| 653 |  |  |  |  |  |  | # $1 is pound,    $4 is inside the brackets, $5 is the op, $6 is the RHS | 
| 654 |  |  |  |  |  |  | my $LHS=$2; | 
| 655 |  |  |  |  |  |  | my $LHS_parens=$4;	# inside parentheses, does not include the parentheses | 
| 656 |  |  |  |  |  |  | my $OP=$5; | 
| 657 |  |  |  |  |  |  | my $RHS=$6; | 
| 658 |  |  |  |  |  |  | # expr="x == '\\x'" is split into:    LHS=[ x == ]    RHS=[ '\\x' ] | 
| 659 |  |  |  |  |  |  | $pounds=$pound=$1; | 
| 660 |  |  |  |  |  |  | $pound=~s/(?:\!\s*\!\s*)*//;	  # remove even # of !s, as these cancel out | 
| 661 |  |  |  |  |  |  |  | 
| 662 |  |  |  |  |  |  | # if no op, and LHS and RHS, FAIL... because (x) b..  -- can be no LHS but RHS and noop | 
| 663 |  |  |  |  |  |  | # if no op and no $RHS, return pound != test(LHS) | 
| 664 |  |  |  |  |  |  | # if op, and no RHS or no LHS, FAIL | 
| 665 |  |  |  |  |  |  | # if op, do op.. return [pound != test(LHS)] op [test(LHS)] | 
| 666 |  |  |  |  |  |  | if ($OP) | 
| 667 |  |  |  |  |  |  | { # LOGICAL COMPARISON && and || | 
| 668 |  |  |  |  |  |  | # NOTE:  && and || have equal precedence | 
| 669 |  |  |  |  |  |  |  | 
| 670 |  |  |  |  |  |  | if ($LHS=~/^\s*$/) | 
| 671 |  |  |  |  |  |  | { | 
| 672 |  |  |  |  |  |  | return $self->SSI_ERROR("empty logical comparison in expr."); | 
| 673 |  |  |  |  |  |  | } | 
| 674 |  |  |  |  |  |  | if ($RHS=~/^\s*$/) | 
| 675 |  |  |  |  |  |  | { | 
| 676 |  |  |  |  |  |  | return $self->SSI_ERROR("empty logical comparison in expr."); | 
| 677 |  |  |  |  |  |  | } | 
| 678 |  |  |  |  |  |  |  | 
| 679 |  |  |  |  |  |  | if ($LHS_parens) {$LHS = $LHS_parens;}  # needs to be done here, because of empty comparison checker | 
| 680 |  |  |  |  |  |  | $LHS = $self->_test($quote.$LHS.$quote); | 
| 681 |  |  |  |  |  |  |  | 
| 682 |  |  |  |  |  |  | if ($@) {return;} # there were errors in the test | 
| 683 |  |  |  |  |  |  |  | 
| 684 |  |  |  |  |  |  | if ($pound) {$LHS = !$LHS;} | 
| 685 |  |  |  |  |  |  | $RHS = $quote.$RHS.$quote; | 
| 686 |  |  |  |  |  |  |  | 
| 687 |  |  |  |  |  |  | if ($OP eq "&&") | 
| 688 |  |  |  |  |  |  | { return ($LHS && $self->_test($RHS)); } # short circuits, faster | 
| 689 |  |  |  |  |  |  | else # ($OP eq "||") | 
| 690 |  |  |  |  |  |  | { return ($LHS || $self->_test($RHS)); } # short circuits, faster | 
| 691 |  |  |  |  |  |  | } | 
| 692 |  |  |  |  |  |  | else | 
| 693 |  |  |  |  |  |  | { # NO OP | 
| 694 |  |  |  |  |  |  | if ($LHS && $RHS) | 
| 695 |  |  |  |  |  |  | { | 
| 696 |  |  |  |  |  |  | if ($LHS_parens) | 
| 697 |  |  |  |  |  |  | { | 
| 698 |  |  |  |  |  |  | #                    	return $self->SSI_ERROR("error in expression."); # NOTE: FIXME: improve this error msg.. | 
| 699 |  |  |  |  |  |  | #                    	return $self->SSI_ERROR("error in expression. LHS and RHS but no OP"); # NOTE: FIXME: improve this error msg.. | 
| 700 |  |  |  |  |  |  | return $self->SSI_ERROR("error in expression. LHS [$LHS] and RHS [$RHS] but no OP"); # NOTE: FIXME: improve this error msg.. | 
| 701 |  |  |  |  |  |  | } | 
| 702 |  |  |  |  |  |  | $test = $LHS.$RHS; | 
| 703 |  |  |  |  |  |  | } | 
| 704 |  |  |  |  |  |  | elsif ($LHS)  # brackets or balanced quotes | 
| 705 |  |  |  |  |  |  | { | 
| 706 |  |  |  |  |  |  | if ($LHS_parens) | 
| 707 |  |  |  |  |  |  | { | 
| 708 |  |  |  |  |  |  | $LHS = $self->_test($quote.$LHS_parens.$quote); | 
| 709 |  |  |  |  |  |  | if ($pound) {$LHS = !$LHS;} | 
| 710 |  |  |  |  |  |  | return $LHS; | 
| 711 |  |  |  |  |  |  | } | 
| 712 |  |  |  |  |  |  | $test = $LHS;  # NOTE: is this redundant? | 
| 713 |  |  |  |  |  |  | } | 
| 714 |  |  |  |  |  |  | elsif ($RHS)  # unbalanced quotes | 
| 715 |  |  |  |  |  |  | { $test = $RHS; }  # NOTE: is this redundant? | 
| 716 |  |  |  |  |  |  | } | 
| 717 |  |  |  |  |  |  | } | 
| 718 |  |  |  |  |  |  | else | 
| 719 |  |  |  |  |  |  | { | 
| 720 |  |  |  |  |  |  | return $self->SSI_ERROR("unknown error in expression."); # SHOULD NOT REACH THIS | 
| 721 |  |  |  |  |  |  | } | 
| 722 |  |  |  |  |  |  | } | 
| 723 |  |  |  |  |  |  |  | 
| 724 |  |  |  |  |  |  |  | 
| 725 |  |  |  |  |  |  | #-------------------------- | 
| 726 |  |  |  |  |  |  | # BAREWORD (no comparison sign) | 
| 727 |  |  |  |  |  |  | if ($test =~ /^(?:$RE_all_quote_NC|(?:[^=<>\/]|[\\]\/)*)$/)	# BAREWORD | 
| 728 |  |  |  |  |  |  | { | 
| 729 |  |  |  |  |  |  | if ($test =~ /^(['])(.*?)(?:\1)$/)  {$test=$2;} # need to trim surrounding single quotes | 
| 730 |  |  |  |  |  |  | if ($test =~ /^$/)   {return ($pound);} # no need to parse | 
| 731 |  |  |  |  |  |  | if ($test =~ /^["]/) {return (! $pound);} # no need to parse | 
| 732 |  |  |  |  |  |  |  | 
| 733 |  |  |  |  |  |  | my $interp_test = $self->_interp_vars($test); | 
| 734 |  |  |  |  |  |  | my $RET = ($interp_test =~ /[^']+/); | 
| 735 |  |  |  |  |  |  | if ($interp_test ne $test) | 
| 736 |  |  |  |  |  |  | {	# var interpolation occurred, NOTE: apache deems only '' or empty to be false in this case. | 
| 737 |  |  |  |  |  |  | $test = ($interp_test !~ /^$/) ; | 
| 738 |  |  |  |  |  |  | return (($pound) xor ($test)); | 
| 739 |  |  |  |  |  |  | } | 
| 740 |  |  |  |  |  |  | return (($pound) xor ($RET));	# non empty string is true, | 
| 741 |  |  |  |  |  |  | } | 
| 742 |  |  |  |  |  |  |  | 
| 743 |  |  |  |  |  |  |  | 
| 744 |  |  |  |  |  |  | #-------------------------- | 
| 745 |  |  |  |  |  |  | # STRING COMPARISON  >,<,==,!=,=~ | 
| 746 |  |  |  |  |  |  | if ($test  =~ m{  \s*((?:$RE_all_quote_NC|[^<>=])*?)\s*([<>=!]=?)\s*([^<>=]*)\s*   }x) | 
| 747 |  |  |  |  |  |  | { | 
| 748 |  |  |  |  |  |  | if ($pounds) | 
| 749 |  |  |  |  |  |  | { return $self->SSI_ERROR("invalid expression $quote$test$quote in file"); } # NOTE: FIXME | 
| 750 |  |  |  |  |  |  |  | 
| 751 |  |  |  |  |  |  | my ($s1,$cmp,$s2)=($1, $2, $3); | 
| 752 |  |  |  |  |  |  | if ($s1=~/^\s*$/) | 
| 753 |  |  |  |  |  |  | { return $self->SSI_ERROR("problem in REGEX. blank comparison \$s1"); } 	# NOTE: FIXME | 
| 754 |  |  |  |  |  |  | if ($s2=~/^\s*$/) | 
| 755 |  |  |  |  |  |  | { return $self->SSI_ERROR("problem in REGEX. blank comparison \$s2"); } 	# NOTE: FIXME | 
| 756 |  |  |  |  |  |  |  | 
| 757 |  |  |  |  |  |  | if ($s2 =~ m{^  \s* (?: (?:/\s*[^/]*) | // ) \s* $}x)	# NOTE: what about escaped or stringed | 
| 758 |  |  |  |  |  |  | { | 
| 759 |  |  |  |  |  |  | if ($cmp =~ m/^==?$/)	{return 1;} | 
| 760 |  |  |  |  |  |  | elsif ($cmp =~ m/^!=$/)	{return;} | 
| 761 |  |  |  |  |  |  | else  { return $self->SSI_ERROR("Invalid expression $quote$test$quote in string comparison."); } | 
| 762 |  |  |  |  |  |  | } | 
| 763 |  |  |  |  |  |  |  | 
| 764 |  |  |  |  |  |  | $s1=$self->_interp_vars($s1); | 
| 765 |  |  |  |  |  |  | if ($s1 =~ /^(['"`])(.*?)(?:\1)$/)  {$s1=$2;} # trim off surrounding (matching) quotes | 
| 766 |  |  |  |  |  |  |  | 
| 767 |  |  |  |  |  |  | # REGEX | 
| 768 |  |  |  |  |  |  | if  ($s2 =~ m{^\s* / ((?:(?:(?:\\\\)*\\/) | [^/] )*) / (.*?)\s*$}x) # wrapped by /xx/ | 
| 769 |  |  |  |  |  |  | { | 
| 770 |  |  |  |  |  |  | if ($2) | 
| 771 |  |  |  |  |  |  | { return $self->SSI_ERROR("problem in REGEX. s2=[$s2] extra stuff=[$2]"); }		# NOTE: FIXME | 
| 772 |  |  |  |  |  |  | $s2=qr/$1/; # regex s2 | 
| 773 |  |  |  |  |  |  | $s2 = $self->_interp_vars($s2); | 
| 774 |  |  |  |  |  |  | if ($cmp =~ m/^==?$/) | 
| 775 |  |  |  |  |  |  | { return  ($s1 =~ m/$s2/);} | 
| 776 |  |  |  |  |  |  | elsif ($cmp eq "!=") | 
| 777 |  |  |  |  |  |  | { return ($s1 !~ $s2); }	# NOTE: FIXME!!! | 
| 778 |  |  |  |  |  |  | } | 
| 779 |  |  |  |  |  |  | else | 
| 780 |  |  |  |  |  |  | { | 
| 781 |  |  |  |  |  |  | if ($s2=~m|^[^\s/]+\s+/|) # unquoted, unescaped slash | 
| 782 |  |  |  |  |  |  | { return $self->SSI_ERROR("problem in REGEX unquoted slash. s2=[$s2]"); }		# NOTE: FIXME | 
| 783 |  |  |  |  |  |  |  | 
| 784 |  |  |  |  |  |  | $s2 = $self->_interp_vars($s2); | 
| 785 |  |  |  |  |  |  | if ($s2 =~ /^(['"])(.*?)(\1)$/)  {$s2 = $2;} # trim off surrounding (matching) quotes | 
| 786 |  |  |  |  |  |  | } | 
| 787 |  |  |  |  |  |  |  | 
| 788 |  |  |  |  |  |  | my $ret; | 
| 789 |  |  |  |  |  |  | $ret = $s1 cmp $s2; | 
| 790 |  |  |  |  |  |  |  | 
| 791 |  |  |  |  |  |  | if ($cmp =~ m/^==?$/)	{$ret = ($ret eq 0);} | 
| 792 |  |  |  |  |  |  | elsif ($cmp =~ m/^!=$/)	{$ret = ($ret ne 0);} | 
| 793 |  |  |  |  |  |  |  | 
| 794 |  |  |  |  |  |  | elsif ($cmp =~ m/^<$/)	{$ret = ($ret lt 0);} | 
| 795 |  |  |  |  |  |  | elsif ($cmp =~ m/^<=$/)	{$ret = ($ret le 0);} | 
| 796 |  |  |  |  |  |  |  | 
| 797 |  |  |  |  |  |  | elsif ($cmp =~ m/^>$/)	{$ret = ($ret gt 0);} | 
| 798 |  |  |  |  |  |  | elsif ($cmp =~ m/^>=$/)	{$ret = ($ret ge 0);} | 
| 799 |  |  |  |  |  |  |  | 
| 800 |  |  |  |  |  |  | else { return $self->SSI_ERROR("unknown comparison"); } # UNKNOWN COMPARISON -- should never reach this | 
| 801 |  |  |  |  |  |  |  | 
| 802 |  |  |  |  |  |  | return $ret; | 
| 803 |  |  |  |  |  |  | } | 
| 804 |  |  |  |  |  |  | else | 
| 805 |  |  |  |  |  |  | { | 
| 806 |  |  |  |  |  |  | if ($test =~ m{[^/]+\s+/})	# NOTE: UNFINISHED!! FIXME non empty unrecognized string that didnt fail | 
| 807 |  |  |  |  |  |  | { return $self->SSI_ERROR("error in expression, regex found in string"); } | 
| 808 |  |  |  |  |  |  | return 1; | 
| 809 |  |  |  |  |  |  | } | 
| 810 |  |  |  |  |  |  |  | 
| 811 |  |  |  |  |  |  | return; # return false.. it seems none of the ops applied.. | 
| 812 |  |  |  |  |  |  | } | 
| 813 |  |  |  |  |  |  |  | 
| 814 |  |  |  |  |  |  | sub _entering_if { | 
| 815 |  |  |  |  |  |  | my $self = shift; | 
| 816 |  |  |  |  |  |  | $self->{'_in_if'}++; | 
| 817 |  |  |  |  |  |  | $self->{'_suspend'}->[$self->{'_in_if'}] = $self->{'_suspend'}->[$self->{'_in_if'} - 1]; | 
| 818 |  |  |  |  |  |  | $self->{'_seen_true'}->[$self->{'_in_if'}] = 0; | 
| 819 |  |  |  |  |  |  | } | 
| 820 |  |  |  |  |  |  |  | 
| 821 |  |  |  |  |  |  | sub _seen_true { | 
| 822 |  |  |  |  |  |  | my $self = shift; | 
| 823 |  |  |  |  |  |  | return $self->{'_seen_true'}->[$self->{'_in_if'}]; | 
| 824 |  |  |  |  |  |  | } | 
| 825 |  |  |  |  |  |  |  | 
| 826 |  |  |  |  |  |  | sub _suspended { | 
| 827 |  |  |  |  |  |  | my $self = shift; | 
| 828 |  |  |  |  |  |  | return $self->{'_suspend'}->[$self->{'_in_if'}]; | 
| 829 |  |  |  |  |  |  | } | 
| 830 |  |  |  |  |  |  |  | 
| 831 |  |  |  |  |  |  | sub _leaving_if { | 
| 832 |  |  |  |  |  |  | my $self = shift; | 
| 833 |  |  |  |  |  |  | $self->{'_in_if'}-- if $self->{'_in_if'}; | 
| 834 |  |  |  |  |  |  | } | 
| 835 |  |  |  |  |  |  |  | 
| 836 |  |  |  |  |  |  | sub _true { | 
| 837 |  |  |  |  |  |  | my $self = shift; | 
| 838 |  |  |  |  |  |  | return $self->{'_seen_true'}->[$self->{'_in_if'}]++; | 
| 839 |  |  |  |  |  |  | } | 
| 840 |  |  |  |  |  |  |  | 
| 841 |  |  |  |  |  |  | sub _suspend { | 
| 842 |  |  |  |  |  |  | my $self = shift; | 
| 843 |  |  |  |  |  |  | $self->{'_suspend'}->[$self->{'_in_if'}]++; | 
| 844 |  |  |  |  |  |  | } | 
| 845 |  |  |  |  |  |  |  | 
| 846 |  |  |  |  |  |  | sub _resume { | 
| 847 |  |  |  |  |  |  | my $self = shift; | 
| 848 |  |  |  |  |  |  | $self->{'_suspend'}->[$self->{'_in_if'}]-- | 
| 849 |  |  |  |  |  |  | if $self->{'_suspend'}->[$self->{'_in_if'}]; | 
| 850 |  |  |  |  |  |  | } | 
| 851 |  |  |  |  |  |  |  | 
| 852 |  |  |  |  |  |  | sub _in_if { | 
| 853 |  |  |  |  |  |  | my $self = shift; | 
| 854 |  |  |  |  |  |  | return $self->{'_in_if'}; | 
| 855 |  |  |  |  |  |  | } | 
| 856 |  |  |  |  |  |  |  | 
| 857 |  |  |  |  |  |  | sub if { | 
| 858 |  |  |  |  |  |  | my($self,$expr,$test) = @_; | 
| 859 |  |  |  |  |  |  | $expr = $test if @_ == 3; | 
| 860 |  |  |  |  |  |  | $self->_entering_if(); | 
| 861 |  |  |  |  |  |  |  | 
| 862 |  |  |  |  |  |  | my $res=$self->_test($expr); | 
| 863 |  |  |  |  |  |  |  | 
| 864 |  |  |  |  |  |  | if($@) { | 
| 865 |  |  |  |  |  |  | $self->_true(); | 
| 866 |  |  |  |  |  |  | return; | 
| 867 |  |  |  |  |  |  | } # any errors cause the expr to evaluate to true..?? | 
| 868 |  |  |  |  |  |  |  | 
| 869 |  |  |  |  |  |  | if($res) { | 
| 870 |  |  |  |  |  |  | $self->_true(); | 
| 871 |  |  |  |  |  |  | } else { | 
| 872 |  |  |  |  |  |  | $self->_suspend(); | 
| 873 |  |  |  |  |  |  | } | 
| 874 |  |  |  |  |  |  | return ''; | 
| 875 |  |  |  |  |  |  | } | 
| 876 |  |  |  |  |  |  |  | 
| 877 |  |  |  |  |  |  | sub elif { | 
| 878 |  |  |  |  |  |  | my($self,$expr,$test) = @_; | 
| 879 |  |  |  |  |  |  |  | 
| 880 |  |  |  |  |  |  | if (! $self->_in_if() ) | 
| 881 |  |  |  |  |  |  | { | 
| 882 |  |  |  |  |  |  | $self->SSI_WARN("Incorrect use of elif ssi directive: no preceeding 'if'."); # NOTE: just a "warn" | 
| 883 |  |  |  |  |  |  | $self->_suspend() unless $self->_suspended(); | 
| 884 |  |  |  |  |  |  | return; | 
| 885 |  |  |  |  |  |  | } | 
| 886 |  |  |  |  |  |  |  | 
| 887 |  |  |  |  |  |  | if ($self->_seen_true()) | 
| 888 |  |  |  |  |  |  | { | 
| 889 |  |  |  |  |  |  | $self->_suspend() unless $self->_suspended(); | 
| 890 |  |  |  |  |  |  | return; | 
| 891 |  |  |  |  |  |  | } | 
| 892 |  |  |  |  |  |  |  | 
| 893 |  |  |  |  |  |  | $expr = $test if @_ == 3; | 
| 894 |  |  |  |  |  |  |  | 
| 895 |  |  |  |  |  |  |  | 
| 896 |  |  |  |  |  |  | my $res= $self->_test($expr); | 
| 897 |  |  |  |  |  |  |  | 
| 898 |  |  |  |  |  |  | if($@) { | 
| 899 |  |  |  |  |  |  | $self->_suspend() unless $self->_suspended(); | 
| 900 |  |  |  |  |  |  | return; | 
| 901 |  |  |  |  |  |  | } | 
| 902 |  |  |  |  |  |  |  | 
| 903 |  |  |  |  |  |  | if($res) { | 
| 904 |  |  |  |  |  |  | $self->_true(); | 
| 905 |  |  |  |  |  |  | $self->_resume(); | 
| 906 |  |  |  |  |  |  | } else { | 
| 907 |  |  |  |  |  |  | $self->_suspend() unless $self->_suspended(); | 
| 908 |  |  |  |  |  |  | } | 
| 909 |  |  |  |  |  |  | return ''; | 
| 910 |  |  |  |  |  |  | } | 
| 911 |  |  |  |  |  |  |  | 
| 912 |  |  |  |  |  |  | sub else { | 
| 913 |  |  |  |  |  |  | my $self = shift; | 
| 914 |  |  |  |  |  |  |  | 
| 915 |  |  |  |  |  |  | if (! $self->_in_if() ) { | 
| 916 |  |  |  |  |  |  | $self->SSI_WARN("Incorrect use of else ssi directive: no preceeding 'if'."); # NOTE: just a "warn" | 
| 917 |  |  |  |  |  |  | $self->_suspend() unless $self->_suspended(); | 
| 918 |  |  |  |  |  |  | return; | 
| 919 |  |  |  |  |  |  | } | 
| 920 |  |  |  |  |  |  | if ($self->_seen_true()) { | 
| 921 |  |  |  |  |  |  | $self->_suspend() unless $self->_suspended(); } | 
| 922 |  |  |  |  |  |  | else { | 
| 923 |  |  |  |  |  |  | $self->_resume(); } | 
| 924 |  |  |  |  |  |  | return ''; | 
| 925 |  |  |  |  |  |  | } | 
| 926 |  |  |  |  |  |  |  | 
| 927 |  |  |  |  |  |  | sub endif { | 
| 928 |  |  |  |  |  |  | my $self = shift; | 
| 929 |  |  |  |  |  |  | if (! $self->_in_if() ) | 
| 930 |  |  |  |  |  |  | { | 
| 931 |  |  |  |  |  |  | # $self->SSI_ERROR("Incorrect use of endif ssi directive: no preceeding 'if'."); | 
| 932 |  |  |  |  |  |  | $self->SSI_WARN("Incorrect use of endif ssi directive: no preceeding 'if'."); | 
| 933 |  |  |  |  |  |  | } | 
| 934 |  |  |  |  |  |  | else | 
| 935 |  |  |  |  |  |  | { $self->_leaving_if(); } | 
| 936 |  |  |  |  |  |  | $self->_resume() if $self->_suspended();	# might be suspended even if not in "if" | 
| 937 |  |  |  |  |  |  | return ''; | 
| 938 |  |  |  |  |  |  | } | 
| 939 |  |  |  |  |  |  |  | 
| 940 |  |  |  |  |  |  | # | 
| 941 |  |  |  |  |  |  | # if we're called like this, it means that we're to handle a CGI request ourselves. | 
| 942 |  |  |  |  |  |  | # that means that we're to open the file and process the content, sending it to STDOUT | 
| 943 |  |  |  |  |  |  | # along with a standard HTTP content header | 
| 944 |  |  |  |  |  |  | # | 
| 945 |  |  |  |  |  |  | unless(caller) { | 
| 946 |  |  |  |  |  |  | goto &handler; | 
| 947 |  |  |  |  |  |  | } | 
| 948 |  |  |  |  |  |  |  | 
| 949 |  |  |  |  |  |  | sub handler { | 
| 950 |  |  |  |  |  |  | eval "use CGI qw(:standard);"; | 
| 951 |  |  |  |  |  |  | print header(); | 
| 952 |  |  |  |  |  |  |  | 
| 953 |  |  |  |  |  |  | unless(UNIVERSAL::isa(tied(*STDOUT),'CGI::apacheSSI')) { | 
| 954 |  |  |  |  |  |  | tie *STDOUT, 'CGI::apacheSSI', filehandle => 'main::STDOUT'; | 
| 955 |  |  |  |  |  |  | } | 
| 956 |  |  |  |  |  |  |  | 
| 957 |  |  |  |  |  |  | my $filename = "$ENV{DOCUMENT_ROOT}$ENV{REQUEST_URI}"; | 
| 958 |  |  |  |  |  |  | if(-f $filename) { | 
| 959 |  |  |  |  |  |  | open my $fh, '<', $filename or die "Failed to open file ($filename): $!"; | 
| 960 |  |  |  |  |  |  | print <$fh>; | 
| 961 |  |  |  |  |  |  | } else { | 
| 962 |  |  |  |  |  |  | print "Failed to find file ($filename)."; | 
| 963 |  |  |  |  |  |  | } | 
| 964 |  |  |  |  |  |  |  | 
| 965 |  |  |  |  |  |  | exit; | 
| 966 |  |  |  |  |  |  | } | 
| 967 |  |  |  |  |  |  |  | 
| 968 |  |  |  |  |  |  | # | 
| 969 |  |  |  |  |  |  | # packages for tie() | 
| 970 |  |  |  |  |  |  | # | 
| 971 |  |  |  |  |  |  |  | 
| 972 |  |  |  |  |  |  | package CGI::apacheSSI::Gmt; | 
| 973 |  |  |  |  |  |  |  | 
| 974 |  |  |  |  |  |  | sub TIESCALAR { bless [@_], shift() } | 
| 975 |  |  |  |  |  |  | sub FETCH { | 
| 976 |  |  |  |  |  |  | my $self = shift; | 
| 977 |  |  |  |  |  |  | if($self->[-1]->{'_config'}->{'timefmt'}) { | 
| 978 |  |  |  |  |  |  | my @gt = gmtime; | 
| 979 |  |  |  |  |  |  | return Date::Format::strftime($self->[-1]->{'_config'}->{'timefmt'},@gt); | 
| 980 |  |  |  |  |  |  | } else { | 
| 981 |  |  |  |  |  |  | return scalar gmtime; | 
| 982 |  |  |  |  |  |  | } | 
| 983 |  |  |  |  |  |  | } | 
| 984 |  |  |  |  |  |  |  | 
| 985 |  |  |  |  |  |  | package CGI::apacheSSI::Local; | 
| 986 |  |  |  |  |  |  |  | 
| 987 |  |  |  |  |  |  | sub TIESCALAR { bless [@_], shift() } | 
| 988 |  |  |  |  |  |  | sub FETCH { | 
| 989 |  |  |  |  |  |  | my $self = shift; | 
| 990 |  |  |  |  |  |  | if($self->[-1]->{'_config'}->{'timefmt'}) { | 
| 991 |  |  |  |  |  |  | my @lt = localtime; | 
| 992 |  |  |  |  |  |  | return Date::Format::strftime($self->[-1]->{'_config'}->{'timefmt'},@lt); | 
| 993 |  |  |  |  |  |  | } else { | 
| 994 |  |  |  |  |  |  | return scalar localtime; | 
| 995 |  |  |  |  |  |  | } | 
| 996 |  |  |  |  |  |  | } | 
| 997 |  |  |  |  |  |  |  | 
| 998 |  |  |  |  |  |  | package CGI::apacheSSI::LMOD; | 
| 999 |  |  |  |  |  |  |  | 
| 1000 |  |  |  |  |  |  | sub TIESCALAR { bless [@_], shift() } | 
| 1001 |  |  |  |  |  |  | sub FETCH { | 
| 1002 |  |  |  |  |  |  | my $self = shift; | 
| 1003 |  |  |  |  |  |  | return $self->[-1]->flastmod('file', $ENV{'SCRIPT_FILENAME'} || $ENV{'PATH_TRANSLATED'} || ''); | 
| 1004 |  |  |  |  |  |  | } | 
| 1005 |  |  |  |  |  |  |  | 
| 1006 |  |  |  |  |  |  | 1; | 
| 1007 |  |  |  |  |  |  | __END__ |