File Coverage

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( [ property => value, ... ] )
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 is an alias for qnames.)
314              
315             =item B( name [, index] )
316              
317             Returns the value of the query variable I. If there is more than one variable with the same name
318             a list will be returned, or if I is provided, the value in the specified list position. I is
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( code [, message] )
326              
327             Sets the HTTP "Status" code and, optionally, the associated message.
328              
329             =item B( params )
330              
331             Sets a cookie. Must be called before headers are sent (see I). I is a hashref
332             with the cookie parameters: I, I, I, I, I, I, I.
333              
334             =item B( name )
335              
336             Returns the value of the named cookie.
337              
338             =item B( params )
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( string )

343              
344             Prints I to the client. Sends the headers first, if they haven't already been sent.
345              
346             =item B( destination )
347              
348             Sends an HTTP redirect (status code 302) to the client with Location set to I.
349              
350             =item B( key, value )
351              
352             Sets header I to I. Must be called before the first call to I

(I) as the headers

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( time )
360              
361             Returns a header-ish date from a unix-epoch time value.
362              
363             =item B( string )
364              
365             Returns an encoded copy of I with all non-matching /[^a-z0-9_\-\.,?:;\(\)\@! ]/ characters
366             replaced with numeric HTML entities.
367              
368             =item B( string )
369              
370             Returns an encoded copy of I with all non-matching /[^a-z0-9_]/ characters
371             replaced with URL-encoded hexadecimal values (e.g., %20 for space).
372              
373             =item B( string )
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 header that gets sent to the client.
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