File Coverage

blib/lib/CGI/Thin.pm
Criterion Covered Total %
statement 13 57 22.8
branch 0 16 0.0
condition 0 5 0.0
subroutine 4 8 50.0
pod 0 4 0.0
total 17 90 18.8


line stmt bran cond sub pod time code
1             #!/usr/local/bin/perl
2              
3             package CGI::Thin;
4 1     1   858 use strict;
  1         2  
  1         41  
5              
6             BEGIN {
7 1     1   6 use Exporter ();
  1         2  
  1         22  
8 1     1   6 use vars qw ($VERSION @ISA @EXPORT @EXPORT_OK);
  1         5  
  1         123  
9 1     1   4 $VERSION = 0.52;
10 1         15 @ISA = qw (Exporter);
11 1         2 @EXPORT = qw (&Parse_CGI);
12 1         952 @EXPORT_OK = qw (&Force_Array);
13             }
14              
15             ########################################### main pod documentation begin ##
16              
17             =head1 NAME
18              
19             CGI::Thin - A very lightweight Parser for CGI Forms
20              
21             =head1 SYNOPSIS
22              
23             C
24              
25             C
26              
27             =head1 DESCRIPTION
28              
29             This module is a very lightweight parser of CGI forms. And it has a
30             special feature that it will return an array if the same key is used
31             twice in the form. You can force an array even if only one value returned
32             to avoid complications.
33              
34             The hash %cgi_data will have all the form data from either a POST or GET form
35             and will also work for "multipart/form-data" forms necessary for uploading files.
36              
37             =head1 USAGE
38              
39             Functions
40              
41             * `CGI::Thin::Parse_CGI(@keys)'
42             The optional @keys will be used to force arrays to be returned.
43              
44             The function also has special features for getting multiple values for a
45             single form key. For example if we have this form...
46              
47             red
48             green
49             blue
50              
51             One of three things can happen.
52              
53             1) The user does not select any color.
54             So $cgi_data{'color'} will not exist.
55             2) The user selects exactly one color.
56             So $cgi_data{'color'} will be the scalar value selected.
57             3) The user selects exactly more than one color.
58             So $cgi_data{'color'} will be a reference to an array of the values selected.
59              
60             To fix this you could call the parser by giving it a list of keys that you want
61             to force to be arrays. In this case like...
62              
63             use CGI::Thin;
64             my %cgi_data = &Parse_CGI ('color');
65              
66             Now it they pick exactly one color, $cgi_data{'color'} will be a reference to
67             an array of the one value selected. And thus there will be no need for
68             special cases later in the code.
69              
70             =head1 BUGS
71              
72             =head2 Fixed
73              
74             =over 4
75              
76             =item *
77              
78             Added %([0-9a-fA-F]{2} to the regular expression to avoid illegal escapes
79              
80             =item *
81              
82             Now split the key/value pairs by [;&] not just the ampersand
83              
84             =back
85              
86             =head2 Pending
87              
88             =over 4
89              
90             =item *
91              
92             Long headers lines that have been broken over multiple lines in
93             multipart/form-data don't seem to be handled.
94              
95             =item *
96              
97             Large file uploads (like 150MB) will clobber main memory. One possible addition is
98             to change how multipart/form-data is read and to spit files directly to the temp directory
99             and return to the script a filename so it can be retreived from there.
100              
101             =item *
102              
103             Any thoughts on adapting it for use withing a mod_perl environment?
104              
105             Under Apache::Registry, which emulates a CGI environmnet, it should be.
106             Under plain ol' mod_perl, we need to interact with the
107             Apache::Request class a bit instead of %ENV and STDIN.
108              
109             This feature may be added in the next incarnation of the module, or possibly a companion
110             CGI::Thin::Mod_Perlish may be created to do it if the code will be too different.
111              
112             =back
113              
114             =head1 SEE ALSO
115              
116             CGI::Thin::Cookies
117              
118             =head1 SUPPORT
119              
120             Visit CGI::Thin's web site at
121             http://www.PlatypiVentures.com/perl/modules/cgi_thin.shtml
122             Send email to
123             mailto:cgi_thin@PlatypiVentures.com
124              
125             =head1 AUTHOR
126              
127             R. Geoffrey Avery
128             CPAN ID: RGEOFFREY
129             modules@PlatypiVentures.com
130             http://www.PlatypiVentures.com/perl
131              
132             =head1 COPYRIGHT
133              
134             This module is free software, you may redistribute it or modify in under the same terms as Perl itself.
135              
136             =cut
137              
138             ############################################# main pod documentation end ##
139              
140             ################################################ subroutine header begin ##
141             ################################################## subroutine header end ##
142              
143             sub Parse_CGI
144             {
145 0     0 0   my %hash = ();
146              
147 0           foreach my $entry (split(/[&;]/, $ENV{'QUERY_STRING'})) {
148 0           &Insert_Item (\%hash, &Divide_Item ($entry));
149             }
150              
151 0 0 0       if ((defined $ENV{'CONTENT_TYPE'}) && ($ENV{'CONTENT_TYPE'} =~ m|multipart/form-data|si)) {
    0          
152 0           my $in;
153 0           read(STDIN, $in, $ENV{'CONTENT_LENGTH'});
154              
155             ### Find the field "boundary" string.
156 0           my $boundary = substr($in, 0, index($in, "\r\n") - 1);
157             ### Tokenize the input.
158 0           my @args = split(/\s*$boundary\s*/s, $in);
159             ### remove extra pieces before first and after last boundary
160 0           shift @args;
161 0           pop @args;
162              
163 0           foreach my $entry (@args) {
164             # Split the token into header and content
165 0           my ($head, $item) = split(/\r\n\r\n/ios, $entry, 2);
166              
167             # ... name="CGI_FILE" filename="myfile.txt" ....
168             # so this is a bit of a trick, based on the double
169             # occurence of 'name'.
170 0           my ($name, $file) = ($head =~ /name="(.*?)"/gios);
171              
172 0           my $mimetype;
173 0 0         if ($head =~ /Content-type:\s*(\S+)/gios) {
174 0           $mimetype = $1;
175             }
176              
177             ### Build a hash for the file if a filename was specified
178             $item = {
179 0 0 0       "Name" => $file,
180             "Content" => $item,
181             "MIME_Type" => $mimetype || 'unknown mime type',
182             "head" => $head,
183             } if ($file);
184              
185 0           &Insert_Item (\%hash, $name, $item);
186              
187             } # foreach
188            
189             } elsif( $ENV{'REQUEST_METHOD'} eq "POST" ){
190 0           my $in;
191 0           read(STDIN, $in, $ENV{'CONTENT_LENGTH'});
192            
193 0           foreach my $entry (split(/[&;]/, $in)) {
194 0           &Insert_Item (\%hash, &Divide_Item ($entry));
195             }
196             }
197              
198 0           foreach (@_) {
199 0 0         $hash{$_} = &Force_Array ($hash{$_}) if ($hash{$_});
200             }
201              
202 0           return (%hash);
203              
204             }
205              
206             ################################################ subroutine header begin ##
207             # Convert plus's to spaces
208             # Convert %XX from hex numbers to alphanumeric
209             # Return key and value
210             ################################################## subroutine header end ##
211              
212             sub Divide_Item
213             {
214 0     0 0   my ($item) = @_;
215              
216 0           $item =~ tr/+/ /;
217 0           my ($key, $value) = split ("=", $item, 2);
218 0           $key =~ s/%([A-Fa-f0-9]{2})/chr(hex($1))/ge;
  0            
219 0           $value =~ s/%([A-Fa-f0-9]{2})/chr(hex($1))/ge;
  0            
220 0           return ($key, $value);
221             }
222              
223              
224             ################################################ subroutine header begin ##
225             ################################################## subroutine header end ##
226              
227             sub Insert_Item
228             {
229 0     0 0   my ($p_hash, $key, $val) = @_;
230              
231 0 0         if ( defined($p_hash->{$key})) {
232 0 0         unless (ref ($p_hash->{$key}) eq "ARRAY") {
233 0           my $firstval = $p_hash->{$key};
234 0           $p_hash->{$key} = [$firstval];
235             }
236 0           push (@{$p_hash->{$key}}, $val);
  0            
237             } else {
238 0           $p_hash->{$key} = $val;
239             }
240             }
241              
242             ################################################ subroutine header begin ##
243             ################################################## subroutine header end ##
244              
245             sub Force_Array
246             {
247 0     0 0   my ($item) = @_;
248              
249 0 0         $item = [$item] unless( ref($item) eq "ARRAY" );
250              
251 0           return ($item);
252             }
253              
254             ###########################################################################
255             ###########################################################################
256              
257             1;
258              
259             __END__