1package CGI::Cookie; 2 3# See the bottom of this file for the POD documentation. Search for the 4# string '=head'. 5 6# You can run this file through either pod2man or pod2html to produce pretty 7# documentation in manual or html file format (these utilities are part of the 8# Perl 5 distribution). 9 10# Copyright 1995-1999, Lincoln D. Stein. All rights reserved. 11# It may be used and modified freely, but I do request that this copyright 12# notice remain attached to the file. You may modify this module as you 13# wish, but if you redistribute a modified version, please attach a note 14# listing the modifications you have made. 15 16$CGI::Cookie::VERSION='1.28'; 17 18use CGI::Util qw(rearrange unescape escape); 19use CGI; 20use overload '""' => \&as_string, 21 'cmp' => \&compare, 22 'fallback'=>1; 23 24# Turn on special checking for Doug MacEachern's modperl 25my $MOD_PERL = 0; 26if (exists $ENV{MOD_PERL}) { 27 if (exists $ENV{MOD_PERL_API_VERSION} && $ENV{MOD_PERL_API_VERSION} == 2) { 28 $MOD_PERL = 2; 29 require Apache2::RequestUtil; 30 require APR::Table; 31 } else { 32 $MOD_PERL = 1; 33 require Apache; 34 } 35} 36 37# fetch a list of cookies from the environment and 38# return as a hash. the cookies are parsed as normal 39# escaped URL data. 40sub fetch { 41 my $class = shift; 42 my $raw_cookie = get_raw_cookie(@_) or return; 43 return $class->parse($raw_cookie); 44} 45 46# Fetch a list of cookies from the environment or the incoming headers and 47# return as a hash. The cookie values are not unescaped or altered in any way. 48 sub raw_fetch { 49 my $class = shift; 50 my $raw_cookie = get_raw_cookie(@_) or return; 51 my %results; 52 my($key,$value); 53 54 my(@pairs) = split("[;,] ?",$raw_cookie); 55 foreach (@pairs) { 56 s/\s*(.*?)\s*/$1/; 57 if (/^([^=]+)=(.*)/) { 58 $key = $1; 59 $value = $2; 60 } 61 else { 62 $key = $_; 63 $value = ''; 64 } 65 $results{$key} = $value; 66 } 67 return \%results unless wantarray; 68 return %results; 69} 70 71sub get_raw_cookie { 72 my $r = shift; 73 $r ||= eval { $MOD_PERL == 2 ? 74 Apache2::RequestUtil->request() : 75 Apache->request } if $MOD_PERL; 76 if ($r) { 77 $raw_cookie = $r->headers_in->{'Cookie'}; 78 } else { 79 if ($MOD_PERL && !exists $ENV{REQUEST_METHOD}) { 80 die "Run $r->subprocess_env; before calling fetch()"; 81 } 82 $raw_cookie = $ENV{HTTP_COOKIE} || $ENV{COOKIE}; 83 } 84} 85 86 87sub parse { 88 my ($self,$raw_cookie) = @_; 89 my %results; 90 91 my(@pairs) = split("; ?",$raw_cookie); 92 foreach (@pairs) { 93 s/\s*(.*?)\s*/$1/; 94 my($key,$value) = split("=",$_,2); 95 96 # Some foreign cookies are not in name=value format, so ignore 97 # them. 98 next if !defined($value); 99 my @values = (); 100 if ($value ne '') { 101 @values = map unescape($_),split(/[&;]/,$value.'&dmy'); 102 pop @values; 103 } 104 $key = unescape($key); 105 # A bug in Netscape can cause several cookies with same name to 106 # appear. The FIRST one in HTTP_COOKIE is the most recent version. 107 $results{$key} ||= $self->new(-name=>$key,-value=>\@values); 108 } 109 return \%results unless wantarray; 110 return %results; 111} 112 113sub new { 114 my $class = shift; 115 $class = ref($class) if ref($class); 116 # Ignore mod_perl request object--compatability with Apache::Cookie. 117 shift if ref $_[0] 118 && eval { $_[0]->isa('Apache::Request::Req') || $_[0]->isa('Apache') }; 119 my($name,$value,$path,$domain,$secure,$expires,$httponly) = 120 rearrange([NAME,[VALUE,VALUES],PATH,DOMAIN,SECURE,EXPIRES,HTTPONLY],@_); 121 122 # Pull out our parameters. 123 my @values; 124 if (ref($value)) { 125 if (ref($value) eq 'ARRAY') { 126 @values = @$value; 127 } elsif (ref($value) eq 'HASH') { 128 @values = %$value; 129 } 130 } else { 131 @values = ($value); 132 } 133 134 bless my $self = { 135 'name'=>$name, 136 'value'=>[@values], 137 },$class; 138 139 # IE requires the path and domain to be present for some reason. 140 $path ||= "/"; 141 # however, this breaks networks which use host tables without fully qualified 142 # names, so we comment it out. 143 # $domain = CGI::virtual_host() unless defined $domain; 144 145 $self->path($path) if defined $path; 146 $self->domain($domain) if defined $domain; 147 $self->secure($secure) if defined $secure; 148 $self->expires($expires) if defined $expires; 149 $self->httponly($httponly) if defined $httponly; 150# $self->max_age($expires) if defined $expires; 151 return $self; 152} 153 154sub as_string { 155 my $self = shift; 156 return "" unless $self->name; 157 158 my(@constant_values,$domain,$path,$expires,$max_age,$secure,$httponly); 159 160 push(@constant_values,"domain=$domain") if $domain = $self->domain; 161 push(@constant_values,"path=$path") if $path = $self->path; 162 push(@constant_values,"expires=$expires") if $expires = $self->expires; 163 push(@constant_values,"max-age=$max_age") if $max_age = $self->max_age; 164 push(@constant_values,"secure") if $secure = $self->secure; 165 push(@constant_values,"HttpOnly") if $httponly = $self->httponly; 166 167 my($key) = escape($self->name); 168 my($cookie) = join("=",(defined $key ? $key : ''),join("&",map escape(defined $_ ? $_ : ''),$self->value)); 169 return join("; ",$cookie,@constant_values); 170} 171 172sub compare { 173 my $self = shift; 174 my $value = shift; 175 return "$self" cmp $value; 176} 177 178sub bake { 179 my ($self, $r) = @_; 180 181 $r ||= eval { 182 $MOD_PERL == 2 183 ? Apache2::RequestUtil->request() 184 : Apache->request 185 } if $MOD_PERL; 186 if ($r) { 187 $r->headers_out->add('Set-Cookie' => $self->as_string); 188 } else { 189 print CGI::header(-cookie => $self); 190 } 191 192} 193 194# accessors 195sub name { 196 my $self = shift; 197 my $name = shift; 198 $self->{'name'} = $name if defined $name; 199 return $self->{'name'}; 200} 201 202sub value { 203 my $self = shift; 204 my $value = shift; 205 if (defined $value) { 206 my @values; 207 if (ref($value)) { 208 if (ref($value) eq 'ARRAY') { 209 @values = @$value; 210 } elsif (ref($value) eq 'HASH') { 211 @values = %$value; 212 } 213 } else { 214 @values = ($value); 215 } 216 $self->{'value'} = [@values]; 217 } 218 return wantarray ? @{$self->{'value'}} : $self->{'value'}->[0] 219} 220 221sub domain { 222 my $self = shift; 223 my $domain = shift; 224 $self->{'domain'} = lc $domain if defined $domain; 225 return $self->{'domain'}; 226} 227 228sub secure { 229 my $self = shift; 230 my $secure = shift; 231 $self->{'secure'} = $secure if defined $secure; 232 return $self->{'secure'}; 233} 234 235sub expires { 236 my $self = shift; 237 my $expires = shift; 238 $self->{'expires'} = CGI::Util::expires($expires,'cookie') if defined $expires; 239 return $self->{'expires'}; 240} 241 242sub max_age { 243 my $self = shift; 244 my $expires = shift; 245 $self->{'max-age'} = CGI::Util::expire_calc($expires)-time() if defined $expires; 246 return $self->{'max-age'}; 247} 248 249sub path { 250 my $self = shift; 251 my $path = shift; 252 $self->{'path'} = $path if defined $path; 253 return $self->{'path'}; 254} 255 256 257sub httponly { # HttpOnly 258 my $self = shift; 259 my $httponly = shift; 260 $self->{'httponly'} = $httponly if defined $httponly; 261 return $self->{'httponly'}; 262} 263 2641; 265 266=head1 NAME 267 268CGI::Cookie - Interface to Netscape Cookies 269 270=head1 SYNOPSIS 271 272 use CGI qw/:standard/; 273 use CGI::Cookie; 274 275 # Create new cookies and send them 276 $cookie1 = new CGI::Cookie(-name=>'ID',-value=>123456); 277 $cookie2 = new CGI::Cookie(-name=>'preferences', 278 -value=>{ font => Helvetica, 279 size => 12 } 280 ); 281 print header(-cookie=>[$cookie1,$cookie2]); 282 283 # fetch existing cookies 284 %cookies = fetch CGI::Cookie; 285 $id = $cookies{'ID'}->value; 286 287 # create cookies returned from an external source 288 %cookies = parse CGI::Cookie($ENV{COOKIE}); 289 290=head1 DESCRIPTION 291 292CGI::Cookie is an interface to Netscape (HTTP/1.1) cookies, an 293innovation that allows Web servers to store persistent information on 294the browser's side of the connection. Although CGI::Cookie is 295intended to be used in conjunction with CGI.pm (and is in fact used by 296it internally), you can use this module independently. 297 298For full information on cookies see 299 300 http://www.ics.uci.edu/pub/ietf/http/rfc2109.txt 301 302=head1 USING CGI::Cookie 303 304CGI::Cookie is object oriented. Each cookie object has a name and a 305value. The name is any scalar value. The value is any scalar or 306array value (associative arrays are also allowed). Cookies also have 307several optional attributes, including: 308 309=over 4 310 311=item B<1. expiration date> 312 313The expiration date tells the browser how long to hang on to the 314cookie. If the cookie specifies an expiration date in the future, the 315browser will store the cookie information in a disk file and return it 316to the server every time the user reconnects (until the expiration 317date is reached). If the cookie species an expiration date in the 318past, the browser will remove the cookie from the disk file. If the 319expiration date is not specified, the cookie will persist only until 320the user quits the browser. 321 322=item B<2. domain> 323 324This is a partial or complete domain name for which the cookie is 325valid. The browser will return the cookie to any host that matches 326the partial domain name. For example, if you specify a domain name 327of ".capricorn.com", then Netscape will return the cookie to 328Web servers running on any of the machines "www.capricorn.com", 329"ftp.capricorn.com", "feckless.capricorn.com", etc. Domain names 330must contain at least two periods to prevent attempts to match 331on top level domains like ".edu". If no domain is specified, then 332the browser will only return the cookie to servers on the host the 333cookie originated from. 334 335=item B<3. path> 336 337If you provide a cookie path attribute, the browser will check it 338against your script's URL before returning the cookie. For example, 339if you specify the path "/cgi-bin", then the cookie will be returned 340to each of the scripts "/cgi-bin/tally.pl", "/cgi-bin/order.pl", and 341"/cgi-bin/customer_service/complain.pl", but not to the script 342"/cgi-private/site_admin.pl". By default, the path is set to "/", so 343that all scripts at your site will receive the cookie. 344 345=item B<4. secure flag> 346 347If the "secure" attribute is set, the cookie will only be sent to your 348script if the CGI request is occurring on a secure channel, such as SSL. 349 350=item B<4. httponly flag> 351 352If the "httponly" attribute is set, the cookie will only be accessible 353through HTTP Requests. This cookie will be inaccessible via JavaScript 354(to prevent XSS attacks). 355 356But, currently this feature only used and recognised by 357MS Internet Explorer 6 Service Pack 1 and later. 358 359See this URL for more information: 360 361L<http://msdn.microsoft.com/workshop/author/dhtml/httponly_cookies.asp> 362 363=back 364 365=head2 Creating New Cookies 366 367 my $c = new CGI::Cookie(-name => 'foo', 368 -value => 'bar', 369 -expires => '+3M', 370 -domain => '.capricorn.com', 371 -path => '/cgi-bin/database', 372 -secure => 1 373 ); 374 375Create cookies from scratch with the B<new> method. The B<-name> and 376B<-value> parameters are required. The name must be a scalar value. 377The value can be a scalar, an array reference, or a hash reference. 378(At some point in the future cookies will support one of the Perl 379object serialization protocols for full generality). 380 381B<-expires> accepts any of the relative or absolute date formats 382recognized by CGI.pm, for example "+3M" for three months in the 383future. See CGI.pm's documentation for details. 384 385B<-domain> points to a domain name or to a fully qualified host name. 386If not specified, the cookie will be returned only to the Web server 387that created it. 388 389B<-path> points to a partial URL on the current server. The cookie 390will be returned to all URLs beginning with the specified path. If 391not specified, it defaults to '/', which returns the cookie to all 392pages at your site. 393 394B<-secure> if set to a true value instructs the browser to return the 395cookie only when a cryptographic protocol is in use. 396 397B<-httponly> if set to a true value, the cookie will not be accessible 398via JavaScript. 399 400For compatibility with Apache::Cookie, you may optionally pass in 401a mod_perl request object as the first argument to C<new()>. It will 402simply be ignored: 403 404 my $c = new CGI::Cookie($r, 405 -name => 'foo', 406 -value => ['bar','baz']); 407 408=head2 Sending the Cookie to the Browser 409 410The simplest way to send a cookie to the browser is by calling the bake() 411method: 412 413 $c->bake; 414 415Under mod_perl, pass in an Apache request object: 416 417 $c->bake($r); 418 419If you want to set the cookie yourself, Within a CGI script you can send 420a cookie to the browser by creating one or more Set-Cookie: fields in the 421HTTP header. Here is a typical sequence: 422 423 my $c = new CGI::Cookie(-name => 'foo', 424 -value => ['bar','baz'], 425 -expires => '+3M'); 426 427 print "Set-Cookie: $c\n"; 428 print "Content-Type: text/html\n\n"; 429 430To send more than one cookie, create several Set-Cookie: fields. 431 432If you are using CGI.pm, you send cookies by providing a -cookie 433argument to the header() method: 434 435 print header(-cookie=>$c); 436 437Mod_perl users can set cookies using the request object's header_out() 438method: 439 440 $r->headers_out->set('Set-Cookie' => $c); 441 442Internally, Cookie overloads the "" operator to call its as_string() 443method when incorporated into the HTTP header. as_string() turns the 444Cookie's internal representation into an RFC-compliant text 445representation. You may call as_string() yourself if you prefer: 446 447 print "Set-Cookie: ",$c->as_string,"\n"; 448 449=head2 Recovering Previous Cookies 450 451 %cookies = fetch CGI::Cookie; 452 453B<fetch> returns an associative array consisting of all cookies 454returned by the browser. The keys of the array are the cookie names. You 455can iterate through the cookies this way: 456 457 %cookies = fetch CGI::Cookie; 458 foreach (keys %cookies) { 459 do_something($cookies{$_}); 460 } 461 462In a scalar context, fetch() returns a hash reference, which may be more 463efficient if you are manipulating multiple cookies. 464 465CGI.pm uses the URL escaping methods to save and restore reserved characters 466in its cookies. If you are trying to retrieve a cookie set by a foreign server, 467this escaping method may trip you up. Use raw_fetch() instead, which has the 468same semantics as fetch(), but performs no unescaping. 469 470You may also retrieve cookies that were stored in some external 471form using the parse() class method: 472 473 $COOKIES = `cat /usr/tmp/Cookie_stash`; 474 %cookies = parse CGI::Cookie($COOKIES); 475 476If you are in a mod_perl environment, you can save some overhead by 477passing the request object to fetch() like this: 478 479 CGI::Cookie->fetch($r); 480 481=head2 Manipulating Cookies 482 483Cookie objects have a series of accessor methods to get and set cookie 484attributes. Each accessor has a similar syntax. Called without 485arguments, the accessor returns the current value of the attribute. 486Called with an argument, the accessor changes the attribute and 487returns its new value. 488 489=over 4 490 491=item B<name()> 492 493Get or set the cookie's name. Example: 494 495 $name = $c->name; 496 $new_name = $c->name('fred'); 497 498=item B<value()> 499 500Get or set the cookie's value. Example: 501 502 $value = $c->value; 503 @new_value = $c->value(['a','b','c','d']); 504 505B<value()> is context sensitive. In a list context it will return 506the current value of the cookie as an array. In a scalar context it 507will return the B<first> value of a multivalued cookie. 508 509=item B<domain()> 510 511Get or set the cookie's domain. 512 513=item B<path()> 514 515Get or set the cookie's path. 516 517=item B<expires()> 518 519Get or set the cookie's expiration time. 520 521=back 522 523 524=head1 AUTHOR INFORMATION 525 526Copyright 1997-1998, Lincoln D. Stein. All rights reserved. 527 528This library is free software; you can redistribute it and/or modify 529it under the same terms as Perl itself. 530 531Address bug reports and comments to: lstein@cshl.org 532 533=head1 BUGS 534 535This section intentionally left blank. 536 537=head1 SEE ALSO 538 539L<CGI::Carp>, L<CGI> 540 541=cut 542