| line | stmt | bran | cond | sub | pod | time | code | 
| 1 |  |  |  |  |  |  | ##---------------------------------------------------------------------------- | 
| 2 |  |  |  |  |  |  | ## Apache2 Server Side Include Parser - ~/lib/Apache2/SSI/URI.pm | 
| 3 |  |  |  |  |  |  | ## Version v0.1.1 | 
| 4 |  |  |  |  |  |  | ## Copyright(c) 2021 DEGUEST Pte. Ltd. | 
| 5 |  |  |  |  |  |  | ## Author: Jacques Deguest <jack@deguest.jp> | 
| 6 |  |  |  |  |  |  | ## Created 2020/12/18 | 
| 7 |  |  |  |  |  |  | ## Modified 2021/02/01 | 
| 8 |  |  |  |  |  |  | ## All rights reserved | 
| 9 |  |  |  |  |  |  | ## | 
| 10 |  |  |  |  |  |  | ## This program is free software; you can redistribute  it  and/or  modify  it | 
| 11 |  |  |  |  |  |  | ## under the same terms as Perl itself. | 
| 12 |  |  |  |  |  |  | ##---------------------------------------------------------------------------- | 
| 13 |  |  |  |  |  |  | package Apache2::SSI::URI; | 
| 14 |  |  |  |  |  |  | BEGIN | 
| 15 |  |  |  |  |  |  | { | 
| 16 | 15 |  |  | 15 |  | 132033 | use strict; | 
|  | 15 |  |  |  |  | 45 |  | 
|  | 15 |  |  |  |  | 661 |  | 
| 17 | 15 |  |  | 15 |  | 95 | use warnings::register; | 
|  | 15 |  |  |  |  | 26 |  | 
|  | 15 |  |  |  |  | 2463 |  | 
| 18 | 15 |  |  | 15 |  | 108 | use parent qw( Apache2::SSI::Common ); | 
|  | 15 |  |  |  |  | 30 |  | 
|  | 15 |  |  |  |  | 144 |  | 
| 19 | 15 |  |  | 15 |  | 1820 | use Apache2::SSI::Finfo; | 
|  | 15 |  |  |  |  | 34 |  | 
|  | 15 |  |  |  |  | 950 |  | 
| 20 | 15 |  |  | 15 |  | 91 | use Cwd; | 
|  | 15 |  |  |  |  | 35 |  | 
|  | 15 |  |  |  |  | 921 |  | 
| 21 |  |  |  |  |  |  | ## Used for debugging | 
| 22 |  |  |  |  |  |  | ## use Devel::Confess; | 
| 23 | 15 |  |  | 15 |  | 92 | use Nice::Try; | 
|  | 15 |  |  |  |  | 40 |  | 
|  | 15 |  |  |  |  | 135 |  | 
| 24 | 15 |  |  | 15 |  | 31642032 | use Scalar::Util (); | 
|  | 15 |  |  |  |  | 45 |  | 
|  | 15 |  |  |  |  | 631 |  | 
| 25 | 15 |  |  | 15 |  | 1924 | require constant; | 
| 26 | 15 |  |  | 15 |  | 132 | use URI; | 
|  | 15 |  |  |  |  | 41 |  | 
|  | 15 |  |  |  |  | 530 |  | 
| 27 | 15 |  |  | 15 |  | 89 | use constant URI_CLASS => 'URI'; | 
|  | 15 |  |  |  |  | 33 |  | 
|  | 15 |  |  |  |  | 1575 |  | 
| 28 | 15 |  |  | 15 |  | 109 | use URI::file; | 
|  | 15 |  |  |  |  | 40 |  | 
|  | 15 |  |  |  |  | 2786 |  | 
| 29 | 15 | 50 |  |  |  | 92 | if( $ENV{MOD_PERL} ) | 
| 30 |  |  |  |  |  |  | { | 
| 31 | 0 |  |  |  |  | 0 | require Apache2::RequestRec; | 
| 32 | 0 |  |  |  |  | 0 | require Apache2::RequestUtil; | 
| 33 | 0 |  |  |  |  | 0 | require Apache2::SubRequest; | 
| 34 | 0 |  |  |  |  | 0 | require Apache2::Access; | 
| 35 | 0 |  |  |  |  | 0 | require Apache2::Const; | 
| 36 | 0 |  |  |  |  | 0 | Apache2::Const->import( compile => qw( :common :http OK DECLINED ) ); | 
| 37 |  |  |  |  |  |  | } | 
| 38 |  |  |  |  |  |  | ## use Devel::Confess; | 
| 39 | 15 |  |  |  |  | 31 | our( $DEBUG ); | 
| 40 |  |  |  |  |  |  | use overload ( | 
| 41 | 92 |  |  | 92 |  | 2177 | q{""}    => sub    { $_[0]->document_uri->as_string }, | 
| 42 |  |  |  |  |  |  | bool     => sub () { 1 }, | 
| 43 | 15 |  |  |  |  | 216 | fallback => 1, | 
| 44 | 15 |  |  | 15 |  | 106 | ); | 
|  | 15 |  |  |  |  | 30 |  | 
| 45 | 15 |  |  |  |  | 41339 | our $VERSION = 'v0.1.1'; | 
| 46 |  |  |  |  |  |  | }; | 
| 47 |  |  |  |  |  |  |  | 
| 48 |  |  |  |  |  |  | ## document_root = /home/joe/www | 
| 49 |  |  |  |  |  |  | ## base_uri      = /my/uri/file.html/some/path/info?q=something&l=ja_JP | 
| 50 |  |  |  |  |  |  | ## base_uri is the current reference document | 
| 51 |  |  |  |  |  |  | ## document_uri  = ./about.html | 
| 52 |  |  |  |  |  |  | ## document_uri is the uri which is the purpose of this object. It will be made absolute and its dots flattened | 
| 53 |  |  |  |  |  |  | ## Example: ../about.html?q=hello would become /my/about.html?q=hello | 
| 54 |  |  |  |  |  |  | sub init | 
| 55 |  |  |  |  |  |  | { | 
| 56 | 129 |  |  | 129 | 1 | 187866 | my $self = shift( @_ ); | 
| 57 | 129 |  |  |  |  | 1645 | $self->{apache_request} = ''; | 
| 58 | 129 | 50 |  |  |  | 766 | $self->{base_uri}       = '/' unless( length( $self->{base_uri} ) ); | 
| 59 |  |  |  |  |  |  | ## By default | 
| 60 | 129 |  |  |  |  | 415 | $self->{code}           = 200; | 
| 61 | 129 |  |  |  |  | 426 | $self->{document_path}  = ''; | 
| 62 | 129 |  |  |  |  | 391 | $self->{document_root}  = ''; | 
| 63 |  |  |  |  |  |  | ## Reference document for the main request | 
| 64 | 129 |  |  |  |  | 328 | $self->{document_uri}   = ''; | 
| 65 | 129 |  |  |  |  | 346 | $self->{filepath}       = ''; | 
| 66 | 129 |  |  |  |  | 457 | $self->{finfo}          = ''; | 
| 67 | 129 |  |  |  |  | 618 | $self->{_init_params_order} = [qw( apache_request document_root base_uri document_uri document_path filepath )]; | 
| 68 | 129 |  |  |  |  | 356 | $self->{_init_strict_use_sub} = 1; | 
| 69 | 129 | 50 |  |  |  | 771 | $self->SUPER::init( @_ ) || return; | 
| 70 | 129 |  |  |  |  | 4978 | $self->{_env}            = {}; | 
| 71 | 129 |  |  |  |  | 302 | $self->{_path_info_processed} = 0; | 
| 72 | 129 |  |  |  |  | 312 | $self->{_uri_reset}      = 0; | 
| 73 | 129 |  | 33 |  |  | 442 | $self->{document_root} ||= $self->env( 'DOCUMENT_ROOT' ); | 
| 74 | 129 |  | 33 |  |  | 431 | $self->{base_uri}      ||= $self->env( 'DOCUMENT_URI' ); | 
| 75 |  |  |  |  |  |  | ## $self->message( 4, "Apache RequestRec object provided ? '$self->{apache_request}' for document uri '$self->{document_uri}'." ); | 
| 76 | 129 | 50 |  |  |  | 446 | return( $self->error( "No document root was provided." ) ) if( !length( $self->{document_root} ) ); | 
| 77 | 129 | 50 |  |  |  | 386 | return( $self->error( "No base uri was provided." ) ) if( !length( $self->{base_uri} ) ); | 
| 78 | 129 | 50 |  |  |  | 425 | return( $self->error( "No document uri was provided." ) ) if( !length( $self->{document_uri} ) ); | 
| 79 |  |  |  |  |  |  | ## Small correction if necessary. If the base uri is a directory, it needs to have a trailing "/", so URI knows this is a directory and not a file. | 
| 80 |  |  |  |  |  |  | ## URI->new( "./file.pl" )->abs( "/ssi/plop" ) becomes "/ssi/file.pl" whereas it should be /ssi/plop/file.pl | 
| 81 |  |  |  |  |  |  | ## $self->{base_uri} .= '/' if( length( $self->{base_uri} ) && -d( "$self->{document_root}$self->{base_uri}" ) && substr( $self->{base_uri}, -1, 1 ) ne '/' ); | 
| 82 | 129 |  |  |  |  | 1333 | return( $self ); | 
| 83 |  |  |  |  |  |  | } | 
| 84 |  |  |  |  |  |  |  | 
| 85 | 2186 |  |  | 2186 | 1 | 5852 | sub apache_request { return( shift->_set_get_object_without_init( 'apache_request', 'Apache2::RequestRec', @_ ) ); } | 
| 86 |  |  |  |  |  |  |  | 
| 87 |  |  |  |  |  |  | sub base_dir | 
| 88 |  |  |  |  |  |  | { | 
| 89 | 0 |  |  | 0 | 0 | 0 | my $self = shift( @_ ); | 
| 90 | 0 | 0 |  |  |  | 0 | return( $self->{base_dir} ) if( length( $self->{base_dir} ) ); | 
| 91 |  |  |  |  |  |  | ## Just in case | 
| 92 | 0 | 0 |  |  |  | 0 | return( $self->root ) if( !length( $self->{base_uri} ) ); | 
| 93 | 0 |  |  |  |  | 0 | my $base = $self->base_uri; | 
| 94 | 0 | 0 |  |  |  | 0 | return( $self->error( "No base uri defined." ) ) if( !length( $base ) ); | 
| 95 | 0 |  |  |  |  | 0 | my $path = $base->document_path; | 
| 96 | 0 |  |  |  |  | 0 | my @segments = split( '/', $path, -1 ); | 
| 97 | 0 |  |  |  |  | 0 | pop( @segments ); | 
| 98 | 0 | 0 |  |  |  | 0 | return( $base ) if( !scalar( @segments ) ); | 
| 99 | 0 |  |  |  |  | 0 | my $r = $self->apache_request; | 
| 100 | 0 |  |  |  |  | 0 | my $dir_path = join( '/', @segments ); | 
| 101 |  |  |  |  |  |  |  | 
| 102 | 0 |  |  |  |  | 0 | my $hash = {}; | 
| 103 | 0 | 0 |  |  |  | 0 | if( $r ) | 
| 104 |  |  |  |  |  |  | { | 
| 105 | 0 |  |  |  |  | 0 | my $rr = $self->lookup_uri( $dir_path ); | 
| 106 | 0 | 0 |  |  |  | 0 | if( !defined( $rr ) ) | 
|  |  | 0 |  |  |  |  |  | 
|  |  | 0 |  |  |  |  |  | 
| 107 |  |  |  |  |  |  | { | 
| 108 | 0 |  |  |  |  | 0 | $self->message( 3, "Error occured looking up uri '$path': ", $self->error ); | 
| 109 | 0 |  |  |  |  | 0 | return; | 
| 110 |  |  |  |  |  |  | } | 
| 111 |  |  |  |  |  |  | elsif( $rr->status != Apache2::Const::HTTP_OK ) | 
| 112 |  |  |  |  |  |  | { | 
| 113 | 0 |  |  |  |  | 0 | $self->message( 3, "There was an error looking up bas directory \"$dir_path\"." ); | 
| 114 | 0 |  |  |  |  | 0 | return( $self->error( "Could not look up base directory \"$dir_path\". Returned code is: ", $rr->status ) ); | 
| 115 |  |  |  |  |  |  | } | 
| 116 |  |  |  |  |  |  | elsif( $rr->finfo->filetype == APR::Const::FILETYPE_NOFILE ) | 
| 117 |  |  |  |  |  |  | { | 
| 118 | 0 |  |  |  |  | 0 | $self->message( 3, "Base directory \"$dir_path\" is not found." ); | 
| 119 | 0 |  |  |  |  | 0 | return( $self->error( "Could not find base directory \"$dir_path\"." ) ); | 
| 120 |  |  |  |  |  |  | } | 
| 121 |  |  |  |  |  |  | ## Remove trailing slash | 
| 122 | 0 |  |  |  |  | 0 | my $u = $self->_trim_trailing_slash( $rr->uri ); | 
| 123 |  |  |  |  |  |  |  | 
| 124 | 0 |  |  |  |  | 0 | $hash = | 
| 125 |  |  |  |  |  |  | { | 
| 126 |  |  |  |  |  |  | apache_request => $self->apache_request, | 
| 127 |  |  |  |  |  |  | base_dir => $self->root, | 
| 128 |  |  |  |  |  |  | base_uri => $self->root, | 
| 129 |  |  |  |  |  |  | document_path => "$u", | 
| 130 |  |  |  |  |  |  | document_root => $rr->document_root, | 
| 131 |  |  |  |  |  |  | document_uri => "$u", | 
| 132 |  |  |  |  |  |  | filename => $rr->filename, | 
| 133 |  |  |  |  |  |  | path_info => $rr->path_info, | 
| 134 |  |  |  |  |  |  | query_string => scalar( $rr->args ), | 
| 135 |  |  |  |  |  |  | _path_info_processed => 1, | 
| 136 |  |  |  |  |  |  | }; | 
| 137 |  |  |  |  |  |  | } | 
| 138 |  |  |  |  |  |  | else | 
| 139 |  |  |  |  |  |  | { | 
| 140 | 0 |  |  |  |  | 0 | $hash = | 
| 141 |  |  |  |  |  |  | { | 
| 142 |  |  |  |  |  |  | base_dir => $self->root, | 
| 143 |  |  |  |  |  |  | base_uri => $self->root, | 
| 144 |  |  |  |  |  |  | document_path => $dir_path, | 
| 145 |  |  |  |  |  |  | document_root => $self->document_root, | 
| 146 |  |  |  |  |  |  | document_uri => $dir_path, | 
| 147 |  |  |  |  |  |  | filename => $self->document_root . $dir_path, | 
| 148 |  |  |  |  |  |  | path_info => '', | 
| 149 |  |  |  |  |  |  | query_string => '', | 
| 150 |  |  |  |  |  |  | _path_info_processed => 1, | 
| 151 |  |  |  |  |  |  | }; | 
| 152 |  |  |  |  |  |  | } | 
| 153 | 0 |  |  |  |  | 0 | $self->{base_dir} = bless( $hash => ref( $self ) ); | 
| 154 | 0 |  |  |  |  | 0 | return( $self->{base_dir} ); | 
| 155 |  |  |  |  |  |  | } | 
| 156 |  |  |  |  |  |  |  | 
| 157 |  |  |  |  |  |  | sub base_uri | 
| 158 |  |  |  |  |  |  | { | 
| 159 | 28 |  |  | 28 | 1 | 693 | my $self = shift( @_ ); | 
| 160 | 28 |  |  |  |  | 67 | my $new; | 
| 161 | 28 | 100 |  |  |  | 219 | if( @_ ) | 
|  |  | 100 |  |  |  |  |  | 
| 162 |  |  |  |  |  |  | { | 
| 163 | 12 |  |  |  |  | 48 | $new = shift( @_ ); | 
| 164 |  |  |  |  |  |  | } | 
| 165 |  |  |  |  |  |  | elsif( !ref( $self->{base_uri} ) ) | 
| 166 |  |  |  |  |  |  | { | 
| 167 | 3 |  |  |  |  | 24 | $new = $self->{base_uri}; | 
| 168 |  |  |  |  |  |  | } | 
| 169 |  |  |  |  |  |  |  | 
| 170 | 28 | 100 |  |  |  | 125 | unless( length( $new ) ) | 
| 171 |  |  |  |  |  |  | { | 
| 172 | 13 |  |  |  |  | 62 | $self->message( 4, "Returning base_uri object '", overload::StrVal( $self->{base_uri} ), "' (", ref( $self->{base_uri} ), ")." ); | 
| 173 | 13 |  |  |  |  | 316 | return( $self->{base_uri} ); | 
| 174 |  |  |  |  |  |  | } | 
| 175 |  |  |  |  |  |  |  | 
| 176 | 15 |  |  |  |  | 208 | $self->message( 4, "Processing new base uri '$new'." ); | 
| 177 | 15 |  |  |  |  | 365 | my $r = $self->apache_request; | 
| 178 |  |  |  |  |  |  | ## We create an URI object, so we can get the path only | 
| 179 | 15 |  |  |  |  | 345 | my $u = $self->new_uri( $new ); | 
| 180 | 15 |  |  |  |  | 86 | my $path = $u->path; | 
| 181 | 15 | 50 |  |  |  | 409 | if( $r ) | 
| 182 |  |  |  |  |  |  | { | 
| 183 | 0 |  |  |  |  | 0 | $self->message( 3, "Looking up uri \"$path\"." ); | 
| 184 | 0 |  |  |  |  | 0 | my $rr = $self->lookup_uri( $path ); | 
| 185 | 0 | 0 |  |  |  | 0 | if( !defined( $rr ) ) | 
|  |  | 0 |  |  |  |  |  | 
|  |  | 0 |  |  |  |  |  | 
| 186 |  |  |  |  |  |  | { | 
| 187 | 0 |  |  |  |  | 0 | $self->message( 3, "Error occured looking up uri '$path': ", $self->error ); | 
| 188 | 0 |  |  |  |  | 0 | return; | 
| 189 |  |  |  |  |  |  | } | 
| 190 |  |  |  |  |  |  | elsif( $rr->status != Apache2::Const::HTTP_OK ) | 
| 191 |  |  |  |  |  |  | { | 
| 192 | 0 |  |  |  |  | 0 | my $hdrs = $rr->headers_out; | 
| 193 | 0 |  |  | 0 |  | 0 | $self->message( 3, "There was an error looking up uri \"$path\" with resulting uri \"", $rr->uri, "\". Headers were: ", sub{ $self->dump( $hdrs ) } ); | 
|  | 0 |  |  |  |  | 0 |  | 
| 194 | 0 |  |  |  |  | 0 | return( $self->error( "Could not look up uri \"$path\". Returned code is: ", $rr->status ) ); | 
| 195 |  |  |  |  |  |  | } | 
| 196 |  |  |  |  |  |  | elsif( $rr->finfo->filetype == APR::Const::FILETYPE_NOFILE ) | 
| 197 |  |  |  |  |  |  | { | 
| 198 | 0 |  |  |  |  | 0 | $self->message( 3, "URI \"$path\" is not found." ); | 
| 199 | 0 |  |  |  |  | 0 | return( $self->error( "Could not find uri \"$path\" (originally $u)." ) ); | 
| 200 |  |  |  |  |  |  | } | 
| 201 |  |  |  |  |  |  |  | 
| 202 |  |  |  |  |  |  | ## Remove trailing slash | 
| 203 | 0 |  |  |  |  | 0 | my $u2 = $self->_trim_trailing_slash( $rr->unparsed_uri ); | 
| 204 |  |  |  |  |  |  |  | 
| 205 | 0 |  |  |  |  | 0 | $self->message( 3, "Setting base_uri value via document_uri to '$u2'. Path info found '", $rr->path_info, "'" ); | 
| 206 | 0 |  |  |  |  | 0 | my $hash = | 
| 207 |  |  |  |  |  |  | { | 
| 208 |  |  |  |  |  |  | apache_request => $r, | 
| 209 |  |  |  |  |  |  | base_dir => $self->root, | 
| 210 |  |  |  |  |  |  | base_uri => $self->root, | 
| 211 |  |  |  |  |  |  | document_path => substr( $u2->path, 0, length( $u2->path ) - length( $rr->path_info ) ), | 
| 212 |  |  |  |  |  |  | document_root => $self->document_root, | 
| 213 |  |  |  |  |  |  | document_uri => $u2, | 
| 214 |  |  |  |  |  |  | filename => $rr->filename, | 
| 215 |  |  |  |  |  |  | path_info => $rr->path_info, | 
| 216 |  |  |  |  |  |  | query_string => scalar( $rr->args ), | 
| 217 |  |  |  |  |  |  | _path_info_processed => 1, | 
| 218 |  |  |  |  |  |  | }; | 
| 219 | 0 | 0 |  |  |  | 0 | if( $rr->finfo->filetype == APR::Const::FILETYPE_DIR ) | 
| 220 |  |  |  |  |  |  | { | 
| 221 | 0 |  |  |  |  | 0 | $self->{base_dir} = bless( $hash => ref( $self ) ); | 
| 222 |  |  |  |  |  |  | } | 
| 223 | 0 |  |  |  |  | 0 | $self->{base_uri} = bless( $hash => ref( $self ) ); | 
| 224 |  |  |  |  |  |  | } | 
| 225 |  |  |  |  |  |  | else | 
| 226 |  |  |  |  |  |  | { | 
| 227 | 15 |  |  |  |  | 147 | $self->message( 4, "Resolving uri \"$path\"." ); | 
| 228 |  |  |  |  |  |  | ## We need to ensure the base uri is free of any path info or query string ! | 
| 229 | 15 |  |  |  |  | 355 | my $ref = $self->_find_path_info( $u->path ); | 
| 230 | 15 |  |  | 0 |  | 227 | $self->message( 4, "_find_path_info reslulted in: ", sub{ $self->dump( $ref ) }); | 
|  | 0 |  |  |  |  | 0 |  | 
| 231 | 15 | 50 |  |  |  | 416 | if( !defined( $ref ) ) | 
|  |  | 50 |  |  |  |  |  | 
| 232 |  |  |  |  |  |  | { | 
| 233 | 0 |  |  |  |  | 0 | $self->message( 3, "Error resolving \"$path\"." ); | 
| 234 | 0 |  |  |  |  | 0 | return( $self->error( "Unable to resolve \"$u\"." ) ); | 
| 235 |  |  |  |  |  |  | } | 
| 236 |  |  |  |  |  |  | elsif( $ref->{code} != 200 ) | 
| 237 |  |  |  |  |  |  | { | 
| 238 | 0 |  |  |  |  | 0 | $self->message( 3, "URI \"$path\" is not found." ); | 
| 239 | 0 |  |  |  |  | 0 | $self->error( "Failed to resolve \"$u\". Resulting code is '$ref->{code}'." ); | 
| 240 |  |  |  |  |  |  | } | 
| 241 | 15 |  |  |  |  | 133 | $self->message( 4, "Creating object." ); | 
| 242 |  |  |  |  |  |  | my $hash = | 
| 243 |  |  |  |  |  |  | { | 
| 244 |  |  |  |  |  |  | base_dir => $self->root, | 
| 245 |  |  |  |  |  |  | base_uri => $self->root, | 
| 246 |  |  |  |  |  |  | document_path => $ref->{path}, | 
| 247 |  |  |  |  |  |  | document_root => $self->document_root, | 
| 248 |  |  |  |  |  |  | filename => $ref->{filepath}, | 
| 249 |  |  |  |  |  |  | path_info => $ref->{path_info}, | 
| 250 |  |  |  |  |  |  | query_string => $ref->{query_string}, | 
| 251 | 15 |  |  |  |  | 312 | _path_info_processed => 1, | 
| 252 |  |  |  |  |  |  | }; | 
| 253 | 15 | 100 |  |  |  | 132 | my $tmp = $self->new_uri( $ref->{path_info} ? join( '', $ref->{path}, $ref->{path_info} ) : $ref->{path} ); | 
| 254 | 15 | 50 |  |  |  | 106 | $tmp->query( $ref->{query_string} ) if( $ref->{query_string} ); | 
| 255 | 15 |  |  |  |  | 50 | $hash->{document_uri} = $tmp; | 
| 256 | 15 | 100 |  |  |  | 345 | $self->{base_dir} = bless( $hash => ref( $self ) ) if( -d( $ref->{path} ) ); | 
| 257 | 15 |  |  |  |  | 146 | $self->{base_uri} = bless( $hash => ref( $self ) ); | 
| 258 |  |  |  |  |  |  | } | 
| 259 | 15 |  |  |  |  | 129 | $self->message( 3, "Returning base_uri: '", overload::StrVal( $self->{base_uri} ), "' ($self->{base_uri})." ); | 
| 260 | 15 |  |  |  |  | 381 | return( $self->{base_uri} ); | 
| 261 |  |  |  |  |  |  | } | 
| 262 |  |  |  |  |  |  |  | 
| 263 |  |  |  |  |  |  | sub clone | 
| 264 |  |  |  |  |  |  | { | 
| 265 | 1 |  |  | 1 | 1 | 346 | my $self = shift( @_ ); | 
| 266 | 1 |  |  |  |  | 6 | my $new = {}; | 
| 267 | 1 |  |  |  |  | 45 | my @fields = grep( !/^(apache_request|finfo)$/, keys( %$self ) ); | 
| 268 | 1 |  |  |  |  | 23 | @$new{ @fields } = @$self{ @fields }; | 
| 269 | 1 |  |  |  |  | 9 | $new->{apache_request} = $self->{apache_request}; | 
| 270 | 1 |  |  |  |  | 8 | my $env = {}; | 
| 271 | 1 |  |  |  |  | 3 | %$env = %{$self->{_env}}; | 
|  | 1 |  |  |  |  | 62 |  | 
| 272 | 1 |  |  |  |  | 11 | $new->{_env} = $env; | 
| 273 | 1 |  | 33 |  |  | 18 | return( bless( $new => ( ref( $self ) || $self ) ) ); | 
| 274 |  |  |  |  |  |  | } | 
| 275 |  |  |  |  |  |  |  | 
| 276 |  |  |  |  |  |  | sub code | 
| 277 |  |  |  |  |  |  | { | 
| 278 | 220 |  |  | 220 | 1 | 584 | my $self = shift( @_ ); | 
| 279 | 220 |  |  |  |  | 526 | my $r = $self->apache_request; | 
| 280 | 220 | 50 |  |  |  | 3421 | if( $r ) | 
| 281 |  |  |  |  |  |  | { | 
| 282 | 0 | 0 |  |  |  | 0 | $r->status( @_ ) if( @_ ); | 
| 283 | 0 |  |  |  |  | 0 | return( $r->status ); | 
| 284 |  |  |  |  |  |  | } | 
| 285 |  |  |  |  |  |  | else | 
| 286 |  |  |  |  |  |  | { | 
| 287 | 220 | 100 |  |  |  | 735 | $self->{code} = shift( @_ ) if( @_ ); | 
| 288 | 220 |  |  |  |  | 690 | return( int( $self->{code} ) ); | 
| 289 |  |  |  |  |  |  | } | 
| 290 |  |  |  |  |  |  | } | 
| 291 |  |  |  |  |  |  |  | 
| 292 | 0 |  |  | 0 | 0 | 0 | sub document_dir { return( shift->document_directory( @_ ) ); } | 
| 293 |  |  |  |  |  |  |  | 
| 294 |  |  |  |  |  |  | sub document_directory | 
| 295 |  |  |  |  |  |  | { | 
| 296 | 1 |  |  | 1 | 1 | 372 | my $self = shift( @_ ); | 
| 297 | 1 |  | 50 |  |  | 13 | my $doc_path = $self->document_path || return( $self->error( "No document path set." ) ); | 
| 298 | 1 |  | 50 |  |  | 20 | my $doc_root = $self->document_root || return( $self->error( "No document root set." ) ); | 
| 299 | 1 |  |  |  |  | 10 | $self->message( 3, "Document path is '$doc_path' and document root is '$doc_root'." ); | 
| 300 | 1 | 50 | 33 |  |  | 46 | return( $self->make( document_uri => $doc_path ) ) if( -e( "${doc_root}${doc_path}" ) && -d( _ ) ); | 
| 301 | 1 |  |  |  |  | 65 | my $parent = $self->parent; | 
| 302 | 1 |  |  |  |  | 15 | $self->message( 3, "Returning parent '$parent'." ); | 
| 303 | 1 |  |  |  |  | 22 | return( $parent ); | 
| 304 |  |  |  |  |  |  | } | 
| 305 |  |  |  |  |  |  |  | 
| 306 | 2 |  |  | 2 | 1 | 353 | sub document_filename { return( shift->filename( @_ ) ); } | 
| 307 |  |  |  |  |  |  |  | 
| 308 |  |  |  |  |  |  | sub document_path | 
| 309 |  |  |  |  |  |  | { | 
| 310 | 155 |  |  | 155 | 1 | 1291 | my $self = shift( @_ ); | 
| 311 | 155 |  |  |  |  | 362 | my $class = ref( $self ); | 
| 312 | 155 |  | 100 |  |  | 1427 | my $caller = (caller(1))[3] // ''; | 
| 313 |  |  |  |  |  |  | ## my $caller = substr( $sub, rindex( $sub, ':' ) + 1 ); | 
| 314 | 155 |  |  |  |  | 563 | my $r = $self->apache_request; | 
| 315 | 155 | 50 |  |  |  | 2771 | if( $r ) | 
| 316 |  |  |  |  |  |  | { | 
| 317 | 0 | 0 |  |  |  | 0 | if( @_ ) | 
|  |  | 0 |  |  |  |  |  | 
| 318 |  |  |  |  |  |  | { | 
| 319 | 0 |  |  |  |  | 0 | my $uri = shift( @_ ); | 
| 320 | 0 |  |  |  |  | 0 | $self->message( 4, "Looking up document path '$uri'." ); | 
| 321 | 0 | 0 |  |  |  | 0 | $r = $r->is_initial_req ? $r : $r->main; | 
| 322 | 0 |  |  |  |  | 0 | my $rr = $self->lookup_uri( $uri ); | 
| 323 | 0 | 0 |  |  |  | 0 | if( !defined( $rr ) ) | 
| 324 |  |  |  |  |  |  | { | 
| 325 | 0 |  |  |  |  | 0 | $self->message( 3, "Error occured looking up uri '$path': ", $self->error ); | 
| 326 | 0 |  |  |  |  | 0 | return; | 
| 327 |  |  |  |  |  |  | } | 
| 328 | 0 |  |  |  |  | 0 | $self->message( 4, "New path looked up is '", $rr->uri, "'." ); | 
| 329 | 0 |  |  |  |  | 0 | my $u = APR::URI->parse( $rr->pool, $r->uri ); | 
| 330 | 0 |  |  |  |  | 0 | $self->message( 4, "Document parsed derived from '", $rr->uri, "' by APR::URI is: '", $u->rpath, "'." ); | 
| 331 |  |  |  |  |  |  | ## Remove trailing slash | 
| 332 | 0 |  |  |  |  | 0 | my $u2 = $self->_trim_trailing_slash( $u->rpath ); | 
| 333 | 0 |  |  |  |  | 0 | $self->{document_path} = $u2; | 
| 334 | 0 | 0 |  |  |  | 0 | $self->{_uri_reset} = 'document_path' unless( $caller eq "${class}\::document_uri" ); | 
| 335 |  |  |  |  |  |  | } | 
| 336 |  |  |  |  |  |  | elsif( !length( $self->{document_path} ) ) | 
| 337 |  |  |  |  |  |  | { | 
| 338 | 0 |  |  |  |  | 0 | $self->message( 4, "No document path set. Guessing it from \$r->uri '", $r->uri, "'." ); | 
| 339 | 0 |  |  |  |  | 0 | my $u = APR::URI->parse( $r->pool, $r->uri ); | 
| 340 | 0 |  |  |  |  | 0 | $self->message( 3, "Setting document path to '", $u->rpath, "'." ); | 
| 341 | 0 |  |  |  |  | 0 | $self->{document_path} = $self->new_uri( $u->rpath ); | 
| 342 |  |  |  |  |  |  | } | 
| 343 |  |  |  |  |  |  | } | 
| 344 |  |  |  |  |  |  | else | 
| 345 |  |  |  |  |  |  | { | 
| 346 | 155 | 100 |  |  |  | 474 | if( @_ ) | 
| 347 |  |  |  |  |  |  | { | 
| 348 | 146 |  |  |  |  | 296 | my $uri = shift( @_ ); | 
| 349 | 146 |  |  |  |  | 643 | $self->message( 4, "Setting new document path for '$uri'." ); | 
| 350 | 146 |  |  |  |  | 2415 | $self->{document_path} = $self->new_uri( $self->collapse_dots( $uri ) ); | 
| 351 | 146 |  |  |  |  | 1059 | $self->message( 3, "Document path value is now: '", $self->{document_path}, "' (", overload::StrVal( $self->{document_path} ), ")." ); | 
| 352 | 146 | 50 |  |  |  | 3939 | $self->{_uri_reset} = 'document_path' unless( $caller eq "${class}\::document_uri" ); | 
| 353 |  |  |  |  |  |  | } | 
| 354 |  |  |  |  |  |  | } | 
| 355 | 155 |  |  |  |  | 380 | return( $self->{document_path} ); | 
| 356 |  |  |  |  |  |  | } | 
| 357 |  |  |  |  |  |  |  | 
| 358 |  |  |  |  |  |  | sub document_root | 
| 359 |  |  |  |  |  |  | { | 
| 360 | 471 |  |  | 471 | 1 | 21755 | my $self = shift( @_ ); | 
| 361 | 471 |  |  |  |  | 1225 | my $r = $self->apache_request; | 
| 362 | 471 |  |  |  |  | 8318 | my $new; | 
| 363 | 471 | 100 |  |  |  | 1291 | if( @_ ) | 
| 364 |  |  |  |  |  |  | { | 
| 365 | 129 |  |  |  |  | 276 | $new = shift( @_ ); | 
| 366 | 129 |  |  |  |  | 871 | $self->message( 4, "New document root provided: '$new'." ); | 
| 367 | 129 | 100 |  |  |  | 2714 | unless( substr( $new, 0, 1 ) eq '/' ) | 
| 368 |  |  |  |  |  |  | { | 
| 369 | 4 |  |  |  |  | 34 | $new = URI::file->new_abs( $new )->file; | 
| 370 |  |  |  |  |  |  | } | 
| 371 |  |  |  |  |  |  | } | 
| 372 |  |  |  |  |  |  |  | 
| 373 | 471 | 50 |  |  |  | 33435 | if( $r ) | 
| 374 |  |  |  |  |  |  | { | 
| 375 | 0 | 0 |  |  |  | 0 | $r->document_root( $new ) if( defined( $new ) ); | 
| 376 | 0 |  |  |  |  | 0 | $r->subprocess_env( DOCUMENT_ROOT => $r->document_root ); | 
| 377 | 0 |  |  |  |  | 0 | return( $r->document_root ); | 
| 378 |  |  |  |  |  |  | } | 
| 379 |  |  |  |  |  |  | else | 
| 380 |  |  |  |  |  |  | { | 
| 381 | 471 | 100 |  |  |  | 1046 | if( defined( $new ) ) | 
| 382 |  |  |  |  |  |  | { | 
| 383 | 129 |  |  |  |  | 378 | $self->{document_root} = $new; | 
| 384 | 129 |  |  |  |  | 717 | $self->_set_env( DOCUMENT_ROOT => $self->{document_root} ); | 
| 385 |  |  |  |  |  |  | } | 
| 386 | 471 |  | 33 |  |  | 2347 | return( $self->{document_root} || $self->env( 'DOCUMENT_ROOT' ) ); | 
| 387 |  |  |  |  |  |  | } | 
| 388 |  |  |  |  |  |  | } | 
| 389 |  |  |  |  |  |  |  | 
| 390 |  |  |  |  |  |  | sub document_uri | 
| 391 |  |  |  |  |  |  | { | 
| 392 | 235 |  |  | 235 | 1 | 2571 | my $self = shift( @_ ); | 
| 393 | 235 |  |  |  |  | 561 | my $r = $self->apache_request; | 
| 394 | 235 |  |  |  |  | 3742 | my $new = ''; | 
| 395 | 235 | 100 |  |  |  | 697 | if( @_ ) | 
| 396 |  |  |  |  |  |  | { | 
| 397 | 129 |  |  |  |  | 343 | $new = shift( @_ ); | 
| 398 | 129 |  |  |  |  | 665 | $self->message( 3, "New document uri provided '$new'." ); | 
| 399 | 129 |  |  |  |  | 2147 | local $URI::ABS_REMOTE_LEADING_DOTS = 1; | 
| 400 | 129 | 100 |  |  |  | 736 | unless( substr( "$new", 0, 1 ) eq '/' ) | 
| 401 |  |  |  |  |  |  | { | 
| 402 | 13 |  |  |  |  | 84 | my $base_uri = $self->base_uri; | 
| 403 | 13 |  |  |  |  | 135 | $self->message( 4, "New document uri '$new' is not absolute. Making it absolute using base uri '", $base_uri->{document_path}, "'." ); | 
| 404 | 13 |  |  |  |  | 259 | $self->message( 4, "Base uri is '", overload::StrVal( $base_uri ), "' ($base_uri)." ); | 
| 405 | 13 |  |  |  |  | 299 | $new = URI->new( $new )->abs( $base_uri->{document_path} ); | 
| 406 |  |  |  |  |  |  | } | 
| 407 |  |  |  |  |  |  | } | 
| 408 |  |  |  |  |  |  |  | 
| 409 |  |  |  |  |  |  | ## return( $self->error( "Document URI needs to be an absolute URL path. Value provided was '$new'." ) ) if( length( $new ) && substr( $new, 0, 1 ) ne '/' ); | 
| 410 |  |  |  |  |  |  |  | 
| 411 | 235 | 50 |  |  |  | 4831 | if( $r ) | 
| 412 |  |  |  |  |  |  | { | 
| 413 |  |  |  |  |  |  | ## We do a lookup unless we are already in a sub request, and we do not want to end up in an infinite loop | 
| 414 |  |  |  |  |  |  | ## $r = $r->is_initial_req ? $r : $r->main; | 
| 415 | 0 | 0 |  |  |  | 0 | if( length( "$new" ) ) | 
|  |  | 0 |  |  |  |  |  | 
|  |  | 0 |  |  |  |  |  | 
| 416 |  |  |  |  |  |  | { | 
| 417 | 0 | 0 |  |  |  | 0 | $r = $r->is_initial_req ? $r : $r->main; | 
| 418 | 0 |  |  |  |  | 0 | my $rr = $self->lookup_uri( "$new" ); | 
| 419 | 0 | 0 |  |  |  | 0 | if( !defined( $rr ) ) | 
| 420 |  |  |  |  |  |  | { | 
| 421 | 0 |  |  |  |  | 0 | $self->message( 3, "Error occured looking up uri '$path': ", $self->error ); | 
| 422 | 0 |  |  |  |  | 0 | return; | 
| 423 |  |  |  |  |  |  | } | 
| 424 | 0 |  |  |  |  | 0 | $self->message( 3, "Resulting uri from lookup_uri is \"", $rr->uri, "\" (", $rr->unparsed_uri, ")." ); | 
| 425 | 0 |  |  |  |  | 0 | $self->apache_request( $rr ); | 
| 426 |  |  |  |  |  |  | ## Remove trailing slash | 
| 427 | 0 |  |  |  |  | 0 | my $u = $self->_trim_trailing_slash( $rr->unparsed_uri ); | 
| 428 | 0 |  |  |  |  | 0 | $self->{document_uri} = $u; | 
| 429 | 0 |  |  |  |  | 0 | $self->_set_env( DOCUMENT_URI => $self->{document_uri} ); | 
| 430 | 0 |  |  |  |  | 0 | $self->_set_env( REQUEST_URI => $self->{document_uri} ); | 
| 431 | 0 | 0 |  |  |  | 0 | $self->_set_env( QUERY_STRING => scalar( $rr->args ) ) if( scalar( $rr->args ) ); | 
| 432 | 0 | 0 |  |  |  | 0 | $self->_set_env( PATH_INFO => $rr->path_info ) if( $rr->path_info ); | 
| 433 |  |  |  |  |  |  | } | 
| 434 |  |  |  |  |  |  | elsif( $self->{_uri_reset} ) | 
| 435 |  |  |  |  |  |  | { | 
| 436 | 0 |  |  |  |  | 0 | $self->message( 4, "URI has been reset by '$self->{_uri_reset}'" ); | 
| 437 | 0 |  | 0 |  |  | 0 | my $u = URI->new( $r->uri . ( $r->path_info // '' ) ); | 
| 438 | 0 | 0 |  |  |  | 0 | $u->query( scalar( $r->args ) ) if( length( scalar( $r->args ) ) ); | 
| 439 |  |  |  |  |  |  | ## Cannot change the value of $r->unparsed_uri | 
| 440 | 0 |  |  |  |  | 0 | $r->uri( "$u" ); | 
| 441 | 0 |  |  |  |  | 0 | $self->message( 4, "Document uri has been updated after reset to '$self->{document_uri}'." ); | 
| 442 | 0 |  |  |  |  | 0 | $self->{document_uri} = $u; | 
| 443 | 0 |  |  |  |  | 0 | $self->{_uri_reset} = 0; | 
| 444 |  |  |  |  |  |  | } | 
| 445 |  |  |  |  |  |  | elsif( !length( $self->{document_uri} ) ) | 
| 446 |  |  |  |  |  |  | { | 
| 447 | 0 |  |  |  |  | 0 | $self->message( 3, "URI not set or reset. Using '", $r->unparsed_uri, "'." ); | 
| 448 | 0 |  |  |  |  | 0 | $self->{document_uri} = $self->new_uri( $r->unparsed_uri ); | 
| 449 | 0 |  |  |  |  | 0 | $self->_set_env( DOCUMENT_URI => $self->{document_uri} ); | 
| 450 | 0 |  |  |  |  | 0 | $self->_set_env( REQUEST_URI => $self->{document_uri} ); | 
| 451 | 0 | 0 |  |  |  | 0 | $self->_set_env( QUERY_STRING => scalar( $r->args ) ) if( scalar( $r->args ) ); | 
| 452 | 0 | 0 |  |  |  | 0 | $self->_set_env( PATH_INFO => $r->path_info ) if( $r->path_info ); | 
| 453 |  |  |  |  |  |  | } | 
| 454 | 0 |  |  |  |  | 0 | $self->message( 4, "Returning document uri value of '$self->{document_uri}'." ); | 
| 455 | 0 |  |  |  |  | 0 | return( $self->{document_uri} ); | 
| 456 |  |  |  |  |  |  | } | 
| 457 |  |  |  |  |  |  | else | 
| 458 |  |  |  |  |  |  | { | 
| 459 | 235 | 100 |  |  |  | 816 | if( length( "$new" ) ) | 
| 460 |  |  |  |  |  |  | { | 
| 461 | 129 |  |  |  |  | 482 | $self->{_path_info_processed} = 0; | 
| 462 |  |  |  |  |  |  | } | 
| 463 | 235 | 50 | 66 |  |  | 1325 | $self->message( 4, "Returning nothing." ) if( !length( $self->{document_uri} ) && $self->{_path_info_processed} ); | 
| 464 | 235 | 50 | 66 |  |  | 1582 | return( '' ) if( !length( $self->{document_uri} ) && $self->{_path_info_processed} ); | 
| 465 | 235 |  | 66 |  |  | 1153 | my $v = $new || $self->{document_uri}; | 
| 466 | 235 |  |  |  |  | 1570 | $self->message( 3, "New document uri provided is '$new' and document_uri value is: '$self->{document_uri}'" ); | 
| 467 | 235 | 100 |  |  |  | 4469 | if( !$self->{_path_info_processed} ) | 
| 468 |  |  |  |  |  |  | { | 
| 469 | 146 |  |  |  |  | 667 | $self->message( 4, "Path info from document uri '$v' not processed yet, doing it now." ); | 
| 470 | 146 |  |  |  |  | 2227 | $self->{_path_info_processed}++; | 
| 471 | 146 |  |  |  |  | 258 | my $res; | 
| 472 | 146 | 50 |  |  |  | 583 | if( defined( $res = $self->_find_path_info( $v ) ) ) | 
| 473 |  |  |  |  |  |  | { | 
| 474 | 146 |  |  | 0 |  | 1562 | $self->message( 4, "_find_path_info returned: ", sub{ $self->dump( $res ) }); | 
|  | 0 |  |  |  |  | 0 |  | 
| 475 | 146 |  |  |  |  | 3680 | $self->{document_uri} = URI->new( $v ); | 
| 476 | 146 |  | 50 |  |  | 7167 | $self->message( 3, "Document uri set to '", ( $self->{document_uri} // '' ), "'" ); | 
| 477 | 146 |  | 50 |  |  | 3800 | $self->message( 4, "Setting document_path to '", ( $res->{path} // '' ), "'" ); | 
| 478 | 146 |  |  |  |  | 2445 | $self->document_path( $res->{path} ); | 
| 479 | 146 |  | 50 |  |  | 768 | $self->message( 4, "Setting filename to '", ( $res->{filepath} // '' ), "'" ); | 
| 480 | 146 |  |  |  |  | 2563 | $self->filename( $res->{filepath} ); | 
| 481 | 146 |  | 100 |  |  | 1214 | $self->message( 4, "Setting path_info to '", ( $res->{path_info} // '' ), "'" ); | 
| 482 | 146 | 100 |  |  |  | 2419 | $self->path_info( $res->{path_info} ) if( length( $res->{path_info} ) ); | 
| 483 | 146 |  | 100 |  |  | 925 | $self->message( 4, "Setting query_string to '", ( $res->{query_string} // '' ), "'" ); | 
| 484 | 146 | 100 |  |  |  | 2355 | $self->query_string( $res->{query_string} ) if( length( $res->{query_string} ) ); | 
| 485 | 146 |  |  |  |  | 612 | $self->_set_env( DOCUMENT_URI => $self->{document_uri} ); | 
| 486 | 146 |  |  |  |  | 523 | $self->_set_env( REQUEST_URI => $self->{document_uri} ); | 
| 487 | 146 |  |  |  |  | 785 | $self->message( 4, "Setting code to '$res->{code}'" ); | 
| 488 | 146 |  |  |  |  | 2653 | $self->code( $res->{code} ); | 
| 489 |  |  |  |  |  |  | } | 
| 490 |  |  |  |  |  |  | else | 
| 491 |  |  |  |  |  |  | { | 
| 492 | 0 |  |  |  |  | 0 | $self->message( 3, "_find_path_info returned an error: ", $self->error ); | 
| 493 |  |  |  |  |  |  | } | 
| 494 |  |  |  |  |  |  | } | 
| 495 |  |  |  |  |  |  |  | 
| 496 | 235 | 100 |  |  |  | 928 | if( $self->{_uri_reset} ) | 
| 497 |  |  |  |  |  |  | { | 
| 498 | 3 |  |  |  |  | 27 | $self->message( 4, "URI has been reset by '$self->{_uri_reset}'" ); | 
| 499 | 3 |  |  |  |  | 45 | $self->{_uri_reset} = 0; | 
| 500 | 3 |  | 50 |  |  | 10 | my $u = URI->new( $self->document_path . ( $self->path_info // '' ) ); | 
| 501 | 3 | 100 |  |  |  | 169 | $u->query( $self->query_string ) if( $self->query_string ); | 
| 502 | 3 |  |  |  |  | 99 | $self->{document_uri} = $u; | 
| 503 | 3 |  |  |  |  | 16 | $self->message( 4, "Document uri reset to '$self->{document_uri}'" ); | 
| 504 |  |  |  |  |  |  | } | 
| 505 | 235 |  |  |  |  | 922 | $self->message( 4, "Returning document_uri = '$self->{document_uri}'" ); | 
| 506 | 235 |  |  |  |  | 4862 | return( $self->{document_uri} ); | 
| 507 |  |  |  |  |  |  | } | 
| 508 |  |  |  |  |  |  | } | 
| 509 |  |  |  |  |  |  |  | 
| 510 |  |  |  |  |  |  | sub env | 
| 511 |  |  |  |  |  |  | { | 
| 512 | 147 |  |  | 147 | 1 | 328 | my $self = shift( @_ ); | 
| 513 |  |  |  |  |  |  | ## The user wants the entire hash reference | 
| 514 | 147 | 50 |  |  |  | 492 | unless( @_ ) | 
| 515 |  |  |  |  |  |  | { | 
| 516 | 0 |  |  |  |  | 0 | my $r = $self->apache_request; | 
| 517 | 0 | 0 |  |  |  | 0 | if( $r ) | 
| 518 |  |  |  |  |  |  | { | 
| 519 |  |  |  |  |  |  | ## $r = $r->is_initial_req ? $r : $r->main; | 
| 520 | 0 |  |  |  |  | 0 | return( $r->subprocess_env ) | 
| 521 |  |  |  |  |  |  | } | 
| 522 |  |  |  |  |  |  | else | 
| 523 |  |  |  |  |  |  | { | 
| 524 | 0 | 0 |  |  |  | 0 | unless( scalar( keys( %{$self->{_env}} ) ) ) | 
|  | 0 |  |  |  |  | 0 |  | 
| 525 |  |  |  |  |  |  | { | 
| 526 | 0 |  |  |  |  | 0 | $self->{_env} = {%ENV}; | 
| 527 |  |  |  |  |  |  | } | 
| 528 | 0 |  |  |  |  | 0 | return( $self->{_env} ); | 
| 529 |  |  |  |  |  |  | } | 
| 530 |  |  |  |  |  |  | } | 
| 531 | 147 |  |  |  |  | 309 | my $name = shift( @_ ); | 
| 532 | 147 | 50 |  |  |  | 416 | return( $self->error( "No environment variable name was provided." ) ) if( !length( $name ) ); | 
| 533 | 147 |  |  |  |  | 379 | my $opts = {}; | 
| 534 | 15 |  |  | 15 |  | 177 | no warnings 'uninitialized'; | 
|  | 15 |  |  |  |  | 36 |  | 
|  | 15 |  |  |  |  | 22179 |  | 
| 535 | 147 | 50 | 33 |  |  | 1075 | $opts = pop( @_ ) if( scalar( @_ ) && Scalar::Util::reftype( $_[-1] ) eq 'HASH' ); | 
| 536 |  |  |  |  |  |  | ## return( $self->error( "Environment variable value provided is a reference data (", overload::StrVal( $val ), ")." ) ) if( ref( $val ) && ( !overload::Overloaded( $val ) || ( overload::Overloaded( $val ) && !overload::Method( $val, '""' ) ) ) ); | 
| 537 | 147 |  | 33 |  |  | 724 | my $r = $opts->{apache_request} || $self->apache_request; | 
| 538 | 147 | 50 |  |  |  | 2648 | if( $r ) | 
| 539 |  |  |  |  |  |  | { | 
| 540 |  |  |  |  |  |  | ## $r = $r->is_initial_req ? $r : $r->main; | 
| 541 | 0 | 0 |  |  |  | 0 | $r->subprocess_env( $name => shift( @_ ) ) if( @_ ); | 
| 542 | 0 |  |  |  |  | 0 | my $v = $r->subprocess_env( $name ); | 
| 543 | 0 |  |  |  |  | 0 | return( $v ); | 
| 544 |  |  |  |  |  |  | } | 
| 545 |  |  |  |  |  |  | else | 
| 546 |  |  |  |  |  |  | { | 
| 547 | 147 |  |  |  |  | 333 | my $env = {}; | 
| 548 | 147 | 100 |  |  |  | 272 | unless( scalar( keys( %{$self->{_env}} ) ) ) | 
|  | 147 |  |  |  |  | 830 |  | 
| 549 |  |  |  |  |  |  | { | 
| 550 |  |  |  |  |  |  | ## Make a copy of the environment variables | 
| 551 | 17 |  |  |  |  | 824 | $self->{_env} = {%ENV}; | 
| 552 |  |  |  |  |  |  | } | 
| 553 | 147 |  |  |  |  | 468 | $env = $self->{_env}; | 
| 554 | 147 | 50 |  |  |  | 503 | if( @_ ) | 
| 555 |  |  |  |  |  |  | { | 
| 556 | 147 |  |  |  |  | 456 | $env->{ $name } = shift( @_ ); | 
| 557 | 147 |  |  |  |  | 423 | my $meth = lc( $name ); | 
| 558 | 147 | 50 |  |  |  | 856 | if( $self->can( $meth ) ) | 
| 559 |  |  |  |  |  |  | { | 
| 560 | 0 |  |  |  |  | 0 | $self->$meth( $env->{ $name } ); | 
| 561 |  |  |  |  |  |  | } | 
| 562 |  |  |  |  |  |  | } | 
| 563 | 147 |  |  |  |  | 411 | return( $env->{ $name } ); | 
| 564 |  |  |  |  |  |  | } | 
| 565 |  |  |  |  |  |  | } | 
| 566 |  |  |  |  |  |  |  | 
| 567 |  |  |  |  |  |  | ## This is set by document_uri | 
| 568 |  |  |  |  |  |  | sub filename | 
| 569 |  |  |  |  |  |  | { | 
| 570 | 277 |  |  | 277 | 1 | 1001 | my $self = shift( @_ ); | 
| 571 | 277 |  |  |  |  | 492 | my $class = ref( $self ); | 
| 572 | 277 |  | 100 |  |  | 1821 | my $caller = (caller(1))[3] // ''; | 
| 573 |  |  |  |  |  |  | ## my $caller = substr( $sub, rindex( $sub, ':' ) + 1 ); | 
| 574 | 277 |  |  |  |  | 819 | my $r = $self->apache_request; | 
| 575 | 277 |  |  |  |  | 4250 | my $newfile; | 
| 576 | 277 | 100 |  |  |  | 675 | if( @_ ) | 
| 577 |  |  |  |  |  |  | { | 
| 578 | 147 |  |  |  |  | 251 | $newfile = shift( @_ ); | 
| 579 | 147 | 50 | 33 |  |  | 919 | return( $self->error( "New file provided, but it was an empty string." ) ) if( !defined( $newfile ) || !length( $newfile ) ); | 
| 580 |  |  |  |  |  |  | } | 
| 581 |  |  |  |  |  |  |  | 
| 582 | 277 | 50 |  |  |  | 605 | if( $r ) | 
| 583 |  |  |  |  |  |  | { | 
| 584 | 0 | 0 |  |  |  | 0 | if( defined( $newfile ) ) | 
|  |  | 0 |  |  |  |  |  | 
| 585 |  |  |  |  |  |  | { | 
| 586 | 0 |  |  |  |  | 0 | $self->message( 4, "Setting new file path '$newfile'. Looking up file." ); | 
| 587 | 0 | 0 |  |  |  | 0 | $r = $r->is_initial_req ? $r : $r->main; | 
| 588 | 0 |  |  |  |  | 0 | my $rr = $r->lookup_file( $newfile ); | 
| 589 | 0 | 0 |  |  |  | 0 | if( $rr->status == Apache2::Const::HTTP_OK ) | 
| 590 |  |  |  |  |  |  | { | 
| 591 | 0 |  |  |  |  | 0 | $newfile = $rr->filename; | 
| 592 | 0 |  |  |  |  | 0 | $self->message( 3, "File found and resolved to: '$newfile'." ); | 
| 593 |  |  |  |  |  |  | } | 
| 594 |  |  |  |  |  |  | else | 
| 595 |  |  |  |  |  |  | { | 
| 596 | 0 |  |  |  |  | 0 | $self->message( 3, "File not found. Setting it to: '$newfile' nevertheless." ); | 
| 597 | 0 |  |  |  |  | 0 | $r->filename( $self->collapse_dots( $newfile ) ); | 
| 598 | 0 |  |  |  |  | 0 | $self->message( 3, "File path is now '", $r->filename, "'." ); | 
| 599 |  |  |  |  |  |  | ## <https://perl.apache.org/docs/2.0/api/Apache2/RequestRec.html#toc_C_filename_> | 
| 600 | 0 |  |  |  |  | 0 | $r->finfo( APR::Finfo::stat( $newfile, APR::Const::FINFO_NORM, $r->pool ) ); | 
| 601 | 0 |  |  |  |  | 0 | $self->finfo( $newfile ); | 
| 602 |  |  |  |  |  |  | } | 
| 603 | 0 |  |  |  |  | 0 | $r->subprocess_env( SCRIPT_FILENAME => $newfile ); | 
| 604 |  |  |  |  |  |  | ## Force to create new Apache2::SSI::URI object | 
| 605 | 0 |  |  |  |  | 0 | $self->{filename} = $newfile; | 
| 606 | 0 | 0 |  |  |  | 0 | $self->{_uri_reset} = 'filename' unless( $caller eq "${class}\::document_uri" ); | 
| 607 |  |  |  |  |  |  | } | 
| 608 |  |  |  |  |  |  | elsif( !length( $self->{filename} ) ) | 
| 609 |  |  |  |  |  |  | { | 
| 610 | 0 |  |  |  |  | 0 | $self->{filename} = $r->filename; | 
| 611 |  |  |  |  |  |  | } | 
| 612 |  |  |  |  |  |  | } | 
| 613 |  |  |  |  |  |  | else | 
| 614 |  |  |  |  |  |  | { | 
| 615 | 277 | 100 |  |  |  | 645 | if( defined( $newfile ) ) | 
| 616 |  |  |  |  |  |  | { | 
| 617 | 147 |  |  |  |  | 768 | $self->message( 4, "New file path provided is: '$newfile'" ); | 
| 618 | 147 |  |  |  |  | 13006 | my $try = Cwd::realpath( $newfile ); | 
| 619 | 147 | 50 |  |  |  | 691 | $newfile = $try if( defined( $try ) ); | 
| 620 | 147 |  |  |  |  | 924 | $self->message( 3, "Getting the new file real path: '$newfile'" ); | 
| 621 | 147 |  |  |  |  | 3151 | $self->env( SCRIPT_FILENAME => $newfile ); | 
| 622 | 147 |  |  |  |  | 556 | $self->finfo( $newfile ); | 
| 623 |  |  |  |  |  |  | ## Force to create new Apache2::SSI::URI object | 
| 624 | 147 |  |  |  |  | 604 | $self->{filename} = $self->collapse_dots( $newfile ); | 
| 625 | 147 |  |  |  |  | 597 | $self->{document_path} = $self->new_uri( substr( $self->{filename}, length( $self->document_root ) ) ); | 
| 626 | 147 | 100 |  |  |  | 839 | $self->{_uri_reset} = 'filename' unless( $caller eq "${class}\::document_uri" ); | 
| 627 |  |  |  |  |  |  | } | 
| 628 |  |  |  |  |  |  | } | 
| 629 | 277 |  |  |  |  | 989 | $self->message( 4, "Returning filename '$self->{filename}'" ); | 
| 630 | 277 |  |  |  |  | 6314 | return( $self->{filename} ); | 
| 631 |  |  |  |  |  |  | } | 
| 632 |  |  |  |  |  |  |  | 
| 633 |  |  |  |  |  |  | ## Alias | 
| 634 | 54 |  |  | 54 | 0 | 415 | sub filepath { return( shift->filename( @_ ) ); } | 
| 635 |  |  |  |  |  |  |  | 
| 636 |  |  |  |  |  |  | sub finfo | 
| 637 |  |  |  |  |  |  | { | 
| 638 | 156 |  |  | 156 | 1 | 704 | my $self = shift( @_ ); | 
| 639 | 156 |  |  |  |  | 362 | my $r = $self->apache_request; | 
| 640 | 156 |  |  |  |  | 2439 | my $newfile; | 
| 641 | 156 | 100 |  |  |  | 440 | if( @_ ) | 
|  |  | 50 |  |  |  |  |  | 
| 642 |  |  |  |  |  |  | { | 
| 643 | 147 |  |  |  |  | 312 | $newfile = shift( @_ ); | 
| 644 | 147 | 50 | 33 |  |  | 900 | return( $self->error( "New file path specified but is an empty string." ) ) if( !defined( $newfile ) || !length( $newfile ) ); | 
| 645 |  |  |  |  |  |  | } | 
| 646 |  |  |  |  |  |  | elsif( !$self->{finfo} ) | 
| 647 |  |  |  |  |  |  | { | 
| 648 | 0 |  |  |  |  | 0 | $newfile = $self->filename; | 
| 649 | 0 | 0 |  |  |  | 0 | return( $self->error( "No file path set. This should not happen." ) ) if( !$newfile ); | 
| 650 |  |  |  |  |  |  | } | 
| 651 |  |  |  |  |  |  |  | 
| 652 | 156 | 100 |  |  |  | 480 | if( defined( $newfile ) ) | 
| 653 |  |  |  |  |  |  | { | 
| 654 | 147 |  |  |  |  | 709 | $self->message( 3, "No finfo object yet, creating one with file '$newfile'." ); | 
| 655 | 147 | 50 |  |  |  | 2745 | $self->{finfo} = Apache2::SSI::Finfo->new( $newfile, ( $r ? ( apache_request => $r ) : () ), debug => $self->debug ); | 
| 656 | 147 | 50 |  |  |  | 1035 | return( $self->pass_error( Apache2::SSI::Finfo->error ) ) if( !$self->{finfo} ); | 
| 657 |  |  |  |  |  |  | } | 
| 658 | 156 |  |  |  |  | 432 | return( $self->{finfo} ); | 
| 659 |  |  |  |  |  |  | } | 
| 660 |  |  |  |  |  |  |  | 
| 661 |  |  |  |  |  |  | sub lookup_uri | 
| 662 |  |  |  |  |  |  | { | 
| 663 | 0 |  |  | 0 | 0 | 0 | my $self = shift( @_ ); | 
| 664 | 0 |  |  |  |  | 0 | my $uri  = ''; | 
| 665 | 0 | 0 | 0 |  |  | 0 | $uri = shift( @_ ) if( @_ && !ref( $_[0] ) && ( scalar( @_ ) % 2 ) ); | 
|  |  |  | 0 |  |  |  |  | 
| 666 | 0 |  |  |  |  | 0 | my $opts = {}; | 
| 667 | 0 | 0 |  |  |  | 0 | $opts = Scalar::Util::reftype( $_[0] ) eq 'HASH' | 
|  |  | 0 |  |  |  |  |  | 
| 668 |  |  |  |  |  |  | ? shift( @_ ) | 
| 669 |  |  |  |  |  |  | : !( scalar( @_ ) % 2 ) | 
| 670 |  |  |  |  |  |  | ? { @_ } | 
| 671 |  |  |  |  |  |  | : {}; | 
| 672 | 0 | 0 |  |  |  | 0 | $uri = $opts->{uri} if( !length( $uri ) ); | 
| 673 | 0 | 0 |  |  |  | 0 | return( $self->error( "No uri provided." ) ) if( !length( $uri ) ); | 
| 674 | 0 |  | 0 |  |  | 0 | my $r = $opts->{apache_request} || $self->apache_request; | 
| 675 | 0 |  | 0 |  |  | 0 | my $max_redirects = $opts->{max_redirect} || 10; | 
| 676 | 0 |  |  |  |  | 0 | my $c = 0; | 
| 677 | 0 |  |  |  |  | 0 | my $rr = $r->lookup_uri( $uri ); | 
| 678 | 0 |  | 0 |  |  | 0 | while( ++$c <= $max_redirects && | 
|  |  |  | 0 |  |  |  |  | 
| 679 |  |  |  |  |  |  | ( $rr->status == Apache2::Const::HTTP_MOVED_PERMANENTLY || | 
| 680 |  |  |  |  |  |  | $rr->status == Apache2::Const::HTTP_MOVED_TEMPORARILY ) ) | 
| 681 |  |  |  |  |  |  | { | 
| 682 | 0 |  |  |  |  | 0 | $self->message( 3, "Getting next \$r in redirect." ); | 
| 683 | 0 |  |  |  |  | 0 | my $next_r = $rr->next; | 
| 684 | 0 | 0 |  |  |  | 0 | if( !defined( $next_r ) ) | 
| 685 |  |  |  |  |  |  | { | 
| 686 | 0 |  |  |  |  | 0 | last; | 
| 687 |  |  |  |  |  |  | } | 
| 688 |  |  |  |  |  |  | else | 
| 689 |  |  |  |  |  |  | { | 
| 690 | 0 |  |  |  |  | 0 | $self->message( 3, "Resulting status is: ", $next_r->status ); | 
| 691 | 0 |  |  |  |  | 0 | $rr = $next_r; | 
| 692 |  |  |  |  |  |  | } | 
| 693 |  |  |  |  |  |  | } | 
| 694 | 0 |  |  |  |  | 0 | $self->message( 3, "Resulting Apache2::RequestRec is '$rr' with status '", $rr->status, "'." ); | 
| 695 | 0 | 0 | 0 |  |  | 0 | if( defined( $rr ) && | 
|  |  |  | 0 |  |  |  |  | 
| 696 |  |  |  |  |  |  | ( $rr->status == Apache2::Const::HTTP_MOVED_PERMANENTLY || | 
| 697 |  |  |  |  |  |  | $rr->status == Apache2::Const::HTTP_MOVED_TEMPORARILY ) ) | 
| 698 |  |  |  |  |  |  | { | 
| 699 | 0 |  |  |  |  | 0 | my $hdrs = $rr->headers_out; | 
| 700 | 0 |  |  | 0 |  | 0 | $self->message( 3, "Redirect headers are: ", sub{ $self->dump( $hdrs ) }); | 
|  | 0 |  |  |  |  | 0 |  | 
| 701 |  |  |  |  |  |  | ## Weird, should not happen, but just in case | 
| 702 | 0 | 0 | 0 |  |  | 0 | if( !exists( $hdrs->{Location} ) || !length( $hdrs->{Location} ) ) | 
| 703 |  |  |  |  |  |  | { | 
| 704 | 0 |  |  |  |  | 0 | $self->message( 3, "Could not find any 'Location' header." ); | 
| 705 | 0 |  |  |  |  | 0 | return( $rr ); | 
| 706 |  |  |  |  |  |  | } | 
| 707 |  |  |  |  |  |  |  | 
| 708 | 0 |  |  |  |  | 0 | try | 
| 709 | 0 |  |  | 0 |  | 0 | { | 
| 710 |  |  |  |  |  |  | ## No, we cannot use $rr->uri. This would give us the initial requested uri, not the redirected uri | 
| 711 | 0 |  |  |  |  | 0 | my $u = URI->new( $hdrs->{Location} ); | 
| 712 | 0 |  |  |  |  | 0 | $uri = $u->path; | 
| 713 | 0 |  |  |  |  | 0 | $self->message( 3, "Found uri \"$uri\" from Location header field." ); | 
| 714 | 0 | 0 |  |  |  | 0 | if( ++$self->{_lookup_looping} > 1 ) | 
| 715 |  |  |  |  |  |  | { | 
| 716 | 0 |  |  |  |  | 0 | $self->message( 3, "Lookup is looping, return current \$r '$rr'." ); | 
| 717 | 0 |  |  |  |  | 0 | return( $rr ); | 
| 718 |  |  |  |  |  |  | } | 
| 719 |  |  |  |  |  |  | else | 
| 720 |  |  |  |  |  |  | { | 
| 721 | 0 |  |  |  |  | 0 | delete( $self->{_lookup_looping} ); | 
| 722 | 0 |  |  |  |  | 0 | my $new_r = $self->lookup_uri( $uri ); | 
| 723 | 0 |  |  |  |  | 0 | $self->message( 3, "Returning new \$r '$new_r' with status '", $new_r->status, "' and uri '", $new_r->uri, "' and filename '", $new_r->filename, "'" ); | 
| 724 | 0 |  |  |  |  | 0 | return( $new_r ); | 
| 725 |  |  |  |  |  |  | } | 
| 726 |  |  |  |  |  |  | } | 
| 727 | 0 | 0 |  |  |  | 0 | catch( $e ) | 
|  | 0 | 0 |  |  |  | 0 |  | 
|  | 0 | 0 |  |  |  | 0 |  | 
|  | 0 | 0 |  |  |  | 0 |  | 
|  | 0 | 0 |  |  |  | 0 |  | 
|  | 0 |  |  |  |  | 0 |  | 
|  | 0 |  |  |  |  | 0 |  | 
|  | 0 |  |  |  |  | 0 |  | 
|  | 0 |  |  |  |  | 0 |  | 
|  | 0 |  |  |  |  | 0 |  | 
|  | 0 |  |  |  |  | 0 |  | 
|  | 0 |  |  |  |  | 0 |  | 
|  | 0 |  |  |  |  | 0 |  | 
|  | 0 |  |  |  |  | 0 |  | 
|  | 0 |  |  |  |  | 0 |  | 
|  | 0 |  |  |  |  | 0 |  | 
|  | 0 |  |  |  |  | 0 |  | 
|  | 0 |  |  |  |  | 0 |  | 
|  | 0 |  |  |  |  | 0 |  | 
|  | 0 |  |  |  |  | 0 |  | 
| 728 | 0 |  |  | 0 |  | 0 | { | 
| 729 | 0 |  |  |  |  | 0 | $self->message( 3, "An error occurred while creating URI object for \"$hdrs->{Location}\": $e" ); | 
| 730 | 0 |  |  |  |  | 0 | $self->error( "An error occurred while creating URI object for \"$hdrs->{Location}\": $e" ); | 
| 731 | 0 |  |  |  |  | 0 | return( $rr ); | 
| 732 | 0 | 0 | 0 |  |  | 0 | } | 
|  | 0 | 0 | 0 |  |  | 0 |  | 
|  | 0 | 0 |  |  |  | 0 |  | 
|  | 0 | 0 |  |  |  | 0 |  | 
|  | 0 |  |  |  |  | 0 |  | 
|  | 0 |  |  |  |  | 0 |  | 
|  | 0 |  |  |  |  | 0 |  | 
|  | 0 |  |  |  |  | 0 |  | 
| 733 |  |  |  |  |  |  | } | 
| 734 | 0 |  |  |  |  | 0 | return( $rr ); | 
| 735 |  |  |  |  |  |  | } | 
| 736 |  |  |  |  |  |  |  | 
| 737 |  |  |  |  |  |  | sub make | 
| 738 |  |  |  |  |  |  | { | 
| 739 | 2 |  |  | 2 | 0 | 10 | my $self = shift( @_ ); | 
| 740 | 2 | 50 |  |  |  | 27 | return( $self->error( "Must be called with an existing object and not as ", __PACKAGE__, "->make()" ) ) if( !Scalar::Util::blessed( $self ) ); | 
| 741 | 2 |  |  |  |  | 9 | my $p = {}; | 
| 742 | 2 | 50 | 33 |  |  | 31 | @_ = () if( scalar( @_ ) == 1 && !defined( $_[0] ) ); | 
| 743 | 2 | 50 |  |  |  | 18 | if( scalar( @_ ) ) | 
| 744 |  |  |  |  |  |  | { | 
| 745 | 15 |  |  | 15 |  | 139 | no warnings 'uninitialized'; | 
|  | 15 |  |  |  |  | 38 |  | 
|  | 15 |  |  |  |  | 26174 |  | 
| 746 | 2 | 50 |  |  |  | 37 | $p = Scalar::Util::reftype( $_[0] ) eq 'HASH' | 
|  |  | 50 |  |  |  |  |  | 
| 747 |  |  |  |  |  |  | ? shift( @_ ) | 
| 748 |  |  |  |  |  |  | : !( scalar( @_ ) % 2 ) | 
| 749 |  |  |  |  |  |  | ? { @_ } | 
| 750 |  |  |  |  |  |  | : {}; | 
| 751 |  |  |  |  |  |  | } | 
| 752 | 2 |  |  |  |  | 15 | my $r = $self->apache_request; | 
| 753 | 2 |  |  |  |  | 50 | my $d = $self->document_root; | 
| 754 | 2 |  |  |  |  | 17 | my $b = $self->base_uri; | 
| 755 | 2 |  |  |  |  | 19 | my $f = $self->document_uri; | 
| 756 | 2 | 50 | 33 |  |  | 36 | $p->{apache_request} = $r if( !$p->{apache_request} && $r ); | 
| 757 | 2 | 50 | 33 |  |  | 30 | $p->{document_root} = "$d" if( !$p->{document_root} && length( $d ) ); | 
| 758 | 2 | 50 | 33 |  |  | 21 | $p->{base_uri} = "$b" if( !$p->{base_uri} && length( $b ) ); | 
| 759 | 2 | 50 |  |  |  | 33 | $p->{document_uri} = "$f" if( !$p->{document_uri} ); | 
| 760 | 2 | 50 |  |  |  | 33 | $p->{debug} = $self->debug if( !length( $p->{debug} ) ); | 
| 761 | 2 |  |  | 0 |  | 69 | $self->message( 4, "Creating new file object with parameters: ", sub{ $self->dump( $p ) }); | 
|  | 0 |  |  |  |  | 0 |  | 
| 762 | 2 |  |  |  |  | 57 | return( $self->new( $p ) ); | 
| 763 |  |  |  |  |  |  | } | 
| 764 |  |  |  |  |  |  |  | 
| 765 |  |  |  |  |  |  | sub new_uri | 
| 766 |  |  |  |  |  |  | { | 
| 767 | 338 |  |  | 338 | 1 | 607 | my $self = shift( @_ ); | 
| 768 | 338 |  |  |  |  | 616 | my $class = URI_CLASS; | 
| 769 | 338 |  |  |  |  | 1013 | my $uri = shift( @_ ); | 
| 770 | 338 |  |  |  |  | 1170 | try | 
| 771 | 338 |  |  | 338 |  | 409 | { | 
| 772 | 338 |  |  |  |  | 1126 | return( $class->new( $uri ) ); | 
| 773 |  |  |  |  |  |  | } | 
| 774 | 338 | 100 |  |  |  | 1632 | catch( $e ) | 
|  | 0 | 50 |  |  |  | 0 |  | 
|  | 338 | 50 |  |  |  | 984 |  | 
|  | 338 | 0 |  |  |  | 559 |  | 
|  | 338 | 50 |  |  |  | 698 |  | 
|  | 338 |  |  |  |  | 424 |  | 
|  | 338 |  |  |  |  | 523 |  | 
|  | 338 |  |  |  |  | 552 |  | 
|  | 338 |  |  |  |  | 986 |  | 
|  | 15 |  |  |  |  | 59 |  | 
|  | 323 |  |  |  |  | 574 |  | 
|  | 0 |  |  |  |  | 0 |  | 
|  | 338 |  |  |  |  | 15659 |  | 
|  | 338 |  |  |  |  | 559 |  | 
|  | 338 |  |  |  |  | 695 |  | 
|  | 338 |  |  |  |  | 847 |  | 
|  | 0 |  |  |  |  | 0 |  | 
|  | 0 |  |  |  |  | 0 |  | 
|  | 0 |  |  |  |  | 0 |  | 
|  | 0 |  |  |  |  | 0 |  | 
| 775 | 0 |  |  | 0 |  | 0 | { | 
| 776 | 0 |  |  |  |  | 0 | return( $self->error( "Unable to instantiate an URI object with \"$uri\": $e" ) ); | 
| 777 | 0 | 0 | 33 |  |  | 0 | } | 
|  | 0 | 0 | 33 |  |  | 0 |  | 
|  | 0 | 100 |  |  |  | 0 |  | 
|  | 0 | 50 |  |  |  | 0 |  | 
|  | 0 |  |  |  |  | 0 |  | 
|  | 0 |  |  |  |  | 0 |  | 
|  | 338 |  |  |  |  | 5895 |  | 
|  | 338 |  |  |  |  | 2564 |  | 
| 778 |  |  |  |  |  |  | } | 
| 779 |  |  |  |  |  |  |  | 
| 780 |  |  |  |  |  |  | sub parent | 
| 781 |  |  |  |  |  |  | { | 
| 782 | 2 |  |  | 2 | 1 | 12 | my $self = shift( @_ ); | 
| 783 | 2 |  |  |  |  | 15 | my $path = $self->document_path; | 
| 784 | 2 |  |  |  |  | 14 | my $r = $self->apache_request; | 
| 785 |  |  |  |  |  |  | ## I deliberately did not do split( '/', $path, -1 ) so that if there is a trailing '/', it will not be counted | 
| 786 | 2 |  |  |  |  | 42 | $self->message( 4, "Document path value is '$path' (", overload::StrVal( $path ), ")." ); | 
| 787 | 2 |  |  |  |  | 68 | my @segments = $self->document_path->path_segments; | 
| 788 | 2 |  |  | 0 |  | 146 | $self->message( 4, "Path segments are: ", sub{ $self->dump( \@segments )} ); | 
|  | 0 |  |  |  |  | 0 |  | 
| 789 | 2 |  |  |  |  | 44 | pop( @segments ); | 
| 790 | 2 | 50 |  |  |  | 12 | return( $self ) if( !scalar( @segments ) ); | 
| 791 | 2 |  |  |  |  | 21 | $self->message( 4, "Creating new object with document uri '", join( '/', @segments ), "'." ); | 
| 792 | 2 |  |  |  |  | 45 | return( $self->make( document_uri => join( '/', @segments ) ) ); | 
| 793 |  |  |  |  |  |  | } | 
| 794 |  |  |  |  |  |  |  | 
| 795 |  |  |  |  |  |  | sub path_info | 
| 796 |  |  |  |  |  |  | { | 
| 797 | 10 |  |  | 10 | 1 | 400 | my $self = shift( @_ ); | 
| 798 | 10 |  |  |  |  | 23 | my $class = ref( $self ); | 
| 799 | 10 |  | 100 |  |  | 63 | my $caller = (caller(1))[3] // ''; | 
| 800 |  |  |  |  |  |  | ## my $caller = substr( $sub, rindex( $sub, ':' ) + 1 ); | 
| 801 | 10 |  |  |  |  | 30 | my $r = $self->apache_request; | 
| 802 | 10 | 50 |  |  |  | 177 | if( $r ) | 
| 803 |  |  |  |  |  |  | { | 
| 804 | 0 | 0 |  |  |  | 0 | if( @_ ) | 
| 805 |  |  |  |  |  |  | { | 
| 806 | 0 |  |  |  |  | 0 | $self->message( 3, "Setting path info to '", $_[0], "'." ); | 
| 807 | 0 |  |  |  |  | 0 | $r->path_info( shift( @_ ) ); | 
| 808 | 0 |  |  |  |  | 0 | $self->message( 4, "Path info updated with '", $r->path_info, "'." ); | 
| 809 | 0 |  |  |  |  | 0 | $self->_set_env( PATH_INFO => $r->path_info ); | 
| 810 | 0 | 0 |  |  |  | 0 | $self->{_uri_reset} = 'path_info' unless( $caller eq "${class}\::document_uri" ); | 
| 811 |  |  |  |  |  |  | } | 
| 812 | 0 |  |  |  |  | 0 | $self->message( 4, "Returning path info '", $r->path_info, "'." ); | 
| 813 | 0 |  |  |  |  | 0 | return( $r->path_info ); | 
| 814 |  |  |  |  |  |  | } | 
| 815 |  |  |  |  |  |  | else | 
| 816 |  |  |  |  |  |  | { | 
| 817 | 10 | 100 |  |  |  | 29 | if( @_ ) | 
| 818 |  |  |  |  |  |  | { | 
| 819 | 4 |  |  |  |  | 21 | $self->message( 3, "Setting path info to '", $_[0], "'." ); | 
| 820 | 4 |  |  |  |  | 79 | $self->{path_info} = shift( @_ ); | 
| 821 | 4 |  |  |  |  | 20 | $self->message( 4, "Path info updated with '", $self->{path_info}, "'." ); | 
| 822 | 4 |  |  |  |  | 72 | $self->_set_env( PATH_INFO => $self->{path_info} ); | 
| 823 | 4 | 100 |  |  |  | 23 | $self->{_uri_reset} = 'path_info' unless( $caller eq "${class}\::document_uri" ); | 
| 824 |  |  |  |  |  |  | } | 
| 825 | 10 |  |  |  |  | 35 | return( $self->{path_info} ); | 
| 826 |  |  |  |  |  |  | } | 
| 827 |  |  |  |  |  |  | } | 
| 828 |  |  |  |  |  |  |  | 
| 829 |  |  |  |  |  |  | sub query_string | 
| 830 |  |  |  |  |  |  | { | 
| 831 | 34 |  |  | 34 | 1 | 837 | my $self = shift( @_ ); | 
| 832 | 34 |  |  |  |  | 96 | my $class = ref( $self ); | 
| 833 | 34 |  | 100 |  |  | 241 | my $caller = (caller(1))[3] // ''; | 
| 834 |  |  |  |  |  |  | ## my $caller = substr( $sub, rindex( $sub, ':' ) + 1 ); | 
| 835 | 34 |  |  |  |  | 117 | my $r = $self->apache_request; | 
| 836 | 34 | 50 |  |  |  | 598 | if( $r ) | 
| 837 |  |  |  |  |  |  | { | 
| 838 | 0 | 0 |  |  |  | 0 | if( @_ ) | 
| 839 |  |  |  |  |  |  | { | 
| 840 | 0 |  |  |  |  | 0 | my $qs = shift( @_ ); | 
| 841 | 0 |  |  |  |  | 0 | $self->message( 3, "Setting query string to '$qs'." ); | 
| 842 | 0 |  |  |  |  | 0 | $r->args( $qs ); | 
| 843 | 0 |  |  |  |  | 0 | $self->message( 4, "Query string is now '", scalar( $r->args ), "'." ); | 
| 844 | 0 |  |  |  |  | 0 | $self->_set_env( QUERY_STRING => $qs ); | 
| 845 | 0 | 0 |  |  |  | 0 | $self->{_uri_reset} = 'query_string' unless( $caller eq "${class}\::document_uri" ); | 
| 846 |  |  |  |  |  |  | } | 
| 847 | 0 |  |  |  |  | 0 | return( $r->args ); | 
| 848 |  |  |  |  |  |  | } | 
| 849 |  |  |  |  |  |  | else | 
| 850 |  |  |  |  |  |  | { | 
| 851 | 34 | 100 |  |  |  | 125 | if( @_ ) | 
| 852 |  |  |  |  |  |  | { | 
| 853 | 22 |  |  |  |  | 105 | $self->message( 3, "Setting query string to '", $_[0], "'." ); | 
| 854 | 22 |  |  |  |  | 361 | $self->{query_string} = shift( @_ ); | 
| 855 | 22 |  |  |  |  | 105 | $self->message( 4, "Query string is now '", $self->{query_string}, "'." ); | 
| 856 | 22 |  |  |  |  | 386 | $self->_set_env( QUERY_STRING => $self->{query_string} ); | 
| 857 | 22 | 100 |  |  |  | 122 | $self->{_uri_reset} = 'query_string' unless( $caller eq "${class}\::document_uri" ); | 
| 858 |  |  |  |  |  |  | } | 
| 859 | 34 |  |  |  |  | 124 | return( $self->{query_string} ); | 
| 860 |  |  |  |  |  |  | } | 
| 861 |  |  |  |  |  |  | } | 
| 862 |  |  |  |  |  |  |  | 
| 863 |  |  |  |  |  |  | sub root | 
| 864 |  |  |  |  |  |  | { | 
| 865 | 30 |  |  | 30 | 1 | 92 | my $self = shift( @_ ); | 
| 866 | 30 | 100 |  |  |  | 256 | return( $self->{root} ) if( $self->{root} ); | 
| 867 | 15 |  |  |  |  | 98 | my $hash = | 
| 868 |  |  |  |  |  |  | { | 
| 869 |  |  |  |  |  |  | code => 200, | 
| 870 |  |  |  |  |  |  | document_uri => $self->new_uri( '/' ), | 
| 871 |  |  |  |  |  |  | document_root => $self->document_root, | 
| 872 |  |  |  |  |  |  | debug => $self->debug, | 
| 873 |  |  |  |  |  |  | path_info => '', | 
| 874 |  |  |  |  |  |  | query_string => '', | 
| 875 |  |  |  |  |  |  | _path_info_processed => 1, | 
| 876 |  |  |  |  |  |  | }; | 
| 877 | 15 |  |  |  |  | 528 | $hash->{document_path} = $hash->{document_uri}; | 
| 878 | 15 | 50 |  |  |  | 64 | $hash->{apache_request} = $self->apache_request if( $self->apache_request ); | 
| 879 | 15 |  |  |  |  | 339 | my $root = bless( $hash => ref( $self ) ); | 
| 880 |  |  |  |  |  |  | # Scalar::Util::weaken( $copy ); | 
| 881 | 15 |  |  |  |  | 100 | $root->{base_dir} = $root; | 
| 882 | 15 |  |  |  |  | 58 | $root->{base_uri} = $root; | 
| 883 | 15 |  |  |  |  | 69 | $self->{root} = $root; | 
| 884 | 15 |  |  |  |  | 106 | return( $root ); | 
| 885 |  |  |  |  |  |  | } | 
| 886 |  |  |  |  |  |  |  | 
| 887 |  |  |  |  |  |  | # shortcut | 
| 888 | 1 |  |  | 1 | 0 | 399 | sub uri { return( shift->document_uri( @_ ) ); } | 
| 889 |  |  |  |  |  |  |  | 
| 890 |  |  |  |  |  |  | ## Path info works as a path added to a document uri, such as: | 
| 891 |  |  |  |  |  |  | ## /my/doc.html/path/info | 
| 892 |  |  |  |  |  |  | ## But we need to distinguish with missing document hierarchy inside a directory, such as: | 
| 893 |  |  |  |  |  |  | ## /my/folder/missing_doc.html/path/info | 
| 894 |  |  |  |  |  |  | ## otherwise we would be treating /missing_doc.html/path/info as a path info | 
| 895 |  |  |  |  |  |  | sub _find_path_info | 
| 896 |  |  |  |  |  |  | { | 
| 897 | 161 |  |  | 161 |  | 580 | my $self = shift( @_ ); | 
| 898 | 161 |  |  |  |  | 469 | my( $path, $doc_root ) = @_; | 
| 899 | 161 |  | 33 |  |  | 876 | $doc_root //= $self->document_root; | 
| 900 | 161 |  |  |  |  | 396 | my $qs = ''; | 
| 901 | 161 | 50 | 66 |  |  | 1060 | if( Scalar::Util::blessed( $path ) && $path->isa( 'URI::file' ) ) | 
| 902 |  |  |  |  |  |  | { | 
| 903 | 0 |  |  |  |  | 0 | $path = $path->file; | 
| 904 |  |  |  |  |  |  | } | 
| 905 | 161 |  |  |  |  | 1104 | my $u = $self->collapse_dots( $path ); | 
| 906 | 161 |  |  |  |  | 950 | $qs = $u->query; | 
| 907 | 161 |  |  |  |  | 2292 | $path = $u->path; | 
| 908 | 161 | 50 | 33 |  |  | 2039 | $doc_root = $doc_root->file if( Scalar::Util::blessed( $doc_root ) && $doc_root->isa( 'URI::file' ) ); | 
| 909 | 161 | 50 |  |  |  | 615 | $doc_root = substr( $doc_root, 0, length( $doc_root ) - 1 ) if( substr( $doc_root, -1, 1 ) eq '/' ); | 
| 910 | 161 |  |  |  |  | 1026 | $self->message( 4, "Document root is '$doc_root' and path is '$path'" ); | 
| 911 | 161 | 50 |  |  |  | 3471 | return( $self->error( "Path must be an absolute path starting with '/'. Path provided was \"$path\"." ) ) if( substr( $path, 0, 1 ) ne '/' ); | 
| 912 |  |  |  |  |  |  | ## No need to go further | 
| 913 | 161 | 100 |  |  |  | 5685 | if( -e( "${doc_root}${path}" ) ) | 
|  |  | 50 |  |  |  |  |  | 
| 914 |  |  |  |  |  |  | { | 
| 915 |  |  |  |  |  |  | return({ | 
| 916 | 150 |  |  |  |  | 2082 | filepath => "${doc_root}${path}", | 
| 917 |  |  |  |  |  |  | path => $path, | 
| 918 |  |  |  |  |  |  | query_string => $qs, | 
| 919 |  |  |  |  |  |  | code => 200, | 
| 920 |  |  |  |  |  |  | }); | 
| 921 |  |  |  |  |  |  | } | 
| 922 |  |  |  |  |  |  | elsif( $path eq '/' ) | 
| 923 |  |  |  |  |  |  | { | 
| 924 |  |  |  |  |  |  | return({ | 
| 925 | 0 | 0 |  |  |  | 0 | filepath => $doc_root, | 
| 926 |  |  |  |  |  |  | path => $path, | 
| 927 |  |  |  |  |  |  | path_info => undef(), | 
| 928 |  |  |  |  |  |  | query_string => $qs, | 
| 929 |  |  |  |  |  |  | code => ( -e( $doc_root ) ? 200 : 404 ), | 
| 930 |  |  |  |  |  |  | }); | 
| 931 |  |  |  |  |  |  | } | 
| 932 | 11 |  |  |  |  | 99 | my @parts = split( '/', substr( $path, 1 ) ); | 
| 933 | 11 |  |  | 0 |  | 178 | $self->message( 4, "Document root is '$doc_root' and parts contains: ", sub{ $self->dump( \@parts ) } ); | 
|  | 0 |  |  |  |  | 0 |  | 
| 934 | 11 |  |  |  |  | 252 | my $trypath = ''; | 
| 935 | 11 |  |  |  |  | 35 | my $pathinfo = ''; | 
| 936 | 11 |  |  |  |  | 51 | foreach my $p ( @parts ) | 
| 937 |  |  |  |  |  |  | { | 
| 938 | 20 | 50 |  |  |  | 166 | $self->message( 4, "Checking path '$trypath/$p'" ) unless( $pathinfo ); | 
| 939 |  |  |  |  |  |  | ## The last path was a directory, and we cannot find the element within. So, the rest of the path is not path info, but rather a 404 missing document hierarchy | 
| 940 |  |  |  |  |  |  | ## We test the $pathinfo string, so we do not bother checking further if it is already set. | 
| 941 | 20 | 100 | 66 |  |  | 1108 | if( !$pathinfo && -d( "${doc_root}${trypath}" ) && !-e( "${doc_root}${trypath}/${p}" ) ) | 
|  |  | 100 | 100 |  |  |  |  | 
|  |  |  | 66 |  |  |  |  | 
| 942 |  |  |  |  |  |  | { | 
| 943 | 7 |  |  |  |  | 96 | $self->message( 4, "Document $p is not found inside directory ${doc_root}${trypath}" ); | 
| 944 |  |  |  |  |  |  | ## We return the original path provided (minus any query string) | 
| 945 |  |  |  |  |  |  | return({ | 
| 946 | 7 | 100 |  |  |  | 296 | filepath => $doc_root . ( length( $trypath ) ? $trypath :  $path ), | 
| 947 |  |  |  |  |  |  | path => $path, | 
| 948 |  |  |  |  |  |  | code => 404, | 
| 949 |  |  |  |  |  |  | query_string => $qs, | 
| 950 |  |  |  |  |  |  | }); | 
| 951 |  |  |  |  |  |  | } | 
| 952 |  |  |  |  |  |  | elsif( !$pathinfo && -e( "${doc_root}${trypath}/${p}" ) ) | 
| 953 |  |  |  |  |  |  | { | 
| 954 | 9 |  |  |  |  | 60 | $self->message( 4, "ok, path ${trypath}/${p} exists." ); | 
| 955 | 9 |  |  |  |  | 188 | $trypath .= "/$p"; | 
| 956 |  |  |  |  |  |  | } | 
| 957 |  |  |  |  |  |  | else | 
| 958 |  |  |  |  |  |  | { | 
| 959 | 4 | 50 |  |  |  | 27 | $self->message( 4, "nope, this path $trypath does not exist." ) if( !$pathinfo ); | 
| 960 | 4 |  |  |  |  | 72 | $pathinfo .= "/$p"; | 
| 961 | 4 |  |  |  |  | 12 | $self->message( 4, "Path info is now: '$pathinfo'." ); | 
| 962 |  |  |  |  |  |  | } | 
| 963 |  |  |  |  |  |  | } | 
| 964 | 4 |  |  |  |  | 65 | $self->message( 4, "Real path: $trypath, path info: $pathinfo" ); | 
| 965 |  |  |  |  |  |  | return({ | 
| 966 | 4 |  |  |  |  | 86 | filepath => "${doc_root}${trypath}", | 
| 967 |  |  |  |  |  |  | path => $trypath, | 
| 968 |  |  |  |  |  |  | path_info => $pathinfo, | 
| 969 |  |  |  |  |  |  | code => 200, | 
| 970 |  |  |  |  |  |  | query_string => $qs, | 
| 971 |  |  |  |  |  |  | }); | 
| 972 |  |  |  |  |  |  | } | 
| 973 |  |  |  |  |  |  |  | 
| 974 |  |  |  |  |  |  | # *_set_env = \&Apache2::SSI::_set_env; | 
| 975 |  |  |  |  |  |  | ## This is different from the env() method. This one is obviously private | 
| 976 |  |  |  |  |  |  | ## whereas the env() one has triggers that could otherwise create an infinite loop. | 
| 977 |  |  |  |  |  |  | sub _set_env | 
| 978 |  |  |  |  |  |  | { | 
| 979 | 447 |  |  | 447 |  | 726 | my $self = shift( @_ ); | 
| 980 | 447 |  |  |  |  | 825 | my $name = shift( @_ ); | 
| 981 | 447 | 50 |  |  |  | 990 | return( $self->error( "No environment variable name provided." ) ) if( !length( $name ) ); | 
| 982 | 447 | 100 |  |  |  | 1442 | $self->{_env} = {} if( !ref( $self->{_env} ) ); | 
| 983 | 447 |  |  |  |  | 720 | my $env = $self->{_env}; | 
| 984 | 447 |  |  |  |  | 958 | my $r = $self->apache_request; | 
| 985 | 447 | 50 |  |  |  | 7246 | if( @_ ) | 
| 986 |  |  |  |  |  |  | { | 
| 987 | 447 |  |  |  |  | 686 | my $v = shift( @_ ); | 
| 988 | 447 | 50 |  |  |  | 937 | $r->subprocess_env( $name => $v ) if( $r ); | 
| 989 | 447 |  |  |  |  | 1137 | $env->{ $name } = $v; | 
| 990 |  |  |  |  |  |  | } | 
| 991 | 447 |  |  |  |  | 706 | return( $self ); | 
| 992 |  |  |  |  |  |  | } | 
| 993 |  |  |  |  |  |  |  | 
| 994 |  |  |  |  |  |  | sub _trim_trailing_slash | 
| 995 |  |  |  |  |  |  | { | 
| 996 | 0 |  |  | 0 |  |  | my $self = shift( @_ ); | 
| 997 | 0 |  |  |  |  |  | my $uri  = shift( @_ ); | 
| 998 | 0 | 0 |  |  |  |  | return( $self->error( "No uri provided to trim trailing slash." ) ) if( !length( "$uri" ) ); | 
| 999 | 0 | 0 | 0 |  |  |  | unless( Scalar::Util::blessed( $uri ) && $uri->isa( 'URI' ) ) | 
| 1000 |  |  |  |  |  |  | { | 
| 1001 | 0 |  |  |  |  |  | $uri = $self->new_uri( "$uri" ); | 
| 1002 |  |  |  |  |  |  | } | 
| 1003 | 0 | 0 | 0 |  |  |  | if( substr( $uri->path, -1, 1 ) eq '/' && length( $uri->path ) > 1 ) | 
| 1004 |  |  |  |  |  |  | { | 
| 1005 |  |  |  |  |  |  | ## By splitting the string on '/' and without the last argument for split being -1, perl will remove trailing blank entries | 
| 1006 | 0 |  |  |  |  |  | $uri->path( join( '/', split( '/', $uri->path ) ) ); | 
| 1007 |  |  |  |  |  |  | } | 
| 1008 | 0 |  |  |  |  |  | $self->message( 3, "Returning uri object '", overload::StrVal( $uri ), "' with value '$uri'." ); | 
| 1009 | 0 |  |  |  |  |  | return( $uri ); | 
| 1010 |  |  |  |  |  |  | } | 
| 1011 |  |  |  |  |  |  |  | 
| 1012 |  |  |  |  |  |  | 1; | 
| 1013 |  |  |  |  |  |  |  | 
| 1014 |  |  |  |  |  |  | __END__ | 
| 1015 |  |  |  |  |  |  |  | 
| 1016 |  |  |  |  |  |  | =encoding utf-8 | 
| 1017 |  |  |  |  |  |  |  | 
| 1018 |  |  |  |  |  |  | =head1 NAME | 
| 1019 |  |  |  |  |  |  |  | 
| 1020 |  |  |  |  |  |  | Apache2::SSI::URI - Apache2 Server Side Include URI Object Class | 
| 1021 |  |  |  |  |  |  |  | 
| 1022 |  |  |  |  |  |  | =head1 SYNOPSIS | 
| 1023 |  |  |  |  |  |  |  | 
| 1024 |  |  |  |  |  |  | # if the global option PerlOptions +GlobalRequest is set in your VirtualHost | 
| 1025 |  |  |  |  |  |  | my $r = Apache2::RequestUtil->request | 
| 1026 |  |  |  |  |  |  | my $uri = Apache2::SSI::URI->new( | 
| 1027 |  |  |  |  |  |  | apache_request => $r, | 
| 1028 |  |  |  |  |  |  | document_uri => '/some/uri/file.html', | 
| 1029 |  |  |  |  |  |  | document_root => '/home/john/www', | 
| 1030 |  |  |  |  |  |  | base_uri => '/', | 
| 1031 |  |  |  |  |  |  | ) || die( "Unable to create an Apache2::SSI::URI object: ", Apache2::SSI::URI->error ); | 
| 1032 |  |  |  |  |  |  |  | 
| 1033 |  |  |  |  |  |  | unless( $uri->code == Apache2::Const::HTTP_OK ) | 
| 1034 |  |  |  |  |  |  | { | 
| 1035 |  |  |  |  |  |  | die( "Sorry, the uri does not exist.\n" ); | 
| 1036 |  |  |  |  |  |  | } | 
| 1037 |  |  |  |  |  |  | print( $uri->slurp_utf8 ); | 
| 1038 |  |  |  |  |  |  |  | 
| 1039 |  |  |  |  |  |  | # Changing the base uri, which is used to resolve relative uri | 
| 1040 |  |  |  |  |  |  | $uri->base_uri( '/ssi' ); | 
| 1041 |  |  |  |  |  |  |  | 
| 1042 |  |  |  |  |  |  | my $uri2 = $uri->clone; | 
| 1043 |  |  |  |  |  |  | $uri2->filename( '/home/john/some-file.txt' ); | 
| 1044 |  |  |  |  |  |  | die( "No such file\n" ) if( $uri2->finfo->filetype == Apache2::SSI::Finfo::FILETYPE_NOFILE ); | 
| 1045 |  |  |  |  |  |  |  | 
| 1046 |  |  |  |  |  |  | my $dir = $uri->document_directory; | 
| 1047 |  |  |  |  |  |  |  | 
| 1048 |  |  |  |  |  |  | # Full path to the filename, e.g. /home/john/www/some/dir/file.html | 
| 1049 |  |  |  |  |  |  | # Possible dots are resolved /home/john/www/some/dir/../ssi/../dir/./file.html => /home/john/www/some/dir/file.html | 
| 1050 |  |  |  |  |  |  | my $filename = $uri->document_filename; | 
| 1051 |  |  |  |  |  |  |  | 
| 1052 |  |  |  |  |  |  | # The uri without path info or query string | 
| 1053 |  |  |  |  |  |  | my $path = $uri->document_path; | 
| 1054 |  |  |  |  |  |  |  | 
| 1055 |  |  |  |  |  |  | my $doc_root = $uri->document_root; | 
| 1056 |  |  |  |  |  |  |  | 
| 1057 |  |  |  |  |  |  | # The document uri including path info, and query string if any | 
| 1058 |  |  |  |  |  |  | my $u = $uri->document_uri; | 
| 1059 |  |  |  |  |  |  |  | 
| 1060 |  |  |  |  |  |  | my $req_uri = $uri->env( 'REQUEST_URI' ); | 
| 1061 |  |  |  |  |  |  |  | 
| 1062 |  |  |  |  |  |  | # Access to the Apache2::SSI::Finfo object | 
| 1063 |  |  |  |  |  |  | my $finfo = $uri->finfo; | 
| 1064 |  |  |  |  |  |  |  | 
| 1065 |  |  |  |  |  |  | # A new Apache2::SSI::URI object | 
| 1066 |  |  |  |  |  |  | my $uri3 = $uri->new_uri( document_uri => '/some/where/about.html', document_root => '/home/john/www' ); | 
| 1067 |  |  |  |  |  |  |  | 
| 1068 |  |  |  |  |  |  | # Returns /some/uri | 
| 1069 |  |  |  |  |  |  | my $parent = $uri->parent; | 
| 1070 |  |  |  |  |  |  |  | 
| 1071 |  |  |  |  |  |  | # The uri is now /some/uri/file.html/some/path | 
| 1072 |  |  |  |  |  |  | $uri->path_info( '/some/path' ); | 
| 1073 |  |  |  |  |  |  |  | 
| 1074 |  |  |  |  |  |  | # The uri is now /some/uri/file.html/some/path?q=something&l=ja_JP | 
| 1075 |  |  |  |  |  |  | $uri->query_string( 'q=something&l=ja_JP' ); | 
| 1076 |  |  |  |  |  |  |  | 
| 1077 |  |  |  |  |  |  | my $html = $uri->slurp_utf8; | 
| 1078 |  |  |  |  |  |  | my $raw = $uri->slurp({ binmode => ':raw' }); | 
| 1079 |  |  |  |  |  |  |  | 
| 1080 |  |  |  |  |  |  | # Same as $uri->document_uri | 
| 1081 |  |  |  |  |  |  | my $uri = $uri->uri; | 
| 1082 |  |  |  |  |  |  |  | 
| 1083 |  |  |  |  |  |  | =head1 VERSION | 
| 1084 |  |  |  |  |  |  |  | 
| 1085 |  |  |  |  |  |  | v0.1.1 | 
| 1086 |  |  |  |  |  |  |  | 
| 1087 |  |  |  |  |  |  | =head1 DESCRIPTION | 
| 1088 |  |  |  |  |  |  |  | 
| 1089 |  |  |  |  |  |  | L<Apache2::SSI::URI> is used to manipulate and query http uri. It is used by L<Apache2::SSI> both for the main query, and also for sub queries like when there is an C<include> directive. | 
| 1090 |  |  |  |  |  |  |  | 
| 1091 |  |  |  |  |  |  | In this case, there would be the main document uri such as C</some/path/file.html> and containing a directive such as: | 
| 1092 |  |  |  |  |  |  |  | 
| 1093 |  |  |  |  |  |  | <!--#include virtual="../other.html" --> | 
| 1094 |  |  |  |  |  |  |  | 
| 1095 |  |  |  |  |  |  | An L<Apache2::SSI::URI> object would be instantiated to process the uri C<../other.html>, flatten the dots and get its underlying filename. | 
| 1096 |  |  |  |  |  |  |  | 
| 1097 |  |  |  |  |  |  | Even if the uri provided does not exist, am L<Apache2::SSI::URI> object would still be returned, so you need to check if the file exists by doing: | 
| 1098 |  |  |  |  |  |  |  | 
| 1099 |  |  |  |  |  |  | if( $uri->code == 404 ) | 
| 1100 |  |  |  |  |  |  | { | 
| 1101 |  |  |  |  |  |  | die( "Not there\n" ); | 
| 1102 |  |  |  |  |  |  | } | 
| 1103 |  |  |  |  |  |  |  | 
| 1104 |  |  |  |  |  |  | Or, this would work too: | 
| 1105 |  |  |  |  |  |  |  | 
| 1106 |  |  |  |  |  |  | if( $uri->finfo->filetype == Apache2::SSI::Finfo::FILETYPE_NOFILE ) | 
| 1107 |  |  |  |  |  |  | { | 
| 1108 |  |  |  |  |  |  | die( "No such file !\n" ); | 
| 1109 |  |  |  |  |  |  | } | 
| 1110 |  |  |  |  |  |  |  | 
| 1111 |  |  |  |  |  |  | =head1 METHODS | 
| 1112 |  |  |  |  |  |  |  | 
| 1113 |  |  |  |  |  |  | =head2 new | 
| 1114 |  |  |  |  |  |  |  | 
| 1115 |  |  |  |  |  |  | This instantiate an object that is used to access other key methods. It takes the following parameters: | 
| 1116 |  |  |  |  |  |  |  | 
| 1117 |  |  |  |  |  |  | =over 4 | 
| 1118 |  |  |  |  |  |  |  | 
| 1119 |  |  |  |  |  |  | =item I<apache_request> | 
| 1120 |  |  |  |  |  |  |  | 
| 1121 |  |  |  |  |  |  | This is the L<Apache2::RequestRec> object that is provided if running under mod_perl. | 
| 1122 |  |  |  |  |  |  |  | 
| 1123 |  |  |  |  |  |  | it can be retrieved from L<Apache2::RequestUtil/request> or via L<Apache2::Filter/r> | 
| 1124 |  |  |  |  |  |  |  | 
| 1125 |  |  |  |  |  |  | You can get this L<Apache2::RequestRec> object by requiring L<Apache2::RequestUtil> and calling its class method L<Apache2::RequestUtil/request> such as C<Apache2::RequestUtil->request> and assuming you have set C<PerlOptions +GlobalRequest> in your Apache Virtual Host configuration. | 
| 1126 |  |  |  |  |  |  |  | 
| 1127 |  |  |  |  |  |  | Note that there is a main request object and subprocess request object, so to find out which one you are dealing with, use L<Apache2::RequestUtil/is_initial_req>, such as: | 
| 1128 |  |  |  |  |  |  |  | 
| 1129 |  |  |  |  |  |  | use Apache2::RequestUtil (); # extends Apache2::RequestRec objects | 
| 1130 |  |  |  |  |  |  | my $r = $r->is_initial_req ? $r : $r->main; | 
| 1131 |  |  |  |  |  |  |  | 
| 1132 |  |  |  |  |  |  | =item I<base_uri> | 
| 1133 |  |  |  |  |  |  |  | 
| 1134 |  |  |  |  |  |  | This is the base uri which is used to make uri absolute. | 
| 1135 |  |  |  |  |  |  |  | 
| 1136 |  |  |  |  |  |  | For example, if the main document uri is C</some/folder/file.html> containing a directive: | 
| 1137 |  |  |  |  |  |  |  | 
| 1138 |  |  |  |  |  |  | <!--#include virtual="../other.html" --> | 
| 1139 |  |  |  |  |  |  |  | 
| 1140 |  |  |  |  |  |  | One would instantiate an object using C</some/folder/file.html> as the base_uri like this: | 
| 1141 |  |  |  |  |  |  |  | 
| 1142 |  |  |  |  |  |  | my $uri = Apache2::SSI::URI->new( | 
| 1143 |  |  |  |  |  |  | base_uri => '/some/folder/file.html', | 
| 1144 |  |  |  |  |  |  | apache_request => $r, | 
| 1145 |  |  |  |  |  |  | document_uri => '../other.html', | 
| 1146 |  |  |  |  |  |  | # No need to specify document_root, because it will be derived from | 
| 1147 |  |  |  |  |  |  | # the Apache2::RequestRec provided with the apache_request parameter. | 
| 1148 |  |  |  |  |  |  | ); | 
| 1149 |  |  |  |  |  |  |  | 
| 1150 |  |  |  |  |  |  | =item I<document_root> | 
| 1151 |  |  |  |  |  |  |  | 
| 1152 |  |  |  |  |  |  | This is only necessary to be provided if this is not running under Apache mod_perl. Without this value, L<Apache2::SSI> has no way to guess the document root and will not be able to function properly and will return an L</error>. | 
| 1153 |  |  |  |  |  |  |  | 
| 1154 |  |  |  |  |  |  | =item I<document_uri> | 
| 1155 |  |  |  |  |  |  |  | 
| 1156 |  |  |  |  |  |  | This is only necessary to be provided if this is not running under Apache mod_perl. This must be the uri of the document being served, such as C</my/path/index.html>. So, if you are using this outside of the rim of Apache mod_perl and your file resides, for example, at C</home/john/www/my/path/index.html> and your document root is C</home/john/www>, then the document uri would be C</my/path/index.html> | 
| 1157 |  |  |  |  |  |  |  | 
| 1158 |  |  |  |  |  |  | =back | 
| 1159 |  |  |  |  |  |  |  | 
| 1160 |  |  |  |  |  |  | =head2 apache_request | 
| 1161 |  |  |  |  |  |  |  | 
| 1162 |  |  |  |  |  |  | Sets or gets the L<Apache2::RequestRec> object. As explained in the L</new> method, you can get this Apache object by requiring the package L<Apache2::RequestUtil> and calling L<Apache2::RequestUtil/request> such as C<Apache2::RequestUtil->request> assuming you have set C<PerlOptions +GlobalRequest> in your Apache Virtual Host configuration. | 
| 1163 |  |  |  |  |  |  |  | 
| 1164 |  |  |  |  |  |  | When running under Apache mod_perl this is set automatically from the special L</handler> method, such as: | 
| 1165 |  |  |  |  |  |  |  | 
| 1166 |  |  |  |  |  |  | my $r = $f->r; # $f is the Apache2::Filter object provided by Apache | 
| 1167 |  |  |  |  |  |  |  | 
| 1168 |  |  |  |  |  |  | =head2 base_uri | 
| 1169 |  |  |  |  |  |  |  | 
| 1170 |  |  |  |  |  |  | Sets or gets the base reference uri. This is used to render the L</document_uri> provided an absolute uri. | 
| 1171 |  |  |  |  |  |  |  | 
| 1172 |  |  |  |  |  |  | =head2 clone | 
| 1173 |  |  |  |  |  |  |  | 
| 1174 |  |  |  |  |  |  | Create a clone of the object and return it. | 
| 1175 |  |  |  |  |  |  |  | 
| 1176 |  |  |  |  |  |  | =head2 code | 
| 1177 |  |  |  |  |  |  |  | 
| 1178 |  |  |  |  |  |  | Sets or gets the http code for this uri. | 
| 1179 |  |  |  |  |  |  |  | 
| 1180 |  |  |  |  |  |  | $uri->code( 404 ); | 
| 1181 |  |  |  |  |  |  |  | 
| 1182 |  |  |  |  |  |  | =head2 collapse_dots | 
| 1183 |  |  |  |  |  |  |  | 
| 1184 |  |  |  |  |  |  | Provided with an uri, and this will resolve the path and removing the dots, such as C<.> and C<..> and return an L<URI> object. | 
| 1185 |  |  |  |  |  |  |  | 
| 1186 |  |  |  |  |  |  | This is done as per the L<RFC 3986 section 5.2.4 algorithm|https://tools.ietf.org/html/rfc3986#page-33> | 
| 1187 |  |  |  |  |  |  |  | 
| 1188 |  |  |  |  |  |  | my $uri = $ssi->collapse_dots( '/../a/b/../c/./d.html' ); | 
| 1189 |  |  |  |  |  |  | # would become /a/c/d.html | 
| 1190 |  |  |  |  |  |  | my $uri = $ssi->collapse_dots( '/../a/b/../c/./d.html?foo=../bar' ); | 
| 1191 |  |  |  |  |  |  | # would become /a/c/d.html?foo=../bar | 
| 1192 |  |  |  |  |  |  | $uri->query # foo=../bar | 
| 1193 |  |  |  |  |  |  |  | 
| 1194 |  |  |  |  |  |  | =head2 document_directory | 
| 1195 |  |  |  |  |  |  |  | 
| 1196 |  |  |  |  |  |  | Returns an L<Apache2::SSI::URI> object of the current directory of the L</document_uri> provided. | 
| 1197 |  |  |  |  |  |  |  | 
| 1198 |  |  |  |  |  |  | This can also be called as C<$uri->document_dir> | 
| 1199 |  |  |  |  |  |  |  | 
| 1200 |  |  |  |  |  |  | =head2 document_filename | 
| 1201 |  |  |  |  |  |  |  | 
| 1202 |  |  |  |  |  |  | This is an alias for L<Apache2::SSI::URI/filename> | 
| 1203 |  |  |  |  |  |  |  | 
| 1204 |  |  |  |  |  |  | =head2 document_path | 
| 1205 |  |  |  |  |  |  |  | 
| 1206 |  |  |  |  |  |  | Sets or gets the uri path to the document. This is the same as L</document_uri>, except it is striped from L</query_string> and L</path_info>. | 
| 1207 |  |  |  |  |  |  |  | 
| 1208 |  |  |  |  |  |  | =head2 document_root | 
| 1209 |  |  |  |  |  |  |  | 
| 1210 |  |  |  |  |  |  | Sets or gets the document root. | 
| 1211 |  |  |  |  |  |  |  | 
| 1212 |  |  |  |  |  |  | Wen running under Apache mod_perl, this value will be available automatically, using L<Apache2::RequestRec/document_root> method. | 
| 1213 |  |  |  |  |  |  |  | 
| 1214 |  |  |  |  |  |  | If it runs outside of Apache, this will use the value provided upon instantiating the object and passing the I<document_root> parameter. If this is not set, it will return the value of the environment variable C<DOCUMENT_ROOT>. | 
| 1215 |  |  |  |  |  |  |  | 
| 1216 |  |  |  |  |  |  | =head2 document_uri | 
| 1217 |  |  |  |  |  |  |  | 
| 1218 |  |  |  |  |  |  | Sets or gets the document uri, which is the uri of the document being processed. | 
| 1219 |  |  |  |  |  |  |  | 
| 1220 |  |  |  |  |  |  | For example: | 
| 1221 |  |  |  |  |  |  |  | 
| 1222 |  |  |  |  |  |  | /index.html | 
| 1223 |  |  |  |  |  |  |  | 
| 1224 |  |  |  |  |  |  | Under Apache, this will get the environment variable C<DOCUMENT_URI> or calls the L<Apache2::RequestRec/uri> method. | 
| 1225 |  |  |  |  |  |  |  | 
| 1226 |  |  |  |  |  |  | Outside of Apache, this will rely on a value being provided upon instantiating an object, or the environment variable C<DOCUMENT_URI> be present. | 
| 1227 |  |  |  |  |  |  |  | 
| 1228 |  |  |  |  |  |  | The value should be an absolute uri. | 
| 1229 |  |  |  |  |  |  |  | 
| 1230 |  |  |  |  |  |  | =head2 env | 
| 1231 |  |  |  |  |  |  |  | 
| 1232 |  |  |  |  |  |  | Sets or gets environment variables that are distinct for this uri. | 
| 1233 |  |  |  |  |  |  |  | 
| 1234 |  |  |  |  |  |  | $uri->env( REQUEST_URI => '/some/path/file.html' ); | 
| 1235 |  |  |  |  |  |  | my $loc = $uri->env( 'REQUEST_URI' ); | 
| 1236 |  |  |  |  |  |  |  | 
| 1237 |  |  |  |  |  |  | If it is called without any parameters, it returns all the environment variables as a hash reference: | 
| 1238 |  |  |  |  |  |  |  | 
| 1239 |  |  |  |  |  |  | my $all_env = $uri->env; | 
| 1240 |  |  |  |  |  |  | print $all_env->{REQUEST_URI}; | 
| 1241 |  |  |  |  |  |  |  | 
| 1242 |  |  |  |  |  |  | Setting an environment variable using L</env> does not actually populate it. So this would not work: | 
| 1243 |  |  |  |  |  |  |  | 
| 1244 |  |  |  |  |  |  | $uri->env( REQUEST_URI => '/some/path/file.html' ); | 
| 1245 |  |  |  |  |  |  | print( $ENV{REQUEST_URI}; | 
| 1246 |  |  |  |  |  |  |  | 
| 1247 |  |  |  |  |  |  | It is the equivalent of L<Apache2::RequestRec/subprocess_env>. Actually it uses L<Apache2::RequestRec/subprocess_env> if running under Apache/mod_perl, other wise it uses a private hash reference to store the values. | 
| 1248 |  |  |  |  |  |  |  | 
| 1249 |  |  |  |  |  |  | =head2 filename | 
| 1250 |  |  |  |  |  |  |  | 
| 1251 |  |  |  |  |  |  | This returns the system file path to the document uri as a string. | 
| 1252 |  |  |  |  |  |  |  | 
| 1253 |  |  |  |  |  |  | =head2 finfo | 
| 1254 |  |  |  |  |  |  |  | 
| 1255 |  |  |  |  |  |  | Returns a L<Apache2::SSI::Finfo> object. This provides access to L<perlfunc/stat> information as method, taking advantage of L<APR::Finfo> when running under Apache, and an identical interface otherwise. See L<Apache2::SSI::Finfo> for more information. | 
| 1256 |  |  |  |  |  |  |  | 
| 1257 |  |  |  |  |  |  | =head2 new_uri | 
| 1258 |  |  |  |  |  |  |  | 
| 1259 |  |  |  |  |  |  | A short-hand for C<Apache2::SSI::URI->new> | 
| 1260 |  |  |  |  |  |  |  | 
| 1261 |  |  |  |  |  |  | =head2 parent | 
| 1262 |  |  |  |  |  |  |  | 
| 1263 |  |  |  |  |  |  | Returns the parent of the document uri, or if there is no parent, it returns the current object itself. | 
| 1264 |  |  |  |  |  |  |  | 
| 1265 |  |  |  |  |  |  | my $up = $uri->parent; | 
| 1266 |  |  |  |  |  |  | # would return /some/path assuming the document uri was /some/path/file.html | 
| 1267 |  |  |  |  |  |  |  | 
| 1268 |  |  |  |  |  |  | =head2 path_info | 
| 1269 |  |  |  |  |  |  |  | 
| 1270 |  |  |  |  |  |  | Sets or gets the path info for the current uri. | 
| 1271 |  |  |  |  |  |  |  | 
| 1272 |  |  |  |  |  |  | Example: | 
| 1273 |  |  |  |  |  |  |  | 
| 1274 |  |  |  |  |  |  | my $string = $ssi->path_info; | 
| 1275 |  |  |  |  |  |  | $ssi->path_info( '/my/path/info' ); | 
| 1276 |  |  |  |  |  |  |  | 
| 1277 |  |  |  |  |  |  | The path info value is also set automatically when L</document_uri> is called, such as: | 
| 1278 |  |  |  |  |  |  |  | 
| 1279 |  |  |  |  |  |  | $ssi->document_uri( '/some/path/to/file.html/my/path/info?q=something&l=ja_JP' ); | 
| 1280 |  |  |  |  |  |  |  | 
| 1281 |  |  |  |  |  |  | This will also set automatically the C<PATH_INFO> environment variable. | 
| 1282 |  |  |  |  |  |  |  | 
| 1283 |  |  |  |  |  |  | =head2 query_string | 
| 1284 |  |  |  |  |  |  |  | 
| 1285 |  |  |  |  |  |  | Set or gets the query string for the current uri. | 
| 1286 |  |  |  |  |  |  |  | 
| 1287 |  |  |  |  |  |  | Example: | 
| 1288 |  |  |  |  |  |  |  | 
| 1289 |  |  |  |  |  |  | my $string = $ssi->query_string; | 
| 1290 |  |  |  |  |  |  | $ssi->query_string( 'q=something&l=ja_JP' ); | 
| 1291 |  |  |  |  |  |  |  | 
| 1292 |  |  |  |  |  |  | or, using the L<URI> module: | 
| 1293 |  |  |  |  |  |  |  | 
| 1294 |  |  |  |  |  |  | $ssi->query_string( $uri->query ); | 
| 1295 |  |  |  |  |  |  |  | 
| 1296 |  |  |  |  |  |  | The query string value is set automatically when you provide an L<document_uri> upon instantiation or after: | 
| 1297 |  |  |  |  |  |  |  | 
| 1298 |  |  |  |  |  |  | $ssi->document_uri( '/some/path/to/file.html?q=something&l=ja_JP' ); | 
| 1299 |  |  |  |  |  |  |  | 
| 1300 |  |  |  |  |  |  | This will also set automatically the C<QUERY_STRING> environment variable. | 
| 1301 |  |  |  |  |  |  |  | 
| 1302 |  |  |  |  |  |  | =head2 root | 
| 1303 |  |  |  |  |  |  |  | 
| 1304 |  |  |  |  |  |  | Returns an object representation of the root uri, i.e. C</> | 
| 1305 |  |  |  |  |  |  |  | 
| 1306 |  |  |  |  |  |  | =head2 slurp | 
| 1307 |  |  |  |  |  |  |  | 
| 1308 |  |  |  |  |  |  | It returns the content of the L</filename> | 
| 1309 |  |  |  |  |  |  |  | 
| 1310 |  |  |  |  |  |  | it takes an hash reference of parameters: | 
| 1311 |  |  |  |  |  |  |  | 
| 1312 |  |  |  |  |  |  | =over 4 | 
| 1313 |  |  |  |  |  |  |  | 
| 1314 |  |  |  |  |  |  | =item I<binmode> | 
| 1315 |  |  |  |  |  |  |  | 
| 1316 |  |  |  |  |  |  | my $content = $uri->slurp({ binmode => ':utf-8' }); | 
| 1317 |  |  |  |  |  |  |  | 
| 1318 |  |  |  |  |  |  | =back | 
| 1319 |  |  |  |  |  |  |  | 
| 1320 |  |  |  |  |  |  | It will return undef and sets an L<Module::Generic/error> if there is no L</filename> value set or if the file cannot be opened. | 
| 1321 |  |  |  |  |  |  |  | 
| 1322 |  |  |  |  |  |  | =head2 slurp_utf8 | 
| 1323 |  |  |  |  |  |  |  | 
| 1324 |  |  |  |  |  |  | It returns the content of the file L</filename> utf-8 decoded. | 
| 1325 |  |  |  |  |  |  |  | 
| 1326 |  |  |  |  |  |  | This is equivalent to: | 
| 1327 |  |  |  |  |  |  |  | 
| 1328 |  |  |  |  |  |  | my $content = $uri->slurp({ binmode => ':utf8' }); | 
| 1329 |  |  |  |  |  |  |  | 
| 1330 |  |  |  |  |  |  | C<:utf8> is slightly a bit more lax than C<:utf-8>, so it you want strict utf8, you can do: | 
| 1331 |  |  |  |  |  |  |  | 
| 1332 |  |  |  |  |  |  | my $content = $uri->slurp({ binmode => ':utf-8' }); | 
| 1333 |  |  |  |  |  |  |  | 
| 1334 |  |  |  |  |  |  | =head1 AUTHOR | 
| 1335 |  |  |  |  |  |  |  | 
| 1336 |  |  |  |  |  |  | Jacques Deguest E<lt>F<jack@deguest.jp>E<gt> | 
| 1337 |  |  |  |  |  |  |  | 
| 1338 |  |  |  |  |  |  | CPAN ID: jdeguest | 
| 1339 |  |  |  |  |  |  |  | 
| 1340 |  |  |  |  |  |  | L<https://git.deguest.jp/jack/Apache2-SSI> | 
| 1341 |  |  |  |  |  |  |  | 
| 1342 |  |  |  |  |  |  | =head1 SEE ALSO | 
| 1343 |  |  |  |  |  |  |  | 
| 1344 |  |  |  |  |  |  | L<Apache2::SSI::File>, L<Apache2::SSI::Finfo>, L<Apache2::SSI> | 
| 1345 |  |  |  |  |  |  |  | 
| 1346 |  |  |  |  |  |  | mod_include, mod_perl(3), L<APR::URI>, L<URI> | 
| 1347 |  |  |  |  |  |  | L<https://httpd.apache.org/docs/current/en/mod/mod_include.html>, | 
| 1348 |  |  |  |  |  |  | L<https://httpd.apache.org/docs/current/en/howto/ssi.html>, | 
| 1349 |  |  |  |  |  |  | L<https://httpd.apache.org/docs/current/en/expr.html> | 
| 1350 |  |  |  |  |  |  | L<https://perl.apache.org/docs/2.0/user/handlers/filters.html#C_PerlOutputFilterHandler_> | 
| 1351 |  |  |  |  |  |  |  | 
| 1352 |  |  |  |  |  |  | =head1 COPYRIGHT & LICENSE | 
| 1353 |  |  |  |  |  |  |  | 
| 1354 |  |  |  |  |  |  | Copyright (c) 2020-2021 DEGUEST Pte. Ltd. | 
| 1355 |  |  |  |  |  |  |  | 
| 1356 |  |  |  |  |  |  | You can use, copy, modify and redistribute this package and associated | 
| 1357 |  |  |  |  |  |  | files under the same terms as Perl itself. | 
| 1358 |  |  |  |  |  |  |  | 
| 1359 |  |  |  |  |  |  | =cut |