| line | stmt | bran | cond | sub | pod | time | code | 
| 1 |  |  |  |  |  |  | ##---------------------------------------------------------------------------- | 
| 2 |  |  |  |  |  |  | ## Apache2 Server Side Include Parser - ~/lib/Apache2/SSI/File.pm | 
| 3 |  |  |  |  |  |  | ## Version v0.1.0 | 
| 4 |  |  |  |  |  |  | ## Copyright(c) 2021 DEGUEST Pte. Ltd. | 
| 5 |  |  |  |  |  |  | ## Author: Jacques Deguest <jack@deguest.jp> | 
| 6 |  |  |  |  |  |  | ## Created 2020/12/18 | 
| 7 |  |  |  |  |  |  | ## Modified 2021/01/13 | 
| 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::File; | 
| 14 |  |  |  |  |  |  | BEGIN | 
| 15 |  |  |  |  |  |  | { | 
| 16 | 15 |  |  | 15 |  | 106917 | use strict; | 
|  | 15 |  |  |  |  | 45 |  | 
|  | 15 |  |  |  |  | 522 |  | 
| 17 | 15 |  |  | 15 |  | 77 | use warnings; | 
|  | 15 |  |  |  |  | 24 |  | 
|  | 15 |  |  |  |  | 430 |  | 
| 18 | 15 |  |  | 15 |  | 75 | use warnings::register; | 
|  | 15 |  |  |  |  | 31 |  | 
|  | 15 |  |  |  |  | 2091 |  | 
| 19 | 15 |  |  | 15 |  | 600 | use parent qw( Apache2::SSI::Common ); | 
|  | 15 |  |  |  |  | 332 |  | 
|  | 15 |  |  |  |  | 157 |  | 
| 20 | 15 |  |  | 15 |  | 9474 | use Apache2::SSI::Finfo; | 
|  | 15 |  |  |  |  | 53 |  | 
|  | 15 |  |  |  |  | 1052 |  | 
| 21 | 15 |  |  | 15 |  | 102 | use File::Spec (); | 
|  | 15 |  |  |  |  | 27 |  | 
|  | 15 |  |  |  |  | 220 |  | 
| 22 | 15 |  |  | 15 |  | 67 | use Scalar::Util (); | 
|  | 15 |  |  |  |  | 25 |  | 
|  | 15 |  |  |  |  | 229 |  | 
| 23 | 15 |  |  | 15 |  | 66 | use URI::file (); | 
|  | 15 |  |  |  |  | 23 |  | 
|  | 15 |  |  |  |  | 2027 |  | 
| 24 | 15 | 50 |  | 15 |  | 1479 | if( $ENV{MOD_PERL} ) | 
| 25 |  |  |  |  |  |  | { | 
| 26 | 0 |  |  |  |  | 0 | require Apache2::RequestRec; | 
| 27 | 0 |  |  |  |  | 0 | require Apache2::RequestUtil; | 
| 28 | 0 |  |  |  |  | 0 | require Apache2::SubRequest; | 
| 29 | 0 |  |  |  |  | 0 | require Apache2::Access; | 
| 30 | 0 |  |  |  |  | 0 | require Apache2::Const; | 
| 31 | 0 |  |  |  |  | 0 | Apache2::Const->import( compile => qw( :common :http OK DECLINED ) ); | 
| 32 | 0 |  |  |  |  | 0 | require APR::Const; | 
| 33 | 0 |  |  |  |  | 0 | APR::Const->import( -compile => qw( :filetype FINFO_NORM ) ); | 
| 34 |  |  |  |  |  |  | } | 
| 35 |  |  |  |  |  |  | ## use Devel::Confess; | 
| 36 | 15 |  |  |  |  | 28 | our( $DEBUG ); | 
| 37 |  |  |  |  |  |  | use overload ( | 
| 38 | 3 |  |  | 3 |  | 9 | q{""}    => sub    { $_[0]->filename }, | 
| 39 |  |  |  |  |  |  | bool     => sub () { 1 }, | 
| 40 | 15 |  |  |  |  | 93 | fallback => 1, | 
| 41 | 15 |  |  | 15 |  | 86 | ); | 
|  | 15 |  |  |  |  | 30 |  | 
| 42 | 15 |  |  |  |  | 27 | our $VERSION = 'v0.1.0'; | 
| 43 | 15 |  |  |  |  | 19797 | our $DIR_SEP = $Apache2::SSI::Common::DIR_SEP; | 
| 44 |  |  |  |  |  |  | }; | 
| 45 |  |  |  |  |  |  |  | 
| 46 |  |  |  |  |  |  | sub init | 
| 47 |  |  |  |  |  |  | { | 
| 48 | 8 |  |  | 8 | 1 | 16423 | my $self = shift( @_ ); | 
| 49 | 8 |  |  |  |  | 44 | my $file = shift( @_ ); | 
| 50 | 8 | 50 | 33 |  |  | 127 | return( $self->error( "No file was provided." ) ) if( !defined( $file ) || !length( $file ) ); | 
| 51 | 8 |  |  |  |  | 338 | $self->{apache_request} = ''; | 
| 52 | 8 | 50 |  |  |  | 71 | $self->{base_dir}       = '' unless( length( $self->{base_dir} ) ); | 
| 53 | 8 |  |  |  |  | 45 | $self->{base_file}      = ''; | 
| 54 | 8 |  |  |  |  | 35 | $self->{code}           = 200; | 
| 55 | 8 |  |  |  |  | 41 | $self->{finfo}          = ''; | 
| 56 | 8 |  |  |  |  | 55 | $self->{_init_strict_use_sub} = 1; | 
| 57 | 8 | 50 |  |  |  | 83 | $self->SUPER::init( @_ ) || return; | 
| 58 | 8 |  |  |  |  | 570 | $self->message( 3, "Returning object for file \"$file\"." ); | 
| 59 | 8 |  |  |  |  | 150 | my $base_dir = ''; | 
| 60 | 8 | 100 |  |  |  | 85 | if( length( $self->{base_file} ) ) | 
|  |  | 100 |  |  |  |  |  | 
| 61 |  |  |  |  |  |  | { | 
| 62 | 3 | 50 |  |  |  | 67 | if( -d( $self->{base_file} ) ) | 
| 63 |  |  |  |  |  |  | { | 
| 64 | 0 |  |  |  |  | 0 | $base_dir = $self->{base_file}; | 
| 65 |  |  |  |  |  |  | } | 
| 66 |  |  |  |  |  |  | else | 
| 67 |  |  |  |  |  |  | { | 
| 68 | 3 |  |  |  |  | 62 | my @segments = split( "\Q${DIR_SEP}\E", $self->{base_file}, -1 ); | 
| 69 | 3 |  |  |  |  | 8 | pop( @segments ); | 
| 70 | 3 |  |  |  |  | 13 | $base_dir = join( $DIR_SEP, @segments ); | 
| 71 |  |  |  |  |  |  | } | 
| 72 | 3 |  |  |  |  | 7 | $self->{base_dir} = $base_dir; | 
| 73 |  |  |  |  |  |  | } | 
| 74 |  |  |  |  |  |  | elsif( !length( $self->{base_dir} ) ) | 
| 75 |  |  |  |  |  |  | { | 
| 76 | 4 |  |  |  |  | 41 | $base_dir = URI->new( URI::file->cwd )->file( $^O ); | 
| 77 | 4 |  |  |  |  | 36469 | $self->{base_dir} = $base_dir; | 
| 78 |  |  |  |  |  |  | } | 
| 79 | 8 | 50 |  |  |  | 175 | $self->filename( $file ) || return; | 
| 80 | 8 |  |  |  |  | 155 | return( $self ); | 
| 81 |  |  |  |  |  |  | } | 
| 82 |  |  |  |  |  |  |  | 
| 83 | 56 |  |  | 56 | 1 | 418 | sub apache_request { return( shift->_set_get_object_without_init( 'apache_request', 'Apache2::RequestRec', @_ ) ); } | 
| 84 |  |  |  |  |  |  |  | 
| 85 | 10 |  |  | 10 | 1 | 466 | sub base_dir { return( shift->_make_abs( 'base_dir', @_ ) ); } | 
| 86 |  |  |  |  |  |  |  | 
| 87 | 3 |  |  | 3 | 0 | 326 | sub base_file { return( shift->_make_abs( 'base_file', @_ ) ); } | 
| 88 |  |  |  |  |  |  |  | 
| 89 |  |  |  |  |  |  | sub clone | 
| 90 |  |  |  |  |  |  | { | 
| 91 | 1 |  |  | 1 | 1 | 10 | my $self = shift( @_ ); | 
| 92 | 1 |  |  |  |  | 10 | my $new = {}; | 
| 93 | 1 |  |  |  |  | 56 | my @fields = grep( !/^(apache_request|finfo)$/, keys( %$self ) ); | 
| 94 | 1 |  |  |  |  | 28 | @$new{ @fields } = @$self{ @fields }; | 
| 95 | 1 |  |  |  |  | 9 | $new->{apache_request} = $self->{apache_request}; | 
| 96 | 1 |  | 33 |  |  | 19 | return( bless( $new => ( ref( $self ) || $self ) ) ); | 
| 97 |  |  |  |  |  |  | } | 
| 98 |  |  |  |  |  |  |  | 
| 99 |  |  |  |  |  |  | sub code | 
| 100 |  |  |  |  |  |  | { | 
| 101 | 11 |  |  | 11 | 1 | 2393 | my $self = shift( @_ ); | 
| 102 | 11 |  |  |  |  | 34 | my $r = $self->apache_request; | 
| 103 | 11 | 50 |  |  |  | 192 | if( $r ) | 
| 104 |  |  |  |  |  |  | { | 
| 105 | 0 | 0 |  |  |  | 0 | $r->status( @_ ) if( @_ ); | 
| 106 | 0 |  |  |  |  | 0 | return( $r->status ); | 
| 107 |  |  |  |  |  |  | } | 
| 108 |  |  |  |  |  |  | else | 
| 109 |  |  |  |  |  |  | { | 
| 110 | 11 | 100 |  |  |  | 59 | $self->{code} = shift( @_ ) if( @_ ); | 
| 111 | 11 |  |  |  |  | 73 | $self->message( 3, "Returning code '$self->{code}'" ); | 
| 112 | 11 |  |  |  |  | 211 | return( $self->{code} ); | 
| 113 |  |  |  |  |  |  | } | 
| 114 |  |  |  |  |  |  | } | 
| 115 |  |  |  |  |  |  |  | 
| 116 |  |  |  |  |  |  | sub filename | 
| 117 |  |  |  |  |  |  | { | 
| 118 | 19 |  |  | 19 | 1 | 87 | my $self = shift( @_ ); | 
| 119 | 19 |  |  |  |  | 85 | my $newfile; | 
| 120 | 19 | 100 |  |  |  | 143 | if( @_ ) | 
| 121 |  |  |  |  |  |  | { | 
| 122 | 9 |  |  |  |  | 91 | $newfile = shift( @_ ); | 
| 123 | 9 | 50 | 33 |  |  | 288 | return( $self->error( "New file provided, but it was an empty string." ) ) if( !defined( $newfile ) || !length( $newfile ) ); | 
| 124 |  |  |  |  |  |  | } | 
| 125 |  |  |  |  |  |  |  | 
| 126 | 19 |  |  |  |  | 148 | my $r = $self->apache_request; | 
| 127 | 19 | 50 |  |  |  | 702 | if( $r ) | 
| 128 |  |  |  |  |  |  | { | 
| 129 | 0 | 0 |  |  |  | 0 | if( defined( $newfile ) ) | 
|  |  | 0 |  |  |  |  |  | 
| 130 |  |  |  |  |  |  | { | 
| 131 | 0 |  |  |  |  | 0 | $self->message( 3, "Setting new file path '$newfile'. Looking up file." ); | 
| 132 | 0 | 0 |  |  |  | 0 | $r = $r->is_initial_req ? $r : $r->main; | 
| 133 | 0 |  |  |  |  | 0 | my $rr = $r->lookup_file( $newfile ); | 
| 134 | 0 |  | 0 |  |  | 0 | $self->message( 3, "File found \"", $rr->filename, "\" has status '", $rr->status, "' and file type '", ( ( $rr->finfo && $rr->finfo->filetype ) || '' ), "'." ); | 
| 135 |  |  |  |  |  |  | ## Amazingly, lookup_file will return ok  even if it does not find the file | 
| 136 | 0 | 0 | 0 |  |  | 0 | if( $rr->status == Apache2::Const::HTTP_OK && | 
|  |  |  | 0 |  |  |  |  | 
| 137 |  |  |  |  |  |  | $rr->finfo && | 
| 138 |  |  |  |  |  |  | $rr->finfo->filetype != APR::Const::FILETYPE_NOFILE ) | 
| 139 |  |  |  |  |  |  | { | 
| 140 | 0 |  |  |  |  | 0 | $self->apache_request( $rr ); | 
| 141 | 0 |  |  |  |  | 0 | $newfile = $rr->filename; | 
| 142 | 0 |  |  |  |  | 0 | $self->message( 3, "File found and resolved to: '$newfile' with code '", $rr->status, "' with finfo object '", $r->finfo, "'." ); | 
| 143 | 0 |  |  |  |  | 0 | my $finfo = $rr->finfo; | 
| 144 | 0 | 0 |  |  |  | 0 | if( $finfo ) | 
| 145 |  |  |  |  |  |  | { | 
| 146 | 0 |  |  |  |  | 0 | $self->message( 3, "File type is '", $finfo->filetype, "'." ); | 
| 147 |  |  |  |  |  |  | } | 
| 148 |  |  |  |  |  |  | } | 
| 149 |  |  |  |  |  |  | else | 
| 150 |  |  |  |  |  |  | { | 
| 151 | 0 |  |  |  |  | 0 | $self->message( 3, "File is not found." ); | 
| 152 | 0 |  |  |  |  | 0 | $self->code( 404 ); | 
| 153 | 0 |  |  |  |  | 0 | $newfile = $self->collapse_dots( $newfile, { separator => $DIR_SEP }); | 
| 154 |  |  |  |  |  |  | ## We don't pass it the Apache2::RequestRec object, because it would trigger a fatal error since the file does not exist. Instead, we use the api without Apache2::RequestRec which is more tolerant | 
| 155 |  |  |  |  |  |  | ## We do this so the user can call our object $file->finfo->filetype == Apache2::SSI::Finfo::FILETYPE_NOFILE | 
| 156 | 0 |  |  |  |  | 0 | $self->{finfo} = Apache2::SSI::Finfo->new( $newfile ); | 
| 157 |  |  |  |  |  |  | } | 
| 158 | 0 |  |  |  |  | 0 | $self->{filename} = $newfile; | 
| 159 |  |  |  |  |  |  | } | 
| 160 |  |  |  |  |  |  | elsif( !length( $self->{filename} ) ) | 
| 161 |  |  |  |  |  |  | { | 
| 162 | 0 |  |  |  |  | 0 | $self->{filename} = $r->filename; | 
| 163 |  |  |  |  |  |  | } | 
| 164 |  |  |  |  |  |  | } | 
| 165 |  |  |  |  |  |  | else | 
| 166 |  |  |  |  |  |  | { | 
| 167 | 19 | 100 |  |  |  | 132 | if( defined( $newfile ) ) | 
| 168 |  |  |  |  |  |  | { | 
| 169 | 9 |  |  |  |  | 89 | my $base_dir = $self->base_dir; | 
| 170 | 9 | 100 |  |  |  | 160 | $base_dir .= $DIR_SEP unless( substr( $base_dir, -length( $DIR_SEP ), length( $DIR_SEP ) ) eq $DIR_SEP ); | 
| 171 | 9 |  |  |  |  | 191 | $self->message( 3, "New file path provided is: '$newfile' and base directory is '$base_dir' and directory separator is '$DIR_SEP'" ); | 
| 172 |  |  |  |  |  |  | ## If we provide a string for the abs() method it works on Unix, but not on Windows | 
| 173 |  |  |  |  |  |  | ## By providing an object, we make it work | 
| 174 | 9 |  |  |  |  | 310 | $newfile = URI::file->new( $newfile )->abs( URI::file->new( $base_dir ) )->file( $^O ); | 
| 175 | 9 |  |  |  |  | 5573 | $self->message( 3, "Getting the new file real path: '$newfile'" ); | 
| 176 | 9 |  |  |  |  | 405 | $self->{filename} = $self->collapse_dots( $newfile, { separator => $DIR_SEP })->file( $^O ); | 
| 177 | 9 |  |  |  |  | 1279 | $self->message( 3, "Filename after dot collapsing is: '$self->{filename}'" ); | 
| 178 | 9 |  |  |  |  | 230 | $self->finfo( $newfile ); | 
| 179 | 9 |  |  |  |  | 37 | my $finfo = $self->finfo; | 
| 180 | 9 |  |  |  |  | 44 | $self->message( 3, "finfo is '", overload::StrVal( $finfo ), "'." ); | 
| 181 | 9 | 100 |  |  |  | 249 | if( !$finfo->exists ) | 
| 182 |  |  |  |  |  |  | { | 
| 183 | 2 |  |  |  |  | 49 | $self->code( 404 ); | 
| 184 |  |  |  |  |  |  | } | 
| 185 |  |  |  |  |  |  | ## Force to create new Apache2::SSI::URI object | 
| 186 |  |  |  |  |  |  | } | 
| 187 |  |  |  |  |  |  | } | 
| 188 | 19 |  |  |  |  | 118 | $self->message( 3, "Returning filename '$self->{filename}'" ); | 
| 189 | 19 |  |  |  |  | 521 | return( $self->{filename} ); | 
| 190 |  |  |  |  |  |  | } | 
| 191 |  |  |  |  |  |  |  | 
| 192 |  |  |  |  |  |  | ## Alias | 
| 193 | 0 |  |  | 0 | 0 | 0 | sub filepath { return( shift->filename( @_ ) ); } | 
| 194 |  |  |  |  |  |  |  | 
| 195 |  |  |  |  |  |  | sub finfo | 
| 196 |  |  |  |  |  |  | { | 
| 197 | 25 |  |  | 25 | 1 | 337 | my $self = shift( @_ ); | 
| 198 | 25 |  |  |  |  | 108 | my $r = $self->apache_request; | 
| 199 | 25 |  |  |  |  | 394 | my $newfile; | 
| 200 | 25 | 100 |  |  |  | 139 | if( @_ ) | 
|  |  | 50 |  |  |  |  |  | 
| 201 |  |  |  |  |  |  | { | 
| 202 | 9 |  |  |  |  | 33 | $newfile = shift( @_ ); | 
| 203 | 9 | 50 | 33 |  |  | 124 | return( $self->error( "New file path specified but is an empty string." ) ) if( !defined( $newfile ) || !length( $newfile ) ); | 
| 204 |  |  |  |  |  |  | } | 
| 205 |  |  |  |  |  |  | elsif( !$self->{finfo} ) | 
| 206 |  |  |  |  |  |  | { | 
| 207 | 0 |  |  |  |  | 0 | $newfile = $self->filename; | 
| 208 | 0 |  |  |  |  | 0 | $self->message( 3, "Initiating finfo object using filename '$newfile'." ); | 
| 209 | 0 | 0 |  |  |  | 0 | return( $self->error( "No file path set. This should not happen." ) ) if( !$newfile ); | 
| 210 |  |  |  |  |  |  | } | 
| 211 |  |  |  |  |  |  |  | 
| 212 | 25 | 100 |  |  |  | 110 | if( defined( $newfile ) ) | 
| 213 |  |  |  |  |  |  | { | 
| 214 | 9 | 50 |  |  |  | 129 | $self->{finfo} = Apache2::SSI::Finfo->new( $newfile, ( $r ? ( apache_request => $r ) : () ), debug => $self->debug ); | 
| 215 | 9 |  |  |  |  | 62 | $self->message( 3, "finfo object is now '", overload::StrVal( $self->{finfo} ), "'" ); | 
| 216 | 9 | 50 |  |  |  | 264 | $self->message( 3, "Error occurred: ", Apache2::SSI::Finfo->error ) if( !$self->{finfo} ); | 
| 217 | 9 | 50 |  |  |  | 57 | return( $self->pass_error( Apache2::SSI::Finfo->error ) ) if( !$self->{finfo} ); | 
| 218 |  |  |  |  |  |  | } | 
| 219 | 25 |  |  |  |  | 82 | $self->message( 3, "Returning finfo object '", overload::StrVal( $self->{finfo} ), "' for file '$self->{finfo}'." ); | 
| 220 | 25 |  |  |  |  | 498 | return( $self->{finfo} ); | 
| 221 |  |  |  |  |  |  | } | 
| 222 |  |  |  |  |  |  |  | 
| 223 |  |  |  |  |  |  | sub parent | 
| 224 |  |  |  |  |  |  | { | 
| 225 | 1 |  |  | 1 | 1 | 3 | my $self = shift( @_ ); | 
| 226 | 1 |  |  |  |  | 8 | my $r = $self->apache_request; | 
| 227 |  |  |  |  |  |  | ## I deliberately did not do split( '/', $path, -1 ) so that if there is a trailing '/', it will not be counted | 
| 228 |  |  |  |  |  |  | ## 2021-03-27: Was working well, but only on Unix systems... | 
| 229 |  |  |  |  |  |  | ## my @segments = split( '/', $self->filename, -1 ); | 
| 230 | 1 |  |  |  |  | 29 | my( $vol, $parent, $file ) = File::Spec->splitpath( $self->filename ); | 
| 231 | 1 |  | 50 |  |  | 5 | $vol //= ''; | 
| 232 | 1 |  | 50 |  |  | 10 | $file //= ''; | 
| 233 | 1 |  |  |  |  | 17 | $self->message( 3, "Filename is '", $self->filename, "', volume is '$vol', parent '$parent' and file is '$file'." ); | 
| 234 | 1 |  |  |  |  | 65 | my @segments = File::Spec->splitpath( File::Spec->catfile( $parent, $file ) ); | 
| 235 |  |  |  |  |  |  | ## $self->message( 3, "Path segments are: ", sub{ $self->dump( \@segments )} ); | 
| 236 | 1 |  |  |  |  | 8 | pop( @segments ); | 
| 237 | 1 | 50 |  |  |  | 12 | return( $self ) if( !scalar( @segments ) ); | 
| 238 | 1 |  |  |  |  | 12 | $self->message( 3, "Creating new object with document uri '", $vol . File::Spec->catdir( @segments ), "'." ); | 
| 239 |  |  |  |  |  |  | ## return( $self->new( join( '/', @segments ), ( $r ? ( apache_request => $r ) : () ) ) ); | 
| 240 | 1 | 50 |  |  |  | 33 | return( $self->new( $vol . File::Spec->catdir( @segments ), ( $r ? ( apache_request => $r ) : () ) ) ); | 
| 241 |  |  |  |  |  |  | } | 
| 242 |  |  |  |  |  |  |  | 
| 243 |  |  |  |  |  |  | sub _make_abs | 
| 244 |  |  |  |  |  |  | { | 
| 245 | 13 |  |  | 13 |  | 85 | my $self = shift( @_ ); | 
| 246 | 13 |  | 50 |  |  | 132 | my $field = shift( @_ ) || return( $self->error( "No field provided." ) ); | 
| 247 | 13 | 100 |  |  |  | 153 | if( @_ ) | 
| 248 |  |  |  |  |  |  | { | 
| 249 | 4 |  |  |  |  | 18 | my $this = shift( @_ ); | 
| 250 | 4 |  |  |  |  | 64 | $self->message( 3, "Setting $field to '$this'." ); | 
| 251 | 4 | 50 | 33 |  |  | 181 | if( Scalar::Util::blessed( $this ) && $this->isa( 'URI::file' ) ) | 
|  |  | 100 |  |  |  |  |  | 
| 252 |  |  |  |  |  |  | { | 
| 253 | 0 |  |  |  |  | 0 | $this = URI->new_abs( $this )->file( $^O ); | 
| 254 |  |  |  |  |  |  | } | 
| 255 |  |  |  |  |  |  | ## elsif( substr( $this, 0, 1 ) ne '/' ) | 
| 256 |  |  |  |  |  |  | elsif( !File::Spec->file_name_is_absolute( $this ) ) | 
| 257 |  |  |  |  |  |  | { | 
| 258 | 1 |  |  |  |  | 23 | $this = URI::file->new_abs( $this )->file( $^O ); | 
| 259 |  |  |  |  |  |  | } | 
| 260 | 4 |  |  |  |  | 10935 | $self->message( 3, "$field is now '$this'" ); | 
| 261 | 4 |  |  |  |  | 137 | $self->{ $field } = $this; | 
| 262 |  |  |  |  |  |  | } | 
| 263 | 13 |  |  |  |  | 95 | return( $self->{ $field } ); | 
| 264 |  |  |  |  |  |  | } | 
| 265 |  |  |  |  |  |  |  | 
| 266 |  |  |  |  |  |  | 1; | 
| 267 |  |  |  |  |  |  |  | 
| 268 |  |  |  |  |  |  | __END__ | 
| 269 |  |  |  |  |  |  |  | 
| 270 |  |  |  |  |  |  | =encoding utf-8 | 
| 271 |  |  |  |  |  |  |  | 
| 272 |  |  |  |  |  |  | =head1 NAME | 
| 273 |  |  |  |  |  |  |  | 
| 274 |  |  |  |  |  |  | Apache2::SSI::File - Apache2 Server Side Include File Object Class | 
| 275 |  |  |  |  |  |  |  | 
| 276 |  |  |  |  |  |  | =head1 SYNOPSIS | 
| 277 |  |  |  |  |  |  |  | 
| 278 |  |  |  |  |  |  | my $f = Apache2::SSI::File->new( | 
| 279 |  |  |  |  |  |  | '/some/file/path/file.html', | 
| 280 |  |  |  |  |  |  | apache_request => $r, | 
| 281 |  |  |  |  |  |  | base_dir => '/home/john/www', | 
| 282 |  |  |  |  |  |  | ); | 
| 283 |  |  |  |  |  |  | $f->base_dir( '/home/joe/www' ); | 
| 284 |  |  |  |  |  |  | my $f2 = $f->clone; | 
| 285 |  |  |  |  |  |  | unless( $f->code == Apache2::Const::HTTP_OK ) | 
| 286 |  |  |  |  |  |  | { | 
| 287 |  |  |  |  |  |  | die( "File is not there!\n" ); | 
| 288 |  |  |  |  |  |  | } | 
| 289 |  |  |  |  |  |  | # You can also use $f->filepath which is an alias to $f->filename | 
| 290 |  |  |  |  |  |  | print "Actual file is here: ", $f->filename, "\n"; | 
| 291 |  |  |  |  |  |  | my $finfo = $f->finfo; | 
| 292 |  |  |  |  |  |  | if( $finfo->can_exec ) | 
| 293 |  |  |  |  |  |  | { | 
| 294 |  |  |  |  |  |  | # do something | 
| 295 |  |  |  |  |  |  | } | 
| 296 |  |  |  |  |  |  | # prints Parent is: /some/file/path | 
| 297 |  |  |  |  |  |  | print "Parent is: ", $f->parent, "\n"; | 
| 298 |  |  |  |  |  |  |  | 
| 299 |  |  |  |  |  |  | =head1 VERSION | 
| 300 |  |  |  |  |  |  |  | 
| 301 |  |  |  |  |  |  | v0.1.0 | 
| 302 |  |  |  |  |  |  |  | 
| 303 |  |  |  |  |  |  | =head1 DESCRIPTION | 
| 304 |  |  |  |  |  |  |  | 
| 305 |  |  |  |  |  |  | This packages serves to resolve files whether inside Apache scope with mod_perl or outside, providing a unified api. | 
| 306 |  |  |  |  |  |  |  | 
| 307 |  |  |  |  |  |  | =head1 METHODS | 
| 308 |  |  |  |  |  |  |  | 
| 309 |  |  |  |  |  |  | =head2 new | 
| 310 |  |  |  |  |  |  |  | 
| 311 |  |  |  |  |  |  | This instantiates an object that is used to access other key methods. It takes the following parameters: | 
| 312 |  |  |  |  |  |  |  | 
| 313 |  |  |  |  |  |  | =over 4 | 
| 314 |  |  |  |  |  |  |  | 
| 315 |  |  |  |  |  |  | =item I<apache_request> | 
| 316 |  |  |  |  |  |  |  | 
| 317 |  |  |  |  |  |  | This is the L<Apache2::RequestRec> object that is provided if running under mod_perl. | 
| 318 |  |  |  |  |  |  |  | 
| 319 |  |  |  |  |  |  | it can be retrieved from L<Apache2::RequestUtil/request> or via L<Apache2::Filter/r> | 
| 320 |  |  |  |  |  |  |  | 
| 321 |  |  |  |  |  |  | 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. | 
| 322 |  |  |  |  |  |  |  | 
| 323 |  |  |  |  |  |  | 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: | 
| 324 |  |  |  |  |  |  |  | 
| 325 |  |  |  |  |  |  | use Apache2::RequestUtil (); # extends Apache2::RequestRec objects | 
| 326 |  |  |  |  |  |  | my $r = $r->is_initial_req ? $r : $r->main; | 
| 327 |  |  |  |  |  |  |  | 
| 328 |  |  |  |  |  |  | =back | 
| 329 |  |  |  |  |  |  |  | 
| 330 |  |  |  |  |  |  | =head2 apache_request | 
| 331 |  |  |  |  |  |  |  | 
| 332 |  |  |  |  |  |  | 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. | 
| 333 |  |  |  |  |  |  |  | 
| 334 |  |  |  |  |  |  | When running under Apache mod_perl this is set automatically from the special L</handler> method, such as: | 
| 335 |  |  |  |  |  |  |  | 
| 336 |  |  |  |  |  |  | my $r = $f->r; # $f is the Apache2::Filter object provided by Apache | 
| 337 |  |  |  |  |  |  |  | 
| 338 |  |  |  |  |  |  | =head2 base_dir | 
| 339 |  |  |  |  |  |  |  | 
| 340 |  |  |  |  |  |  | Sets or gets the base directory to be used as a reference to the files provided so they can be transformed into absolute file path. | 
| 341 |  |  |  |  |  |  |  | 
| 342 |  |  |  |  |  |  | my $f = Apache2::SSI::File->new( './index.html', | 
| 343 |  |  |  |  |  |  | base_dir => '/home/joe/www', | 
| 344 |  |  |  |  |  |  | ); | 
| 345 |  |  |  |  |  |  | # This would now be /home/joe/www/index.html | 
| 346 |  |  |  |  |  |  | $f->filename; | 
| 347 |  |  |  |  |  |  |  | 
| 348 |  |  |  |  |  |  | =head2 clone | 
| 349 |  |  |  |  |  |  |  | 
| 350 |  |  |  |  |  |  | Create a clone of the object and return it. | 
| 351 |  |  |  |  |  |  |  | 
| 352 |  |  |  |  |  |  | =head2 code | 
| 353 |  |  |  |  |  |  |  | 
| 354 |  |  |  |  |  |  | Sets or gets the http code for this file. | 
| 355 |  |  |  |  |  |  |  | 
| 356 |  |  |  |  |  |  | $f->code( 404 ); | 
| 357 |  |  |  |  |  |  |  | 
| 358 |  |  |  |  |  |  | =head2 collapse_dots | 
| 359 |  |  |  |  |  |  |  | 
| 360 |  |  |  |  |  |  | Provided with an uri or a file path, and this will resolve the path and removing the dots, such as C<.> and C<..> and return an L<URI> object. | 
| 361 |  |  |  |  |  |  |  | 
| 362 |  |  |  |  |  |  | This is done as per the L<RFC 3986 section 5.2.4 algorithm|https://tools.ietf.org/html/rfc3986#page-33> | 
| 363 |  |  |  |  |  |  |  | 
| 364 |  |  |  |  |  |  | my $file = $f->collapse_dots( '/../a/b/../c/./d.html' ); | 
| 365 |  |  |  |  |  |  | # would become /a/c/d.html | 
| 366 |  |  |  |  |  |  |  | 
| 367 |  |  |  |  |  |  | =head2 filename | 
| 368 |  |  |  |  |  |  |  | 
| 369 |  |  |  |  |  |  | Sets or gets the system file path to the file, as a string. | 
| 370 |  |  |  |  |  |  |  | 
| 371 |  |  |  |  |  |  | If a new file name is provided, under Apache/mod_perl2, this will perform a query with L<Apache2::SubRequest/lookup_file> | 
| 372 |  |  |  |  |  |  |  | 
| 373 |  |  |  |  |  |  | Any filename provided will be resolved with its dots flattened and transformed into an absolute system file path if it is not already. | 
| 374 |  |  |  |  |  |  |  | 
| 375 |  |  |  |  |  |  | =head2 finfo | 
| 376 |  |  |  |  |  |  |  | 
| 377 |  |  |  |  |  |  | Returns a L<Apache2::SSI::Finfo> object. This provides access to L<perlfunc/stat> information as methods, taking advantage of L<APR::Finfo> when running under Apache, and an identical interface otherwise. See L<Apache2::SSI::Finfo> for more information. | 
| 378 |  |  |  |  |  |  |  | 
| 379 |  |  |  |  |  |  | =head2 parent | 
| 380 |  |  |  |  |  |  |  | 
| 381 |  |  |  |  |  |  | Returns the parent of the file, or if there is no parent, it returns the current object itself. | 
| 382 |  |  |  |  |  |  |  | 
| 383 |  |  |  |  |  |  | my $up = $f->parent; | 
| 384 |  |  |  |  |  |  | # would return /home/john/some/path assuming the file was /home/john/some/path/file.html | 
| 385 |  |  |  |  |  |  |  | 
| 386 |  |  |  |  |  |  | =head2 slurp | 
| 387 |  |  |  |  |  |  |  | 
| 388 |  |  |  |  |  |  | It returns the content of the L</filename> | 
| 389 |  |  |  |  |  |  |  | 
| 390 |  |  |  |  |  |  | it takes an hash reference of parameters: | 
| 391 |  |  |  |  |  |  |  | 
| 392 |  |  |  |  |  |  | =over 4 | 
| 393 |  |  |  |  |  |  |  | 
| 394 |  |  |  |  |  |  | =item I<binmode> | 
| 395 |  |  |  |  |  |  |  | 
| 396 |  |  |  |  |  |  | my $content = $uri->slurp({ binmode => ':utf-8' }); | 
| 397 |  |  |  |  |  |  |  | 
| 398 |  |  |  |  |  |  | =back | 
| 399 |  |  |  |  |  |  |  | 
| 400 |  |  |  |  |  |  | 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. | 
| 401 |  |  |  |  |  |  |  | 
| 402 |  |  |  |  |  |  | =head2 slurp_utf8 | 
| 403 |  |  |  |  |  |  |  | 
| 404 |  |  |  |  |  |  | It returns the content of the file L</filename> utf-8 decoded. | 
| 405 |  |  |  |  |  |  |  | 
| 406 |  |  |  |  |  |  | This is equivalent to: | 
| 407 |  |  |  |  |  |  |  | 
| 408 |  |  |  |  |  |  | my $content = $uri->slurp({ binmode => ':utf8' }); | 
| 409 |  |  |  |  |  |  |  | 
| 410 |  |  |  |  |  |  | C<:utf8> is slightly a bit more lax than C<:utf-8>, so it you want strict utf8, you can do: | 
| 411 |  |  |  |  |  |  |  | 
| 412 |  |  |  |  |  |  | my $content = $uri->slurp({ binmode => ':utf-8' }); | 
| 413 |  |  |  |  |  |  |  | 
| 414 |  |  |  |  |  |  | =head1 AUTHOR | 
| 415 |  |  |  |  |  |  |  | 
| 416 |  |  |  |  |  |  | Jacques Deguest E<lt>F<jack@deguest.jp>E<gt> | 
| 417 |  |  |  |  |  |  |  | 
| 418 |  |  |  |  |  |  | CPAN ID: jdeguest | 
| 419 |  |  |  |  |  |  |  | 
| 420 |  |  |  |  |  |  | L<https://git.deguest.jp/jack/Apache2-SSI> | 
| 421 |  |  |  |  |  |  |  | 
| 422 |  |  |  |  |  |  | =head1 SEE ALSO | 
| 423 |  |  |  |  |  |  |  | 
| 424 |  |  |  |  |  |  | L<Apache2::SSI::URI>, L<Apache2::SSI::Finfo>, L<Apache2::SSI> | 
| 425 |  |  |  |  |  |  |  | 
| 426 |  |  |  |  |  |  | mod_include, mod_perl(3), L<APR::URI>, L<URI> | 
| 427 |  |  |  |  |  |  | L<https://httpd.apache.org/docs/current/en/mod/mod_include.html>, | 
| 428 |  |  |  |  |  |  | L<https://httpd.apache.org/docs/current/en/howto/ssi.html>, | 
| 429 |  |  |  |  |  |  | L<https://httpd.apache.org/docs/current/en/expr.html> | 
| 430 |  |  |  |  |  |  | L<https://perl.apache.org/docs/2.0/user/handlers/filters.html#C_PerlOutputFilterHandler_> | 
| 431 |  |  |  |  |  |  |  | 
| 432 |  |  |  |  |  |  | =head1 COPYRIGHT & LICENSE | 
| 433 |  |  |  |  |  |  |  | 
| 434 |  |  |  |  |  |  | Copyright (c) 2020-2021 DEGUEST Pte. Ltd. | 
| 435 |  |  |  |  |  |  |  | 
| 436 |  |  |  |  |  |  | You can use, copy, modify and redistribute this package and associated | 
| 437 |  |  |  |  |  |  | files under the same terms as Perl itself. | 
| 438 |  |  |  |  |  |  |  | 
| 439 |  |  |  |  |  |  | =cut | 
| 440 |  |  |  |  |  |  |  |