diff --git a/readme.html b/readme.html
index c4897a991a5db2ec1738af1b862d26f639a745c9..16433f0d5bde823c090ba0c0573c0156623772b9 100644
--- a/readme.html
+++ b/readme.html
@@ -8,7 +8,7 @@
 <body>
 <h1 id="logo">
 	<a href="http://wordpress.org/"><img alt="WordPress" src="wp-admin/images/wordpress-logo.png" width="250" height="68" /></a>
-	<br /> Version 3.0.3
+	<br /> Version 3.0.4
 </h1>
 <p style="text-align: center">Semantic Personal Publishing Platform</p>
 
diff --git a/wp-admin/includes/update-core.php b/wp-admin/includes/update-core.php
index 1855ce26f2161fcf2ee45d8f3975492e83ffc7ab..eae7f2428e821427eee4c37d7f23e5af18bd85c4 100644
--- a/wp-admin/includes/update-core.php
+++ b/wp-admin/includes/update-core.php
@@ -274,7 +274,7 @@ function update_core($from, $to) {
 	$mysql_version  = $wpdb->db_version();
 	$required_php_version = '4.3';
 	$required_mysql_version = '4.1.2';
-	$wp_version = '3.0.3';
+	$wp_version = '3.0.4';
 	$php_compat     = version_compare( $php_version, $required_php_version, '>=' );
 	$mysql_compat   = version_compare( $mysql_version, $required_mysql_version, '>=' ) || file_exists( WP_CONTENT_DIR . '/db.php' );
 
diff --git a/wp-includes/formatting.php b/wp-includes/formatting.php
index 58c826f903e6414209e60bbff1ff6163b0376e4c..3d203e6fc2d71144cbc336eb181a0637b1fb129e 100644
--- a/wp-includes/formatting.php
+++ b/wp-includes/formatting.php
@@ -2236,7 +2236,8 @@ function esc_url( $url, $protocols = null, $_context = 'display' ) {
 
 	// Replace ampersands and single quotes only when displaying.
 	if ( 'display' == $_context ) {
-		$url = preg_replace('/&([^#])(?![a-z]{2,8};)/', '&#038;$1', $url);
+		$url = wp_kses_normalize_entities( $url );
+		$url = str_replace( '&amp;', '&#038;', $url );
 		$url = str_replace( "'", '&#039;', $url );
 	}
 
diff --git a/wp-includes/kses.php b/wp-includes/kses.php
index 4a0a67901c58b4904b0b6314526d8a935b974d9f..792b15e7ee1246bf77e4e2679e9e77cd8eabb5df 100644
--- a/wp-includes/kses.php
+++ b/wp-includes/kses.php
@@ -670,7 +670,7 @@ function wp_kses_attr($element, $attr, $allowed_html, $allowed_protocols) {
 					break;
 				}
 
-			if ( $arreach['name'] == 'style' ) {
+			if ( strtolower($arreach['name']) == 'style' ) {
 				$orig_value = $arreach['value'];
 
 				$value = safecss_filter_attr($orig_value);
@@ -762,7 +762,7 @@ function wp_kses_hair($attr, $allowed_protocols) {
 					# "value"
 					{
 					$thisval = $match[1];
-					if ( in_array($attrname, $uris) )
+					if ( in_array(strtolower($attrname), $uris) )
 						$thisval = wp_kses_bad_protocol($thisval, $allowed_protocols);
 
 					if(FALSE === array_key_exists($attrname, $attrarr)) {
@@ -778,7 +778,7 @@ function wp_kses_hair($attr, $allowed_protocols) {
 					# 'value'
 					{
 					$thisval = $match[1];
-					if ( in_array($attrname, $uris) )
+					if ( in_array(strtolower($attrname), $uris) )
 						$thisval = wp_kses_bad_protocol($thisval, $allowed_protocols);
 
 					if(FALSE === array_key_exists($attrname, $attrarr)) {
@@ -794,7 +794,7 @@ function wp_kses_hair($attr, $allowed_protocols) {
 					# value
 					{
 					$thisval = $match[1];
-					if ( in_array($attrname, $uris) )
+					if ( in_array(strtolower($attrname), $uris) )
 						$thisval = wp_kses_bad_protocol($thisval, $allowed_protocols);
 
 					if(FALSE === array_key_exists($attrname, $attrarr)) {
@@ -1017,14 +1017,9 @@ function wp_kses_html_error($string) {
  * @return string Sanitized content
  */
 function wp_kses_bad_protocol_once($string, $allowed_protocols) {
-	global $_kses_allowed_protocols;
-	$_kses_allowed_protocols = $allowed_protocols;
-
-	$string2 = preg_split('/:|&#58;|&#x3a;/i', $string, 2);
-	if ( isset($string2[1]) && !preg_match('%/\?%', $string2[0]) )
-		$string = wp_kses_bad_protocol_once2($string2[0]) . trim($string2[1]);
-	else
-		$string = preg_replace_callback('/^((&[^;]*;|[\sA-Za-z0-9])*)'.'(:|&#58;|&#[Xx]3[Aa];)\s*/', 'wp_kses_bad_protocol_once2', $string);
+	$string2 = preg_split( '/:|&#0*58;|&#x0*3a;/i', $string, 2 );
+	if ( isset($string2[1]) && ! preg_match('%/\?%', $string2[0]) )
+		$string = wp_kses_bad_protocol_once2( $string2[0], $allowed_protocols ) . trim( $string2[1] );
 
 	return $string;
 }
@@ -1038,29 +1033,19 @@ function wp_kses_bad_protocol_once($string, $allowed_protocols) {
  * @access private
  * @since 1.0.0
  *
- * @param mixed $matches string or preg_replace_callback() matches array to check for bad protocols
+ * @param string $string URI scheme to check against the whitelist
+ * @param string $allowed_protocols Allowed protocols
  * @return string Sanitized content
  */
-function wp_kses_bad_protocol_once2($matches) {
-	global $_kses_allowed_protocols;
-
-	if ( is_array($matches) ) {
-		if ( empty($matches[1]) )
-			return '';
-
-		$string = $matches[1];
-	} else {
-		$string = $matches;
-	}
-
+function wp_kses_bad_protocol_once2( $string, $allowed_protocols ) {
 	$string2 = wp_kses_decode_entities($string);
 	$string2 = preg_replace('/\s/', '', $string2);
 	$string2 = wp_kses_no_null($string2);
 	$string2 = strtolower($string2);
 
 	$allowed = false;
-	foreach ( (array) $_kses_allowed_protocols as $one_protocol)
-		if (strtolower($one_protocol) == $string2) {
+	foreach ( (array) $allowed_protocols as $one_protocol )
+		if ( strtolower($one_protocol) == $string2 ) {
 			$allowed = true;
 			break;
 		}
diff --git a/wp-includes/version.php b/wp-includes/version.php
index 00fe1c8fcaa9e97645d0dc97f6c54a186341a526..a19d30f91ac2584c1a47ca79b7c058b1abd763f1 100644
--- a/wp-includes/version.php
+++ b/wp-includes/version.php
@@ -8,7 +8,7 @@
  *
  * @global string $wp_version
  */
-$wp_version = '3.0.3';
+$wp_version = '3.0.4';
 
 /**
  * Holds the WordPress DB revision, increments when changes are made to the WordPress DB schema.