Index: wp-admin/favicon-upload.php
===================================================================
--- wp-admin/favicon-upload.php	(revision 0)
+++ wp-admin/favicon-upload.php	(working copy)
@@ -0,0 +1,314 @@
+<?php
+/**
+ * Handles the uploading, cropping and scaling of favicons.
+ * 
+ * @package WordPress
+ * @subpackage Administration
+ * @since 3.4.1
+ */
+
+// Bootstrap admin and all its goodies
+require_once('admin.php');
+
+define( 'FAVICON_SIZE', 32 ); // Width (and height) of the favicon (in pixels)
+
+/*	Upload is a 2-step process:	
+ *	1. Process the uploaded file and show the crop UI
+ *	2. Manipulate the pixel data, save to PNG and ICO and write to options.
+ */
+
+if ( isset( $_POST['CROP_AND_SAVE'] ) ) {
+	if ( isset( $_POST['attachment_id'] ) && is_numeric( $_POST['attachment_id'] ) ){
+		$image_basename = process_crop_thumbnail( $_POST['attachment_id'] );
+
+		if ( is_wp_error( $image_basename ) ) {
+			include_once('./admin-header.php');
+			echo '<div class="wrap">';
+				echo '<h2>' . __( 'Image upload error' ) . '</h2>';
+				echo '<p>' . $image_basename->get_error_message() . '</p>';
+				echo '<p><a href="' . admin_url( 'options-general.php' ) . '">&laquo;' . __( 'Back to Settings &gt; General' ) . '</a></p>';
+			echo '</div><!-- .wrap -->';
+			include_once('./admin-footer.php');
+		} else {
+			foreach ( array( $image_basename . '.png', $image_basename . '.ico' ) as $image_filename )
+				save_thumbnail_attachment( $image_filename, $attachment_id );
+
+			// And save the basename out to options.
+			update_option( 'sitefavicon', basename( $image_basename ) );
+			
+			/** @TODO need to find a way to notify the user that the process has completed successfully - admin_notices? */
+			wp_redirect( admin_url( 'options-general.php' ) );
+		} 
+	} else {
+		return new WP_Error( 'attachment_id_missing', 'Form submission error.' );
+	}
+} elseif ( isset( $_POST['REMOVE_FAVICON'] ) ) {
+	remove_thumbnail();
+} else {
+	/** @TODO make sure that we trap for someone just pressing "Upload image" but with no image attached */
+	// Enqueue the JS for the cropper...
+	add_action( 'admin_enqueue_scripts', 'enqueue_cropper' );
+	// ...and our own script for populating the crop form
+	add_action( 'admin_footer', 'cropping_js', 10,  1);
+	
+	// Process the upload and create the attachment file in the media center
+	$image_results = process_thumbnail_upload();
+	
+	include_once('./admin-header.php');
+	
+	// hack because image replication isn't fast enough. See https://wpcom.automattic.com/ticket/1294
+	sleep( 2 );
+
+	echo '<div class="wrap">';
+	
+	if ( is_wp_error( $image_results ) )  {
+		echo '<h2>' . __( 'Image upload error' ) . '</h2>';
+		echo '<p>' . $image_results->get_error_message() . '</p>';
+		echo '<p><a href="' . admin_url( 'options-general.php' ) . '">&laquo;' . __( 'Back to Settings &gt; General' ) . '</a></p>';
+	} else {
+		// Image upload successful.
+		// Now we can hook in our javascript and provide the width/height of our image as the default crop size
+		$crop_size = min( $image_results['width'], $image_results['height'] );
+		echo '<script type="text/javascript">var jcrop_starting_size = ' . $crop_size . '; // Initialize jcrop crop area starting size</script>';
+	
+		echo '<h2>' . __( 'Crop uploaded image' ) . '</h2>';
+		echo '<p>' . __( 'Choose the part of the image you want to use for your favicon.' ) . '</p>';
+		
+		echo '<form id="favicon-crop-form" method="post" action="' . $_SERVER['REQUEST_URI'] . '">'; // Point the form action back to this script
+		
+			echo <<<CROP_FORM
+			<input type="hidden" name="x1" id="x1" />
+			<input type="hidden" name="y1" id="y1" />
+			<input type="hidden" name="x2" id="x2" />
+			<input type="hidden" name="y2" id="y2" />
+			<input type="hidden" name="width" id="width" />
+			<input type="hidden" name="height" id="height" />
+			<input type="hidden" name="attachment_id" id="attachment_id" value="{$image_results['attachment_id']}" />
+			<input type="hidden" name="scaling_factor" id="scaling_factor" value="{$image_results['scaling_factor']}" />
+CROP_FORM;
+		
+			echo '<img src="' . $image_results['src'] . '" id="upload" width="' . $image_results['width'] . '" height="' . $image_results['height'] . '" />';
+		
+			echo '<p class="submit"><input type="submit" name="CROP_AND_SAVE" value="' . __( 'Crop image' ) . ' &raquo;" /></p>';
+		echo '</form>';
+	}
+	
+	echo '</div><!-- .wrap -->';
+	
+	include_once('./admin-footer.php');
+}
+
+
+/**
+ * Process the image file upload and return a WP_Error or details about the attachment image file.
+ * 
+ * @return mixed WP_Error | $image_results array 
+ */
+function process_thumbnail_upload(){
+	$file = wp_handle_upload( $_FILES['avatarfile'], array( 'action' => 'wp_upload_favicon') );
+	if ( isset($file['error']) ) die( $file['error'] );
+	
+	$url = $file['url'];
+	$file = $file['file'];
+	$filename = basename($file);
+	
+	// Construct the object array
+	$object = array(
+		'post_title' => $filename,
+		'post_content' => $url,
+		'post_mime_type' => 'import',
+		'guid' => $url
+	);
+
+	// Save the data.  Also makes replication work
+	$id = wp_insert_attachment($object, $file);
+
+	// Retrieve the image dimensions
+	list( $orig_width, $orig_height, $type, $attr ) = getimagesize( $file );
+	
+	// Do we need to scale down the image so we can display it nicely in the interactive Crop tool?
+	if ( $orig_width > 600 || $orig_height > 600 ) {
+		$image = wp_create_thumbnail( $file, 600 );
+		list( $width, $height, $type, $attr ) = getimagesize( $image );
+		
+		 // Update the attachment record to reflect the newly-scaled thumbnail image
+		$thumb = basename( $image );
+		$metadata = array( 'thumb' => $thumb );
+		wp_update_attachment_metadata( $id, $metadata );
+
+		$url = str_replace( basename( $url ), $thumb, $url );
+
+		$scaling = $orig_width / $width;
+	} else {
+		// No scaling required; just copy original values.
+		$width = $orig_width;
+		$height = $orig_height;
+		$scaling = 1;
+	}
+
+	// Check image file format
+	$image_type = exif_imagetype( get_attached_file( $id ) );
+	if (! in_array( $image_type, array( IMAGETYPE_PNG, IMAGETYPE_JPEG, IMAGETYPE_BMP ) ) )
+		$error = new WP_Error( 'bad_file_format', __( 'Please only use PNG (.png), JPEG (.jpg) or BMP (.bmp) image files for favicons. ' ) );
+	
+	// return WP_Error or the $image_results array
+	if ( $error ){
+		return $error;
+	} else {
+		return array( 
+			'attachment_id' => $id,
+			'src' => $url,
+			'width' => $width,
+			'height' => $height,
+			'scaling_factor' => $scaling
+		);
+	}
+}
+
+/**
+ * Create PNG and BMP image resources based on the form submission of the cropped thumbnail.
+ * 
+ * @param int $attachment_id The ID of the original attachment's post record.
+ * @return mixed WP_Error | Favicon file base name (ie: fully qualified file name without any TLA file extension)
+ */
+function process_crop_thumbnail( $attachment_id ){
+	$src_file = get_attached_file( $attachment_id );
+
+	// Highly unlikely, but let's check
+	if (! file_exists( $src_file ) )
+		return new WP_Error( 'file_missing', __( 'Attachment image file missing (possible save error: check space on web server).' ) );
+
+	// Make sure we're still within accepted image types
+	$image_type = exif_imagetype( $src_file );
+	if (! $image_type || ! in_array( $image_type, array( IMAGETYPE_PNG, IMAGETYPE_JPEG, IMAGETYPE_BMP ) ) )
+		return new WP_Error( 'bad_file_format', __( 'Please only use PNG (.png), JPEG (.jpg) or BMP (.bmp) image files for favicons. ' ) );
+
+	// Parse image file bytes
+	$src_image = wp_load_image( $src_file );
+	if ( !is_resource($src_image) )
+		return new WP_Error( 'is_not_resource', __( 'Error loading image. You got me: I\'m stumped.' ) ); 
+
+	// We crop from the original, not the medium sized, display-only thumbnail
+	$src_x = $_POST['x1'] * $_POST['scaling_factor'];
+	$src_y = $_POST['y1'] * $_POST['scaling_factor'];
+	$src_width = $_POST['width'] * $_POST['scaling_factor'];
+	$src_height = $_POST['height'] * $_POST['scaling_factor'];
+
+	$dst_width = $dst_height = FAVICON_SIZE;
+	// Avoid upscaling
+	if ( $src_width < $dst_width || $src_height < $dst_height ) {
+		$dst_width = $src_width;
+		$dst_height = $src_height;
+	}
+
+	$dst_image = wp_imagecreatetruecolor( $dst_width, $dst_height );
+	if (function_exists('imageantialias')) imageantialias($dst_image, true);
+	imagealphablending($dst_image, false);
+	imagesavealpha($dst_image, true);
+	imagecopyresampled($dst_image, $src_image, 0, 0, $src_x, $src_y, $dst_width, $dst_height, $src_width, $src_height);
+	imagedestroy( $src_image );
+
+
+	// Save the image in PNG and BMP formats
+	$file_info = pathinfo( $src_file );
+	$src_basename = basename( $src_file, '.' . $file_info['extension'] );
+	$dst_filename = str_replace( $src_basename, $src_basename . '_' . FAVICON_SIZE . 'x' . FAVICON_SIZE, $src_file );
+	// Strip the TLA from the filename
+	$dst_filename = preg_replace( '/\\.[^\\.]+$/', '', $dst_filename );
+
+	$png_filename = $dst_filename . '.png';
+	if (! imagepng( $dst_image, $png_filename, 0 ) )
+		return new WP_Error( 'png_write_error', 'Error writing PNG favicon file.' );
+	$ico_filename = $dst_filename . '.ico';
+	if (!image2wbmp( $dst_image, $ico_filename, 1 ) )
+		return new WP_Error( 'ico_write_error', 'Error writing ICO favicon file.' );
+
+	imagedestroy($dst_image);
+
+	return $dst_filename;
+}
+
+/**
+ * Creates an attachment post record for a newly created thumbnail
+ * 
+ * @param string $file_name Fully qualified file name for the image asset file.
+ * @param int $parent_attachment_id The ID of the original thumbnail's attachment post record
+ * 
+ * @return int The ID of the newly-created thumbnail attachment post record
+ */
+function save_thumbnail_attachment( $file_name, $parent_attachment_id ){
+	$file_info = pathinfo( $file_name ); // So we can get the TLA later on
+	
+	$file_name = apply_filters( 'wp_create_file_in_uploads', $file_name, $parent_attachment_id ); // For replication
+
+	$parent = get_post( $parent_attachment_id );
+	$parent_url = $parent->guid;
+	
+	// Update the attachment
+	$mimes = get_allowed_mime_types();
+	$attachment_id = wp_insert_attachment( array(
+		'ID' => $attachment_id,
+		'post_title' => basename( $file_name ),
+		'post_content' => $url,
+		'post_mime_type' => $mimes[ $file_info['extension'] ],
+		'guid' => $url,
+		'context' => 'favicon'
+	), $file_name );
+	wp_update_attachment_metadata( $attachment_id, wp_generate_attachment_metadata( $attachment_id, $file_name ) );
+	
+	return $attachment_id;
+}
+
+function remove_thumbnail(){
+	
+}
+
+
+/**
+ * Called in admin_enqueue_scripts to add the cropper.js script and styles
+ */
+function enqueue_cropper(){
+	wp_enqueue_script( 'jcrop', 'jquery' );
+	wp_enqueue_style('jcrop'); // We can enqueue styles within the admin_enqueue_script action hook {@link http://wpdevel.wordpress.com/2011/12/12/use-wp_enqueue_scripts-not-wp_print_styles-to-enqueue-scripts-and-styles-for-the-frontend/}
+}
+
+function cropping_js(){
+	// Purely for coding convenience and legibility
+	$favicon_size = FAVICON_SIZE;
+	
+	echo <<<CROP_JS
+	<!-- Favicon cropping -->
+	<script type="text/javascript">
+		// Update the crop form
+		function onEndCrop( coords ) {
+			jQuery( '#x1' ).val(coords.x);
+			jQuery( '#y1' ).val(coords.y);
+			jQuery( '#x2' ).val(coords.x2);
+			jQuery( '#y2' ).val(coords.y2);
+			jQuery( '#width' ).val(coords.w);
+			jQuery( '#height' ).val(coords.h);
+		}
+
+		// with a supplied ratio
+		jQuery(function($) {
+			if (! jcrop_starting_size) jcrop_starting_size = {$favicon_size}; // jcrop_starting_size should be set in the body once the image has been processed
+
+			// Set up default values on the crop form
+			jQuery( '#x1' ).val(0);
+			jQuery( '#y1' ).val(0);
+			jQuery( '#x2' ).val(jcrop_starting_size);
+			jQuery( '#y2' ).val(jcrop_starting_size);
+			jQuery( '#width' ).val(jcrop_starting_size);
+			jQuery( '#height' ).val(jcrop_starting_size);
+
+			// Initialize Jcrop
+			$('#upload').Jcrop({
+				aspectRatio: 1,
+				setSelect: [0, 0, jcrop_starting_size, jcrop_starting_size],
+				onSelect: onEndCrop
+			});
+		});
+	</script>
+CROP_JS;
+
+}
\ No newline at end of file
Index: wp-admin/js/wp-favicon.dev.js
===================================================================
--- wp-admin/js/wp-favicon.dev.js	(revision 0)
+++ wp-admin/js/wp-favicon.dev.js	(working copy)
@@ -0,0 +1,12 @@
+(function($){
+    $('#faviconfile').change(function(){
+        // check the file extention
+        if (! $.inArray( $(this).val().split('.').pop().toLowerCase() , /* valid file extentions */ ['gif','png','jpg','jpeg'] ) ) 
+            $('#favicon-invalid-filetype').show();
+        else
+        {
+            $('#faviconupload').submit();
+        }
+    });
+
+})(jQuery);
Index: wp-admin/js/wp-favicon.js
===================================================================
--- wp-admin/js/wp-favicon.js	(revision 0)
+++ wp-admin/js/wp-favicon.js	(working copy)
@@ -0,0 +1,12 @@
+(function($){
+    $('#faviconfile').change(function(){
+        // check the file extention
+        if (! $.inArray( $(this).val().split('.').pop().toLowerCase() , /* valid file extentions */ ['gif','png','jpg','jpeg'] ) ) 
+            $('#favicon-invalid-filetype').show();
+        else
+        {
+            $('#faviconupload').submit();
+        }
+    });
+
+})(jQuery);
Index: wp-admin/options-general.php
===================================================================
--- wp-admin/options-general.php	(revision 19934)
+++ wp-admin/options-general.php	(working copy)
@@ -81,6 +81,8 @@
 	'<p>' . __('<a href="http://wordpress.org/support/" target="_blank">Support Forums</a>') . '</p>'
 );
 
