| 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 |