Index: wp-admin/includes/plugin-install.php
===================================================================
--- wp-admin/includes/plugin-install.php	(revision 12730)
+++ wp-admin/includes/plugin-install.php	(working copy)
@@ -215,6 +215,8 @@
 		<?php wp_nonce_field( 'plugin-upload') ?>
 		<label class="screen-reader-text" for="pluginzip"><?php _e('Plugin zip file'); ?></label>
 		<input type="file" id="pluginzip" name="pluginzip" />
+		<input type="checkbox" id="upgrade_checked" name="upgrade_checked" />
+		<label for="upgrade_checked"><?php _e('Replace current plugin'); ?></label>
 		<input type="submit" class="button" value="<?php esc_attr_e('Install Now') ?>" />
 	</form>
 <?php
Index: wp-admin/includes/class-wp-upgrader.php
===================================================================
--- wp-admin/includes/class-wp-upgrader.php	(revision 12730)
+++ wp-admin/includes/class-wp-upgrader.php	(working copy)
@@ -265,6 +265,51 @@
 		return $this->result;
 	}
 
+	/**
+	 * Find out which plugin is in the working dir.
+	 * 
+	 * Similar to get_plugins(), but in the working directory
+	 * Should that function be adapted to this purpose?
+	 *
+	 * @param string $working_dir Mystery plugin directory.
+	 * @return string|WP_Error The plugin in the directory or an error.
+	 */
+	function get_working_plugin( $working_dir ) {
+		global $wp_filesystem;
+
+		$working_files = $wp_filesystem->dirlist( $working_dir );
+		$working_subdir = '.';
+		$plugin_dir = basename( $working_dir );
+		$plugins = array();
+		if ( 1 == count( $working_files ) ) {
+			// Check for a subdirectory
+			$dir_names = array_keys( $working_files );
+			if ( 'd' == $working_files[$dir_names[0]]['type'] ) {
+				$working_subdir = $plugin_dir = $dir_names[0];
+				$working_files = $wp_filesystem->dirlist( trailingslashit( $working_dir ) . $working_subdir );
+			}
+		}
+		if ( ! empty( $working_files ) ) {
+			foreach( $working_files as $file => $properties ) {
+				if ( substr( $file, -4 ) == '.php' ) {
+					$plugin_data = get_plugin_data( trailingslashit( $working_dir ) . $working_subdir . '/' . $file, false, false );
+
+					if ( empty( $plugin_data['Name'] ) )
+						continue;
+
+					$plugins[] = $plugin_dir . '/' . $file;
+				}
+			}
+		}
+
+		if ( count( $plugins ) != 1 ) {
+			// More trouble than it's worth handling multple plugins in a package?
+			return new WP_Error('bad_package', $this->strings['bad_package']); 
+		}
+
+		return $plugins[0];
+	}
+
 	function run($options) {
 
 		$defaults = array( 	'package' => '', //Please always pass this.
@@ -272,7 +317,8 @@
 							'clear_destination' => false,
 							'clear_working' => true,
 							'is_multi' => false,
-							'hook_extra' => array() //Pass any extra $hook_extra args here, this will be passed to any hooked filters.
+							'hook_extra' => array(), //Pass any extra $hook_extra args here, this will be passed to any hooked filters.
+						  'is_upload_upgrade' => false	
 						);
 
 		$options = wp_parse_args($options, $defaults);
@@ -307,6 +353,19 @@
 			return $working_dir;
 		}
 
+		if ( $is_upload_upgrade and empty( $hook_extra ) ) {
+			// Doesn't work on plugins without their own subdirectory?
+			$plugin = $this->get_working_plugin( $working_dir );
+
+			if ( is_wp_error($plugin) ) {
+				$this->skin->error($plugin);
+				return $plugin;
+			}
+
+			// This upgrade method depends on this identification of the plugin for hooks
+			$hook_extra = array( 'plugin' => $plugin );
+		}
+
 		//With the given options, this installs it to the destination directory.
 		$result = $this->install_package( array(
 											'source' => $working_dir,
@@ -402,6 +461,34 @@
 
 	}
 
+	function upload_upgrade($package) {
+
+		$this->init();
+		$this->upgrade_strings();
+
+		add_filter('upgrader_pre_install', array(&$this, 'deactivate_plugin_before_upgrade'), 10, 2);
+		add_filter('upgrader_clear_destination', array(&$this, 'delete_old_plugin'), 10, 4);
+
+		$this->run(array(
+					'package' => $package,
+					'destination' => WP_PLUGIN_DIR,
+					'clear_destination' => true,
+					'clear_working' => true,
+					'hook_extra' => array(), // run must supply this after looking in the package
+					'is_upload_upgrade' => true
+				));
+
+		// Cleanup our hooks, incase something else does a upgrade on this connection.
+		remove_filter('upgrader_pre_install', array(&$this, 'deactivate_plugin_before_upgrade'));
+		remove_filter('upgrader_clear_destination', array(&$this, 'delete_old_plugin'));
+
+		if ( ! $this->result || is_wp_error($this->result) )
+			return $this->result;
+
+		// Force refresh of plugin update information
+		delete_site_transient('update_plugins');
+	}
+
 	function upgrade($plugin) {
 
 		$this->init();
Index: wp-admin/update.php
===================================================================
--- wp-admin/update.php	(revision 12730)
+++ wp-admin/update.php	(working copy)
@@ -110,7 +110,10 @@
 		$type = 'upload'; //Install plugin type, From Web or an Upload.
 
 		$upgrader = new Plugin_Upgrader( new Plugin_Installer_Skin( compact('type', 'title', 'nonce', 'url') ) );
-		$upgrader->install( $file_upload->package );
+		if ( isset( $_REQUEST['upgrade_checked'] ) ) 
+			$upgrader->upload_upgrade( $file_upload->package );
+		else 
+			$upgrader->install( $file_upload->package );
 
 		include('admin-footer.php');
 
@@ -195,4 +198,4 @@
 	} else {
 		do_action('update-custom_' . $action);
 	}
-}
\ No newline at end of file
+}
