1 : <?php
2 :
3 : // does not support network paths
4 :
5 1 : class HTMLPurifier_URIFilter_MakeAbsolute extends HTMLPurifier_URIFilter
6 : {
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_ERROR);
15 : return;
16 : }
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 : }
23 : public function filter(&$uri, $config, $context) {
24 0 : if (is_null($this->base)) return true; // abort early
25 : if (
26 0 : $uri->path === '' && is_null($uri->scheme) &&
27 0 : is_null($uri->host) && is_null($uri->query) && is_null($uri->fragment)
28 0 : ) {
29 : // reference to current document
30 0 : $uri = clone $this->base;
31 0 : return true;
32 : }
33 0 : if (!is_null($uri->scheme)) {
34 : // absolute URI already: don't change
35 0 : if (!is_null($uri->host)) return true;
36 0 : $scheme_obj = $uri->getSchemeObj($config, $context);
37 0 : if (!$scheme_obj) {
38 : // scheme not recognized
39 0 : return false;
40 : }
41 0 : if (!$scheme_obj->hierarchical) {
42 : // non-hierarchal URI with explicit scheme, don't change
43 0 : return true;
44 : }
45 : // special case: had a scheme but always is hierarchical and had no authority
46 0 : }
47 0 : if (!is_null($uri->host)) {
48 : // network path, don't bother
49 0 : return true;
50 : }
51 0 : if ($uri->path === '') {
52 0 : $uri->path = $this->base->path;
53 0 : }elseif ($uri->path[0] !== '/') {
54 : // relative path, needs more complicated processing
55 0 : $stack = explode('/', $uri->path);
56 0 : $new_stack = array_merge($this->basePathStack, $stack);
57 0 : $new_stack = $this->_collapseStack($new_stack);
58 0 : $uri->path = implode('/', $new_stack);
59 0 : }
60 : // re-combine
61 0 : $uri->scheme = $this->base->scheme;
62 0 : if (is_null($uri->userinfo)) $uri->userinfo = $this->base->userinfo;
63 0 : if (is_null($uri->host)) $uri->host = $this->base->host;
64 0 : if (is_null($uri->port)) $uri->port = $this->base->port;
65 0 : return true;
66 : }
67 :
68 : /**
69 : * Resolve dots and double-dots in a path stack
70 : */
71 : private function _collapseStack($stack) {
72 0 : $result = array();
73 0 : for ($i = 0; isset($stack[$i]); $i++) {
74 0 : $is_folder = false;
75 : // absorb an internally duplicated slash
76 0 : if ($stack[$i] == '' && $i && isset($stack[$i+1])) continue;
77 0 : if ($stack[$i] == '..') {
78 0 : if (!empty($result)) {
79 0 : $segment = array_pop($result);
80 0 : if ($segment === '' && empty($result)) {
81 : // error case: attempted to back out too far:
82 : // restore the leading slash
83 0 : $result[] = '';
84 0 : } elseif ($segment === '..') {
85 0 : $result[] = '..'; // cannot remove .. with ..
86 0 : }
87 0 : } else {
88 : // relative path, preserve the double-dots
89 0 : $result[] = '..';
90 : }
91 0 : $is_folder = true;
92 0 : continue;
93 : }
94 0 : if ($stack[$i] == '.') {
95 : // silently absorb
96 0 : $is_folder = true;
97 0 : continue;
98 : }
99 0 : $result[] = $stack[$i];
100 0 : }
101 0 : if ($is_folder) $result[] = '';
102 0 : return $result;
103 : }
104 : }
105 :
|