Index: wp-includes/canonical.php
===================================================================
--- wp-includes/canonical.php	(revision 15444)
+++ wp-includes/canonical.php	(working copy)
@@ -47,6 +47,8 @@
 		$requested_url .= $_SERVER['REQUEST_URI'];
 	}
 
+	$requested_url = url_normalize( $requested_url );
+
 	$original = @parse_url($requested_url);
 	if ( false === $original )
 		return;
@@ -343,16 +345,6 @@
 
 	if ( !$redirect_url || $redirect_url == $requested_url )
 		return false;
-		
-	// Hex encoded octets are case-insensitive. 
-	if ( false !== strpos($requested_url, '%') ) {
-		if ( !function_exists('lowercase_octets') ) {
-			function lowercase_octets($matches) { 
-				return strtolower( $matches[0] ); 
-			} 
-		}
-		$requested_url = preg_replace_callback('|%[a-fA-F0-9][a-fA-F0-9]|', 'lowercase_octets', $requested_url);
-	}
 
 	// Note that you can use the "redirect_canonical" filter to cancel a canonical redirect for whatever reason by returning FALSE
 	$redirect_url = apply_filters('redirect_canonical', $redirect_url, $requested_url);
@@ -408,6 +400,107 @@
 	return get_permalink($post_id);
 }
 
+/**
+ * compare two or more URLs with each other
+ * 
+ * @since 3.0.1
+ * @see RFC 2612 section 3.2.3 {@link http://www.ietf.org/rfc/rfc2616.txt}
+ * 
+ * @param  string $url1 first URL
+ * @param  string $url2 other URL
+ * @return int number of different URLs (0 for no difference)
+ */
+function url_compare( $url1, $url2 ) {
+	$urls = func_get_args();
+	$urls = array_map( 'url_normalize', $urls );
+	$urls = array_unique( $urls );
+	return count( $urls ) - 1;
+}
+
+/**
+ * normalize a URL
+ * 
+ * some basic protocols are supported like HTTP, HTTPS, FTP
+ * 
+ * @since 3.0.1
+ * @param string $url URL to normalize
+ * @param array $default_ports (optional) sheme-keyed ([a-z]+) array of default ports (integer)
+ * @param string normalized URL, empty string if URL was invalid 
+ */
+function url_normalize( $url , $default_ports = array() ) {	
+	// most popular default ports e.g. file, ftp, gopher, http, mailto, nntp, news, telnet or further ldap, irc, phone, fax, tv [POWELL 1998 HTML])
+	$default_ports = array_merge( array( 'ftp' => 21, 'http' => 80, 'https' => 443 ), $default_ports );
+	
+	// check for invalid characters, an invalid URL is "normalized" as empty string
+	$nurl   = (string) $url;
+	$result = preg_match('([\x00-\x20\x7F])', $nurl);
+	if ( $result )
+		return '';
+	$result = null;
+
+	// normalize triplets
+	if ( false !== strpos( $nurl, '%' ) ) {
+		// normalize triplets case to lowercase
+		$nurl = preg_replace_callback( '(%[a-f0-9]{2})i', 'url_normalize_triplets', $nurl );
+		
+		// normalize mark triplets which might but are not to be encoded in a normlaized URL
+		$unreserved = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-_.!~*'()";
+		$i = 0;	
+		$m = strlen( $unreserved );
+		while ( $i < $m )
+			$nurl = str_replace( '%' . dechex( ord( $c = $unreserved[$i++] ) ), $c, $nurl );
+		$i = $m = $c = null;
+	}
+
+	// malformed invalid URL is "normalized" as emtpy string
+	$parts = @parse_url( $nurl );
+	if ( false === $parts )
+		return '';
+
+	// normalize sheme, host, port and (abs_)path
+	$count = extract( $parts );
+
+	// normalize host
+	isset( $host ) && $host = strtolower( $host );
+
+	// normalize scheme and it's according default port
+	isset( $scheme ) && ( $scheme = strtolower( $scheme ) ) 
+	&& isset( $port ) && isset( $default_ports[$scheme] ) && $default_ports[$scheme] == $port && ( $port = null );
+
+	// normalize (abs_)path
+	isset( $path ) && $path == '/' && $path = '';
+	
+	// build normalized URL
+	$nurl = '';
+	isset( $scheme ) && $nurl = $scheme . '://';
+	
+	if ( isset( $user ) || isset( $pass) ) {
+		isset( $user ) && $nurl .= $user;
+		isset( $pass ) && $nurl .= ':' . $pass;
+		$nurl .= '@';
+	}
+	
+	isset ( $host )     && $nurl .= $host;
+	isset ( $path )     && $nurl .= $path;
+	isset ( $query )    && $nurl .= '?' . $query;
+	isset ( $fragment ) && $nurl .= '#' . $fragment;
+
+	return $nurl;
+}
+
+/**
+ * callback to lowercase first match
+ * 
+ * @since 3.0.1
+ * @note only to be used by url_normalize
+ * @param array $matches matches
+ * @return string lowercase first match
+ */
+function url_normalize_triplets( $matches ) {#
+	return strtolower( $matches[0] );
+}
+
+
 add_action('template_redirect', 'redirect_canonical');
 
-?>
+?>
\ No newline at end of file
