[3.1.1] Implement %URI.SecureMunge and %URI.SecureMungeSecretKey, thanks Chris!
[htmlpurifier.git] / library / HTMLPurifier / URIFilter / MakeAbsolute.php
blob289db51ad3a1614e087ac25a5c5e823c08ee6b99
1 <?php
3 // does not support network paths
5 class HTMLPurifier_URIFilter_MakeAbsolute extends HTMLPurifier_URIFilter
7 public $name = 'MakeAbsolute';
8 protected $base;
9 protected $basePathStack = array();
10 public function prepare($config) {
11 $def = $config->getDefinition('URI');
12 $this->base = $def->base;
13 if (is_null($this->base)) {
14 trigger_error('URI.MakeAbsolute is being ignored due to lack of value for URI.Base configuration', E_USER_WARNING);
15 return false;
17 $this->base->fragment = null; // fragment is invalid for base URI
18 $stack = explode('/', $this->base->path);
19 array_pop($stack); // discard last segment
20 $stack = $this->_collapseStack($stack); // do pre-parsing
21 $this->basePathStack = $stack;
22 return true;
24 public function filter(&$uri, $config, $context) {
25 if (is_null($this->base)) return true; // abort early
26 if (
27 $uri->path === '' && is_null($uri->scheme) &&
28 is_null($uri->host) && is_null($uri->query) && is_null($uri->fragment)
29 ) {
30 // reference to current document
31 $uri = clone $this->base;
32 return true;
34 if (!is_null($uri->scheme)) {
35 // absolute URI already: don't change
36 if (!is_null($uri->host)) return true;
37 $scheme_obj = $uri->getSchemeObj($config, $context);
38 if (!$scheme_obj) {
39 // scheme not recognized
40 return false;
42 if (!$scheme_obj->hierarchical) {
43 // non-hierarchal URI with explicit scheme, don't change
44 return true;
46 // special case: had a scheme but always is hierarchical and had no authority
48 if (!is_null($uri->host)) {
49 // network path, don't bother
50 return true;
52 if ($uri->path === '') {
53 $uri->path = $this->base->path;
54 }elseif ($uri->path[0] !== '/') {
55 // relative path, needs more complicated processing
56 $stack = explode('/', $uri->path);
57 $new_stack = array_merge($this->basePathStack, $stack);
58 $new_stack = $this->_collapseStack($new_stack);
59 $uri->path = implode('/', $new_stack);
61 // re-combine
62 $uri->scheme = $this->base->scheme;
63 if (is_null($uri->userinfo)) $uri->userinfo = $this->base->userinfo;
64 if (is_null($uri->host)) $uri->host = $this->base->host;
65 if (is_null($uri->port)) $uri->port = $this->base->port;
66 return true;
69 /**
70 * Resolve dots and double-dots in a path stack
72 private function _collapseStack($stack) {
73 $result = array();
74 for ($i = 0; isset($stack[$i]); $i++) {
75 $is_folder = false;
76 // absorb an internally duplicated slash
77 if ($stack[$i] == '' && $i && isset($stack[$i+1])) continue;
78 if ($stack[$i] == '..') {
79 if (!empty($result)) {
80 $segment = array_pop($result);
81 if ($segment === '' && empty($result)) {
82 // error case: attempted to back out too far:
83 // restore the leading slash
84 $result[] = '';
85 } elseif ($segment === '..') {
86 $result[] = '..'; // cannot remove .. with ..
88 } else {
89 // relative path, preserve the double-dots
90 $result[] = '..';
92 $is_folder = true;
93 continue;
95 if ($stack[$i] == '.') {
96 // silently absorb
97 $is_folder = true;
98 continue;
100 $result[] = $stack[$i];
102 if ($is_folder) $result[] = '';
103 return $result;