+wp_enqueue_script('wp-favicon');
+
 include('./admin-header.php');
 ?>
 
@@ -88,6 +90,28 @@
 <?php screen_icon(); ?>
 <h2><?php echo esc_html( $title ); ?></h2>
 
+<form action="<?php echo admin_url('favicon-upload.php')?>" method="post" enctype="multipart/form-data" id="faviconupload">
+	<input type="hidden" name="action" value="wp_upload_favicon" />
+
+	<table class="form-table">
+		<tr valign="top">
+			<th scope="row"><label for="sitefavicon"><?php _e('Favicon') ?></label></th>
+			<td>
+				<?php
+					// display the icon and the remove link if appropriate
+					if ( has_custom_favicon() ){
+						if ( $thumbnail = get_favicon_thumbnail_img() ) echo $thumbnail;
+					}
+				
+				?>
+				<input class="button" name="avatarfile" type="file" id="faviconfile" size="20" />
+				<p class="submit no-js hide-if-js"><input type="submit" name="Submit" value="Upload Image &raquo;" id="faviconsubmit" /></p>
+				<span class="description no-js"><?php _e('Click to upload your own custom icon ("favicon") for your blog. You\'ll be able to crop and scale it once it\'s uploaded.') ?></span>
+			</td>
+		</tr>
+	</table>
+</form>
+
 <form method="post" action="options.php">
 <?php settings_fields('general'); ?>
 
