Index: src/wp-admin/includes/user.php
===================================================================
--- src/wp-admin/includes/user.php	(revision 37416)
+++ src/wp-admin/includes/user.php	(working copy)
@@ -20,7 +20,7 @@
 /**
  * Edit user settings based on contents of $_POST
  *
- * Used on user-edit.php and profile.php to manage and process user options, passwords etc.
+ * Used on user-edit.php, user-new.php, and profile.php to manage and process user options, passwords etc.
  *
  * @since 2.0.0
  *
@@ -39,9 +39,6 @@
 		$update = false;
 	}
 
-	if ( !$update && isset( $_POST['user_login'] ) )
-		$user->user_login = sanitize_user($_POST['user_login'], true);
-
 	$pass1 = $pass2 = '';
 	if ( isset( $_POST['pass1'] ) )
 		$pass1 = $_POST['pass1'];
@@ -104,10 +101,17 @@
 
 	$errors = new WP_Error();
 
-	/* checking that username has been typed */
-	if ( $user->user_login == '' )
-		$errors->add( 'user_login', __( '<strong>ERROR</strong>: Please enter a username.' ) );
+	/* Validate the user_login when not updating the user */
+	if ( ! $update ) {
+		$user_login = '';
 
+		if ( isset( $_POST['user_login'] ) ) {
+			$user_login = $_POST['user_login'];
+		}
+
+		$user->user_login = wp_validate_user_login( $user_login, $errors );
+	}
+
 	/* checking that nickname has been typed */
 	if ( $update && empty( $user->nickname ) ) {
 		$errors->add( 'nickname', __( '<strong>ERROR</strong>: Please enter a nickname.' ) );
@@ -142,12 +146,6 @@
 	if ( !empty( $pass1 ) )
 		$user->user_pass = $pass1;
 
-	if ( !$update && isset( $_POST['user_login'] ) && !validate_username( $_POST['user_login'] ) )
-		$errors->add( 'user_login', __( '<strong>ERROR</strong>: This username is invalid because it uses illegal characters. Please enter a valid username.' ));
-
-	if ( !$update && username_exists( $user->user_login ) )
-		$errors->add( 'user_login', __( '<strong>ERROR</strong>: This username is already registered. Please choose another one.' ));
-
 	/** This filter is documented in wp-includes/user.php */
 	$illegal_logins = (array) apply_filters( 'illegal_user_logins', array() );
 
Index: src/wp-includes/ms-functions.php
===================================================================
--- src/wp-includes/ms-functions.php	(revision 37416)
+++ src/wp-includes/ms-functions.php	(working copy)
@@ -400,58 +400,21 @@
  *
  * @global wpdb $wpdb WordPress database abstraction object.
  *
- * @param string $user_name  The login name provided by the user.
+ * @param string $user_login The login name provided by the user.
  * @param string $user_email The email provided by the user.
  * @return array Contains username, email, and error messages.
  */
-function wpmu_validate_user_signup($user_name, $user_email) {
+function wpmu_validate_user_signup( $user_login, $user_email ) {
 	global $wpdb;
 
 	$errors = new WP_Error();
-
-	$orig_username = $user_name;
-	$user_name = preg_replace( '/\s+/', '', sanitize_user( $user_name, true ) );
-
-	if ( $user_name != $orig_username || preg_match( '/[^a-z0-9]/', $user_name ) ) {
-		$errors->add( 'user_name', __( 'Usernames can only contain lowercase letters (a-z) and numbers.' ) );
-		$user_name = $orig_username;
-	}
-
+	$orig_userlogin = $user_login;
+	$user_login = wp_validate_user_login( $user_login, $errors );
 	$user_email = sanitize_email( $user_email );
 
-	if ( empty( $user_name ) )
-	   	$errors->add('user_name', __( 'Please enter a username.' ) );
-
-	$illegal_names = get_site_option( 'illegal_names' );
-	if ( ! is_array( $illegal_names ) ) {
-		$illegal_names = array(  'www', 'web', 'root', 'admin', 'main', 'invite', 'administrator' );
-		add_site_option( 'illegal_names', $illegal_names );
-	}
-	if ( in_array( $user_name, $illegal_names ) ) {
-		$errors->add( 'user_name',  __( 'Sorry, that username is not allowed.' ) );
-	}
-
-	/** This filter is documented in wp-includes/user.php */
-	$illegal_logins = (array) apply_filters( 'illegal_user_logins', array() );
-
-	if ( in_array( strtolower( $user_name ), array_map( 'strtolower', $illegal_logins ) ) ) {
-		$errors->add( 'user_name',  __( 'Sorry, that username is not allowed.' ) );
-	}
-
 	if ( is_email_address_unsafe( $user_email ) )
 		$errors->add('user_email',  __('You cannot use that email address to signup. We are having problems with them blocking some of our email. Please use another email provider.'));
 
-	if ( strlen( $user_name ) < 4 )
-		$errors->add('user_name',  __( 'Username must be at least 4 characters.' ) );
-
-	if ( strlen( $user_name ) > 60 ) {
-		$errors->add( 'user_name', __( 'Username may not be longer than 60 characters.' ) );
-	}
-
-	// all numeric?
-	if ( preg_match( '/^[0-9]*$/', $user_name ) )
-		$errors->add('user_name', __('Sorry, usernames must have letters too!'));
-
 	if ( !is_email( $user_email ) )
 		$errors->add('user_email', __( 'Please enter a valid email address.' ) );
 
@@ -463,16 +426,12 @@
 		}
 	}
 
-	// Check if the username has been used already.
-	if ( username_exists($user_name) )
-		$errors->add( 'user_name', __( 'Sorry, that username already exists!' ) );
-
 	// Check if the email address has been used already.
 	if ( email_exists($user_email) )
 		$errors->add( 'user_email', __( 'Sorry, that email address is already used!' ) );
 
 	// Has someone already signed up for this username?
-	$signup = $wpdb->get_row( $wpdb->prepare("SELECT * FROM $wpdb->signups WHERE user_login = %s", $user_name) );
+	$signup = $wpdb->get_row( $wpdb->prepare( "SELECT * FROM $wpdb->signups WHERE user_login = %s", $user_login ) );
 	if ( $signup != null ) {
 		$registered_at =  mysql2date('U', $signup->registered);
 		$now = current_time( 'timestamp', true );
@@ -479,7 +438,7 @@
 		$diff = $now - $registered_at;
 		// If registered more than two days ago, cancel registration and let this signup go through.
 		if ( $diff > 2 * DAY_IN_SECONDS )
-			$wpdb->delete( $wpdb->signups, array( 'user_login' => $user_name ) );
+			$wpdb->delete( $wpdb->signups, array( 'user_login' => $user_login ) );
 		else
 			$errors->add('user_name', __('That username is currently reserved but may be available in a couple of days.'));
 	}
@@ -494,7 +453,7 @@
 			$errors->add('user_email', __('That email address has already been used. Please check your inbox for an activation email. It will become available in a couple of days if you do nothing.'));
 	}
 
