Index: wp-includes/user.php
===================================================================
--- wp-includes/user.php	(revision 21852)
+++ wp-includes/user.php	(working copy)
@@ -1316,6 +1316,19 @@
 		$last_name = '';
 	$last_name = apply_filters('pre_user_last_name', $last_name);
 
+	if ( empty( $avatar_type ) || $avatar_type != 'custom' )
+		$avatar_type = 'gravatar';
+
+	if ( empty( $custom_avatar ) )
+		$custom_avatar = array();
+
+	if ( empty( $custom_avatar_rating ) || ! in_array( $custom_avatar_rating, array( 'G', 'PG', 'R', 'X' ) ) )
+		$custom_avatar_rating = 'G';
+
+	if ( empty( $has_custom_avatar ) )
+		$has_custom_avatar = false;
+	$has_custom_avatar = (bool) $has_custom_avatar;
+
 	if ( empty($description) )
 		$description = '';
 	$description = apply_filters('pre_user_description', $description);
@@ -1482,7 +1495,7 @@
  * @return array
  */
 function _get_additional_user_keys( $user ) {
-	$keys = array( 'first_name', 'last_name', 'nickname', 'description', 'rich_editing', 'comment_shortcuts', 'admin_color', 'use_ssl', 'show_admin_bar_front' );
+	$keys = array( 'first_name', 'last_name', 'nickname', 'description', 'rich_editing', 'comment_shortcuts', 'admin_color', 'use_ssl', 'show_admin_bar_front', 'avatar_type', 'custom_avatar', 'custom_avatar_rating', 'has_custom_avatar' );
 	return array_merge( $keys, array_keys( _wp_get_user_contactmethods( $user ) ) );
 }
 
Index: wp-includes/formatting.php
===================================================================
--- wp-includes/formatting.php	(revision 21852)
+++ wp-includes/formatting.php	(working copy)
@@ -2771,6 +2771,14 @@
 				$value = absint( $value );
 			break;
 
+		case 'avatar_size':
+			$value = absint( $value );
+			if ( $value < 1 )
+				$value = 1;
+			elseif ( $value > 512 )
+				$value = 512;
+			break;
+
 		case 'posts_per_page':
 		case 'posts_per_rss':
 			$value = (int) $value;
Index: wp-includes/pluggable.php
===================================================================
--- wp-includes/pluggable.php	(revision 21852)
+++ wp-includes/pluggable.php	(working copy)
@@ -1579,9 +1579,10 @@
  * @param int $size Size of the avatar image
  * @param string $default URL to a default image to use if no avatar is available
  * @param string $alt Alternate text to use in image tag. Defaults to blank
+ * @param string $avatar_type Override user preference (Used in user-edit.php)
  * @return string <img> tag for the user's avatar
 */
