blib/lib/BW/CGI.pm | |||
---|---|---|---|
Criterion | Covered | Total | % |
statement | 15 | 153 | 9.8 |
branch | 0 | 70 | 0.0 |
condition | 0 | 16 | 0.0 |
subroutine | 5 | 32 | 15.6 |
pod | 20 | 23 | 86.9 |
total | 40 | 294 | 13.6 |
line | stmt | bran | cond | sub | pod | time | code |
---|---|---|---|---|---|---|---|
1 | # CGI.pm | ||||||
2 | # by Bill Weinman -- Simple OO CGI | ||||||
3 | # Copyright (c) 1995-2008 The BearHeart Group, LLC | ||||||
4 | # | ||||||
5 | # See POD for History | ||||||
6 | # | ||||||
7 | package BW::CGI; | ||||||
8 | 1 | 1 | 1435 | use strict; | |||
1 | 3 | ||||||
1 | 49 | ||||||
9 | 1 | 1 | 7 | use warnings; | |||
1 | 1 | ||||||
1 | 113 | ||||||
10 | |||||||
11 | 1 | 1 | 6 | use BW::Constants; | |||
1 | 2 | ||||||
1 | 76 | ||||||
12 | 1 | 1 | 955 | use IO::File; | |||
1 | 12302 | ||||||
1 | 193 | ||||||
13 | 1 | 1 | 10 | use base qw( BW::Base ); | |||
1 | 2 | ||||||
1 | 2325 | ||||||
14 | |||||||
15 | our $VERSION = "0.1.7"; | ||||||
16 | |||||||
17 | sub _init | ||||||
18 | { | ||||||
19 | 0 | 0 | my $self = shift; | ||||
20 | 0 | 0 | return FAILURE unless $ENV{GATEWAY_INTERFACE}; | ||||
21 | 0 | $self->SUPER::_init(@_); | |||||
22 | |||||||
23 | # set defaults | ||||||
24 | 0 | 0 | $self->max_content_length( 1024 * 1024 ) unless $self->max_content_length; | ||||
25 | 0 | 0 | $self->content_type('text/html') unless $self->content_type; | ||||
26 | 0 | 0 | $self->host( $ENV{HTTP_HOST} ) unless $self->host; | ||||
27 | |||||||
28 | 0 | $self->_set_query_string; | |||||
29 | |||||||
30 | 0 | return SUCCESS; | |||||
31 | } | ||||||
32 | |||||||
33 | # _setter_getter entry points (see BW::Base) | ||||||
34 | 0 | 0 | 1 | sub content_type { BW::Base::_setter_getter(@_); } | |||
35 | 0 | 0 | 1 | sub host { BW::Base::_setter_getter(@_); } | |||
36 | 0 | 0 | 0 | sub query_string { BW::Base::_setter_getter(@_); } | |||
37 | 0 | 0 | 1 | sub max_content_length { BW::Base::_setter_getter(@_); } | |||
38 | |||||||
39 | sub vars | ||||||
40 | { | ||||||
41 | 0 | 0 | 1 | my $self = shift; | |||
42 | 0 | return $self->{vars}; | |||||
43 | } | ||||||
44 | |||||||
45 | 0 | 0 | 0 | sub q_names { qnames(@_) } | |||
46 | sub qnames | ||||||
47 | { | ||||||
48 | 0 | 0 | 1 | my $self = shift; | |||
49 | 0 | return $self->{q_names}; | |||||
50 | } | ||||||
51 | |||||||
52 | # smart value getter | ||||||
53 | sub qv | ||||||
54 | { | ||||||
55 | 0 | 0 | 1 | my ( $self, $name, $index ) = @_; | |||
56 | 0 | 0 | 0 | return VOID unless $name and $self->{vars}{$name}; | |||
57 | |||||||
58 | 0 | 0 | if ( ref( $self->{vars}{$name} ) ) { | ||||
59 | 0 | 0 | if ( defined $index ) { | ||||
60 | 0 | $self->{q_index}{$name} = $index; | |||||
61 | } else { | ||||||
62 | 0 | 0 | $self->{q_index}{$name} = 0 unless defined $self->{q_index}{$name}; | ||||
63 | 0 | return $self->{vars}{$name}[ $self->{q_index}{$name}++ ]; | |||||
64 | } | ||||||
65 | } else { | ||||||
66 | 0 | return $self->{vars}{$name}; | |||||
67 | } | ||||||
68 | } | ||||||
69 | |||||||
70 | # provide a link back for use in form action attribute | ||||||
71 | sub linkback { | ||||||
72 | 0 | 0 | 0 | 1 | my $l = $ENV{REQUEST_URI} || $ENV{SCRIPT_NAME} || FALSE; | ||
73 | 0 | 0 | $l =~ s/\?.*// if $l; # lose any query part | ||||
74 | 0 | return $l | |||||
75 | } | ||||||
76 | |||||||
77 | 0 | 0 | 0 | sub status { set_status(@_) } # obsolescent alias | |||
78 | sub set_status | ||||||
79 | { | ||||||
80 | 0 | 0 | 1 | my ( $self, $status, $message ) = @_; | |||
81 | 0 | $self->{status} = "$status $message"; | |||||
82 | } | ||||||
83 | |||||||
84 | sub set_header | ||||||
85 | { | ||||||
86 | 0 | 0 | 1 | my ( $self, $k, $v ) = @_; | |||
87 | 0 | push( @{ $self->{headers} }, { k => $k, v => $v } ); | |||||
0 | |||||||
88 | } | ||||||
89 | |||||||
90 | sub set_cookie | ||||||
91 | { | ||||||
92 | 0 | 0 | 1 | my $sn = 'set_cookie'; | |||
93 | 0 | my ( $self, $params, @list ) = @_; | |||||
94 | |||||||
95 | 0 | 0 | if ( !ref($params) ) { # make hashref from list | ||||
96 | 0 | unshift( @list, $params ); | |||||
97 | 0 | $params = {@list}; | |||||
98 | } | ||||||
99 | |||||||
100 | 0 | 0 | my $k = $params->{name} or return $self->_error("$sn: no name"); | ||||
101 | 0 | 0 | my $v = $params->{value} || ''; | ||||
102 | 0 | my $cs = "$k=$v"; | |||||
103 | |||||||
104 | 0 | 0 | $cs .= "; expires=" . $self->header_date( $params->{expires} ) if defined $params->{expires}; | ||||
105 | 0 | 0 | $cs .= "; path=" . $params->{path} if $params->{path}; | ||||
106 | 0 | 0 | $cs .= "; domain=" . $params->{domain} if $params->{domain}; | ||||
107 | 0 | 0 | $cs .= "; secure" if defined $params->{secure}; | ||||
108 | 0 | 0 | $cs .= "; httponly" if defined $params->{httponly}; | ||||
109 | |||||||
110 | 0 | $self->set_header( 'Set-Cookie', $cs ); | |||||
111 | 0 | return SUCCESS; | |||||
112 | } | ||||||
113 | |||||||
114 | sub get_cookie | ||||||
115 | { | ||||||
116 | 0 | 0 | 1 | my ( $self, $cookie_name ) = @_; | |||
117 | 0 | 0 | $self->_get_cookies or return VOID; | ||||
118 | 0 | return $self->{cookies}{$cookie_name}; | |||||
119 | } | ||||||
120 | |||||||
121 | sub _get_cookies | ||||||
122 | { | ||||||
123 | 0 | 0 | my $self = shift; | ||||
124 | |||||||
125 | 0 | 0 | unless ( $self->{get_cookies_flag} ) { | ||||
126 | 0 | 0 | if ( $ENV{HTTP_COOKIE} ) { | ||||
127 | 0 | my @cookies = split( /;\s*/, $ENV{HTTP_COOKIE} ); | |||||
128 | 0 | foreach my $c (@cookies) { | |||||
129 | 0 | my ( $n, $v ) = split( /=/, $c ); | |||||
130 | 0 | $self->{cookies}{$n} = $v; | |||||
131 | } | ||||||
132 | } | ||||||
133 | 0 | $self->{get_cookies_flag} = TRUE; | |||||
134 | } | ||||||
135 | 0 | 0 | return $self->{cookies} || VOID; | ||||
136 | } | ||||||
137 | |||||||
138 | sub clear_cookie | ||||||
139 | { | ||||||
140 | 0 | 0 | 1 | my ( $self, $params, @list ) = @_; | |||
141 | |||||||
142 | 0 | 0 | if ( !ref($params) ) { # make hashref from list | ||||
143 | 0 | unshift( @list, $params ); | |||||
144 | 0 | $params = {@list}; | |||||
145 | } | ||||||
146 | |||||||
147 | 0 | $params->{expires} = 1; # a date in the past: 1970-01-01 00:00:01 | |||||
148 | 0 | return $self->set_cookie($params); | |||||
149 | } | ||||||
150 | |||||||
151 | # print is a necessary alias so that this can be called from Template::process | ||||||
152 | 0 | 0 | 1 | sub print { p(@_) } | |||
153 | sub p | ||||||
154 | { | ||||||
155 | 0 | 0 | 1 | my ( $self, $string ) = @_; | |||
156 | 0 | $self->p_headers; | |||||
157 | 0 | 0 | print $string || ''; | ||||
158 | } | ||||||
159 | |||||||
160 | sub redirect | ||||||
161 | { | ||||||
162 | 0 | 0 | 1 | my ( $self, $dest ) = @_; | |||
163 | |||||||
164 | 0 | $self->set_status( 302, 'Yonder' ); | |||||
165 | 0 | $self->set_header( 'Cache-control', 'no-cache' ); | |||||
166 | 0 | $self->set_header( 'Location', $dest ); | |||||
167 | 0 | $self->p_headers; | |||||
168 | } | ||||||
169 | |||||||
170 | sub p_headers | ||||||
171 | { | ||||||
172 | 0 | 0 | 1 | my $self = shift; | |||
173 | 0 | 0 | return if $self->{header_flag}; | ||||
174 | |||||||
175 | 0 | STDOUT->autoflush(1); | |||||
176 | 0 | 0 | if ( $self->{headers} ) { | ||||
177 | 0 | foreach my $h ( @{ $self->{headers} } ) { | |||||
0 | |||||||
178 | 0 | print $h->{k} . ': ' . $h->{v} . CRLF; | |||||
179 | } | ||||||
180 | } | ||||||
181 | 0 | 0 | print "Status: " . $self->{status} . CRLF if $self->{status}; | ||||
182 | 0 | print "Content-Type: " . $self->content_type . CRLF; | |||||
183 | 0 | print CRLF; | |||||
184 | 0 | $self->{header_flag} = TRUE; | |||||
185 | } | ||||||
186 | |||||||
187 | # make a header-ish date from a time value | ||||||
188 | sub header_date | ||||||
189 | { | ||||||
190 | 0 | 0 | 1 | my ( $self, $t ) = @_; | |||
191 | 0 | 0 | $t = time unless defined $t; | ||||
192 | |||||||
193 | 0 | my ( $sec, $min, $hour, $mday, $mon, $year, $wday, $yday ) = gmtime($t); | |||||
194 | 0 | my @day = qw( Sun Mon Tue Wed Thu Fri Sat ); | |||||
195 | 0 | my @month = qw( Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec ); | |||||
196 | 0 | my $tstr = sprintf( "%s %02d-%s-%04d %02d:%02d:%02d GMT", $day[$wday], $mday, $month[$mon], $year + 1900, $hour, $min, $sec ); | |||||
197 | 0 | return $tstr; | |||||
198 | } | ||||||
199 | |||||||
200 | # allows for more than one value for each key | ||||||
201 | sub _set_query | ||||||
202 | { | ||||||
203 | 0 | 0 | my ( $self, $n, $v ) = @_; | ||||
204 | 0 | 0 | return unless $n; | ||||
205 | |||||||
206 | 0 | $n = $self->url_decode($n); | |||||
207 | 0 | $v = $self->url_decode($v); | |||||
208 | |||||||
209 | 0 | push( @{ $self->{q_names} }, $n ); | |||||
0 | |||||||
210 | |||||||
211 | 0 | 0 | 0 | if ( defined( $self->{vars}{$n} ) and $v ) { | |||
212 | 0 | 0 | if ( ref( $self->{vars}{$n} ) ) { | ||||
213 | 0 | push( @{ $self->{vars}{$n} }, $v ); | |||||
0 | |||||||
214 | } else { | ||||||
215 | 0 | my $qn = [ $self->{vars}{$n}, $v ]; | |||||
216 | 0 | $self->{vars}{$n} = $qn; | |||||
217 | } | ||||||
218 | } else { | ||||||
219 | 0 | $self->{vars}{$n} = $v; | |||||
220 | } | ||||||
221 | |||||||
222 | } | ||||||
223 | |||||||
224 | sub _set_query_string | ||||||
225 | { | ||||||
226 | 0 | 0 | my $sn = '_set_query_string'; | ||||
227 | 0 | my $self = shift; | |||||
228 | |||||||
229 | 0 | 0 | $self->{q_names} = [] unless $self->{q_names}; | ||||
230 | |||||||
231 | 0 | 0 | if ( uc( $ENV{REQUEST_METHOD} ) eq 'GET' ) { | ||||
0 | |||||||
232 | 0 | $self->query_string( $ENV{QUERY_STRING} ); | |||||
233 | } elsif ( uc( $ENV{REQUEST_METHOD} ) eq 'POST' ) { | ||||||
234 | 0 | my $buf; | |||||
235 | 0 | 0 | my $content_length = $ENV{'CONTENT_LENGTH'} || 0; | ||||
236 | 0 | 0 | return FAILURE if $content_length > $self->max_content_length; | ||||
237 | 0 | STDIN->read( $buf, $content_length ); | |||||
238 | 0 | $self->query_string($buf); | |||||
239 | } | ||||||
240 | |||||||
241 | 0 | 0 | my $qs = $self->query_string or return SUCCESS; | ||||
242 | 0 | foreach my $qnv ( split( /[&;]/, $qs ) ) { | |||||
243 | 0 | $self->_set_query( split( /=/, $qnv ) ); | |||||
244 | } | ||||||
245 | |||||||
246 | 0 | return SUCCESS; | |||||
247 | } | ||||||
248 | |||||||
249 | sub html_encode | ||||||
250 | { | ||||||
251 | 0 | 0 | 1 | my ( $self, $s ) = @_; | |||
252 | 0 | 0 | return $s unless $s; | ||||
253 | 0 | $s =~ s/([^a-z0-9_\-\.,?:;\(\)\@! ])/sprintf("%d;", ord($1))/segi; | |||||
0 | |||||||
254 | 0 | return $s; | |||||
255 | } | ||||||
256 | |||||||
257 | sub url_encode | ||||||
258 | { | ||||||
259 | 0 | 0 | 1 | my ( $self, $s ) = @_; | |||
260 | 0 | 0 | return $s unless $s; | ||||
261 | 0 | $s =~ s/([^a-z0-9_ ])/sprintf("%%%02X", ord($1))/segi; | |||||
0 | |||||||
262 | 0 | $s =~ s/ /+/g; | |||||
263 | 0 | return $s; | |||||
264 | } | ||||||
265 | |||||||
266 | sub url_decode | ||||||
267 | { | ||||||
268 | 0 | 0 | 1 | my ( $self, $s ) = @_; | |||
269 | 0 | 0 | return $s unless $s; | ||||
270 | 0 | $s =~ s/\+/ /g; # + is space | |||||
271 | 0 | $s =~ s/\%([a-f0-9]{2})/pack('C', hex($1))/segi; | |||||
0 | |||||||
272 | 0 | return $s; | |||||
273 | } | ||||||
274 | |||||||
275 | 1; | ||||||
276 | |||||||
277 | =head1 NAME | ||||||
278 | |||||||
279 | BW::CGI - Simple OO CGI | ||||||
280 | |||||||
281 | =head1 SYNOPSIS | ||||||
282 | |||||||
283 | use BW::CGI; | ||||||
284 | my $o = BW::CGI->new; | ||||||
285 | |||||||
286 | =head1 METHODS | ||||||
287 | |||||||
288 | =over 4 | ||||||
289 | |||||||
290 | =item B |
||||||
291 | |||||||
292 | Constructs a new BW::CGI object. | ||||||
293 | |||||||
294 | Returns a blessed BW::CGI object reference. | ||||||
295 | Returns undef (VOID) if the object cannot be created. | ||||||
296 | |||||||
297 | Properties can be set by passing their values in a hash or hashref | ||||||
298 | like this: | ||||||
299 | |||||||
300 | my $o = BW::CGI->new ( content_type => 'text/plain' ); | ||||||
301 | |||||||
302 | Or by hashref, like this: | ||||||
303 | |||||||
304 | my $properties = { content_type => 'text/plain' }; | ||||||
305 | my $o = BW::CGI->new ( $properties ); | ||||||
306 | |||||||
307 | =item B |
||||||
308 | |||||||
309 | Returns the parsed results of the query string as a hashref, or undef. | ||||||
310 | |||||||
311 | =item B |
||||||
312 | |||||||
313 | Returns a list of query variable names. (B |
||||||
314 | |||||||
315 | =item B |
||||||
316 | |||||||
317 | Returns the value of the query variable I |
||||||
318 | a list will be returned, or if I |
||||||
319 | zero-based. | ||||||
320 | |||||||
321 | =item B |
||||||
322 | |||||||
323 | Returns a URI for use as a link back in the form action attribute. | ||||||
324 | |||||||
325 | =item B |
||||||
326 | |||||||
327 | Sets the HTTP "Status" code and, optionally, the associated message. | ||||||
328 | |||||||
329 | =item B |
||||||
330 | |||||||
331 | Sets a cookie. Must be called before headers are sent (see I |
||||||
332 | with the cookie parameters: I |
||||||
333 | |||||||
334 | =item B |
||||||
335 | |||||||
336 | Returns the value of the named cookie. | ||||||
337 | |||||||
338 | =item B |
||||||
339 | |||||||
340 | Clears the specified cookie from the browser by setting an empty cookie. The same parameter rules as in set_cookie apply. | ||||||
341 | |||||||
342 | =item B ( string ) B |
||||||
343 | |||||||
344 | Prints I |
||||||
345 | |||||||
346 | =item B |
||||||
347 | |||||||
348 | Sends an HTTP redirect (status code 302) to the client with Location set to I |
||||||
349 | |||||||
350 | =item B |
||||||
351 | |||||||
352 | Sets header I (I |
||||||
353 | are sent to the client at that time. | ||||||
354 | |||||||
355 | =item B |
||||||
356 | |||||||
357 | Sends the headers that have been set with set_header. | ||||||
358 | |||||||
359 | =item B |
||||||
360 | |||||||
361 | Returns a header-ish date from a unix-epoch time value. | ||||||
362 | |||||||
363 | =item B |
||||||
364 | |||||||
365 | Returns an encoded copy of I |
||||||
366 | replaced with numeric HTML entities. | ||||||
367 | |||||||
368 | =item B |
||||||
369 | |||||||
370 | Returns an encoded copy of I |
||||||
371 | replaced with URL-encoded hexadecimal values (e.g., %20 for space). | ||||||
372 | |||||||
373 | =item B |
||||||
374 | |||||||
375 | Returns a URL-decoded copy of I |
||||||
376 | |||||||
377 | =item B |
||||||
378 | |||||||
379 | Returns and clears the object error message. | ||||||
380 | |||||||
381 | =back | ||||||
382 | |||||||
383 | =head1 PROPERTIES | ||||||
384 | |||||||
385 | Properties can be set or retrieved by using their name as a method, e.g.: | ||||||
386 | |||||||
387 | $o->content_type( 'text/plain' ); | ||||||
388 | my $ct = $o->content_type; | ||||||
389 | |||||||
390 | The available properties for this method are: | ||||||
391 | |||||||
392 | =over 4 | ||||||
393 | |||||||
394 | =item B |
||||||
395 | |||||||
396 | The C |
||||||
397 | |||||||
398 | =item B |
||||||
399 | |||||||
400 | Value of HTTP_HOST environment variable. Used for creating links back to self, | ||||||
401 | e.g., in the "action" attribute of form. | ||||||
402 | |||||||
403 | =item B |
||||||
404 | |||||||
405 | The maximum content length allowed from POST method queries. Defaults to 1MB (1,0485,776). | ||||||
406 | |||||||
407 | =back | ||||||
408 | |||||||
409 | =head1 AUTHOR | ||||||
410 | |||||||
411 | Written by Bill Weinman | ||||||
412 | |||||||
413 | =head1 COPYRIGHT | ||||||
414 | |||||||
415 | Copyright (c) 1995-2008 The BearHeart Group, LLC | ||||||
416 | |||||||
417 | =head1 HISTORY | ||||||
418 | |||||||
419 | 2009-11-04 bw -- added linkback method | ||||||
420 | 2008-03-26 bw -- updated and documented | ||||||
421 | 2007-10-20 bw -- initial release. | ||||||
422 | |||||||
423 | =cut | ||||||
424 |