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