]>
Commit | Line | Data |
---|---|---|
d4949327 NL |
1 | <?php\r |
2 | \r | |
3 | // does not support network paths\r | |
4 | \r | |
5 | class HTMLPurifier_URIFilter_MakeAbsolute extends HTMLPurifier_URIFilter\r | |
6 | {\r | |
7 | /**\r | |
8 | * @type string\r | |
9 | */\r | |
10 | public $name = 'MakeAbsolute';\r | |
11 | \r | |
12 | /**\r | |
13 | * @type\r | |
14 | */\r | |
15 | protected $base;\r | |
16 | \r | |
17 | /**\r | |
18 | * @type array\r | |
19 | */\r | |
20 | protected $basePathStack = array();\r | |
21 | \r | |
22 | /**\r | |
23 | * @param HTMLPurifier_Config $config\r | |
24 | * @return bool\r | |
25 | */\r | |
26 | public function prepare($config)\r | |
27 | {\r | |
28 | $def = $config->getDefinition('URI');\r | |
29 | $this->base = $def->base;\r | |
30 | if (is_null($this->base)) {\r | |
31 | trigger_error(\r | |
32 | 'URI.MakeAbsolute is being ignored due to lack of ' .\r | |
33 | 'value for URI.Base configuration',\r | |
34 | E_USER_WARNING\r | |
35 | );\r | |
36 | return false;\r | |
37 | }\r | |
38 | $this->base->fragment = null; // fragment is invalid for base URI\r | |
39 | $stack = explode('/', $this->base->path);\r | |
40 | array_pop($stack); // discard last segment\r | |
41 | $stack = $this->_collapseStack($stack); // do pre-parsing\r | |
42 | $this->basePathStack = $stack;\r | |
43 | return true;\r | |
44 | }\r | |
45 | \r | |
46 | /**\r | |
47 | * @param HTMLPurifier_URI $uri\r | |
48 | * @param HTMLPurifier_Config $config\r | |
49 | * @param HTMLPurifier_Context $context\r | |
50 | * @return bool\r | |
51 | */\r | |
52 | public function filter(&$uri, $config, $context)\r | |
53 | {\r | |
54 | if (is_null($this->base)) {\r | |
55 | return true;\r | |
56 | } // abort early\r | |
57 | if ($uri->path === '' && is_null($uri->scheme) &&\r | |
58 | is_null($uri->host) && is_null($uri->query) && is_null($uri->fragment)) {\r | |
59 | // reference to current document\r | |
60 | $uri = clone $this->base;\r | |
61 | return true;\r | |
62 | }\r | |
63 | if (!is_null($uri->scheme)) {\r | |
64 | // absolute URI already: don't change\r | |
65 | if (!is_null($uri->host)) {\r | |
66 | return true;\r | |
67 | }\r | |
68 | $scheme_obj = $uri->getSchemeObj($config, $context);\r | |
69 | if (!$scheme_obj) {\r | |
70 | // scheme not recognized\r | |
71 | return false;\r | |
72 | }\r | |
73 | if (!$scheme_obj->hierarchical) {\r | |
74 | // non-hierarchal URI with explicit scheme, don't change\r | |
75 | return true;\r | |
76 | }\r | |
77 | // special case: had a scheme but always is hierarchical and had no authority\r | |
78 | }\r | |
79 | if (!is_null($uri->host)) {\r | |
80 | // network path, don't bother\r | |
81 | return true;\r | |
82 | }\r | |
83 | if ($uri->path === '') {\r | |
84 | $uri->path = $this->base->path;\r | |
85 | } elseif ($uri->path[0] !== '/') {\r | |
86 | // relative path, needs more complicated processing\r | |
87 | $stack = explode('/', $uri->path);\r | |
88 | $new_stack = array_merge($this->basePathStack, $stack);\r | |
89 | if ($new_stack[0] !== '' && !is_null($this->base->host)) {\r | |
90 | array_unshift($new_stack, '');\r | |
91 | }\r | |
92 | $new_stack = $this->_collapseStack($new_stack);\r | |
93 | $uri->path = implode('/', $new_stack);\r | |
94 | } else {\r | |
95 | // absolute path, but still we should collapse\r | |
96 | $uri->path = implode('/', $this->_collapseStack(explode('/', $uri->path)));\r | |
97 | }\r | |
98 | // re-combine\r | |
99 | $uri->scheme = $this->base->scheme;\r | |
100 | if (is_null($uri->userinfo)) {\r | |
101 | $uri->userinfo = $this->base->userinfo;\r | |
102 | }\r | |
103 | if (is_null($uri->host)) {\r | |
104 | $uri->host = $this->base->host;\r | |
105 | }\r | |
106 | if (is_null($uri->port)) {\r | |
107 | $uri->port = $this->base->port;\r | |
108 | }\r | |
109 | return true;\r | |
110 | }\r | |
111 | \r | |
112 | /**\r | |
113 | * Resolve dots and double-dots in a path stack\r | |
114 | * @param array $stack\r | |
115 | * @return array\r | |
116 | */\r | |
117 | private function _collapseStack($stack)\r | |
118 | {\r | |
119 | $result = array();\r | |
120 | $is_folder = false;\r | |
121 | for ($i = 0; isset($stack[$i]); $i++) {\r | |
122 | $is_folder = false;\r | |
123 | // absorb an internally duplicated slash\r | |
124 | if ($stack[$i] == '' && $i && isset($stack[$i + 1])) {\r | |
125 | continue;\r | |
126 | }\r | |
127 | if ($stack[$i] == '..') {\r | |
128 | if (!empty($result)) {\r | |
129 | $segment = array_pop($result);\r | |
130 | if ($segment === '' && empty($result)) {\r | |
131 | // error case: attempted to back out too far:\r | |
132 | // restore the leading slash\r | |
133 | $result[] = '';\r | |
134 | } elseif ($segment === '..') {\r | |
135 | $result[] = '..'; // cannot remove .. with ..\r | |
136 | }\r | |
137 | } else {\r | |
138 | // relative path, preserve the double-dots\r | |
139 | $result[] = '..';\r | |
140 | }\r | |
141 | $is_folder = true;\r | |
142 | continue;\r | |
143 | }\r | |
144 | if ($stack[$i] == '.') {\r | |
145 | // silently absorb\r | |
146 | $is_folder = true;\r | |
147 | continue;\r | |
148 | }\r | |
149 | $result[] = $stack[$i];\r | |
150 | }\r | |
151 | if ($is_folder) {\r | |
152 | $result[] = '';\r | |
153 | }\r | |
154 | return $result;\r | |
155 | }\r | |
156 | }\r | |
157 | \r | |
158 | // vim: et sw=4 sts=4\r |