File Coverage

blib/lib/CGI/UploadEasy.pm
Criterion Covered Total %
statement 18 87 20.6
branch 0 34 0.0
condition 0 15 0.0
subroutine 6 13 46.1
pod 4 4 100.0
total 28 153 18.3


line stmt bran cond sub pod time code
1             package CGI::UploadEasy;
2              
3 1     1   18900 use 5.006;
  1         4  
  1         43  
4 1     1   6 use strict;
  1         3  
  1         35  
5 1     1   5 use warnings;
  1         5  
  1         47  
6 1     1   2146 use CGI 2.76;
  1         32019  
  1         7  
7 1     1   102 use File::Spec;
  1         3  
  1         25  
8 1     1   6 use Carp;
  1         2  
  1         2105  
9              
10             $Carp::CarpLevel = 1;
11              
12             our $VERSION = '1.00';
13             # $Id: UploadEasy.pm,v 1.8 2009/02/01 21:04:22 gunnarh Exp $
14              
15             =head1 NAME
16              
17             CGI::UploadEasy - Facilitate file uploads
18              
19             =head1 SYNOPSIS
20              
21             use CGI::UploadEasy;
22             my $ue = CGI::UploadEasy->new(-uploaddir => '/path/to/upload/dir');
23             my $cgi = $ue->cgiobject;
24             my $info = $ue->fileinfo;
25              
26             =head1 DESCRIPTION
27              
28             C is a wrapper around, and relies heavily on, L. Its
29             purpose is to provide a simple interface to the upload functionality of C.
30              
31             At creation of the C object, the module saves one or more files
32             from a file upload request in the upload directory, and information about uploaded
33             files is made available via the B method. C performs
34             a number of tests, which limit the risk that you encounter difficulties when
35             developing a file upload application.
36              
37             =head2 Methods
38              
39             =cut
40              
41             sub new {
42 0     0 1   my $class = shift;
43 0           my $self = {
44             maxsize => 1000,
45             &_argscheck,
46             };
47              
48 0           $CGI::POST_MAX = $self->{maxsize} * 1024;
49 0           $CGI::DISABLE_UPLOADS = 0;
50 0 0         $CGITempFile::TMPDIRECTORY = $self->{tempdir} if $self->{tempdir};
51 0           $self->{cgi} = CGI->new;
52 0 0         if ( my $status = $self->{cgi}->cgi_error ) {
53 0           _error($self, $status, "Post too large: Maxsize $self->{maxsize} KiB exceeded.");
54             }
55              
56 0 0 0       if ( $ENV{REQUEST_METHOD} eq 'POST' and $ENV{CONTENT_TYPE} !~ /^multipart\/form-data\b/i ) {
57 0           _error($self, '400 Bad Request', 'The content-type at file uploads shall be '
58             . "'multipart/form-data'.
\nMake sure that the 'FORM' tag includes the "
59             . 'attribute: enctype="multipart/form-data"');
60             }
61              
62 0           $self->{files} = _upload($self);
63              
64 0           bless $self, $class;
65             }
66              
67             =over 4
68              
69             =item Bnew( -uploaddir =E $dir [ , -maxsize =E $kibibytes, ... ] )>
70              
71             The B constructor takes hash style arguments. The following arguments are
72             recognized:
73              
74             =over 4
75              
76             =item B<-uploaddir>
77              
78             Specifying the upload directory is mandatory.
79              
80             =item B<-tempdir>
81              
82             To control which directory will be used for temporary files, set the -tempdir
83             argument.
84              
85             =item B<-maxsize>
86              
87             Specifies the maximum size in KiB (kibibytes) of a POST request data set.
88             Default limit is 1,000 KiB. To disable this ceiling for POST requests, set a
89             negative -maxsize value.
90              
91             =back
92              
93             =back
94              
95             =cut
96              
97             sub cgiobject {
98 0     0 1   my $self = shift;
99 0           $self->{cgi};
100             }
101              
102             =over 4
103              
104             =item B<$ue-Ecgiobject>
105              
106             Returns a reference to the C object that C uses internally,
107             which gives access to all the L methods.
108              
109             If you prefer the function-oriented style, you can import a set of methods
110             instead. Example:
111              
112             use CGI qw/:standard/;
113             print header;
114              
115             =back
116              
117             =cut
118              
119             sub fileinfo {
120 0     0 1   my $self = shift;
121 0 0         if ( @_ ) { croak "The 'fileinfo' method does not take arguments" }
  0            
122 0           $self->{files};
123             }
124              
125             =over 4
126              
127             =item B<$ue-Efileinfo>
128              
129             Returns a reference to a 'hash of hashes' with info about uploaded files. The info
130             may be of use for a result page and/or an email notification, and it lets you use
131             e.g. MIME type and file size as criteria for how to further process the files.
132              
133             =back
134              
135             =cut
136              
137             sub otherparam {
138 0     0 1   my $self = shift;
139 0 0         if ( @_ ) { croak "The 'otherparam' method does not take arguments",
  0            
140             "--use CGI.pm's 'param' method to access values" }
141 0           my $cgi = $self->{cgi};
142 0           grep ! ref $cgi->param($_), $cgi->param;
143             }
144              
145             =over 4
146              
147             =item B<$ue-Eotherparam>
148              
149             The B method returns a list of parameter names besides the names
150             of the file select controls that were used for file uploads. To access the values,
151             use L's B method.
152              
153             =back
154              
155             =cut
156              
157             sub _argscheck {
158 0     0     my %args;
159 0           my %names = (
160             -uploaddir => 'uploaddir',
161             -tempdir => 'tempdir',
162             -maxsize => 'maxsize',
163             );
164 0           local $Carp::CarpLevel = 2;
165              
166 0 0 0       @_ % 2 == 0 and @_ > 0 or croak 'One or more name=>argument pairs are ',
167             'expected at the creation of the CGI::UploadEasy object';
168              
169 0           while ( my $arg = shift ) {
170 0           my $name = lc $arg;
171 0 0         $names{$name} or croak "Unknown argument: '$arg'";
172 0           $args{ $names{$name} } = shift;
173             }
174 0 0         $args{uploaddir} or croak "The compulsory argument '-uploaddir' is missing";
175              
176 0           for my $dir ( @args{ grep exists $args{$_}, qw/uploaddir tempdir/ } ) {
177 0 0         -d $dir or croak "Can't find any directory '$dir'";
178 0 0 0       -r $dir and -w _ and -x _ or croak 'The user this script runs as ',
      0        
179             "does not have write access to '$dir'";
180             }
181 0 0 0       $args{maxsize} and $args{maxsize} !~ /^-?\d+$/
182             and croak "The '-maxsize' argument shall be an integer";
183              
184 0           %args;
185             }
186              
187             sub _upload {
188 0     0     my $self = shift;
189 0           my $cgi = $self->{cgi};
190 0           my %files;
191              
192 0           for my $TEMP ( map $cgi->upload($_), $cgi->param ) {
193 0           ( my $name = $TEMP ) =~ s#.*[\]:\\/]##;
194 0 0         $name =~ tr/ /_/ unless $^O eq 'MSWin32';
195 0           $name =~ tr/-+@a-zA-Z0-9. /_/cs;
196 0           ($name) = $name =~ /^([-+@\w. ]+)$/;
197 0           my $path = File::Spec->catfile( $self->{uploaddir}, $name );
198              
199             # don't overwrite file with same name
200 0           my $i = 2;
201 0           while (1) {
202 0 0         last unless -e $path;
203 0           $name =~ s/([^.]+?)(?:_\d+)?(\.|$)/$1_$i$2/;
204 0           $path = File::Spec->catfile( $self->{uploaddir}, $name );
205 0           $i++;
206             }
207              
208 0           my ($cntrname) = $cgi->uploadInfo($TEMP)->{'Content-Disposition'} =~ /\bname="([^"]+)"/;
209 0           $files{$name} = {
210             ctrlname => $cntrname,
211             mimetype => $cgi->uploadInfo($TEMP)->{'Content-Type'},
212             };
213              
214 0 0         open my $OUT, '>', $path or die "Couldn't open file: $!";
215 0 0         if ( $files{$name}{mimetype} =~ /^text\b/ ) {
216 0           binmode $TEMP, ':crlf';
217 0           print $OUT $_ while <$TEMP>;
218             } else {
219 0           binmode $OUT, ':raw';
220 0           while ( read $TEMP, my $buffer, 1024 ) {
221 0           print $OUT $buffer;
222             }
223             }
224 0 0         close $TEMP or die $!; # so the temporary file gets deleted
225 0 0         close $OUT or die $!; # so file size can be grabbed below
226              
227 0           $files{$name}{bytes} = -s $path;
228             }
229              
230 0           \%files;
231             }
232              
233             sub _error {
234 0     0     my ($self, $status, $msg) = @_;
235 0           my $cgi = $self->{cgi};
236 0           print $cgi->header(-status => $status),
237             $cgi->start_html(-title => "Error $status"),
238             $cgi->h1('Error'),
239             $cgi->tt($msg),
240             $cgi->end_html;
241 0           exit 1;
242             }
243              
244             1;
245              
246             __END__