File Coverage

blib/lib/Brick/Files.pm
Criterion Covered Total %
statement 18 72 25.0
branch 0 28 0.0
condition 0 21 0.0
subroutine 6 15 40.0
pod 0 3 0.0
total 24 139 17.2


line stmt bran cond sub pod time code
1             package Brick::File;
2 5     5   31 use strict;
  5         9  
  5         196  
3              
4 5     5   28 use base qw(Exporter);
  5         12  
  5         450  
5 5     5   38 use vars qw($VERSION);
  5         10  
  5         257  
6              
7             $VERSION = '0.228';
8              
9             package Brick::Bucket;
10 5     5   31 use strict;
  5         11  
  5         151  
11              
12 5     5   29 use Carp qw(croak);
  5         10  
  5         809  
13              
14             =encoding utf8
15              
16             =head1 NAME
17              
18             Brick::File - This is the description
19              
20             =head1 SYNOPSIS
21              
22             see L
23              
24             =head1 DESCRIPTION
25              
26             See C for the general discussion of constraint
27             creation.
28              
29             =head2 Utilities
30              
31             =over 4
32              
33             =cut
34              
35             # returns MIME type from File::MMagic on success, undef otherwise
36             sub _file_magic_type
37             {
38 0     0     my( $bucket, $file ) = @_;
39              
40 0           require File::MMagic;
41              
42 0           my $mm = File::MMagic->new;
43              
44 0   0       my $format = $mm->checktype_filename( $file || '' );
45              
46             ## File::MMagic returns the illegal "application/msword" for all
47             ## microsoft junk.
48             ## We map this to either application/x-msword (default)
49             ## or application/vnd.ms-excel, depending on the extension
50              
51 0           my( $uploaded_ext ) = $file =~ m/\.(\w*)?$/g;
52              
53 0 0         if( $format eq "application/msword" )
    0          
54             {
55 5     5   39 no warnings 'uninitialized';
  5         10  
  5         6339  
56              
57 0 0         $format = ($uploaded_ext =~ /^xl[st]$/)
58             ?
59             "application/vnd.ms-excel"
60             :
61             "application/x-msword";
62             }
63             elsif( $format =~ m|x-system/x-error| )
64             {
65 0           $format = undef;
66             }
67              
68 0           return $format;
69             }
70              
71             sub _get_file_extensions_by_mime_type
72             {
73 0     0     my( $bucket, $type ) = @_;
74              
75 0           require MIME::Types;
76              
77 0           my $mime_types = MIME::Types->new;
78 0   0       my $t = $mime_types->type( $type || '' );
79 0 0         my @extensions = $t ? $t->extensions : ();
80             }
81              
82             =item is_mime_type( HASH_REF )
83              
84             Passes if the file matches one of the listed MIME types.
85              
86             mime_types array reference of possible MIME types
87             file_field the name of the file to check
88              
89             =cut
90              
91             sub is_mime_type {
92 0     0 0   my( $bucket, $setup ) = @_;
93              
94 0           my @caller = $bucket->__caller_chain_as_list;
95              
96 0 0         unless( UNIVERSAL::isa( $setup->{mime_types}, ref [] ) )
97             {
98 0           croak( "The mime_types key must be an array reference!" );
99             }
100              
101             my $hash = {
102             name => $setup->{name} || $caller[0]{'sub'},
103             description => ( $setup->{description} || "Match a file extension" ),
104             fields => [ $setup->{field} ],
105             code => sub {
106 0     0     my( $input ) = @_;
107              
108             die {
109             message => "[$input->{ $setup->{file_field} }] did not exist.",
110             failed_field => $setup->{file_field},
111             failed_value => $input->{ $setup->{file_field} },
112             handler => $caller[0]{'sub'},
113 0 0         } unless -e $input->{ $setup->{file_field} };
114              
115 0           my $mime_type = $bucket->_file_magic_type( $input->{ $setup->{file_field} } );
116              
117             die {
118             message => "[$input->{ $setup->{file_field} }] did not yeild a mime type.",
119             failed_field => $setup->{file_field},
120             failed_value => $input->{ $setup->{file_field} },
121 0 0         handler => $caller[0]{'sub'},
122             } unless $mime_type;
123              
124 0           foreach my $expected_type ( @{ $setup->{mime_types} } )
  0            
125             {
126 0 0         return 1 if lc $mime_type eq lc $expected_type;
127             }
128              
129             die {
130             message => "[$input->{ $setup->{file_field} }] did not have the right mime type. I think it's $mime_type.",
131             failed_field => $setup->{filename},
132             failed_value => $input->{ $setup->{file_field} },
133 0           handler => $caller[0]{'sub'},
134             };
135             },
136 0   0       };
      0        
137              
138 0           $bucket->__make_constraint(
139             $bucket->add_to_bucket ( $hash )
140             );
141              
142             }
143              
144             =item has_file_extension( HASH_REF )
145              
146             This constraint checks the filename against a list of extensions
147             which are the elements of ARRAY_REF.
148              
149             field the name of the field holding the filename
150             extensions an array reference of possible extensions
151              
152             =cut
153              
154             sub Brick::_get_file_extension # just a sub, not a method
155             {
156 0     0     lc +( split /\./, $_[0] )[-1];
157             }
158              
159             sub has_file_extension
160             {
161 0     0 0   my( $bucket, $setup ) = @_;
162              
163 0           my @caller = $bucket->__caller_chain_as_list;
164              
165 0 0         unless( UNIVERSAL::isa( $setup->{extensions}, ref [] ) )
166             {
167 0           croak( "The extensions key must be an array reference!" );
168             }
169              
170 0           my %extensions = map { lc $_, 1 } @{ $setup->{extensions} };
  0            
  0            
171              
172             my $hash = {
173             name => $setup->{name} || $caller[0]{'sub'},
174             description => ( $setup->{description} || "Match a file extension" ),
175             fields => [ $setup->{field} ],
176             code => sub {
177 0     0     my $extension = Brick::_get_file_extension( $_[0]->{ $setup->{field} } );
178              
179             die {
180             message => "[$_[0]->{ $setup->{field} }] did not have the right extension",
181             failed_field => $setup->{field},
182             failed_value => $_[0]->{ $setup->{field} },
183             handler => $caller[0]{'sub'},
184 0 0         } unless exists $extensions{ $extension };
185             },
186 0   0       };
      0        
187              
188 0           $bucket->__make_constraint(
189             $bucket->add_to_bucket ( $hash )
190             );
191              
192             }
193              
194             =item is_clamav_clean( HASH_REF )
195              
196             Passes if ClamAV doesn't complain about the file.
197              
198             clamscan_location the location of ClamAV, or /usr/local/bin/clamscan
199             filename the filename to check
200              
201             The filename can only contain word characters or a period.
202              
203             =cut
204              
205             sub is_clamav_clean {
206 0     0 0   my( $bucket, $setup ) = @_;
207              
208 0           my @caller = $bucket->__caller_chain_as_list;
209              
210 0   0       my $clamscan = $setup->{clamscan_location} || "/usr/local/bin/clamscan";
211              
212             my $hash = {
213             name => $setup->{name} || $caller[0]{'sub'},
214             description => ( $setup->{description} || "Check for viruses" ),
215             fields => [ $setup->{field} ],
216             code => sub {
217 0     0     my( $input ) = @_;
218              
219             die {
220             message => "Could not find clamscan",
221             failed_field => $setup->{clamscan_location},
222             failed_value => $_[0]->{ $setup->{clamscan_location} },
223 0 0         handler => $caller[0]{'sub'},
224             } unless -x $clamscan;
225              
226             die {
227             message => "File name has odd characters",
228             failed_field => $setup->{filename},
229             failed_value => $_[0]->{ $setup->{filename} },
230             handler => $caller[0]{'sub'},
231 0 0         } unless $setup->{filename} =~ m/^[\w.]+\z/;
232              
233             die {
234             message => "Could not find file to check for viruses",
235             failed_field => $setup->{filename},
236             failed_value => $_[0]->{ $setup->{filename} },
237             handler => $caller[0]{'sub'},
238 0 0         } unless -f $setup->{filename};
239              
240 0           my $results = do {
241 0           local $ENV{PATH} = '';
242              
243 0           `$clamscan --no-summary -i --stdout $setup->{filename}`;
244             };
245              
246             die {
247             message => "ClamAV complained: $results",
248             failed_field => $setup->{filename},
249             failed_value => $_[0]->{ $setup->{filename} },
250 0 0         handler => $caller[0]{'sub'},
251             } if $results;
252              
253 0           1;
254             },
255 0   0       };
      0        
256              
257 0           $bucket->__make_constraint(
258             $bucket->add_to_bucket ( $hash )
259             );
260              
261             }
262              
263             =pod
264              
265             sub file_clamav_clean {
266             my $clamscan = "/usr/local/bin/clamscan";
267              
268             return sub {
269             my $dfv = shift;
270             $dfv->name_this('file_clamav_clean');
271             my $q = $dfv->get_input_data;
272              
273             # Set $ENV{PATH} to the empty string to avoid taint error from
274             # exec call. Use local to temporarily clear it out in the context
275             # of this sub.
276             local $ENV{PATH} = q{};
277              
278              
279             $q->UNIVERSAL::can('param') or
280             die 'valid_file_clamav_clean: data object missing param() method';
281              
282             my $field = $dfv->get_current_constraint_field;
283              
284             my $img = $q->upload($field);
285              
286             if (not $img and my $err = $q->cgi_error) {
287             warn $err;
288             return undef;
289             }
290              
291             my $tmp_file = $q->tmpFileName($q->param($field)) or
292             (warn "$0: can't find tmp file for field named $field"),
293             return undef;
294              
295             ## now return true if $tmp_file is not a virus, false otherwise
296             unless (-x $clamscan) {
297             warn "$0: can't find clamscan, skipping test";
298             return 1; # it's valid because we don't see it
299             }
300              
301             defined (my $pid = open KID, "-|") or die "Can't fork: $!";
302             unless ($pid) { # child does:
303             open STDIN, "<$tmp_file" or die "Cannot open $tmp_file for input: $!";
304             exec $clamscan, qw(--no-summary -i --stdout -);
305             die "Cannot find $clamscan: $!";
306             }
307             ## parent does:
308             my $results = join '', ;
309             close KID;
310             return if $results; ## if clamscan spoke, it's a virus
311              
312             return 1;
313             };
314             }
315              
316             =back
317              
318             =head1 TO DO
319              
320             Regex::Common support
321              
322             =head1 SEE ALSO
323              
324             TBA
325              
326             =head1 SOURCE AVAILABILITY
327              
328             This source is in Github:
329              
330             https://github.com/briandfoy/brick
331              
332             =head1 AUTHOR
333              
334             brian d foy, C<< >>
335              
336             =head1 COPYRIGHT
337              
338             Copyright © 2007-2018, brian d foy . All rights reserved.
339              
340             You may redistribute this under the terms of the Artistic License 2.0.
341              
342             =cut
343              
344             1;