first reimport from platal
[platal.git] / htdocs / TESTS / simpletest / user_agent.php
1 <?php
2 /**
3 * Base include file for SimpleTest
4 * @package SimpleTest
5 * @subpackage WebTester
6 * @version $Id: user_agent.php,v 1.38 2004/06/30 20:40:07 lastcraft Exp $
7 */
8
9 /**#@+
10 * include other SimpleTest class files
11 */
12 require_once(dirname(__FILE__) . '/http.php');
13 require_once(dirname(__FILE__) . '/authentication.php');
14 /**#@-*/
15
16 define('DEFAULT_MAX_REDIRECTS', 3);
17 define('DEFAULT_CONNECTION_TIMEOUT', 15);
18
19 /**
20 * Repository for cookies. This stuff is a
21 * tiny bit browser dependent.
22 * @package SimpleTest
23 * @subpackage WebTester
24 */
25 class SimpleCookieJar {
26 var $_cookies;
27
28 /**
29 * Constructor. Jar starts empty.
30 * @access public
31 */
32 function SimpleCookieJar() {
33 $this->_cookies = array();
34 }
35
36 /**
37 * Removes expired and temporary cookies as if
38 * the browser was closed and re-opened.
39 * @param string/integer $now Time to test expiry against.
40 * @access public
41 */
42 function restartSession($date = false) {
43 $surviving_cookies = array();
44 for ($i = 0; $i < count($this->_cookies); $i++) {
45 if (! $this->_cookies[$i]->getValue()) {
46 continue;
47 }
48 if (! $this->_cookies[$i]->getExpiry()) {
49 continue;
50 }
51 if ($date && $this->_cookies[$i]->isExpired($date)) {
52 continue;
53 }
54 $surviving_cookies[] = $this->_cookies[$i];
55 }
56 $this->_cookies = $surviving_cookies;
57 }
58
59 /**
60 * Ages all cookies in the cookie jar.
61 * @param integer $interval The old session is moved
62 * into the past by this number
63 * of seconds. Cookies now over
64 * age will be removed.
65 * @access public
66 */
67 function agePrematurely($interval) {
68 for ($i = 0; $i < count($this->_cookies); $i++) {
69 $this->_cookies[$i]->agePrematurely($interval);
70 }
71 }
72
73 /**
74 * Adds a cookie to the jar. This will overwrite
75 * cookies with matching host, paths and keys.
76 * @param SimpleCookie $cookie New cookie.
77 * @access public
78 */
79 function setCookie($cookie) {
80 for ($i = 0; $i < count($this->_cookies); $i++) {
81 $is_match = $this->_isMatch(
82 $cookie,
83 $this->_cookies[$i]->getHost(),
84 $this->_cookies[$i]->getPath(),
85 $this->_cookies[$i]->getName());
86 if ($is_match) {
87 $this->_cookies[$i] = $cookie;
88 return;
89 }
90 }
91 $this->_cookies[] = $cookie;
92 }
93
94 /**
95 * Fetches a hash of all valid cookies filtered
96 * by host, path and keyed by name
97 * Any cookies with missing categories will not
98 * be filtered out by that category. Expired
99 * cookies must be cleared by restarting the session.
100 * @param string $host Host name requirement.
101 * @param string $path Path encompassing cookies.
102 * @return hash Valid cookie objects keyed
103 * on the cookie name.
104 * @access public
105 */
106 function getValidCookies($host = false, $path = "/") {
107 $valid_cookies = array();
108 foreach ($this->_cookies as $cookie) {
109 if ($this->_isMatch($cookie, $host, $path, $cookie->getName())) {
110 $valid_cookies[] = $cookie;
111 }
112 }
113 return $valid_cookies;
114 }
115
116 /**
117 * Tests cookie for matching against search
118 * criteria.
119 * @param SimpleTest $cookie Cookie to test.
120 * @param string $host Host must match.
121 * @param string $path Cookie path must be shorter than
122 * this path.
123 * @param string $name Name must match.
124 * @return boolean True if matched.
125 * @access private
126 */
127 function _isMatch($cookie, $host, $path, $name) {
128 if ($cookie->getName() != $name) {
129 return false;
130 }
131 if ($host && $cookie->getHost() && !$cookie->isValidHost($host)) {
132 return false;
133 }
134 if (! $cookie->isValidPath($path)) {
135 return false;
136 }
137 return true;
138 }
139
140 /**
141 * Adds the current cookies to a request.
142 * @param SimpleHttpRequest $request Request to modify.
143 * @param SimpleUrl $url Cookie selector.
144 * @access private
145 */
146 function addHeaders(&$request, $url) {
147 $cookies = $this->getValidCookies($url->getHost(), $url->getPath());
148 foreach ($cookies as $cookie) {
149 $request->setCookie($cookie);
150 }
151 }
152 }
153
154 /**
155 * Fetches web pages whilst keeping track of
156 * cookies and authentication.
157 * @package SimpleTest
158 * @subpackage WebTester
159 */
160 class SimpleUserAgent {
161 var $_cookie_jar;
162 var $_authenticator;
163 var $_max_redirects;
164 var $_proxy;
165 var $_proxy_username;
166 var $_proxy_password;
167 var $_connection_timeout;
168 var $_additional_headers;
169
170 /**
171 * Starts with no cookies, realms or proxies.
172 * @access public
173 */
174 function SimpleUserAgent() {
175 $this->_cookie_jar = &new SimpleCookieJar();
176 $this->_authenticator = &new SimpleAuthenticator();
177 $this->setMaximumRedirects(DEFAULT_MAX_REDIRECTS);
178 $this->_proxy = false;
179 $this->_proxy_username = false;
180 $this->_proxy_password = false;
181 $this->setConnectionTimeout(DEFAULT_CONNECTION_TIMEOUT);
182 $this->_additional_headers = array();
183 }
184
185 /**
186 * Removes expired and temporary cookies as if
187 * the browser was closed and re-opened.
188 * @param string/integer $date Time when session restarted.
189 * If omitted then all persistent
190 * cookies are kept.
191 * @access public
192 */
193 function restartSession($date = false) {
194 $this->_cookie_jar->restartSession($date);
195 }
196
197 /**
198 * Adds a header to every fetch.
199 * @param string $header Header line to add to every
200 * request until cleared.
201 * @access public
202 */
203 function addHeader($header) {
204 $this->_additional_headers[] = $header;
205 }
206
207 /**
208 * Ages the cookies by the specified time.
209 * @param integer $interval Amount in seconds.
210 * @access public
211 */
212 function ageCookies($interval) {
213 $this->_cookie_jar->agePrematurely($interval);
214 }
215
216 /**
217 * Sets an additional cookie. If a cookie has
218 * the same name and path it is replaced.
219 * @param string $name Cookie key.
220 * @param string $value Value of cookie.
221 * @param string $host Host upon which the cookie is valid.
222 * @param string $path Cookie path if not host wide.
223 * @param string $expiry Expiry date.
224 * @access public
225 */
226 function setCookie($name, $value, $host = false, $path = '/', $expiry = false) {
227 $cookie = new SimpleCookie($name, $value, $path, $expiry);
228 if ($host) {
229 $cookie->setHost($host);
230 }
231 $this->_cookie_jar->setCookie($cookie);
232 }
233
234 /**
235 * Reads the most specific cookie value from the
236 * browser cookies.
237 * @param string $host Host to search.
238 * @param string $path Applicable path.
239 * @param string $name Name of cookie to read.
240 * @return string False if not present, else the
241 * value as a string.
242 * @access public
243 */
244 function getCookieValue($host, $path, $name) {
245 $longest_path = '';
246 foreach ($this->_cookie_jar->getValidCookies($host, $path) as $cookie) {
247 if ($name == $cookie->getName()) {
248 if (strlen($cookie->getPath()) > strlen($longest_path)) {
249 $value = $cookie->getValue();
250 $longest_path = $cookie->getPath();
251 }
252 }
253 }
254 return (isset($value) ? $value : false);
255 }
256
257 /**
258 * Reads the current cookies within the base URL.
259 * @param string $name Key of cookie to find.
260 * @param SimpleUrl $base Base URL to search from.
261 * @return string Null if there is no base URL, false
262 * if the cookie is not set.
263 * @access public
264 */
265 function getBaseCookieValue($name, $base) {
266 if (! $base) {
267 return null;
268 }
269 return $this->getCookieValue($base->getHost(), $base->getPath(), $name);
270 }
271
272 /**
273 * Sets the socket timeout for opening a connection.
274 * @param integer $timeout Maximum time in seconds.
275 * @access public
276 */
277 function setConnectionTimeout($timeout) {
278 $this->_connection_timeout = $timeout;
279 }
280
281 /**
282 * Sets the maximum number of redirects before
283 * a page will be loaded anyway.
284 * @param integer $max Most hops allowed.
285 * @access public
286 */
287 function setMaximumRedirects($max) {
288 $this->_max_redirects = $max;
289 }
290
291 /**
292 * Sets proxy to use on all requests for when
293 * testing from behind a firewall. Set URL
294 * to false to disable.
295 * @param string $proxy Proxy URL.
296 * @param string $username Proxy username for authentication.
297 * @param string $password Proxy password for authentication.
298 * @access public
299 */
300 function useProxy($proxy, $username, $password) {
301 if (! $proxy) {
302 $this->_proxy = false;
303 return;
304 }
305 if (strncmp($proxy, 'http://', 7) != 0) {
306 $proxy = 'http://'. $proxy;
307 }
308 $this->_proxy = &new SimpleUrl($proxy);
309 $this->_proxy_username = $username;
310 $this->_proxy_password = $password;
311 }
312
313 /**
314 * Test to see if the redirect limit is passed.
315 * @param integer $redirects Count so far.
316 * @return boolean True if over.
317 * @access private
318 */
319 function _isTooManyRedirects($redirects) {
320 return ($redirects > $this->_max_redirects);
321 }
322
323 /**
324 * Sets the identity for the current realm.
325 * @param string $host Host to which realm applies.
326 * @param string $realm Full name of realm.
327 * @param string $username Username for realm.
328 * @param string $password Password for realm.
329 * @access public
330 */
331 function setIdentity($host, $realm, $username, $password) {
332 $this->_authenticator->setIdentityForRealm($host, $realm, $username, $password);
333 }
334
335 /**
336 * Fetches a URL as a response object. Will keep trying if redirected.
337 * It will also collect authentication realm information.
338 * @param string $method GET, POST, etc.
339 * @param string/SimpleUrl $url Target to fetch.
340 * @param hash $parameters Additional parameters for request.
341 * @return SimpleHttpResponse Hopefully the target page.
342 * @access public
343 */
344 function &fetchResponse($method, $url, $parameters = false) {
345 if ($method != 'POST') {
346 $url->addRequestParameters($parameters);
347 $parameters = false;
348 }
349 $response = &$this->_fetchWhileRedirected($method, $url, $parameters);
350 if ($headers = $response->getHeaders()) {
351 if ($headers->isChallenge()) {
352 $this->_authenticator->addRealm(
353 $url,
354 $headers->getAuthentication(),
355 $headers->getRealm());
356 }
357 }
358 return $response;
359 }
360
361 /**
362 * Fetches the page until no longer redirected or
363 * until the redirect limit runs out.
364 * @param string $method GET, POST, etc.
365 * @param SimpleUrl $url Target to fetch.
366 * @param hash $parameters Additional parameters for request.
367 * @return SimpleHttpResponse Hopefully the target page.
368 * @access private
369 */
370 function &_fetchWhileRedirected($method, $url, $parameters) {
371 $redirects = 0;
372 do {
373 $response = &$this->_fetch($method, $url, $parameters);
374 if ($response->isError()) {
375 return $response;
376 }
377 $headers = $response->getHeaders();
378 $location = new SimpleUrl($headers->getLocation());
379 $url = $location->makeAbsolute($url);
380 $this->_addCookiesToJar($url, $headers->getNewCookies());
381 if (! $headers->isRedirect()) {
382 break;
383 }
384 $method = 'GET';
385 $parameters = false;
386 } while (! $this->_isTooManyRedirects(++$redirects));
387 return $response;
388 }
389
390 /**
391 * Actually make the web request.
392 * @param string $method GET, POST, etc.
393 * @param SimpleUrl $url Target to fetch.
394 * @param hash $parameters Additional parameters for request.
395 * @return SimpleHttpResponse Headers and hopefully content.
396 * @access protected
397 */
398 function &_fetch($method, $url, $parameters) {
399 if (! $parameters) {
400 $parameters = array();
401 }
402 $request = &$this->_createRequest($method, $url, $parameters);
403 return $request->fetch($this->_connection_timeout);
404 }
405
406 /**
407 * Creates a full page request.
408 * @param string $method Fetching method.
409 * @param SimpleUrl $url Target to fetch as url object.
410 * @param hash $parameters POST/GET parameters.
411 * @return SimpleHttpRequest New request.
412 * @access private
413 */
414 function &_createRequest($method, $url, $parameters) {
415 $request = &$this->_createHttpRequest($method, $url, $parameters);
416 $this->_addAdditionalHeaders($request);
417 $this->_cookie_jar->addHeaders($request, $url);
418 $this->_authenticator->addHeaders($request, $url);
419 return $request;
420 }
421
422 /**
423 * Builds the appropriate HTTP request object.
424 * @param string $method Fetching method.
425 * @param SimpleUrl $url Target to fetch as url object.
426 * @param hash $parameters POST/GET parameters.
427 * @return SimpleHttpRequest New request object.
428 * @access protected
429 */
430 function &_createHttpRequest($method, $url, $parameters) {
431 if ($method == 'POST') {
432 $request = &new SimpleHttpPostRequest(
433 $this->_createRoute($url),
434 $parameters);
435 return $request;
436 }
437 if ($parameters) {
438 $url->addRequestParameters($parameters);
439 }
440 return new SimpleHttpRequest($this->_createRoute($url), $method);
441 }
442
443 /**
444 * Sets up either a direct route or via a proxy.
445 * @param SimpleUrl $url Target to fetch as url object.
446 * @return SimpleRoute Route to take to fetch URL.
447 * @access protected
448 */
449 function &_createRoute($url) {
450 if ($this->_proxy) {
451 return new SimpleProxyRoute(
452 $url,
453 $this->_proxy,
454 $this->_proxy_username,
455 $this->_proxy_password);
456 }
457 return new SimpleRoute($url);
458 }
459
460 /**
461 * Adds additional manual headers.
462 * @param SimpleHttpRequest $request Outgoing request.
463 * @access private
464 */
465 function _addAdditionalHeaders(&$request) {
466 foreach ($this->_additional_headers as $header) {
467 $request->addHeaderLine($header);
468 }
469 }
470
471 /**
472 * Extracts new cookies into the cookie jar.
473 * @param SimpleUrl $url Target to fetch as url object.
474 * @param array $cookies New cookies.
475 * @access private
476 */
477 function _addCookiesToJar($url, $cookies) {
478 foreach ($cookies as $cookie) {
479 if ($url->getHost()) {
480 $cookie->setHost($url->getHost());
481 }
482 $this->_cookie_jar->setCookie($cookie);
483 }
484 }
485 }
486 ?>