-	$result = array('user_name' => $user_name, 'orig_username' => $orig_username, 'user_email' => $user_email, 'errors' => $errors);
+	$result = array( 'user_name' => $user_login, 'orig_username' => $orig_userlogin, 'user_email' => $user_email, 'errors' => $errors );
 
 	/**
 	 * Filter the validated user registration details.
Index: src/wp-includes/user.php
===================================================================
--- src/wp-includes/user.php	(revision 37416)
+++ src/wp-includes/user.php	(working copy)
@@ -2225,6 +2225,100 @@
 }
 
 /**
+ * Validate a provided user_login
+ *
+ * user_login requirements:
+ *     - minimum of 4 characters
+ *     - maximum of 60 characters
+ *     - only contains (case-insensitive) characters: a-z 0-9 _ . - @
+ *     - no whitespace
+ *     - not on blacklist of illegal names
+ *     - contains at least one letter
+ *     - must be unique
+ *     - not pending signup already
+ *
+ * @since TBD
+ *
+ * @param string $user_login The user_login value to be be validated.
+ *
+ * @return array Contains user_login, original_user_login, and any generated errors
+ */
+function wp_validate_user_login( $user_login = '', $errors = null ) {
+	$original_user_login = $user_login;
+
+	if ( ! is_wp_error( $errors ) ) {
+		$errors = new WP_Error();
+	}
+
+	// User login cannot be empty
+	if ( empty( $user_login ) ) {
+		$errors->add( 'user_name', __( 'Please enter a username.' ) );
+	}
+
+	// User login must be less than 60 characters
+	if ( strlen( $user_login ) > 60 ) {
+		$errors->add( 'user_name', __( 'Username may not be longer than 60 characters.' ) );
+	}
+
+	// Strip any whitespace and then match against case insensitive characters a-z 0-9 _ . - @
+	$user_login = preg_replace( '/\s+/', '', sanitize_user( $user_login, true ) );
+
+	// If the previous operation generated a different value, the username is invalid
+	if ( $user_login !== $original_user_login ) {
+		$errors->add( 'user_name', __( '<strong>ERROR</strong>: This username is invalid because it uses illegal characters. Please enter a valid username.' ) );
+	}
+
+	if ( is_multisite() ) {
+		// Check the user_login against an array of illegal names
+		$illegal_logins = get_site_option( 'illegal_names' );
+		if ( false == is_array( $illegal_logins ) ) {
+			$illegal_logins = array( 'www', 'web', 'root', 'admin', 'main', 'invite', 'administrator' );
+			add_site_option( 'illegal_names', $illegal_logins );
+		}
+		if ( in_array( $user_login, $illegal_logins ) ) {
+			$errors->add( 'user_name',  __( 'Sorry, that username is not allowed.' ) );
+		}
+	}
+
+	/** This filter is documented in wp-includes/user.php */
+	$illegal_logins = (array) apply_filters( 'illegal_user_logins', array() );
+
+	if ( in_array( strtolower( $user_login ), array_map( 'strtolower', $illegal_logins ) ) ) {
+		$errors->add( 'user_name',  __( 'Sorry, that username is not allowed.' ) );
+	}
+
+	if ( is_multisite() ) {
+		// User login must have at least one letter
+		if ( ! preg_match( '/[a-zA-Z]+/', $user_login ) ) {
+			$errors->add( 'user_name', __( 'Sorry, usernames must have letters too!' ) );
+		}
+	}
+
+	// Check if the username has been used already.
+	if ( username_exists( $user_login ) ) {
+		$errors->add( 'user_name', __( 'Sorry, that username already exists!' ) );
+	}
+
+	/**
+	 * Filter a user's login after it has been validated for creation.
+	 *
+	 * @since TBD
+	 *
+	 * @param string   $user_login          The user's login.
+	 * @param string   $original_user_login The original user login.
+	 * @param WP_Error $errors              User's feedback error messages.
+	 * }
+	 */
+	$user_login = apply_filters_ref_array( 'wp_validate_user_login', array( $user_login, $original_user_login, &$errors ) );
+
+	if ( $errors->errors ) {
+		return $errors;
+	}
+
+	return $user_login;
+}
+
+/**
  * Handles registering a new user.
  *
  * @since 2.5.0
@@ -2236,7 +2330,6 @@
 function register_new_user( $user_login, $user_email ) {
 	$errors = new WP_Error();
 
-	$sanitized_user_login = sanitize_user( $user_login );
 	/**
 	 * Filter the email address of a user being registered.
 	 *
@@ -2246,23 +2339,9 @@
 	 */
 	$user_email = apply_filters( 'user_registration_email', $user_email );
 
