]> scripts.mit.edu Git - autoinstallsdev/mediawiki.git/blob - includes/libs/Cookie.php
MediaWiki 1.30.2
[autoinstallsdev/mediawiki.git] / includes / libs / Cookie.php
1 <?php
2 /**
3  * Cookie for HTTP requests.
4  *
5  * This program is free software; you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation; either version 2 of the License, or
8  * (at your option) any later version.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13  * GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License along
16  * with this program; if not, write to the Free Software Foundation, Inc.,
17  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
18  * http://www.gnu.org/copyleft/gpl.html
19  *
20  * @file
21  * @ingroup HTTP
22  */
23
24 class Cookie {
25         protected $name;
26         protected $value;
27         protected $expires;
28         protected $path;
29         protected $domain;
30         protected $isSessionKey = true;
31         // TO IMPLEMENT  protected $secure
32         // TO IMPLEMENT? protected $maxAge (add onto expires)
33         // TO IMPLEMENT? protected $version
34         // TO IMPLEMENT? protected $comment
35
36         function __construct( $name, $value, $attr ) {
37                 $this->name = $name;
38                 $this->set( $value, $attr );
39         }
40
41         /**
42          * Sets a cookie.  Used before a request to set up any individual
43          * cookies. Used internally after a request to parse the
44          * Set-Cookie headers.
45          *
46          * @param string $value The value of the cookie
47          * @param array $attr Possible key/values:
48          *        expires A date string
49          *        path    The path this cookie is used on
50          *        domain  Domain this cookie is used on
51          * @throws InvalidArgumentException
52          */
53         public function set( $value, $attr ) {
54                 $this->value = $value;
55
56                 if ( isset( $attr['expires'] ) ) {
57                         $this->isSessionKey = false;
58                         $this->expires = strtotime( $attr['expires'] );
59                 }
60
61                 if ( isset( $attr['path'] ) ) {
62                         $this->path = $attr['path'];
63                 } else {
64                         $this->path = '/';
65                 }
66
67                 if ( isset( $attr['domain'] ) ) {
68                         if ( self::validateCookieDomain( $attr['domain'] ) ) {
69                                 $this->domain = $attr['domain'];
70                         }
71                 } else {
72                         throw new InvalidArgumentException( '$attr must contain a domain' );
73                 }
74         }
75
76         /**
77          * Return the true if the cookie is valid is valid.  Otherwise,
78          * false.  The uses a method similar to IE cookie security
79          * described here:
80          * http://kuza55.blogspot.com/2008/02/understanding-cookie-security.html
81          * A better method might be to use a blacklist like
82          * http://publicsuffix.org/
83          *
84          * @todo fixme fails to detect 3-letter top-level domains
85          * @todo fixme fails to detect 2-letter top-level domains for single-domain use (probably
86          * not a big problem in practice, but there are test cases)
87          *
88          * @param string $domain The domain to validate
89          * @param string $originDomain (optional) the domain the cookie originates from
90          * @return bool
91          */
92         public static function validateCookieDomain( $domain, $originDomain = null ) {
93                 $dc = explode( ".", $domain );
94
95                 // Don't allow a trailing dot or addresses without a or just a leading dot
96                 if ( substr( $domain, -1 ) == '.' ||
97                         count( $dc ) <= 1 ||
98                         count( $dc ) == 2 && $dc[0] === ''
99                 ) {
100                         return false;
101                 }
102
103                 // Only allow full, valid IP addresses
104                 if ( preg_match( '/^[0-9.]+$/', $domain ) ) {
105                         if ( count( $dc ) != 4 ) {
106                                 return false;
107                         }
108
109                         if ( ip2long( $domain ) === false ) {
110                                 return false;
111                         }
112
113                         if ( $originDomain == null || $originDomain == $domain ) {
114                                 return true;
115                         }
116
117                 }
118
119                 // Don't allow cookies for "co.uk" or "gov.uk", etc, but allow "supermarket.uk"
120                 if ( strrpos( $domain, "." ) - strlen( $domain ) == -3 ) {
121                         if ( ( count( $dc ) == 2 && strlen( $dc[0] ) <= 2 )
122                                 || ( count( $dc ) == 3 && strlen( $dc[0] ) == "" && strlen( $dc[1] ) <= 2 ) ) {
123                                 return false;
124                         }
125                         if ( ( count( $dc ) == 2 || ( count( $dc ) == 3 && $dc[0] == '' ) )
126                                 && preg_match( '/(com|net|org|gov|edu)\...$/', $domain ) ) {
127                                 return false;
128                         }
129                 }
130
131                 if ( $originDomain != null ) {
132                         if ( substr( $domain, 0, 1 ) != '.' && $domain != $originDomain ) {
133                                 return false;
134                         }
135
136                         if ( substr( $domain, 0, 1 ) == '.'
137                                 && substr_compare(
138                                         $originDomain,
139                                         $domain,
140                                         -strlen( $domain ),
141                                         strlen( $domain ),
142                                         true
143                                 ) != 0
144                         ) {
145                                 return false;
146                         }
147                 }
148
149                 return true;
150         }
151
152         /**
153          * Serialize the cookie jar into a format useful for HTTP Request headers.
154          *
155          * @param string $path The path that will be used. Required.
156          * @param string $domain The domain that will be used. Required.
157          * @return string
158          */
159         public function serializeToHttpRequest( $path, $domain ) {
160                 $ret = '';
161
162                 if ( $this->canServeDomain( $domain )
163                                 && $this->canServePath( $path )
164                                 && $this->isUnExpired() ) {
165                         $ret = $this->name . '=' . $this->value;
166                 }
167
168                 return $ret;
169         }
170
171         /**
172          * @param string $domain
173          * @return bool
174          */
175         protected function canServeDomain( $domain ) {
176                 if ( $domain == $this->domain
177                         || ( strlen( $domain ) > strlen( $this->domain )
178                                 && substr( $this->domain, 0, 1 ) == '.'
179                                 && substr_compare(
180                                         $domain,
181                                         $this->domain,
182                                         -strlen( $this->domain ),
183                                         strlen( $this->domain ),
184                                         true
185                                 ) == 0
186                         )
187                 ) {
188                         return true;
189                 }
190
191                 return false;
192         }
193
194         /**
195          * @param string $path
196          * @return bool
197          */
198         protected function canServePath( $path ) {
199                 return ( $this->path && substr_compare( $this->path, $path, 0, strlen( $this->path ) ) == 0 );
200         }
201
202         /**
203          * @return bool
204          */
205         protected function isUnExpired() {
206                 return $this->isSessionKey || $this->expires > time();
207         }
208 }