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 60     60   190 use strict;
  60         59  
  60         1275  
3 60     60   280 use warnings;
  60         52  
  60         1178  
4 60     60   162 use constant MY_IO_LAYER => 0;
  60         49  
  60         2536  
5 60     60   197 use constant MY_INCLUDE_PATHS => 1;
  60         56  
  60         2006  
6 60     60   191 use constant MY_TAINT_MODE => 2;
  60         68  
  60         2032  
7              
8 60     60   191 use File::Spec;
  60         71  
  60         1085  
9 60     60   181 use Text::Template::Simple::Constants qw(:all);
  60         59  
  60         20654  
10 60         48733 use Text::Template::Simple::Util qw(
11             binary_mode
12             fatal
13             DEBUG
14             LOG
15 60     60   257 );
  60         57  
16              
17             our $VERSION = '0.90';
18              
19             sub new {
20 90     90 1 200 my $class = shift;
21 90         130 my $layer = shift;
22 90         127 my $paths = shift;
23 90         114 my $tmode = shift;
24 90         177 my $self = [ undef, undef, undef ];
25 90         148 bless $self, $class;
26 90 50       442 $self->[MY_IO_LAYER] = $layer if defined $layer;
27 90 50       233 $self->[MY_INCLUDE_PATHS] = [ @{ $paths } ] if $paths; # copy
  90         181  
28 90         138 $self->[MY_TAINT_MODE] = $tmode;
29 90         233 return $self;
30             }
31              
32             sub validate {
33 4     4 1 6 my $self = shift;
34 4   33     22 my $type = shift || fatal('tts.io.validate.type');
35 4   33     11 my $path = shift || fatal('tts.io.validate.path');
36              
37 4 50       13 if ( $type eq 'dir' ) {
38 4         28 require File::Spec;
39 4         14 $path = File::Spec->canonpath( $path );
40 4         7 my $wdir;
41              
42 4         24 if ( IS_WINDOWS ) {
43             $wdir = Win32::GetFullPathName( $path );
44             if( Win32::GetLastError() ) {
45             LOG( FAIL => "Win32::GetFullPathName( $path ): $^E" ) if DEBUG;
46             $wdir = EMPTY_STRING; # die "Win32::GetFullPathName: $^E";
47             }
48             else {
49             my $ok = -e $wdir && -d _;
50             $wdir = EMPTY_STRING if not $ok;
51             }
52             }
53              
54 4 50       13 $path = $wdir if $wdir;
55 4   33     72 my $ok = -e $path && -d _;
56 4 50       16 return if not $ok;
57 4         21 return $path;
58             }
59              
60 0         0 return fatal('tts.io.validate.file');
61             }
62              
63             sub layer {
64 834     834 1 616 return if ! UNICODE_PERL;
65 834         811 my $self = shift;
66 834   33     1260 my $fh = shift || fatal('tts.io.layer.fh');
67 834         929 my $layer = $self->[MY_IO_LAYER];
68 834 100       1174 binary_mode( $fh, $layer ) if $layer;
69 834         887 return;
70             }
71              
72             sub slurp {
73 832     832 1 18012 require IO::File;
74 832         208131 require Fcntl;
75 832         768 my $self = shift;
76 832         626 my $file = shift;
77 832         610 my($fh, $seek);
78              
79 832 50       1451 LOG(IO_SLURP => $file) if DEBUG;
80              
81 832 50 33     1766 if ( ref $file && fileno $file ) {
82 0         0 $fh = $file;
83 0         0 $seek = 1;
84             }
85             else {
86 832         3107 $fh = IO::File->new;
87 832 100       17265 $fh->open($file, 'r') or fatal('tts.io.slurp.open', $file, $!);
88             }
89              
90 828         34137 flock $fh, Fcntl::LOCK_SH();
91 828 50       1426 seek $fh, 0, Fcntl::SEEK_SET() if $seek;
92 828 50       2161 $self->layer( $fh ) if ! $seek; # apply the layer only if we opened this
93              
94 828 50       1164 if ( $self->_handle_looks_safe( $fh ) ) {
95 828         2553 require IO::Handle;
96 828         1524 my $rv = IO::Handle::untaint( $fh );
97 828 50       1617 fatal('tts.io.slurp.taint') if $rv != 0;
98             }
99              
100 828         629 my $tmp = do { local $/; my $rv = <$fh>; $rv };
  828         1803  
  828         11692  
  828         2118  
101 828         2599 flock $fh, Fcntl::LOCK_UN();
102 828 50       1625 if ( ! $seek ) {
103             # close only if we opened this
104 828 50       4712 close $fh or die "Unable to close filehandle: $!\n";
105             }
106 828         3208 return $tmp;
107             }
108              
109             sub _handle_looks_safe {
110             # Cargo Culting: original taint checking code was taken from "The Camel"
111 828     828   685 my $self = shift;
112 828         595 my $fh = shift;
113 828 50 33     2297 fatal('tts.io.hls.invalid') if ! $fh || ! fileno $fh;
114              
115 828         17981 require File::stat;
116 828         169855 my $i = File::stat::stat( $fh );
117 828 50       66026 return if ! $i;
118              
119 828         865 my $tmode = $self->[MY_TAINT_MODE];
120              
121             # ignore this check if the user is root
122             # can happen with cpan clients
123 828 50       1909 if ( $< != 0 ) {
124             # owner neither superuser nor "me", whose
125             # real uid is in the $< variable
126 0 0 0     0 return if $i->uid != 0 && $i->uid != $<;
127             }
128              
129             # Check whether group or other can write file.
130             # Read check is disabled by default
131             # Mode is always 0666 on Windows, so all tests below are disabled on Windows
132             # unless you force them to run
133 828 50       1538 LOG( FILE_MODE => sprintf '%04o', $i->mode & FTYPE_MASK) if DEBUG;
134              
135 828         728 my $bypass = IS_WINDOWS && ! ( $tmode & TAINT_CHECK_WINDOWS ) ? 1 : 0;
136 828 50       13193 my $go_write = $bypass ? 0 : $i->mode & FMODE_GO_WRITABLE;
137 828 50 33     5723 my $go_read = ! $bypass && ( $tmode & TAINT_CHECK_FH_READ )
138             ? $i->mode & FMODE_GO_READABLE
139             : 0;
140              
141 828 50       1223 LOG( TAINT => "tmode:$tmode; bypass:$bypass; "
142             ."go_write:$go_write; go_read:$go_read") if DEBUG;
143              
144 828 50 33     2561 return if $go_write || $go_read;
145 828         2391 return 1;
146             }
147              
148             sub is_file {
149             # safer than a simple "-e"
150 1342     1342 1 1036 my $self = shift;
151 1342   50     1897 my $file = shift || return;
152 1342   66     1631 return $self->_looks_like_file( $file ) && ! -d $file;
153             }
154              
155             sub is_dir {
156             # safer than a simple "-d"
157 272     272 1 231 my $self = shift;
158 272   50     450 my $file = shift || return;
159 272   66     336 return $self->_looks_like_file( $file ) && -d $file;
160             }
161              
162             sub file_exists {
163 1330     1330 1 1060 my $self = shift;
164 1330         1087 my $file = shift;
165              
166 1330 100       1626 return $file if $self->is_file( $file );
167              
168 242         249 foreach my $path ( @{ $self->[MY_INCLUDE_PATHS] } ) {
  242         628  
169 12         159 my $test = File::Spec->catfile( $path, $file );
170 12 100       30 return $test if $self->is_file( $test );
171             }
172              
173 234         567 return; # fail!
174             }
175              
176             sub _looks_like_file {
177 1614     1614   1224 my $self = shift;
178 1614   50     2211 my $file = shift || return;
179 1614 100       29133 return ref $file ? 0
    50          
    100          
    50          
180             : $file =~ RE_NONFILE ? 0
181             : length $file >= MAX_PATH_LENGTH ? 0
182             : -e $file ? 1
183             : 0
184             ;
185             }
186              
187             sub DESTROY {
188 0     0     my $self = shift;
189 0 0         LOG( DESTROY => ref $self ) if DEBUG;
190 0           return;
191             }
192              
193             1;
194              
195             __END__
196              
197             =head1 NAME
198              
199             Text::Template::Simple::IO - I/O methods
200              
201             =head1 SYNOPSIS
202              
203             TODO
204              
205             =head1 DESCRIPTION
206              
207             This document describes version C<0.90> of C<Text::Template::Simple::IO>
208             released on C<5 July 2016>.
209              
210             TODO
211              
212             =head1 METHODS
213              
214             =head2 new IO_LAYER
215              
216             Constructor. Accepts an I/O layer name as the parameter.
217              
218             =head2 layer FILE_HANDLE
219              
220             Sets the I/O layer of the supplied file handle if there is a layer and C<perl>
221             version is greater or equal to C<5.8>.
222              
223             =head2 slurp FILE_PATH
224              
225             Returns the contents of the supplied file as a string.
226              
227             =head2 validate TYPE, PATH
228              
229             C<TYPE> can either be C<dir> or C<file>. Returns the corrected path if
230             it is valid, C<undef> otherwise.
231              
232             =head2 is_dir THING
233              
234             Test if C<THING> is a directory.
235              
236             =head2 is_file THING
237              
238             Test if C<THING> is a file.
239              
240             =head2 file_exists THING
241              
242             Test if C<THING> is a file. This method also searches all the C<include paths>
243             and returns the full path to the file if it exists.
244              
245             =head1 AUTHOR
246              
247             Burak Gursoy <burak@cpan.org>.
248              
249             =head1 COPYRIGHT
250              
251             Copyright 2004 - 2016 Burak Gursoy. All rights reserved.
252              
253             =head1 LICENSE
254              
255             This library is free software; you can redistribute it and/or modify
256             it under the same terms as Perl itself, either Perl version 5.24.0 or,
257             at your option, any later version of Perl 5 you may have available.
258             =cut