File indexing completed on 2024-05-12 06:02:06

0001 <?php
0002 
0003 // does not support network paths
0004 
0005 class HTMLPurifier_URIFilter_MakeAbsolute extends HTMLPurifier_URIFilter
0006 {
0007     /**
0008      * @type string
0009      */
0010     public $name = 'MakeAbsolute';
0011 
0012     /**
0013      * @type
0014      */
0015     protected $base;
0016 
0017     /**
0018      * @type array
0019      */
0020     protected $basePathStack = array();
0021 
0022     /**
0023      * @param HTMLPurifier_Config $config
0024      * @return bool
0025      */
0026     public function prepare($config)
0027     {
0028         $def = $config->getDefinition('URI');
0029         $this->base = $def->base;
0030         if (is_null($this->base)) {
0031             trigger_error(
0032                 'URI.MakeAbsolute is being ignored due to lack of ' .
0033                 'value for URI.Base configuration',
0034                 E_USER_WARNING
0035             );
0036             return false;
0037         }
0038         $this->base->fragment = null; // fragment is invalid for base URI
0039         $stack = explode('/', $this->base->path);
0040         array_pop($stack); // discard last segment
0041         $stack = $this->_collapseStack($stack); // do pre-parsing
0042         $this->basePathStack = $stack;
0043         return true;
0044     }
0045 
0046     /**
0047      * @param HTMLPurifier_URI $uri
0048      * @param HTMLPurifier_Config $config
0049      * @param HTMLPurifier_Context $context
0050      * @return bool
0051      */
0052     public function filter(&$uri, $config, $context)
0053     {
0054         if (is_null($this->base)) {
0055             return true;
0056         } // abort early
0057         if ($uri->path === '' && is_null($uri->scheme) &&
0058             is_null($uri->host) && is_null($uri->query) && is_null($uri->fragment)) {
0059             // reference to current document
0060             $uri = clone $this->base;
0061             return true;
0062         }
0063         if (!is_null($uri->scheme)) {
0064             // absolute URI already: don't change
0065             if (!is_null($uri->host)) {
0066                 return true;
0067             }
0068             $scheme_obj = $uri->getSchemeObj($config, $context);
0069             if (!$scheme_obj) {
0070                 // scheme not recognized
0071                 return false;
0072             }
0073             if (!$scheme_obj->hierarchical) {
0074                 // non-hierarchal URI with explicit scheme, don't change
0075                 return true;
0076             }
0077             // special case: had a scheme but always is hierarchical and had no authority
0078         }
0079         if (!is_null($uri->host)) {
0080             // network path, don't bother
0081             return true;
0082         }
0083         if ($uri->path === '') {
0084             $uri->path = $this->base->path;
0085         } elseif ($uri->path[0] !== '/') {
0086             // relative path, needs more complicated processing
0087             $stack = explode('/', $uri->path);
0088             $new_stack = array_merge($this->basePathStack, $stack);
0089             if ($new_stack[0] !== '' && !is_null($this->base->host)) {
0090                 array_unshift($new_stack, '');
0091             }
0092             $new_stack = $this->_collapseStack($new_stack);
0093             $uri->path = implode('/', $new_stack);
0094         } else {
0095             // absolute path, but still we should collapse
0096             $uri->path = implode('/', $this->_collapseStack(explode('/', $uri->path)));
0097         }
0098         // re-combine
0099         $uri->scheme = $this->base->scheme;
0100         if (is_null($uri->userinfo)) {
0101             $uri->userinfo = $this->base->userinfo;
0102         }
0103         if (is_null($uri->host)) {
0104             $uri->host = $this->base->host;
0105         }
0106         if (is_null($uri->port)) {
0107             $uri->port = $this->base->port;
0108         }
0109         return true;
0110     }
0111 
0112     /**
0113      * Resolve dots and double-dots in a path stack
0114      * @param array $stack
0115      * @return array
0116      */
0117     private function _collapseStack($stack)
0118     {
0119         $result = array();
0120         $is_folder = false;
0121         for ($i = 0; isset($stack[$i]); $i++) {
0122             $is_folder = false;
0123             // absorb an internally duplicated slash
0124             if ($stack[$i] == '' && $i && isset($stack[$i + 1])) {
0125                 continue;
0126             }
0127             if ($stack[$i] == '..') {
0128                 if (!empty($result)) {
0129                     $segment = array_pop($result);
0130                     if ($segment === '' && empty($result)) {
0131                         // error case: attempted to back out too far:
0132                         // restore the leading slash
0133                         $result[] = '';
0134                     } elseif ($segment === '..') {
0135                         $result[] = '..'; // cannot remove .. with ..
0136                     }
0137                 } else {
0138                     // relative path, preserve the double-dots
0139                     $result[] = '..';
0140                 }
0141                 $is_folder = true;
0142                 continue;
0143             }
0144             if ($stack[$i] == '.') {
0145                 // silently absorb
0146                 $is_folder = true;
0147                 continue;
0148             }
0149             $result[] = $stack[$i];
0150         }
0151         if ($is_folder) {
0152             $result[] = '';
0153         }
0154         return $result;
0155     }
0156 }
0157 
0158 // vim: et sw=4 sts=4