mark: A photo of Mark kneeling on top of the Taal Volcano in the Philippines. It was a long hike. (Default)
Mark Smith ([staff profile] mark) wrote in [site community profile] changelog2010-04-30 07:08 pm

[dw-free] Multiple domain cookies for same domain cause infinite loop.

[commit: http://hg.dwscoalition.org/dw-free/rev/cc792aafd776]

http://bugs.dwscoalition.org/show_bug.cgi?id=2574

Fix support for multiple cookies with the same name.

Patch by [personal profile] exor674.

Files modified:
  • cgi-bin/DW/Request/Base.pm
  • cgi-bin/LJ/Session.pm
--------------------------------------------------------------------------------
diff -r 7e8ff7c26de6 -r cc792aafd776 cgi-bin/DW/Request/Base.pm
--- a/cgi-bin/DW/Request/Base.pm	Fri Apr 30 09:21:49 2010 +0000
+++ b/cgi-bin/DW/Request/Base.pm	Fri Apr 30 19:08:00 2010 +0000
@@ -19,9 +19,11 @@ use strict;
 use strict;
 use Carp qw/ confess cluck /;
 use CGI::Cookie;
+use CGI::Util qw( unescape );
 
 use fields (
             'cookies_in',
+            'cookies_in_multi',
         );
 
 sub new {
@@ -30,14 +32,22 @@ sub new {
         unless ref $self;
 
     $self->{cookies_in} = undef;
+    $self->{cookies_in_multi} = undef;
 }
 
 sub cookie {
     my DW::Request::Base $self = $_[0];
 
-    $self->{cookies_in} ||= { CGI::Cookie->parse( $self->header_in( 'Cookie' ) ) };
-    return unless exists $self->{cookies_in}->{$_[1]};
-    return $self->{cookies_in}->{$_[1]}->value;
+    $self->parse( $self->header_in( 'Cookie' ) ) unless defined $self->{cookies_in};
+    my $val = $self->{cookies_in}->{$_[1]};
+    return wantarray ? @{ $val } : $val->[0];
+}
+
+sub cookie_multi {
+    my DW::Request::Base $self = $_[0];
+
+    $self->parse( $self->header_in( 'Cookie' ) ) unless defined $self->{cookies_in_multi};
+    return @{ $self->{cookies_in_multi}->{$_[1]} || [] };
 }
 
 sub add_cookie {
@@ -66,4 +76,40 @@ sub delete_cookie {
     return $self->add_cookie( %args );
 }
 
+#
+# Following sub was copied from CGI::Cookie and modified.
+#
+# Copyright 1995-1999, Lincoln D. Stein.  All rights reserved.
+# It may be used and modified freely, but I do request that this copyright
+# notice remain attached to the file.  You may modify this module as you 
+# wish, but if you redistribute a modified version, please attach a note
+# listing the modifications you have made.
+#
+sub parse {
+    my DW::Request::Base $self = $_[0];
+    my %results;
+    my %results_multi;
+
+    my @pairs = split( "[;,] ?", $_[1] );
+    foreach ( @pairs ) {
+        $_ =~ s/\s*(.*?)\s*/$1/;
+        my ( $key, $value ) = split( "=", $_, 2 );
+        
+        # Some foreign cookies are not in name=value format, so ignore
+        # them.
+        next unless defined( $value );
+        my @values = ();
+        if ( $value ne '' ) {
+          @values = map unescape( $_ ), split( /[&;]/, $value . '&dmy' );
+          pop @values;
+        }
+        $key = unescape( $key );
+        $results{$key} ||= \@values;
+        push @{ $results_multi{$key} }, \@values;
+    }
+
+    $self->{cookies_in} = \%results;
+    $self->{cookies_in_multi} = \%results_multi;
+}
+
 1;
diff -r 7e8ff7c26de6 -r cc792aafd776 cgi-bin/LJ/Session.pm
--- a/cgi-bin/LJ/Session.pm	Fri Apr 30 09:21:49 2010 +0000
+++ b/cgi-bin/LJ/Session.pm	Fri Apr 30 19:08:00 2010 +0000
@@ -491,14 +491,14 @@ sub session_from_cookies {
     my $domain_cookie = LJ::Session->domain_cookie;
     if ($domain_cookie) {
         # journal domain
-        $sessobj = LJ::Session->session_from_domain_cookie( \%getopts, $r->cookie( $domain_cookie ) );
+        $sessobj = LJ::Session->session_from_domain_cookie( \%getopts, $r->cookie_multi( $domain_cookie ) );
     } else {
         # this is the master cookie at "www.livejournal.com" or "livejournal.com";
-        my @cookies = $r->cookie( 'ljmastersession' );
+        my @cookies = $r->cookie_multi( 'ljmastersession' );
         # but support old clients who are just sending an "ljsession" cookie which they got
         # from ljprotocol's "generatesession" mode.
-        unless (@cookies) {
-            @cookies = $r->cookie( 'ljsession' );
+        unless ( @cookies ) {
+            @cookies = $r->cookie_multi( 'ljsession' );
             $getopts{old_cookie} = 1;
         }
         $sessobj = LJ::Session->session_from_master_cookie(\%getopts, @cookies);
@@ -535,7 +535,7 @@ sub session_from_domain_cookie {
     my $domcook = LJ::Session->domain_cookie;
 
     foreach my $cookie (@cookies) {
-        my $sess = valid_domain_cookie($domcook, $cookie, $li_cook);
+        my $sess = valid_domain_cookie($domcook, $cookie->[0], $li_cook);
         return $sess if $sess;
     }
 
@@ -561,7 +561,7 @@ sub session_from_master_cookie {
     my $ignore_ip  = delete $opts->{ignore_ip} ? 1 : 0;
     my $old_cookie = delete $opts->{old_cookie} ? 1 : 0;
 
-    delete $opts->{'redirect_ref'};  # we don't use this
+    delete $opts->{redirect_ref};  # we don't use this
     croak("Unknown options") if %$opts;
 
     my $now = time();
@@ -572,8 +572,8 @@ sub session_from_master_cookie {
     my $li_cook = $r->cookie( 'ljloggedin' );
 
   COOKIE:
-    foreach my $sessdata (@cookies) {
-        my ($cookie, $gen) = split(m!//!, $sessdata);
+    foreach my $sessdata ( @cookies ) {
+        my ( $cookie, $gen ) = split( m!//!, $sessdata->[0] );
 
         my ($version, $userid, $sessid, $auth, $flags);
 
--------------------------------------------------------------------------------