[3.0.0] Convert all $context calls away from references
[htmlpurifier.git] / library / HTMLPurifier / URIFilter / MakeAbsolute.php
blobea0d154017a8ac9297e8085b0d62d367bbb269b5
1 <?php
3 // does not support network paths
5 require_once 'HTMLPurifier/URIFilter.php';
7 HTMLPurifier_ConfigSchema::define(
8 'URI', 'MakeAbsolute', false, 'bool', '
9 <p>
10 Converts all URIs into absolute forms. This is useful when the HTML
11 being filtered assumes a specific base path, but will actually be
12 viewed in a different context (and setting an alternate base URI is
13 not possible). %URI.Base must be set for this directive to work.
14 This directive has been available since 2.1.0.
15 </p>
16 ');
18 class HTMLPurifier_URIFilter_MakeAbsolute extends HTMLPurifier_URIFilter
20 public $name = 'MakeAbsolute';
21 protected $base;
22 protected $basePathStack = array();
23 public function prepare($config) {
24 $def = $config->getDefinition('URI');
25 $this->base = $def->base;
26 if (is_null($this->base)) {
27 trigger_error('URI.MakeAbsolute is being ignored due to lack of value for URI.Base configuration', E_USER_ERROR);
28 return;
30 $this->base->fragment = null; // fragment is invalid for base URI
31 $stack = explode('/', $this->base->path);
32 array_pop($stack); // discard last segment
33 $stack = $this->_collapseStack($stack); // do pre-parsing
34 $this->basePathStack = $stack;
36 public function filter(&$uri, $config, $context) {
37 if (is_null($this->base)) return true; // abort early
38 if (
39 $uri->path === '' && is_null($uri->scheme) &&
40 is_null($uri->host) && is_null($uri->query) && is_null($uri->fragment)
41 ) {
42 // reference to current document
43 $uri = $this->base->copy();
44 return true;
46 if (!is_null($uri->scheme)) {
47 // absolute URI already: don't change
48 if (!is_null($uri->host)) return true;
49 $scheme_obj = $uri->getSchemeObj($config, $context);
50 if (!$scheme_obj) {
51 // scheme not recognized
52 return false;
54 if (!$scheme_obj->hierarchical) {
55 // non-hierarchal URI with explicit scheme, don't change
56 return true;
58 // special case: had a scheme but always is hierarchical and had no authority
60 if (!is_null($uri->host)) {
61 // network path, don't bother
62 return true;
64 if ($uri->path === '') {
65 $uri->path = $this->base->path;
66 }elseif ($uri->path[0] !== '/') {
67 // relative path, needs more complicated processing
68 $stack = explode('/', $uri->path);
69 $new_stack = array_merge($this->basePathStack, $stack);
70 $new_stack = $this->_collapseStack($new_stack);
71 $uri->path = implode('/', $new_stack);
73 // re-combine
74 $uri->scheme = $this->base->scheme;
75 if (is_null($uri->userinfo)) $uri->userinfo = $this->base->userinfo;
76 if (is_null($uri->host)) $uri->host = $this->base->host;
77 if (is_null($uri->port)) $uri->port = $this->base->port;
78 return true;
81 /**
82 * Resolve dots and double-dots in a path stack
84 private function _collapseStack($stack) {
85 $result = array();
86 for ($i = 0; isset($stack[$i]); $i++) {
87 $is_folder = false;
88 // absorb an internally duplicated slash
89 if ($stack[$i] == '' && $i && isset($stack[$i+1])) continue;
90 if ($stack[$i] == '..') {
91 if (!empty($result)) {
92 $segment = array_pop($result);
93 if ($segment === '' && empty($result)) {
94 // error case: attempted to back out too far:
95 // restore the leading slash
96 $result[] = '';
97 } elseif ($segment === '..') {
98 $result[] = '..'; // cannot remove .. with ..
100 } else {
101 // relative path, preserve the double-dots
102 $result[] = '..';
104 $is_folder = true;
105 continue;
107 if ($stack[$i] == '.') {
108 // silently absorb
109 $is_folder = true;
110 continue;
112 $result[] = $stack[$i];
114 if ($is_folder) $result[] = '';
115 return $result;