MakeAbsolute.php 4.8 KB

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