MDL-32081 csslib: Improved handling of parentheses when processing styles
[moodle.git] / lib / simpletestlib / cookies.php
1 <?php
2 /**
3  *  Base include file for SimpleTest
4  *  @package    SimpleTest
5  *  @subpackage WebTester
6  *  @version    $Id$
7  */
9 /**#@+
10  *  include other SimpleTest class files
11  */
12 require_once(dirname(__FILE__) . '/url.php');
13 /**#@-*/
15 /**
16  *    Cookie data holder. Cookie rules are full of pretty
17  *    arbitary stuff. I have used...
18  *    http://wp.netscape.com/newsref/std/cookie_spec.html
19  *    http://www.cookiecentral.com/faq/
20  *    @package SimpleTest
21  *    @subpackage WebTester
22  */
23 class SimpleCookie {
24     var $_host;
25     var $_name;
26     var $_value;
27     var $_path;
28     var $_expiry;
29     var $_is_secure;
30     
31     /**
32      *    Constructor. Sets the stored values.
33      *    @param string $name            Cookie key.
34      *    @param string $value           Value of cookie.
35      *    @param string $path            Cookie path if not host wide.
36      *    @param string $expiry          Expiry date as string.
37      *    @param boolean $is_secure      Currently ignored.
38      */
39     function SimpleCookie($name, $value = false, $path = false, $expiry = false, $is_secure = false) {
40         $this->_host = false;
41         $this->_name = $name;
42         $this->_value = $value;
43         $this->_path = ($path ? $this->_fixPath($path) : "/");
44         $this->_expiry = false;
45         if (is_string($expiry)) {
46             $this->_expiry = strtotime($expiry);
47         } elseif (is_integer($expiry)) {
48             $this->_expiry = $expiry;
49         }
50         $this->_is_secure = $is_secure;
51     }
52     
53     /**
54      *    Sets the host. The cookie rules determine
55      *    that the first two parts are taken for
56      *    certain TLDs and three for others. If the
57      *    new host does not match these rules then the
58      *    call will fail.
59      *    @param string $host       New hostname.
60      *    @return boolean           True if hostname is valid.
61      *    @access public
62      */
63     function setHost($host) {
64         if ($host = $this->_truncateHost($host)) {
65             $this->_host = $host;
66             return true;
67         }
68         return false;
69     }
70     
71     /**
72      *    Accessor for the truncated host to which this
73      *    cookie applies.
74      *    @return string       Truncated hostname.
75      *    @access public
76      */
77     function getHost() {
78         return $this->_host;
79     }
80     
81     /**
82      *    Test for a cookie being valid for a host name.
83      *    @param string $host    Host to test against.
84      *    @return boolean        True if the cookie would be valid
85      *                           here.
86      */
87     function isValidHost($host) {
88         return ($this->_truncateHost($host) === $this->getHost());
89     }
90     
91     /**
92      *    Extracts just the domain part that determines a
93      *    cookie's host validity.
94      *    @param string $host    Host name to truncate.
95      *    @return string        Domain or false on a bad host.
96      *    @access private
97      */
98     function _truncateHost($host) {
99         $tlds = SimpleUrl::getAllTopLevelDomains();
100         if (preg_match('/[a-z\-]+\.(' . $tlds . ')$/i', $host, $matches)) {
101             return $matches[0];
102         } elseif (preg_match('/[a-z\-]+\.[a-z\-]+\.[a-z\-]+$/i', $host, $matches)) {
103             return $matches[0];
104         }
105         return false;
106     }
107     
108     /**
109      *    Accessor for name.
110      *    @return string       Cookie key.
111      *    @access public
112      */
113     function getName() {
114         return $this->_name;
115     }
116     
117     /**
118      *    Accessor for value. A deleted cookie will
119      *    have an empty string for this.
120      *    @return string       Cookie value.
121      *    @access public
122      */
123     function getValue() {
124         return $this->_value;
125     }
126     
127     /**
128      *    Accessor for path.
129      *    @return string       Valid cookie path.
130      *    @access public
131      */
132     function getPath() {
133         return $this->_path;
134     }
135     
136     /**
137      *    Tests a path to see if the cookie applies
138      *    there. The test path must be longer or
139      *    equal to the cookie path.
140      *    @param string $path       Path to test against.
141      *    @return boolean           True if cookie valid here.
142      *    @access public
143      */
144     function isValidPath($path) {
145         return (strncmp(
146                 $this->_fixPath($path),
147                 $this->getPath(),
148                 strlen($this->getPath())) == 0);
149     }
150     
151     /**
152      *    Accessor for expiry.
153      *    @return string       Expiry string.
154      *    @access public
155      */
156     function getExpiry() {
157         if (! $this->_expiry) {
158             return false;
159         }
160         return gmdate("D, d M Y H:i:s", $this->_expiry) . " GMT";
161     }
162     
163     /**
164      *    Test to see if cookie is expired against
165      *    the cookie format time or timestamp.
166      *    Will give true for a session cookie.
167      *    @param integer/string $now  Time to test against. Result
168      *                                will be false if this time
169      *                                is later than the cookie expiry.
170      *                                Can be either a timestamp integer
171      *                                or a cookie format date.
172      *    @access public
173      */
174     function isExpired($now) {
175         if (! $this->_expiry) {
176             return true;
177         }
178         if (is_string($now)) {
179             $now = strtotime($now);
180         }
181         return ($this->_expiry < $now);
182     }
183     
184     /**
185      *    Ages the cookie by the specified number of
186      *    seconds.
187      *    @param integer $interval   In seconds.
188      *    @public
189      */
190     function agePrematurely($interval) {
191         if ($this->_expiry) {
192             $this->_expiry -= $interval;
193         }
194     }
195     
196     /**
197      *    Accessor for the secure flag.
198      *    @return boolean       True if cookie needs SSL.
199      *    @access public
200      */
201     function isSecure() {
202         return $this->_is_secure;
203     }
204     
205     /**
206      *    Adds a trailing and leading slash to the path
207      *    if missing.
208      *    @param string $path            Path to fix.
209      *    @access private
210      */
211     function _fixPath($path) {
212         if (substr($path, 0, 1) != '/') {
213             $path = '/' . $path;
214         }
215         if (substr($path, -1, 1) != '/') {
216             $path .= '/';
217         }
218         return $path;
219     }
222 /**
223  *    Repository for cookies. This stuff is a
224  *    tiny bit browser dependent.
225  *    @package SimpleTest
226  *    @subpackage WebTester
227  */
228 class SimpleCookieJar {
229     var $_cookies;
230     
231     /**
232      *    Constructor. Jar starts empty.
233      *    @access public
234      */
235     function SimpleCookieJar() {
236         $this->_cookies = array();
237     }
238     
239     /**
240      *    Removes expired and temporary cookies as if
241      *    the browser was closed and re-opened.
242      *    @param string/integer $now   Time to test expiry against.
243      *    @access public
244      */
245     function restartSession($date = false) {
246         $surviving_cookies = array();
247         for ($i = 0; $i < count($this->_cookies); $i++) {
248             if (! $this->_cookies[$i]->getValue()) {
249                 continue;
250             }
251             if (! $this->_cookies[$i]->getExpiry()) {
252                 continue;
253             }
254             if ($date && $this->_cookies[$i]->isExpired($date)) {
255                 continue;
256             }
257             $surviving_cookies[] = $this->_cookies[$i];
258         }
259         $this->_cookies = $surviving_cookies;
260     }
261     
262     /**
263      *    Ages all cookies in the cookie jar.
264      *    @param integer $interval     The old session is moved
265      *                                 into the past by this number
266      *                                 of seconds. Cookies now over
267      *                                 age will be removed.
268      *    @access public
269      */
270     function agePrematurely($interval) {
271         for ($i = 0; $i < count($this->_cookies); $i++) {
272             $this->_cookies[$i]->agePrematurely($interval);
273         }
274     }
275     
276     /**
277      *    Sets an additional cookie. If a cookie has
278      *    the same name and path it is replaced.
279      *    @param string $name       Cookie key.
280      *    @param string $value      Value of cookie.
281      *    @param string $host       Host upon which the cookie is valid.
282      *    @param string $path       Cookie path if not host wide.
283      *    @param string $expiry     Expiry date.
284      *    @access public
285      */
286     function setCookie($name, $value, $host = false, $path = '/', $expiry = false) {
287         $cookie = new SimpleCookie($name, $value, $path, $expiry);
288         if ($host) {
289             $cookie->setHost($host);
290         }
291         $this->_cookies[$this->_findFirstMatch($cookie)] = $cookie;
292     }
293     
294     /**
295      *    Finds a matching cookie to write over or the
296      *    first empty slot if none.
297      *    @param SimpleCookie $cookie    Cookie to write into jar.
298      *    @return integer                Available slot.
299      *    @access private
300      */
301     function _findFirstMatch($cookie) {
302         for ($i = 0; $i < count($this->_cookies); $i++) {
303             $is_match = $this->_isMatch(
304                     $cookie,
305                     $this->_cookies[$i]->getHost(),
306                     $this->_cookies[$i]->getPath(),
307                     $this->_cookies[$i]->getName());
308             if ($is_match) {
309                 return $i;
310             }
311         }
312         return count($this->_cookies);
313     }
314     
315     /**
316      *    Reads the most specific cookie value from the
317      *    browser cookies. Looks for the longest path that
318      *    matches.
319      *    @param string $host        Host to search.
320      *    @param string $path        Applicable path.
321      *    @param string $name        Name of cookie to read.
322      *    @return string             False if not present, else the
323      *                               value as a string.
324      *    @access public
325      */
326     function getCookieValue($host, $path, $name) {
327         $longest_path = '';
328         foreach ($this->_cookies as $cookie) {
329             if ($this->_isMatch($cookie, $host, $path, $name)) {
330                 if (strlen($cookie->getPath()) > strlen($longest_path)) {
331                     $value = $cookie->getValue();
332                     $longest_path = $cookie->getPath();
333                 }
334             }
335         }
336         return (isset($value) ? $value : false);
337     }
338     
339     /**
340      *    Tests cookie for matching against search
341      *    criteria.
342      *    @param SimpleTest $cookie    Cookie to test.
343      *    @param string $host          Host must match.
344      *    @param string $path          Cookie path must be shorter than
345      *                                 this path.
346      *    @param string $name          Name must match.
347      *    @return boolean              True if matched.
348      *    @access private
349      */
350     function _isMatch($cookie, $host, $path, $name) {
351         if ($cookie->getName() != $name) {
352             return false;
353         }
354         if ($host && $cookie->getHost() && ! $cookie->isValidHost($host)) {
355             return false;
356         }
357         if (! $cookie->isValidPath($path)) {
358             return false;
359         }
360         return true;
361     }
362     
363     /**
364      *    Uses a URL to sift relevant cookies by host and
365      *    path. Results are list of strings of form "name=value".
366      *    @param SimpleUrl $url       Url to select by.
367      *    @return array               Valid name and value pairs.
368      *    @access public
369      */
370     function selectAsPairs($url) {
371         $pairs = array();
372         foreach ($this->_cookies as $cookie) {
373             if ($this->_isMatch($cookie, $url->getHost(), $url->getPath(), $cookie->getName())) {
374                 $pairs[] = $cookie->getName() . '=' . $cookie->getValue();
375             }
376         }
377         return $pairs;
378     }
380 ?>