File Coverage

blib/lib/Text/Template/Simple/IO.pm
Criterion Covered Total %
statement 109 116 93.9
branch 35 62 56.4
condition 15 39 38.4
subroutine 17 18 94.4
pod 7 7 100.0
total 183 242 75.6


line stmt bran cond sub pod time code
1             package Text::Template::Simple::IO;
2 62     62   417 use strict;
  62         136  
  62         3008  
3 62     62   388 use warnings;
  62         138  
  62         2683  
4 62     62   382 use constant MY_IO_LAYER => 0;
  62         134  
  62         4683  
5 62     62   376 use constant MY_INCLUDE_PATHS => 1;
  62         141  
  62         12585  
6 62     62   354 use constant MY_TAINT_MODE => 2;
  62         136  
  62         6272  
7              
8 62     62   395 use File::Spec;
  62         126  
  62         1744  
9 62     62   673 use Text::Template::Simple::Constants qw(:all);
  62         147  
  62         59330  
10 62         127829 use Text::Template::Simple::Util qw(
11             binary_mode
12             fatal
13             ishref
14             DEBUG
15             LOG
16 62     62   493 );
  62         179  
17              
18             our $VERSION = '0.86';
19              
20             sub new {
21 90     90 1 222 my $class = shift;
22 90         203 my $layer = shift;
23 90         223 my $paths = shift;
24 90         199 my $tmode = shift;
25 90         275 my $self = [ undef, undef, undef ];
26 90         314 bless $self, $class;
27 90 50       1012 $self->[MY_IO_LAYER] = $layer if defined $layer;
28 90 50       447 $self->[MY_INCLUDE_PATHS] = [ @{ $paths } ] if $paths; # copy
  90         293  
29 90         230 $self->[MY_TAINT_MODE] = $tmode;
30 90         381 return $self;
31             }
32              
33             sub validate {
34 4     4 1 11 my $self = shift;
35 4   33     25 my $type = shift || fatal('tts.io.validate.type');
36 4   33     18 my $path = shift || fatal('tts.io.validate.path');
37              
38 4 50       25 if ( $type eq 'dir' ) {
39 4         47 require File::Spec;
40 4         22 $path = File::Spec->canonpath( $path );
41 4         10 my $wdir;
42              
43 4         8 if ( IS_WINDOWS ) {
44             $wdir = Win32::GetFullPathName( $path );
45             if( Win32::GetLastError() ) {
46             LOG( FAIL => "Win32::GetFullPathName( $path ): $^E" ) if DEBUG;
47             $wdir = EMPTY_STRING; # die "Win32::GetFullPathName: $^E";
48             }
49             else {
50             my $ok = -e $wdir && -d _;
51             $wdir = EMPTY_STRING if not $ok;
52             }
53             }
54              
55 4 50       18 $path = $wdir if $wdir;
56 4   33     121 my $ok = -e $path && -d _;
57 4 50       21 return if not $ok;
58 4         27 return $path;
59             }
60              
61 0         0 return fatal('tts.io.validate.file');
62             }
63              
64             sub layer {
65 834     834 1 1183 return if ! NEW_PERL;
66 834         1570 my $self = shift;
67 834   33     2391 my $fh = shift || fatal('tts.io.layer.fh');
68 834         2395 my $layer = $self->[MY_IO_LAYER];
69 834 100       4126 binary_mode( $fh, $layer ) if $layer;
70 834         1640 return;
71             }
72              
73             sub slurp {
74 832     832 1 63867 require IO::File;
75 832         631106 require Fcntl;
76 832         2415 my $self = shift;
77 832         1264 my $file = shift;
78 832         1372 my($fh, $seek);
79 832 50       3622 LOG(IO_SLURP => $file) if DEBUG;
80              
81             # perl 5.5.3 compat: we need to check if it's a ref first
82 832 50 33     5466 if ( ref $file && fileno $file ) {
83 0         0 $fh = $file;
84 0         0 $seek = 1;
85             }
86             else {
87 832         13509 $fh = IO::File->new;
88 832 100       61540 $fh->open($file, 'r') or fatal('tts.io.slurp.open', $file, $!);
89             }
90              
91 828         179100 flock $fh, Fcntl::LOCK_SH() if IS_FLOCK;
92 828 50       2241 seek $fh, 0, Fcntl::SEEK_SET() if IS_FLOCK && $seek;
93 828 50       3654 $self->layer( $fh ) if ! $seek; # apply the layer only if we opened this
94              
95 828 50       2467 if ( $self->_handle_looks_safe( $fh ) ) {
96 828         13760 require IO::Handle;
97 828         4476 my $rv = IO::Handle::untaint( $fh );
98 828 50       8682 fatal('tts.io.slurp.taint') if $rv != 0;
99             }
100              
101 828         1356 my $tmp = do { local $/; my $rv = <$fh>; $rv };
  828         3881  
  828         1388923  
  828         4055  
102 828         5717 flock $fh, Fcntl::LOCK_UN() if IS_FLOCK;
103 828 50       2307 if ( ! $seek ) {
104             # close only if we opened this
105 828 50       17238 close $fh or die "Unable to close filehandle: $!\n";
106             }
107 828         7130 return $tmp;
108             }
109              
110             sub _handle_looks_safe {
111             # Cargo Culting: original taint checking code was taken from "The Camel"
112 828     828   1168 my $self = shift;
113 828         1276 my $fh = shift;
114 828 50 33     4250 fatal('tts.io.hls.invalid') if ! $fh || ! fileno $fh;
115              
116 828         45636 require File::stat;
117 828         509154 my $i = File::stat::stat( $fh );
118 828 50       172081 return if ! $i;
119              
120 828         1918 my $tmode = $self->[MY_TAINT_MODE];
121              
122             # ignore this check if the user is root
123             # can happen with cpan clients
124 828 50       7507 if ( $< != 0 ) {
125             # owner neither superuser nor "me", whose
126             # real uid is in the $< variable
127 0 0 0     0 return if $i->uid != 0 && $i->uid != $<;
128             }
129              
130             # Check whether group or other can write file.
131             # Read check is disabled by default
132             # Mode is always 0666 on Windows, so all tests below are disabled on Windows
133             # unless you force them to run
134 828 50       3419 LOG( FILE_MODE => sprintf '%04o', $i->mode & FTYPE_MASK) if DEBUG;
135              
136 828         2957 my $bypass = IS_WINDOWS && ! ( $tmode & TAINT_CHECK_WINDOWS ) ? 1 : 0;
137 828 50       24184 my $go_write = $bypass ? 0 : $i->mode & FMODE_GO_WRITABLE;
138 828 50 33     28489 my $go_read = ! $bypass && ( $tmode & TAINT_CHECK_FH_READ )
139             ? $i->mode & FMODE_GO_READABLE
140             : 0;
141              
142 828 50       2299 LOG( TAINT => "tmode:$tmode; bypass:$bypass; "
143             ."go_write:$go_write; go_read:$go_read") if DEBUG;
144              
145 828 50 33     4370 return if $go_write || $go_read;
146 828         6808 return 1;
147             }
148              
149             sub is_file {
150             # safer than a simple "-e"
151 1342     1342 1 1739 my $self = shift;
152 1342   50     4801 my $file = shift || return;
153 1342   66     4557 return $self->_looks_like_file( $file ) && ! -d $file;
154             }
155              
156             sub is_dir {
157             # safer than a simple "-d"
158 272     272 1 518 my $self = shift;
159 272   50     866 my $file = shift || return;
160 272   66     735 return $self->_looks_like_file( $file ) && -d $file;
161             }
162              
163             sub file_exists {
164 1330     1330 1 2198 my $self = shift;
165 1330         2636 my $file = shift;
166              
167 1330 100       3650 return $file if $self->is_file( $file );
168              
169 242         863 foreach my $path ( @{ $self->[MY_INCLUDE_PATHS] } ) {
  242         1356  
170 12         268 my $test = File::Spec->catfile( $path, $file );
171 12 100       43 return $test if $self->is_file( $test );
172             }
173              
174 234         1551 return; # fail!
175             }
176              
177             sub _looks_like_file {
178 1614     1614   4916 my $self = shift;
179 1614   50     17000 my $file = shift || return;
180 1614 100       165930 return ref $file ? 0
    50          
    100          
    50          
181             : $file =~ RE_NONFILE ? 0
182             : length $file >= MAX_PATH_LENGTH ? 0
183             : -e $file ? 1
184             : 0
185             ;
186             }
187              
188             sub DESTROY {
189 0     0     my $self = shift;
190 0 0         LOG( DESTROY => ref $self ) if DEBUG;
191 0           return;
192             }
193              
194             1;
195              
196             __END__
197              
198             =head1 NAME
199              
200             Text::Template::Simple::IO - I/O methods
201              
202             =head1 SYNOPSIS
203              
204             TODO
205              
206             =head1 DESCRIPTION
207              
208             This document describes version C<0.86> of C<Text::Template::Simple::IO>
209             released on C<5 March 2012>.
210              
211             TODO
212              
213             =head1 METHODS
214              
215             =head2 new IO_LAYER
216              
217             Constructor. Accepts an I/O layer name as the parameter.
218              
219             =head2 layer FH
220              
221             Sets the I/O layer of the supplied filehandle if there is a layer and perl
222             version is greater or equal to C<5.8>.
223              
224             =head2 slurp FILE_PATH
225              
226             Returns the contents of the supplied file as a string.
227              
228             =head2 validate TYPE, PATH
229              
230             C<TYPE> can either be C<dir> or C<file>. Returns the corrected path if
231             it is valid, C<undef> otherwise.
232              
233             =head2 is_dir THING
234              
235             Test if C<THING> is a directory.
236              
237             =head2 is_file THING
238              
239             Test if C<THING> is a file.
240              
241             =head2 file_exists THING
242              
243             Test if C<THING> is a file. This method also searches all the C<include paths>
244             and returns the full path to the file if it exists.
245              
246             =head1 AUTHOR
247              
248             Burak Gursoy <burak@cpan.org>.
249              
250             =head1 COPYRIGHT
251              
252             Copyright 2004 - 2012 Burak Gursoy. All rights reserved.
253              
254             =head1 LICENSE
255              
256             This library is free software; you can redistribute it and/or modify
257             it under the same terms as Perl itself, either Perl version 5.12.3 or,
258             at your option, any later version of Perl 5 you may have available.
259              
260             =cut