# # Permission is hereby granted, free of charge, to any person obtaining # a copy of this software and associated documentation files (the # "Software"), to deal in the Software without restriction, including # without limitation the rights to use, copy, modify, merge, publish, # distribute, sublicense, and/or sell copies of the Software, and to # permit persons to whom the Software is furnished to do so. # # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR # A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT # OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT # LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. function parse_uri($uri) { if (substr($uri, 0, 2) == '//') { # Work around PHP parse_url bug. PHP doesn't like URIs like # "//example.com/foo" or "//example.com:80/foo" $parts = parse_url('x-dummy-scheme:'.$uri); unset($parts['scheme']); return $parts; } return parse_url($uri); } function unparse_url($parts, $loose=false) { return unparse_uri($parts, $loose); } # This function is indended to be the inverse of parse_url, with some optional # sanity checks against RFC 3986. function unparse_uri($parts, $loose=true) { $p_scheme = @$parts['scheme']; $p_host = @$parts['host']; $p_port = @$parts['port']; $p_user = @$parts['user']; $p_pass = @$parts['pass']; $p_path = @$parts['path']; $p_query = @$parts['query']; $p_fragment = @$parts['fragment']; if (!$loose) { $dec_octet = '(?:\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5])'; $IPv4address = "(?:$dec_octet\\.$dec_octet\\.$dec_octet\\.$dec_octet)"; $h16 = '(?:[[:xdigit:]]{1,4})'; $ls32 = '(?:'.$h16.':'.$h16.'|'.$IPv4address.')'; $IPv6address = "(?:". "(?:$h16:){6}$ls32|". "::(?:$h16:){5}$ls32|". "$h16?::(?:$h16:){4}$ls32|". "(?:(?:$h16:){0,1}$h16)?::(?:$h16:){3}$ls32|". "(?:(?:$h16:){0,2}$h16)?::(?:$h16:){2}$ls32|". "(?:(?:$h16:){0,3}$h16)?::(?:$h16:){1}$ls32|". "(?:(?:$h16:){0,4}$h16)?::" . "$ls32|". "(?:(?:$h16:){0,5}$h16)?::" . "$h16|". "(?:(?:$h16:){0,6}$h16)?::". ")"; $unreserved = '[[:alpha:]\d\-\._~]'; $sub_delims = "[!\$&'()\*\+,;=]"; $IPvFuture = "(?:v[[:xdigit:]]+\\.[$unreserved$sub_delims\\:]+)"; $IP_literal = "(?:\\[(?:$IPv6address|$IPvFuture)\\])"; $pct_encoded = "(?:%[[:xdigit:]]{2})"; $reg_name = "(?:$unreserved|$pct_encoded|$sub_delims)*"; $pchar = "(?:$unreserved|$pct_encoded|$sub_delims|\:@)"; $segment = "$pchar*"; $segment_nz = "$pchar+"; $path_absolute = "(?:/(?:$segment_nz(?:/$segment)*)?)"; $path_rootless = "$segment_nz(?:/$segment)*"; # Validate the scheme part # scheme = ALPHA *( ALPHA / DIGIT / "+" / "-" / "." ) # NB: "file" is hard-coded in PHP if (isset($p_scheme) and !preg_match('|^[[:alpha:]][[:alpha:]\d\+\-\.]*$|s', $p_scheme)) { trigger_error('Illegal URI scheme', E_USER_WARNING); return false; } # Validate the host part if (isset($p_host) and !preg_match("#^(?:$IP_literal|$IPv4address|$reg_name)\$#s", $p_host)) { trigger_error('Illegal host part', E_USER_WARNING); return false; } # Validate the port part if (isset($p_port) and !preg_match("#^\d*\$#s", $p_port)) { trigger_error('Illegal port part', E_USER_WARNING); return false; } # Validate the user part if (isset($p_user) and !preg_match("#^(?:$unreserved|$pct_encoded|$sub_delims)*\$#s", $p_user)) { trigger_error('Illegal user part', E_USER_WARNING); return false; } # Validate the password part if (isset($p_pass) and !preg_match("#^(?:$unreserved|$pct_encoded|$sub_delims|:)*\$#s", $p_pass)) { trigger_error('Illegal pass part', E_USER_WARNING); return false; } # Validate the path part if (isset($p_path) and !preg_match("#^$path_absolute|$path_rootless\$#s", $p_path)) { trigger_error('Illegal path part', E_USER_WARNING); return false; } # Validate the query part if (isset($p_query) and !preg_match("#^(?:$pchar|/|\?)*\$#s", $p_query)) { trigger_error('Illegal query part', E_USER_WARNING); return false; } # Validate the fragment part if (isset($p_fragment) and !preg_match("#^(?:$pchar|/|\?)*\$#s", $p_fragment)) { trigger_error('Illegal fragment part', E_USER_WARNING); return false; } } # Build the URI $retval = ""; if (isset($p_scheme)) { $retval = $p_scheme . ":"; if (strtolower($p_scheme) == "file" and !isset($p_host)) { $retval .= "//"; } } if (isset($p_host)) { $retval .= "//"; if (isset($p_user) or isset($p_pass)) { $retval .= isset($p_user) ? $p_user : ""; $retval .= isset($p_pass) ? ":" . $p_pass : ""; $retval .= '@'; } $retval .= $p_host; if (isset($p_port)) { $retval .= ':' . $p_port; } } if (isset($p_path)) { $retval .= $p_path; } if (isset($p_query)) { $retval .= '?' . $p_query; } if (isset($p_fragment)) { $retval .= '#' . $p_fragment; } return $retval; } function get_current_url() { $host = $_SERVER['SERVER_NAME']; $port = $_SERVER['SERVER_PORT']; $req_uri = $_SERVER['REQUEST_URI']; $https = !empty($_SERVER['HTTPS']); $parts = array( 'scheme' => ($https ? 'https' : 'http'), 'host' => $host, 'port' => $port); if (($https and $port == 443) or (!$https and $port == 80)) { unset($parts['port']); } $uri = unparse_url($parts) . $req_uri; return $uri; } # See RFC 2396, section 5.2 # Note that $base_absolute_uri MUST be an absolute URI function absolute_url($url, $base_absolute_uri=null) { # 5.2.1 if (is_null($base_absolute_uri)) { $base_absolute_uri = get_current_url(); } $base = parse_uri($base_absolute_uri); $parts = parse_uri($url); do { # 5.2.2 if (empty($parts['path']) and !isset($parts['scheme']) and !isset($parts['host']) and !isset($parts['query'])) { # $url is a reference to this document $tmp = $base; if (isset($parts['fragment'])) { $tmp['fragment'] = $parts['fragment']; } return unparse_uri($tmp); } # 5.2.3 if (isset($parts['scheme'])) { return $url; } else { $parts['scheme'] = $base['scheme']; } # 5.2.4 if (isset($parts['host'])) { # We are resolving a network-path (starts with "//") break; } $parts['host'] = @$base['host']; $parts['user'] = @$base['user']; $parts['pass'] = @$base['pass']; $parts['port'] = @$base['port']; # 5.2.5 if (substr($parts['path'], 0, 1) == '/') { # We have an absolute path (starts with "/") break; } # 5.2.6 - We are resolving a relative path # 5.2.6(a) $basedir = explode('/', $base['path']); if (count($basedir) > 1) { array_pop($basedir); $basedir = implode('/', $basedir) . '/'; } else { $basedir = ''; } # 5.2.6(b) and (c) $buf = explode('/', $basedir . $parts['path']); array_shift($buf); // Remove the "" before the leading "/" for ($i = 0; $i < count($buf)-1; $i++) { if ($buf[$i] == '.') { array_splice($buf, $i, 1); $i--; } } # 5.2.6(d) if ($buf[count($buf)-1] == '.') { $buf[count($buf)-1] = ''; } # 5.2.6(e) and (f) do { $segRemoved = false; for($i = 1; $i < count($buf); $i++) { if ($buf[$i] == ".." and $buf[$i-1] != "..") { if ($i == count($buf)-1) { $buf[$i] = ''; array_splice($buf, $i-1, 1); } else { array_splice($buf, $i-1, 2); } $segRemoved = true; break; } } } while($segRemoved); $parts['path'] = "/" . implode('/', $buf); } while(0); # 5.2.7 return unparse_uri($parts); } /* vim:set ts=4 sw=4 sts=4 expandtab: */