diff -u includes/theme.maintenance.inc includes/theme.maintenance.inc
--- includes/theme.maintenance.inc	15 Oct 2009 09:49:31 -0000
+++ includes/theme.maintenance.inc	15 Oct 2009 11:00:32 -0000
@@ -235,12 +235,12 @@
  *   - message: The log message.
  *   - success: A boolean indicating failure or success.
  */
-function theme_update_plugin_manager_message($variables) {
+function theme_authorize_message($variables) {
   $output = '';
   $message = $variables['message'];
   $success = $variables['success'];
   if ($success) {
-    $output .= '<li class="success">' . check_plain($message) . '</li>';
+    $output .= '<li class="success">' . $message . '</li>';
   }
   else {
     $output .= '<li class="failure"><strong>' . t('Failed') . ':</strong> ' . $message . '</li>';
diff -u includes/updater.inc includes/updater.inc
--- includes/updater.inc	15 Oct 2009 08:54:53 -0000
+++ includes/updater.inc	15 Oct 2009 10:47:36 -0000
@@ -220,6 +220,9 @@
         throw new UpdaterException(t('Fatal error in update, cowardly refusing to wipe out the install directory.'));
       }
 
+      // Make sure the installation parent directory exists and is writable.
+      $this->prepareInstallDirectory($filetransfer, $args['install_dir']);
+
       // Note: If the project is installed in sites/all, it will not be
       // deleted. It will be installed in sites/default as that will override
       // the sites/all reference and not break other sites which are using it.
@@ -230,10 +233,14 @@
 
       // Copy the directory in place.
       $filetransfer->copyDirectory($this->source, $args['install_dir']);
-//      plugin_make_world_executable($filetransfer, $args['install_dir'] . '/' . $this->name);
+
+      // Make sure what we just installed is readable by the web server.
+      $this->makeWorldReadable($filetransfer, $args['install_dir'] . '/' . $this->name);
+
       // Run the updates.
       // @TODO: decide if we want to implement this.
       $this->postUpdate();
+
       // For now, just return a list of links of things to do.
       return $this->postUpdateTasks();
     }
@@ -257,9 +264,16 @@
     try {
       // Establish arguments with possible overrides.
       $args = $this->getInstallArgs($overrides);
+
+      // Make sure the installation parent directory exists and is writable.
+      $this->prepareInstallDirectory($filetransfer, $args['install_dir']);
+
       // Copy the directory in place.
       $filetransfer->copyDirectory($this->source, $args['install_dir']);
-//      plugin_make_world_executable($filetransfer, $args['install_dir'] . '/' . $this->name);
+
+      // Make sure what we just installed is readable by the web server.
+      $this->makeWorldReadable($filetransfer, $args['install_dir'] . '/' . $this->name);
+
       // Potentially enable something?
       // @TODO: decide if we want to implement this.
       $this->postInstall();
@@ -272,6 +286,67 @@
   }
 
   /**
+   * Make sure the installation parent directory exists and is writable.
+   *
+   * @param FileTransfer $filetransfer
+   *   Object which is a child of FileTransfer.
+   * @param string $directory
+   *   The installation directory to prepare.
+   */
+  public function prepareInstallDirectory(&$filetransfer, $directory) {
+    // Make the parent dir writable if need be and create the dir.
+    if (!is_dir($directory)) {
+      $parent_dir = dirname($directory);
+      if (!is_writable($parent_dir)) {
+        @chmod($parent_dir, 0755);
+        // It is expected that this will fail if the directory is owned by the
+        // FTP user. If the FTP user == web server, it will succeed.
+        try {
+          $filetransfer->createDirectory($directory);
+          $this->makeWorldReadable($filetransfer, $directory);
+        }
+        catch (FileTransferException $e) {
+          // Probably still not writable. Try to chmod and do it again.
+          // @todo: Make a new exception class so we can catch it differently.
+          try {
+            $old_perms = substr(sprintf('%o', fileperms($parent_dir)), -4);
+            $filetransfer->chmod($parent_dir, 0755);
+            $filetransfer->createDirectory($directory);
+            $this->makeWorldReadable($filetransfer, $directory);
+            // Put the permissions back.
+            $filetransfer->chmod($parent_dir, intval($old_perms, 8));
+          }
+          catch (FileTransferException $e) {
+            $message = t($e->getMessage(), $e->arguments);
+            $throw_message = t('Unable to create %directory due to the following: %reason', array('%directory' => $install_location, '%reason' => $message));
+            throw new UpdaterException($throw_message);
+          }
+        }
+        // Put the parent directory back.
+        @chmod($parent_dir, 0555);
+      }
+    }
+  }
+
+  /**
+   * Ensure that a given directory is world readable.
+   *
+   * @param FileTransfer $filetransfer
+   *   Object which is a child of FileTransfer.
+   * @param string $path
+   *   The file path to make world readable.
+   * @param bool $recursive
+   *   If the chmod should be applied recursively.
+   */
+  public function makeWorldReadable(&$filetransfer, $path, $recursive = TRUE) {
+    if (!is_executable($path)) {
+      // Set it to read + execute.
+      $new_perms = substr(sprintf('%o', fileperms($path)), -4, -1) . "5";
+      $filetransfer->chmod($path, intval($new_perms, 8), $recursive);
+    }
+  }
+
+  /**
    * Perform a backup.
    *
    * @todo Not implemented.
diff -u modules/update/update.authorize.inc modules/update/update.authorize.inc
--- modules/update/update.authorize.inc	15 Oct 2009 01:20:59 -0000
+++ modules/update/update.authorize.inc	15 Oct 2009 11:22:35 -0000
@@ -43,7 +43,7 @@
     'title' => t('Installing updates'),
     'init_message' => t('Preparing to update your site'),
     'operations' => $operations,
-    'finished' => 'update_authorize_batch_finished',
+    'finished' => 'update_authorize_update_batch_finished',
     'file' => drupal_get_path('module', 'update') . '/update.authorize.inc',
   );
 
@@ -85,7 +85,7 @@
     'init_message' => t('Preparing to install'),
     'operations' => $operations,
     // @todo Use a different finished callback for different messages?
-    'finished' => 'update_authorize_batch_finished',
+    'finished' => 'update_authorize_install_batch_finished',
     'file' => drupal_get_path('module', 'update') . '/update.authorize.inc',
   );
   batch_set($batch);
@@ -167,24 +167,94 @@
 }
 
 /**
- * Batch callback for when the authorized operations batch is finished.
+ * Batch callback for when the authorized update batch is finished.
+ *
+ * This processes the results and stashes them into SESSION such that
+ * authorize.php will render a report. Also responsible for putting the site
+ * back online and clearing the update status cache after a successful update.
  */
-function update_authorize_batch_finished($success, $results) {
+function update_authorize_update_batch_finished($success, $results) {
   foreach ($results['log'] as $project => $messages) {
     if (!empty($messages['#abort'])) {
       $success = FALSE;
     }
   }
+  $offline = variable_get('site_offline', FALSE);
   if ($success) {
+    // Now that the update completed, we need to clear the cache of available
+    // update data and recompute our status, so prevent show bogus results.
+    _update_authorize_clear_update_status();
+
+    if ($offline) {
+      variable_set('site_offline', FALSE);
+      $page_message = array(
+        'message' => t('Update was completed successfully. Your site has been taken out of maintenance mode.'),
+        'type' => 'status',
+      );
+    }
+    else {
+      $page_message = array(
+        'message' => t('Update was completed successfully.'),
+        'type' => 'status',
+      );
+    }
+  }
+  elseif (!$offline) {
+    $page_message = array(
+      'message' => t('Update failed! See the log below for more information.'),
+      'type' => 'error',
+    );
+  }
+  else {
+    $page_message = array(
+      'message' => t('Update failed! See the log below for more information. Your site is still in maintenance mode.'),
+      'type' => 'error',
+    );
+  }
+
+  // Set all these values into the SESSION so authorize.php can display them.
+  $_SESSION['authorize_results']['success'] = $success;
+  $_SESSION['authorize_results']['page_message'] = $page_message;
+  $_SESSION['authorize_results']['messages'] = $results['log'];
+  $_SESSION['authorize_results']['tasks'] = $results['tasks'];
+}
+
+/**
+ * Batch callback for when the authorized install batch is finished.
+ *
+ * This processes the results and stashes them into SESSION such that
+ * authorize.php will render a report. Also responsible for putting the site
+ * back online after a successful install if necessary.
+ */
+function update_authorize_install_batch_finished($success, $results) {
+  foreach ($results['log'] as $project => $messages) {
+    if (!empty($messages['#abort'])) {
+      $success = FALSE;
+    }
+  }
+  $offline = variable_get('site_offline', FALSE);
+  if ($success && $offline) {
     variable_set('site_offline', FALSE);
     $page_message = array(
-      'message' => t('Update was completed successfully! Your site has been taken out of maintenance mode.'),
+      'message' => t('Installation was completed successfully. Your site has been taken out of maintenance mode.'),
+      'type' => 'status',
+    );
+  }
+  elseif ($success && !$offline) {
+    $page_message = array(
+      'message' => t('Installation was completed successfully.'),
       'type' => 'status',
     );
   }
+  elseif (!$success && !$offline) {
+    $page_message = array(
+      'message' => t('Installation failed! See the log below for more information.'),
+      'type' => 'error',
+    );
+  }
   else {
     $page_message = array(
-      'message' => t('Update failed! See the log below for more information. Your site is still in maintenance mode.'),
+      'message' => t('Installation failed! See the log below for more information. Your site is still in maintenance mode.'),
       'type' => 'error',
     );
   }
@@ -209,0 +280,23 @@
+/**
+ * Private helper function to clear cached available update status data.
+ *
+ * Since this function is run at such a low bootstrap level, update.module is
+ * not loaded. So, we can't just call _update_cache_clear(). However, the
+ * database is bootstrapped, so we can do a query ourselves to clear out what
+ * we want to clear.
+ *
+ * Note that we do not want to just truncate the table, since that would
+ * remove items related to currently pending fetch attempts.
+ *
+ * @see update_authorize_update_batch_finished()
+ * @see _update_cache_clear()
+ */
+function _update_authorize_clear_update_status() {
+  $query = db_delete('cache_update');
+  $query->condition(
+    db_or()
+    ->condition('cid', 'update_project_%', 'LIKE')
+    ->condition('cid', 'available_releases::%', 'LIKE')
+  );
+  $query->execute();
+}