-	// Check the username
-	if ( $sanitized_user_login == '' ) {
-		$errors->add( 'empty_username', __( '<strong>ERROR</strong>: Please enter a username.' ) );
-	} elseif ( ! validate_username( $user_login ) ) {
-		$errors->add( 'invalid_username', __( '<strong>ERROR</strong>: This username is invalid because it uses illegal characters. Please enter a valid username.' ) );
-		$sanitized_user_login = '';
-	} elseif ( username_exists( $sanitized_user_login ) ) {
-		$errors->add( 'username_exists', __( '<strong>ERROR</strong>: This username is already registered. Please choose another one.' ) );
+	// Validate the username
+	$sanitized_user_login = wp_validate_user_login( $user_login, $errors );
 
-	} else {
-		/** This filter is documented in wp-includes/user.php */
-		$illegal_user_logins = array_map( 'strtolower', (array) apply_filters( 'illegal_user_logins', array() ) );
-		if ( in_array( strtolower( $sanitized_user_login ), $illegal_user_logins ) ) {
-			$errors->add( 'invalid_username', __( '<strong>ERROR</strong>: Sorry, that username is not allowed.' ) );
-		}
-	}
-
 	// Check the email address
 	if ( $user_email == '' ) {
 		$errors->add( 'empty_email', __( '<strong>ERROR</strong>: Please type your email address.' ) );
Index: src/wp-signup.php
===================================================================
--- src/wp-signup.php	(revision 37416)
+++ src/wp-signup.php	(working copy)
@@ -639,7 +639,7 @@
 	$errors = $filtered_results['errors'];
 
 	if ( empty($blogname) )
-		$blogname = $user_name;
+		$blogname = preg_replace( '|[ _.\-@]|i', '', $user_name );
 	?>
 	<form id="setupform" method="post" action="wp-signup.php">
 		<input type="hidden" name="stage" value="validate-blog-signup" />
Index: tests/phpunit/includes/install.php
===================================================================
--- tests/phpunit/includes/install.php	(revision 37416)
+++ tests/phpunit/includes/install.php	(working copy)
@@ -55,7 +55,7 @@
 // Prefill a permalink structure so that WP doesn't try to determine one itself.
 add_action( 'populate_options', '_set_default_permalink_structure_for_tests' );
 
-wp_install( WP_TESTS_TITLE, 'admin', WP_TESTS_EMAIL, true, null, 'password' );
+wp_install( WP_TESTS_TITLE, 'some-admin', WP_TESTS_EMAIL, true, null, 'password' );
 
 // Delete dummy permalink structure, as prefilled above.
 if ( ! is_multisite() ) {
Index: tests/phpunit/tests/multisite/wpmuValidateUserSignup.php
===================================================================
--- tests/phpunit/tests/multisite/wpmuValidateUserSignup.php	(revision 37416)
+++ tests/phpunit/tests/multisite/wpmuValidateUserSignup.php	(working copy)
@@ -9,8 +9,8 @@
 	/**
 	 * @dataProvider data_user_name
 	 */
-	public function test_user_name( $user_name, $error_message ) {
-		$v = wpmu_validate_user_signup( $user_name, 'foo@example.com' );
+	public function test_user_name( $user_login, $error_message ) {
+		$v = wpmu_validate_user_signup( $user_login, 'foo@example.com' );
 		$this->assertContains( 'user_name', $v['errors']->get_error_codes(), $error_message );
 	}
 
@@ -18,7 +18,6 @@
 		return array(
 			array( 'contains spaces', 'User names with spaces are not allowed.' ),
 			array( 'ContainsCaps', 'User names with capital letters are not allowed.'  ),
-			array( 'contains_underscores', 'User names with underscores are not allowed.'  ),
 			array( 'contains%^*()junk', 'User names with non-alphanumeric characters are not allowed.'  ),
 			array( '', 'Empty user names are not allowed.'  ),
 			array( 'foo', 'User names of 3 characters are not allowed.'  ),
Index: tests/phpunit/tests/user/wpValidateUserLogin.php
===================================================================
--- tests/phpunit/tests/user/wpValidateUserLogin.php	(nonexistent)
+++ tests/phpunit/tests/user/wpValidateUserLogin.php	(working copy)
@@ -0,0 +1,112 @@
+<?php
+
+/**
+ * @group user
+ */
+class Tests_User_WpValidateUserLogin extends WP_UnitTestCase {
+
+	/**
+	 * A function to filter add an illegal user login.
+	 *
+	 * @param  array $illegal_logins
+	 * @return array
+	 */
+	public function illegal_user_logins( $illegal_logins ) {
+		$illegal_logins[] = 'adm1n';
+		return $illegal_logins;
+	}
+
+	/**
+	 * A function to filter and invalidate a login.
+	 *
+	 * @param  array $illegal_logins
+	 * @return array
+	 */
+	public function invalidate_login( $user_login, $original_user_login, $errors ) {
+		$errors->add( 'user_name',  __( 'Sorry, that username is not allowed.' ) );
+		return $user_login;
+	}
+
+	public function test_user_login_is_valid() {
+		$this->assertSame( 'jonsnow', wp_validate_user_login( 'jonsnow' ) );
+	}
+
+	public function test_user_login_can_be_less_than_three_characters() {
+		$validated = wp_validate_user_login( 'tom' );
+		$this->assertSame( 'tom', $validated );
+	}
+
+	public function test_user_login_cannot_be_more_than_sixty_characters() {
+		$validated = wp_validate_user_login( 'john-jacob-jingleheimer-david-marcus-castro-manuel-andrew-schmidt' );
+		$this->assertTrue( is_wp_error( $validated ) );
+	}
+
+	public function allowed_usernames_with_special_characters() {
+			return array(
+				array( 'arya.stark' ),
+				array( 'arya_stark' ),
+				array( 'arya-stark' ),
+				array( 'arya.stark@hotmail.com' ),
+			);
+	}
+
+	/**
+	 * @dataProvider allowed_usernames_with_special_characters
+	 */
+	public function test_user_login_can_contain_some_special_characters( $user_login ) {
+		$this->assertEquals( $user_login, wp_validate_user_login( $user_login ) );
+	}
+
+	public function test_user_login_cannot_contain_illegal_characters() {
+		$validated = wp_validate_user_login( 'cer$ei' );
+		$this->assertTrue( is_wp_error( $validated ) );
+	}
+
+	public function illegal_logins_in_multisite() {
+			return array(
+				array( 'www' ),
+				array( 'web' ),
+				array( 'root' ),
+				array( 'admin' ),
+				array( 'main' ),
+				array( 'invite' ),
+				array( 'administrator' ),
+			);
+	}
+
+	/**
+	 * @dataProvider illegal_logins_in_multisite
+	 */
+	public function test_user_login_cannot_contain_illegal_login_in_multisite( $user_login ) {
+		$validated = wp_validate_user_login( $user_login );
+		if ( is_multisite() ) {
+			$this->assertTrue( is_wp_error( $validated ) );
+		} else {
+			$this->assertEquals( $user_login, $validated );
+		}
+	}
+
+	public function test_user_login_must_contain_letter_in_multisite() {
+		$validated = wp_validate_user_login( '.' );
+		if ( is_multisite() ) {
+			$this->assertTrue( is_wp_error( $validated ) );
+		} else {
+			$this->assertEquals( '.', $validated );
+		}
+	}
+
+	public function test_illegal_login_filter_should_invalidate_user_logins() {
+		add_filter( 'illegal_user_logins', array( $this, 'illegal_user_logins' ) );
+		$validated = wp_validate_user_login( 'adm1n' );
+		$this->assertTrue( is_wp_error( $validated ) );
+		remove_filter( 'illegal_user_logins', array( $this, 'illegal_user_logins' ) );
+	}
+
+	public function test_additional_validation_can_invalidate() {
+		add_filter( 'wp_validate_user_login', array( $this, 'invalidate_login' ), 10, 3 );
+		$validated = wp_validate_user_login( 'glerf' );
+		$this->assertTrue( is_wp_error( $validated ) );
+		remove_filter( 'wp_validate_user_login', array( $this, 'invalidate_login'), 10, 3  );
+	}
+
+}