-function get_avatar( $id_or_email, $size = '96', $default = '', $alt = false ) {
+function get_avatar( $id_or_email, $size = '', $default = '', $alt = false, $avatar_type = '' ) {
 	if ( ! get_option('show_avatars') )
 		return false;
 
@@ -1590,9 +1591,21 @@
 	else
 		$safe_alt = esc_attr( $alt );
 
-	if ( !is_numeric($size) )
-		$size = '96';
+	if ( empty( $size ) || ! is_numeric( $size ) ) {
+		$size = get_option( 'avatar_size' );
+	} else {
+		$size = absint( $size );
 
+		if ( $size < 1 )
+			$size = 1;
+		elseif ( $size > 512 )
+			$size = 512;
+	}
+ 
+	if ( ! empty( $avatar_type ) )
+		if ( $avatar_type != 'custom' )
+			$avatar_type = 'gravatar';
+
 	$email = '';
 	if ( is_numeric($id_or_email) ) {
 		$id = (int) $id_or_email;
@@ -1617,6 +1630,16 @@
 		$email = $id_or_email;
 	}
 
+	if ( isset( $user ) ) {
+		if ( empty( $avatar_type ) )
+			$avatar_type = $user->avatar_type;
+
+		if ( ! $user->has_custom_avatar )
+			$avatar_type = 'gravatar';
+	} else {
+		$avatar_type = 'gravatar';
+	}
+
 	if ( empty($default) ) {
 		$avatar_default = get_option('avatar_default');
 		if ( empty($avatar_default) )
@@ -1650,22 +1673,55 @@
 	elseif ( strpos($default, 'http://') === 0 )
 		$default = add_query_arg( 's', $size, $default );
 
-	if ( !empty($email) ) {
-		$out = "$host/avatar/";
-		$out .= $email_hash;
-		$out .= '?s='.$size;
-		$out .= '&amp;d=' . urlencode( $default );
+	$img = "<img alt='{$safe_alt}' src='{$default}' class='avatar avatar-{$size} photo avatar-default' height='{$size}' width='{$size}' />";
+ 
+	if ( $avatar_type == 'custom' ) {
+		$rating = get_option( 'avatar_rating' );
 
+		if ( ! in_array( $rating, array( 'G', 'PG', 'R', 'X' ) ) ) {
+			break;
+		} else {
+			$ratings['G'] = 1;
+			$ratings['PG'] = 2;
+			$ratings['R'] = 3;
+			$ratings['X'] = 4;
+
+			if ( $ratings[ $user->custom_avatar_rating ] <= $ratings[ $rating ] ) {
+				if ( empty( $user->custom_avatar[ $size ] ) ) {
+					$upload_dir = wp_upload_dir();
+					$file = str_replace( $upload_dir['baseurl'], $upload_dir['basedir'], $user->custom_avatar['full'] );
+					$file = image_resize( $file, $size, $size, true );
+
+					if ( is_wp_error( $file ) ) {
+						$src = $user->custom_avatar['full'];
+					} else {
+						$custom_avatar = $user->custom_avatar;
+						$custom_avatar[ $size ] = str_replace( $upload_dir['basedir'], $upload_dir['baseurl'], $file );
+						$src = $custom_avatar[ $size ];
+
+						update_user_meta( $user->ID, 'custom_avatar', $custom_avatar );
+					}
+				} else {
+					$src = $user->custom_avatar[ $size ];
+				}
+
+				$img = "<img alt='{$safe_alt}' src='{$src}' class='avatar avatar-{$size} photo' height='{$size}' width='{$size}' />";
+			}
+		}
+	} else if ( !empty($email) ) {
+		$src = "$host/avatar/";
+		$src .= $email_hash;
+		$src .= '?s='.$size;
+		$src .= '&amp;d=' . urlencode( $default );
+
 		$rating = get_option('avatar_rating');
 		if ( !empty( $rating ) )
-			$out .= "&amp;r={$rating}";
+			$src .= "&amp;r={$rating}";
 
-		$avatar = "<img alt='{$safe_alt}' src='{$out}' class='avatar avatar-{$size} photo' height='{$size}' width='{$size}' />";
-	} else {
-		$avatar = "<img alt='{$safe_alt}' src='{$default}' class='avatar avatar-{$size} photo avatar-default' height='{$size}' width='{$size}' />";
+		$img = "<img alt='{$safe_alt}' src='{$src}' class='avatar avatar-{$size} photo' height='{$size}' width='{$size}' />";
 	}
 
-	return apply_filters('get_avatar', $avatar, $id_or_email, $size, $default, $alt);
+	return apply_filters('get_avatar', $img, $id_or_email, $size, $default, $alt, $avatar_type);
 }
 endif;
 
Index: wp-admin/includes/schema.php
===================================================================
--- wp-admin/includes/schema.php	(revision 21852)
+++ wp-admin/includes/schema.php	(working copy)
@@ -482,6 +482,7 @@
 
 	// 3.5
 	'link_manager_enabled' => 0,
+	'avatar_size' => 96
 	);
 
 	// 3.3
Index: wp-admin/includes/user.php
===================================================================
--- wp-admin/includes/user.php	(revision 21852)
+++ wp-admin/includes/user.php	(working copy)
@@ -94,6 +94,67 @@
 		$user->rich_editing = isset( $_POST['rich_editing'] ) && 'false' == $_POST['rich_editing'] ? 'false' : 'true';
 		$user->admin_color = isset( $_POST['admin_color'] ) ? sanitize_text_field( $_POST['admin_color'] ) : 'fresh';
 		$user->show_admin_bar_front = isset( $_POST['admin_bar_front'] ) ? 'true' : 'false';
+		$user->avatar_type = isset( $_POST['avatar_type'] ) ? sanitize_text_field( $_POST['avatar_type'] ) : 'gravatar';
+		$user->custom_avatar = $userdata->custom_avatar;
+		$user->has_custom_avatar = $userdata->has_custom_avatar;
+		$user->custom_avatar_rating = isset( $_POST['custom_avatar_rating'] ) ? sanitize_text_field( $_POST['custom_avatar_rating'] ) : 'G';
+
+		if ( isset( $_POST['uploadavatar'] ) && $_POST['uploadavatar'] ) {
+			$upload_dir = wp_upload_dir();
+
+			if ( is_array( $user->custom_avatar ) && $user->has_custom_avatar ) {
+				foreach ( $user->custom_avatar as $custom_avatar ) {
+					$file = str_replace( $upload_dir['baseurl'], $upload_dir['basedir'], $custom_avatar );
+					@unlink( $file );
+				}
+			}
+
+			$mimes = array(
+				'bmp'   =>  'image/bmp',
+				'gif'   =>  'image/gif',
+				'jpeg'  =>  'image/jpeg',
+				'jpg'   =>  'image/jpeg',
+				'jpe'   =>  'image/jpeg',
+				'png'   =>  'image/png',
+				'tiff'  =>  'image/tiff',
+				'tif'   =>  'image/tiff'
+			);
+
+			$overrides = array( 'mimes' => $mimes, 'test_form' => false );
+			$file = wp_handle_upload( $_FILES['import'], $overrides );
+
+			if ( isset( $file['error'] ) )
+				wp_die( $file['error'],  __( 'Image Upload Error' ) );
+
+			$user->avatar_type = 'custom';
+			$user->custom_avatar = array( 'full' => $file['url'] );
+
+			$size = get_option( 'avatar_size' );
+			$file = str_replace( $upload_dir['baseurl'], $upload_dir['basedir'], $user->custom_avatar['full'] );
+			$file = image_resize( $file, $size, $size, true );
+
+			if ( ! is_wp_error( $file ) )
+				$user->custom_avatar[ $size ] = str_replace( $upload_dir['basedir'], $upload_dir['baseurl'], $file );
+
+			$user->custom_avatar_rating = 'G';
+			$user->has_custom_avatar = true;
+		}
+
+		if ( isset( $_POST['removeavatar'] ) && $_POST['removeavatar'] ) {
+			if ( is_array( $user->custom_avatar ) ) {
+				$upload_dir = wp_upload_dir();
+
+				foreach ( $user->custom_avatar as $custom_avatar ) {
+					$file = str_replace( $upload_dir['baseurl'], $upload_dir['basedir'], $custom_avatar );
+					@unlink( $file );
+				}
+			}
+
+			$user->avatar_type = 'gravatar';
+			$user->custom_avatar = array();
+			$user->custom_avatar_rating = 'G';
+			$user->has_custom_avatar = false;
+		}
 	}
 
 	$user->comment_shortcuts = isset( $_POST['comment_shortcuts'] ) && 'true' == $_POST['comment_shortcuts'] ? 'true' : '';
Index: wp-admin/options-discussion.php
===================================================================
--- wp-admin/options-discussion.php	(revision 21852)
+++ wp-admin/options-discussion.php	(working copy)
@@ -243,6 +243,18 @@
 
 </fieldset></td>
 </tr>
+<tr valign="top">
+	<th scope="row"><?php _e( 'Avatar Size' ) ?></th>
+	<td>
+		<fieldset>
+			<legend class="screen-reader-text"><span><?php _e( 'Avatar Size' ); ?></span></legend>
+			<label>
+				<?php _e( 'Size of the avatar image' ); ?>
+				<input name="avatar_size" type="number" step="1" min="0" value="<?php form_option( 'avatar_size' ); ?>" class="small-text" />
+			</label>
+		</fieldset>
+	</td>
+</tr>
 <?php do_settings_fields('discussion', 'avatars'); ?>
 </table>
 
Index: wp-admin/options.php
===================================================================
--- wp-admin/options.php	(revision 21852)
+++ wp-admin/options.php	(working copy)
@@ -60,7 +60,7 @@
 
 $whitelist_options = array(
 	'general' => array( 'blogname', 'blogdescription', 'gmt_offset', 'date_format', 'time_format', 'start_of_week', 'timezone_string' ),
-	'discussion' => array( 'default_pingback_flag', 'default_ping_status', 'default_comment_status', 'comments_notify', 'moderation_notify', 'comment_moderation', 'require_name_email', 'comment_whitelist', 'comment_max_links', 'moderation_keys', 'blacklist_keys', 'show_avatars', 'avatar_rating', 'avatar_default', 'close_comments_for_old_posts', 'close_comments_days_old', 'thread_comments', 'thread_comments_depth', 'page_comments', 'comments_per_page', 'default_comments_page', 'comment_order', 'comment_registration' ),
+	'discussion' => array( 'default_pingback_flag', 'default_ping_status', 'default_comment_status', 'comments_notify', 'moderation_notify', 'comment_moderation', 'require_name_email', 'comment_whitelist', 'comment_max_links', 'moderation_keys', 'blacklist_keys', 'show_avatars', 'avatar_rating', 'avatar_default', 'avatar_size', 'close_comments_for_old_posts', 'close_comments_days_old', 'thread_comments', 'thread_comments_depth', 'page_comments', 'comments_per_page', 'default_comments_page', 'comment_order', 'comment_registration' ),
 	'media' => array( 'thumbnail_size_w', 'thumbnail_size_h', 'thumbnail_crop', 'medium_size_w', 'medium_size_h', 'large_size_w', 'large_size_h', 'image_default_size', 'image_default_align', 'image_default_link_type', 'embed_autourls', 'embed_size_w', 'embed_size_h' ),
 	'reading' => array( 'posts_per_page', 'posts_per_rss', 'rss_use_excerpt', 'show_on_front', 'page_on_front', 'page_for_posts', 'blog_public' ),
 	'writing' => array( 'default_post_edit_rows', 'use_smilies', 'default_category', 'default_email_category', 'use_balanceTags', 'default_link_category', 'default_post_format' )
Index: wp-admin/user-edit.php
===================================================================
--- wp-admin/user-edit.php	(revision 21852)
+++ wp-admin/user-edit.php	(working copy)
@@ -189,7 +189,7 @@
 } ?>
 </h2>
 
-<form id="your-profile" action="<?php echo esc_url( self_admin_url( IS_PROFILE_PAGE ? 'profile.php' : 'user-edit.php' ) ); ?>" method="post"<?php do_action('user_edit_form_tag'); ?>>
+<form enctype="multipart/form-data" id="your-profile" action="<?php echo esc_url( self_admin_url( IS_PROFILE_PAGE ? 'profile.php' : 'user-edit.php' ) ); ?>" method="post"<?php do_action('user_edit_form_tag'); ?>>
 <?php wp_nonce_field('update-user_' . $user_id) ?>
 <?php if ( $wp_http_referer ) : ?>
 	<input type="hidden" name="wp_http_referer" value="<?php echo esc_url($wp_http_referer); ?>" />
@@ -362,6 +362,86 @@
 ?>
 </table>
 
+<h3><?php _e( 'Avatar' ); ?></h3>
+
+<?php
+$avatar_type = isset( $profileuser->avatar_type ) ? $profileuser->avatar_type : 'gravatar';
+$custom_avatar_rating = isset( $profileuser->custom_avatar_rating ) ? $profileuser->custom_avatar_rating : 'G';
+$has_custom_avatar = isset( $profileuser->has_custom_avatar ) ? $profileuser->has_custom_avatar : false;
+?>
+
+<table class="form-table">
+<tr>
+	<th><?php _e( 'Display this avatar' ); ?></th>
+	<td class="avatar-picker">
+		<fieldset>
+			<legend class="screen-reader-text"><span><?php _e( 'Display this avatar' ); ?></span></legend>
+			<label>
+				<input <?php checked( $avatar_type, 'gravatar' ); ?> name="avatar_type" type="radio" value="gravatar" />
+				<?php echo get_avatar( $profileuser->ID, 32, '', false, 'gravatar' ); ?>
+				<?php _e( 'Gravatar' ); ?>
+				<span class="description"><?php _e( '<a href="http://codex.wordpress.org/How_to_Use_Gravatars_in_WordPress" target="_blank">More information</a>' ); ?></span>
+			</label>
+			<?php if ( $has_custom_avatar ) : ?>
+			<br />
+			<label>
+				<input <?php checked( $avatar_type, 'custom' ); ?> name="avatar_type" type="radio" value="custom" />
+				<?php echo get_avatar( $profileuser->ID, 32, '', false, 'custom' ); ?>
+				<?php _e( 'Custom' ); ?>
+			</label>
+			<?php endif; ?>
+		</fieldset>
+	</td>
+</tr>
+<tr>
+	<th><?php _e( 'Select Image' ); ?></th>
+	<td>
+		<fieldset>
+			<legend class="screen-reader-text"><span><?php _e( 'Select Image' ); ?></span></legend>
+			<label class="description" for="upload">Choose an image from your computer:</label><br />
+			<input name="import" type="file" />
+			<?php submit_button( __( 'Upload' ), 'button', 'uploadavatar', false ); ?>
+		</fieldset>
+	</td>
+</tr>
+<?php if ( $has_custom_avatar ) : ?>
+<tr>
+	<th><?php _e( 'Avatar Rating' ); ?></th>
+	<td>
+		<fieldset>
+			<legend class="screen-reader-text"><span><?php _e( 'Avatar Rating' ); ?></span></legend>
+			<?php
+			$ratings = array(
+				/* translators: Content suitability rating: http://bit.ly/89QxZA */
+				'G' => __( 'G &#8212; Suitable for all audiences' ),
+				/* translators: Content suitability rating: http://bit.ly/89QxZA */
+				'PG' => __( 'PG &#8212; Possibly offensive, usually for audiences 13 and above' ),
+				/* translators: Content suitability rating: http://bit.ly/89QxZA */
+				'R' => __( 'R &#8212; Intended for adult audiences above 17' ),
+				/* translators: Content suitability rating: http://bit.ly/89QxZA */
+				'X' => __( 'X &#8212; Even more mature than above' )
+			);
+
+			foreach ( $ratings as $key => $rating ) {
+				$selected = ( $custom_avatar_rating == $key ) ? 'checked="checked"' : '';
+				echo '<label><input ' . $selected . ' name="custom_avatar_rating" type="radio" value="' . esc_attr( $key ) . '" /> ' . $rating . '</label><br />';
+			};
+			?>
+			<span class="description"><?php _e( 'Choose a rating for your custom avatar.' ); ?></span>
+		</fieldset>
+	</td>
+</tr>
+<tr>
+	<th><?php _e( 'Remove Image' ); ?></th>
+	<td>
+		<legend class="screen-reader-text"><span><?php _e( 'Remove Image' ); ?></span></legend>
+		<?php submit_button( __( 'Remove Avatar Image' ), 'button', 'removeavatar', false ); ?>
+		<span class="description"><?php _e( 'This will remove the avatar image. You will not be able to restore any customizations.' ); ?></span>
+	</td>
+</tr>
+<?php endif; ?>
+</table>
+
 <h3><?php IS_PROFILE_PAGE ? _e('About Yourself') : _e('About the user'); ?></h3>
 
 <table class="form-table">
Index: wp-admin/css/wp-admin.css
===================================================================
--- wp-admin/css/wp-admin.css	(revision 21852)
+++ wp-admin/css/wp-admin.css	(working copy)
@@ -4786,6 +4786,11 @@
 	width: 15em;
 }
 
+#profile-page .avatar-picker img {
+	margin: 2px 0;
+	vertical-align: middle;
+}
+
 #createuser .form-field input {
 	width: 25em;
 }