Index: wp-includes/functions.php
===================================================================
--- wp-includes/functions.php	(revision 19934)
+++ wp-includes/functions.php	(working copy)
@@ -1845,7 +1845,7 @@
 		'png' => 'image/png',
 		'bmp' => 'image/bmp',
 		'tif|tiff' => 'image/tiff',
-		'ico' => 'image/x-icon',
+		'ico' => 'image/vnd.microsoft.icon',
 		'asf|asx|wax|wmv|wmx' => 'video/asf',
 		'avi' => 'video/avi',
 		'divx' => 'video/divx',
Index: wp-includes/general-template.php
===================================================================
--- wp-includes/general-template.php	(revision 19934)
+++ wp-includes/general-template.php	(working copy)
@@ -1587,6 +1587,134 @@
 }
 
 /**
+ * Convenience function that echoes the HTML for the site's favicon icon.
+ * By default, automatically included in the header via the 'wp_head' action, which can be removed by themes if a custom favicon is desired.
+ * 
+ * @uses generate_site_favicon_html() to do the actual heavy lifting
+ */
+function site_favicon(){
+	echo generate_site_favicon_html();
+}
+add_action( 'wp_head', 'site_favicon' );
+add_action( 'admin_head', 'site_favicon' );
+
+/**
+ * Return the HTML for the site's favicon icon, if such has been defined.
+ * 
+ * @uses get_site_favicon_uri();
+ * 
+ * Includes the conditional tag wrapper for an IE (.ico) version. 
+ */
+function generate_site_favicon_html() {
+	$ie_favicon_uri = get_site_favicon_uri( 'ico' );
+	$favicon_uri = get_site_favicon_uri();
+
+	$content = "";
+	if (! is_wp_error( $ie_favicon_uri ) && ! is_wp_error( $favicon_uri ) ){
+
+		$content .= <<<FAVICON_HTML
+<!--Favicon (via 'wp_head' action) -->
+<!--[if IE]>
+<link rel="shortcut icon" href="{$ie_favicon_uri}" />
+<![endif]-->
+<!--[if !IE]>-->
+<link href="{$favicon_uri}" rel="icon" type="image/png" />
+<!--<![endif]-->
+FAVICON_HTML;
+    }
+	return $content;
+}
+
+/**
+ * Get the attachment post object associated with the current site favicon, based on the 'sitefavicon' option
+ * 
+ * @param string $format Default 'png'. Format of the file we're looking for
+ * @return object If found, returns the post object; if not, a WP_Error object
+ */
+function get_site_favicon_attachment( $format = 'png' ){
+	$favicon_basename = get_option ( 'sitefavicon' ); 
+	
+	if ( ! empty( $favicon_basename ) ) {
+		$favicon_fullname = $favicon_basename . '-' . $format;
+		
+		$posts = get_posts( array( 'name' => $favicon_fullname, 'post_type' => 'attachment' ) );
+		if ( $posts[0] ){
+			return $posts[0];
+		} else {
+			return new WP_Error( 'attachment_missing', __( "No attachment for '$favicon_fullname' was found." ) );
+		}
+	} else {
+		return new WP_Error( 'not_defined', __( "No favicon file provided." ) );
+	}
+}
+
+/**
+ * Returns the URI for the site's favicon based on the option set in  Admin > Settings > General.
+ * 
+ * @param string $format png|ico default 'png'. Use 'ico' for serving up an IE-compatible ICO file
+ * @return string fully qualified URI 
+ */
+function get_site_favicon_uri( $format = 'png' ){
+	/** @TODO provide error checking for validity of $format and $size */
+	$favicon_attachment = get_site_favicon_attachment( $format );
+	
+	/** @TODO provide the ability to define a 'default' favicon that would be distributed with fresh WP installations */
+	if ( ! is_wp_error( $favicon_attachment ) ) {
+		return wp_get_attachment_url( $favicon_attachment->ID );
+	}
+	
+	// We get here because of an error condition
+	/** @TODO default to the theme's favicon **/
+	// ATM do nothing (so URI is blank, rather than a WP_Error)
+}
+
+/**
+ * Gets the path to the favicon file, or returns a WP_Error
+ * @param string $format Default 'png'
+ * @return mixed File string or WP_Error object 
+ */
+function get_site_favicon_file( $format = 'png' ){
+	$favicon_attachment = get_site_favicon_attachment( $format );
+	
+	/** @TODO provide the ability to define a 'default' favicon that would be distributed with fresh WP installations */
+	if ( ! is_wp_error( $favicon_attachment ) ) {
+		return get_attached_file( $favicon_attachment->ID );
+	} else {
+		return $favicon_attachment; // returns the WP_Error object
+	}
+}
+
+/**
+ * Returns true or false depending on whether a custom favicon has been defined in Admin
+ * @return boolean 
+ */
+function has_custom_favicon(){
+	$favicon_basename = get_option ( 'sitefavicon' ); 
+	/** @TODO more robust checking: don't just check that the option has been set: check that the file exists */
+	return ( ! empty( $favicon_basename ) );
+}
+
+/**
+ * Returns an HTML <img> tag populated with the site favicon, in the format specified (usually PNG)
+ * @param string $format Default 'png'. Valid values are 'png', 'bmp' (note 'ico' is NOT valid)
+ * @return mixed Returns HTML <img> tag or WP_Error if invalid format given. Returns nothing if the file is missing. 
+ */
+function get_favicon_thumbnail_img( $format = 'png' ){
+	if (in_array( strtolower( $format ), array( 'png', 'bmp' ) ) ){
+		// Does the file actually exist?
+		$file = get_site_favicon_file( $format );
+		if (! is_wp_error( $file ) && file_exists( $file ) ){
+			$src = get_site_favicon_uri( $format );
+			if (!is_wp_error( $src ) ){
+				return '<img src="' . $src . '" alt="' . _x( 'Site favicon thumbnail', 'Thumbnail image accessibility text' ) .'" />';
+			}
+		}
+	} else {
+		return new WP_Error( 'invalid_file_format', __( 'Invalid file format. Valid formats are "png", "bmp".' ) );
+	}
+}
+
+/**
  * Display the links to the general feeds.
  *
  * @since 2.8.0
Index: wp-includes/script-loader.php
===================================================================
--- wp-includes/script-loader.php	(revision 19934)
+++ wp-includes/script-loader.php	(working copy)
@@ -88,6 +88,8 @@
 
 	$scripts->add( 'wp-fullscreen', "/wp-admin/js/wp-fullscreen$suffix.js", array('jquery'), false, 1 );
 
+	$scripts->add( 'wp-favicon', "/wp-admin/js/wp-favicon$suffix.js", array('jquery'), false, 1 );
+
 	$scripts->add( 'prototype', '/wp-includes/js/prototype.js', array(), '1.6.1');
 
 	$scripts->add( 'wp-ajax-response', "/wp-includes/js/wp-ajax-response$suffix.js", array('jquery'), false, 1 );
