=== modified file 'includes/bootstrap.inc'
--- includes/bootstrap.inc	2009-02-25 13:49:26 +0000
+++ includes/bootstrap.inc	2009-02-25 18:24:26 +0000
@@ -28,13 +28,6 @@ define('CACHE_DISABLED', 0);
 define('CACHE_NORMAL', 1);
 
 /**
- * Indicates that page caching is using "aggressive" mode. This bypasses
- * loading any modules for additional speed, which may break functionality in
- * modules that expect to be run on each page load.
- */
-define('CACHE_AGGRESSIVE', 2);
-
-/**
  * Log message severity -- Emergency: system is unusable.
  *
  * @see watchdog()
@@ -104,10 +97,9 @@ define('WATCHDOG_DEBUG', 7);
 define('DRUPAL_BOOTSTRAP_CONFIGURATION', 0);
 
 /**
- * Second bootstrap phase: try to call a non-database cache
- * fetch routine.
+ * Second bootstrap phase: serve the page from cache if possible.
  */
-define('DRUPAL_BOOTSTRAP_EARLY_PAGE_CACHE', 1);
+define('DRUPAL_BOOTSTRAP_PAGE_CACHE', 1);
 
 /**
  * Third bootstrap phase: initialize database layer.
@@ -120,20 +112,19 @@ define('DRUPAL_BOOTSTRAP_DATABASE', 2);
 define('DRUPAL_BOOTSTRAP_ACCESS', 3);
 
 /**
- * Fifth bootstrap phase: initialize session handling.
+ * Fifth bootstrap phase: initialize the variable system.
  */
-define('DRUPAL_BOOTSTRAP_SESSION', 4);
+define('DRUPAL_BOOTSTRAP_VARIABLES', 4);
 
 /**
- * Sixth bootstrap phase: initialize the variable system.
+ * Sixth bootstrap phase: initialize session handling.
  */
-define('DRUPAL_BOOTSTRAP_VARIABLES', 5);
+define('DRUPAL_BOOTSTRAP_SESSION', 5);
 
 /**
- * Seventh bootstrap phase: load bootstrap.inc and module.inc, start
- * the variable system and try to serve a page from the cache.
+ * Seventh bootstrap phase: set up the page header.
  */
-define('DRUPAL_BOOTSTRAP_LATE_PAGE_CACHE', 6);
+define('DRUPAL_BOOTSTRAP_PAGE_HEADER', 6);
 
 /**
  * Eighth bootstrap phase: find out language of the page.
@@ -393,6 +384,15 @@ function conf_path($require_settings = T
 }
 
 /**
+ * Return the base URL path of the Drupal installation.
+ *
+ * At the very least, this will always default to '/'.
+ */
+function base_path() {
+  return $GLOBALS['base_path'];
+}
+
+/**
  * Initialize variables needed for the rest of the execution.
  */
 function drupal_initialize_variables() {
@@ -526,7 +526,9 @@ function conf_init() {
   if (count(explode('.', $cookie_domain)) > 2 && !is_numeric(str_replace('.', '', $cookie_domain))) {
     ini_set('session.cookie_domain', $cookie_domain);
   }
-  session_name('SESS' . md5($session_name));
+  // For Drupal 7, we add a 7 to SESS, ensuring that no user from Drupal 6
+  // stays logged in.
+  session_name('SESS7' . md5($session_name));
 }
 
 /**
@@ -677,41 +679,71 @@ function variable_del($name) {
 /**
  * Retrieve the current page from the cache.
  *
- * Note: we do not serve cached pages to authenticated users, or to anonymous
+ * Note: We do not serve cached pages to authenticated users, or to anonymous
  * users when $_SESSION is non-empty. $_SESSION may contain status messages
- * from a form submission, the contents of a shopping cart, or other user-
+ * from a form submission, the content of a shopping cart, or other user-
  * specific content that should not be cached and displayed to other users.
  *
- * @param $retrieve
- *   If TRUE, look up and return the current page in the cache, or start output
- *   buffering if the conditions for caching are satisfied. If FALSE, only
- *   return a boolean value indicating whether the current request may be
- *   cached.
+ * @param $caching_mode
+ *   If set, then one of CACHE_DISABLED or CACHE_NORMAL. If NULL, then it will
+ *   be looked up in the database.
  * @return
- *   The cache object, if the page was found in the cache; TRUE if the page was
- *   not found, but output buffering was started in order to possibly cache the
- *   current request; FALSE if the page was not found, and the current request
- *   may not be cached (e.g. because it belongs to an authenticated user). If
- *   $retrieve is TRUE, only return either TRUE or FALSE.
+ *   The cache object, if the page was found in the cache; FALSE if the page
+ *   was not found.
  */
-function page_get_cache($retrieve) {
-  global $user, $base_root;
-  static $ob_started = FALSE;
+function page_get_cache($caching_mode = NULL) {
+  global $base_root;
 
-  if ($user->uid || ($_SERVER['REQUEST_METHOD'] != 'GET' && $_SERVER['REQUEST_METHOD'] != 'HEAD') || count(drupal_get_messages(NULL, FALSE)) || $_SERVER['SERVER_SOFTWARE'] === 'PHP CLI') {
-    return FALSE;
-  }
-  if ($retrieve) {
-    $cache = cache_get($base_root . request_uri(), 'cache_page');
-    if ($cache) {
-      return $cache;
-    }
-    else {
+  $cache = FALSE;
+  if (_page_cache_can_be_set($caching_mode)) {
+    // Although this cookie is easy to be forged or deleted, the only
+    // consequence of forgery is that the attacker receives an uncached page.
+    if (!isset($_COOKIE['drupal_nocache'])) {
+      $cache = cache_get($base_root . request_uri(), 'cache_page');
+    }
+    // Regardless of the cookie, start output buffering to allow destroying
+    // the session at the end of the request if it only contained messages.
+    if (!$cache) {
       ob_start();
-      $ob_started = TRUE;
     }
   }
-  return $ob_started;
+  return $cache;
+}
+
+/**
+ * Check whether page caching is allowed for this request.
+ *
+ * Note that if this function returns TRUE then output buffering has been
+ * started in page_get_cache().
+ *
+ * @param $caching_mode
+ *   If set, then one of CACHE_DISABLED or CACHE_NORMAL. If NULL, then it will
+ *   be looked up in the database.
+ * @return
+ *   Boolean, TRUE if the current request may be cached.
+ */
+function _page_cache_can_be_set($caching_mode = NULL) {
+  global $user;
+
+  if (!isset($caching_mode)) {
+    $caching_mode = variable_get('cache', CACHE_DISABLED);
+  }
+  return $caching_mode == CACHE_NORMAL && empty($user->uid) && ($_SERVER['REQUEST_METHOD'] == 'GET' || $_SERVER['REQUEST_METHOD'] == 'HEAD') && $_SERVER['SERVER_SOFTWARE'] != 'CLI';
+}
+
+/**
+ * Set a cookie to disallow page caching.
+ *
+ * @param $set
+ *   Whether to set the cookie. Internal use only.
+ */
+function page_cache_set_disable_cookie($set = TRUE) {
+  static $cookie_sent = FALSE;
+  if ($set) {
+    $cookie_sent = TRUE;
+    setcookie('drupal_nocache', 1, time() + 20 * 365 * 86400, base_path(), ini_get('session.cookie_domain'));
+  }
+  return $cookie_sent;
 }
 
 /**
@@ -770,7 +802,7 @@ function drupal_page_header() {
  * logged out and the page has not been modified.
  *
  */
-function drupal_page_cache_header($cache) {
+function drupal_serve_cached_page($cache) {
   // Create entity tag based on cache update time.
   $etag = '"' . md5($cache->created) . '"';
 
@@ -989,6 +1021,7 @@ function drupal_set_message($message = N
 
     if ($repeat || !in_array($message, $_SESSION['messages'][$type])) {
       $_SESSION['messages'][$type][] = $message;
+      page_cache_set_disable_cookie();
     }
   }
 
@@ -1039,20 +1072,31 @@ function drupal_get_messages($type = NUL
  *
  * @param $ip string
  *   IP address to check.
+ * @param $full
+ *   TRUE if you want to issue a 403 header and exit if the IP is denied.
  * @return bool
  *   TRUE if access is denied, FALSE if access is allowed.
  */
-function drupal_is_denied($ip) {
+function drupal_is_denied($ip, $full = FALSE) {
   // Because this function is called on every page request, we first check
   // for an array of IP addresses in settings.php before querying the
   // database.
   $blocked_ips = variable_get('blocked_ips');
+  $denied = FALSE;
   if (isset($blocked_ips) && is_array($blocked_ips)) {
-    return in_array($ip, $blocked_ips);
+    $denied = in_array($ip, $blocked_ips);
   }
-  else {
-    return (bool)db_query("SELECT 1 FROM {blocked_ips} WHERE ip = :ip", array(':ip' => $ip))->fetchField();
+  // Only check if database.inc is loaded already.
+  elseif (function_exists('db_is_active')) {
+    $denied = (bool)db_query("SELECT 1 FROM {blocked_ips} WHERE ip = :ip", array(':ip' => $ip))->fetchField();
+  }
+  // Deny access to blocked IP addresses - t() is not yet available.
+  if ($denied && $full) {
+    header($_SERVER['SERVER_PROTOCOL'] . ' 403 Forbidden');
+    print 'Sorry, ' . check_plain(ip_address()) . ' has been banned.';
+    exit();
   }
+  return $denied;
 }
 
 /**
@@ -1093,13 +1137,16 @@ function drupal_anonymous_user($session 
  *     DRUPAL_BOOTSTRAP_FULL: Drupal is fully loaded, validate and fix input data.
  */
 function drupal_bootstrap($phase = NULL) {
-  static $phases = array(DRUPAL_BOOTSTRAP_CONFIGURATION, DRUPAL_BOOTSTRAP_EARLY_PAGE_CACHE, DRUPAL_BOOTSTRAP_DATABASE, DRUPAL_BOOTSTRAP_ACCESS, DRUPAL_BOOTSTRAP_SESSION, DRUPAL_BOOTSTRAP_VARIABLES, DRUPAL_BOOTSTRAP_LATE_PAGE_CACHE, DRUPAL_BOOTSTRAP_LANGUAGE, DRUPAL_BOOTSTRAP_PATH, DRUPAL_BOOTSTRAP_FULL), $completed_phase = -1;
+  static $phases = array(DRUPAL_BOOTSTRAP_CONFIGURATION, DRUPAL_BOOTSTRAP_PAGE_CACHE, DRUPAL_BOOTSTRAP_DATABASE, DRUPAL_BOOTSTRAP_ACCESS, DRUPAL_BOOTSTRAP_VARIABLES, DRUPAL_BOOTSTRAP_SESSION, DRUPAL_BOOTSTRAP_PAGE_HEADER, DRUPAL_BOOTSTRAP_LANGUAGE, DRUPAL_BOOTSTRAP_PATH, DRUPAL_BOOTSTRAP_FULL), $completed_phase = -1;
 
   if (isset($phase)) {
     while ($phases && $phase > $completed_phase) {
       $current_phase = array_shift($phases);
       _drupal_bootstrap($current_phase);
-      $completed_phase = $current_phase;
+       // This function is reentrant. Only update the completed phase when the current call actually resulted in a progress in the bootstrap process.
+      if ($current_phase > $completed_phase) {
+        $completed_phase = $current_phase;
+      }
     }
   }
   return $completed_phase;
@@ -1129,15 +1176,34 @@ function _drupal_bootstrap($phase) {
       conf_init();
       break;
 
-    case DRUPAL_BOOTSTRAP_EARLY_PAGE_CACHE:
+    case DRUPAL_BOOTSTRAP_PAGE_CACHE:
       // Allow specifying special cache handlers in settings.php, like
       // using memcached or files for storing cache information.
       require_once DRUPAL_ROOT . '/' . variable_get('cache_inc', 'includes/cache.inc');
-      // If the page_cache_fastpath is set to TRUE in settings.php and
-      // page_cache_fastpath (implemented in the special implementation of
-      // cache.inc) printed the page and indicated this with a returned TRUE
-      // then we are done.
-      if (variable_get('page_cache_fastpath', FALSE) && page_cache_fastpath()) {
+      // Here we only check whether the cache mode is set in settings.php,
+      // whether caching is on or not is checked in _page_cache_can_be_set()
+      // called from page_set_cache().
+      $cache_mode = variable_get('cache');
+      if (isset($cache_mode)) {
+        // We have a cache mode override from settings.php, we check for denied
+        // addresses but only from settings.php.
+        drupal_is_denied(ip_address(), TRUE);
+      }
+      else {
+        // We need to initialize the variable system. This will also load the
+        // database and check denied addresses.
+        drupal_bootstrap(DRUPAL_BOOTSTRAP_VARIABLES);
+      }
+      $cache = page_get_cache($cache_mode);
+      if (is_object($cache)) {
+        if (variable_get('page_cache_invoke_hooks', TRUE)) {
+          require_once DRUPAL_ROOT . '/includes/module.inc';
+          module_invoke_all('boot');
+        }
+        drupal_serve_cached_page($cache);
+        if (variable_get('page_cache_invoke_hooks', TRUE)) {
+          module_invoke_all('exit');
+        }
         exit;
       }
       break;
@@ -1152,12 +1218,12 @@ function _drupal_bootstrap($phase) {
       break;
 
     case DRUPAL_BOOTSTRAP_ACCESS:
-      // Deny access to blocked IP addresses - t() is not yet available.
-      if (drupal_is_denied(ip_address())) {
-        header($_SERVER['SERVER_PROTOCOL'] . ' 403 Forbidden');
-        print 'Sorry, ' . check_plain(ip_address()) . ' has been banned.';
-        exit();
-      }
+      // Check denied addresses, using the database if necessary.
+      drupal_is_denied(ip_address(), TRUE);
+
+    case DRUPAL_BOOTSTRAP_VARIABLES:
+      // Initialize configuration variables, using values from settings.php if available.
+      $conf = variable_init(isset($conf) ? $conf : array());
       break;
 
     case DRUPAL_BOOTSTRAP_SESSION:
@@ -1175,44 +1241,23 @@ function _drupal_bootstrap($phase) {
       }
       break;
 
-    case DRUPAL_BOOTSTRAP_VARIABLES:
-      // Initialize configuration variables, using values from settings.php if available.
-      $conf = variable_init(isset($conf) ? $conf : array());
-      break;
-
-    case DRUPAL_BOOTSTRAP_LATE_PAGE_CACHE:
-      $cache_mode = variable_get('cache', CACHE_DISABLED);
-      // Get the page from the cache.
-      $cache = $cache_mode == CACHE_DISABLED ? FALSE : page_get_cache(TRUE);
-      // If the skipping of the bootstrap hooks is not enforced, call hook_boot.
-      if (!is_object($cache) || $cache_mode != CACHE_AGGRESSIVE) {
-        // Load module handling.
-        require_once DRUPAL_ROOT . '/includes/module.inc';
-        module_invoke_all('boot');
-      }
-      // If there is a cached page, display it.
-      if (is_object($cache)) {
-        // Destroy empty anonymous sessions.
-        if (drupal_session_is_started() && empty($_SESSION)) {
-          session_destroy();
-        }
-        drupal_page_cache_header($cache);
-        // If the skipping of the bootstrap hooks is not enforced, call hook_exit.
-        if ($cache_mode != CACHE_AGGRESSIVE) {
-          module_invoke_all('exit');
-        }
-        // We are done.
-        exit;
-      }
+    case DRUPAL_BOOTSTRAP_PAGE_HEADER:
       // Prepare for non-cached page workflow.
-      drupal_page_header();
+      require_once DRUPAL_ROOT . '/includes/module.inc';
+      // If the user is anonymous but has the nocache cookie then it was only
+      // valid for one request, so delete it by setting it back to a past time.
+      if (!$user->uid && isset($_COOKIE['drupal_nocache'])) {
+        setcookie('drupal_nocache', 1, 280299600, base_path(), ini_get('session.cookie_domain'));
+      }
       // If the session has not already been started and output buffering is
       // not enabled, the session must be started now before the HTTP headers
       // are sent. If output buffering is enabled, the session may be started
       // at any time using drupal_session_start().
-      if ($cache === FALSE) {
+      if (!_page_cache_can_be_set()) {
         drupal_session_start();
       }
+      drupal_page_header();
+      module_invoke_all('boot');
       break;
 
     case DRUPAL_BOOTSTRAP_LANGUAGE:

=== modified file 'includes/common.inc'
--- includes/common.inc	2009-02-18 15:07:26 +0000
+++ includes/common.inc	2009-02-25 17:17:44 +0000
@@ -1856,9 +1856,7 @@ function drupal_page_footer() {
     watchdog('session', '$_SESSION is non-empty yet no code has called drupal_session_start().', array(), WATCHDOG_NOTICE);
   }
 
-  if (variable_get('cache', CACHE_DISABLED) != CACHE_DISABLED) {
-    page_set_cache();
-  }
+  page_set_cache();
 
   module_invoke_all('exit');
 
@@ -1959,14 +1957,6 @@ function drupal_get_path($type, $name) {
 }
 
 /**
- * Returns the base URL path of the Drupal installation.
- * At the very least, this will always default to /.
- */
-function base_path() {
-  return $GLOBALS['base_path'];
-}
-
-/**
  * Add a <link> tag to the page's HEAD.
  *
  * This function can be called as long the HTML header hasn't been sent.
@@ -3003,7 +2993,7 @@ function _drupal_bootstrap_full() {
 function page_set_cache() {
   global $user, $base_root;
 
-  if (page_get_cache(FALSE)) {
+  if (_page_cache_can_be_set() && !isset($_COOKIE['drupal_nocache']) && !page_cache_set_disable_cookie(FALSE)) {
     $cache = TRUE;
     $data = ob_get_contents();
     if (variable_get('page_compression', TRUE) && function_exists('gzencode')) {
@@ -3256,7 +3246,7 @@ function drupal_render_page($page) {
  *
  * Recursively iterates over each of the array elements, generating HTML code.
  *
- * HTML generation is controlled by two properties containing theme functions, 
+ * HTML generation is controlled by two properties containing theme functions,
  * #theme and #theme_wrapper.
  *
  * #theme is the theme function called first. If it is set and the element has any
@@ -3267,13 +3257,13 @@ function drupal_render_page($page) {
  *
  * The theme function in #theme_wrapper will be called after #theme has run. It
  * can be used to add further markup around the rendered children, e.g. fieldsets
- * add the required markup for a fieldset around their rendered child elements. 
+ * add the required markup for a fieldset around their rendered child elements.
  * A wrapper theme function always has to include the element's #children property
- * in its output, as this contains the rendered children. 
+ * in its output, as this contains the rendered children.
  *
  * For example, for the form element type, by default only the #theme_wrapper
  * property is set, which adds the form markup around the rendered child elements
- * of the form. This allows you to set the #theme property on a specific form to 
+ * of the form. This allows you to set the #theme property on a specific form to
  * a custom theme function, giving you complete control over the placement of the
  * form's children while not at all having to deal with the form markup itself.
  *
@@ -3307,7 +3297,7 @@ function drupal_render(&$elements) {
   else {
     $elements += element_basic_defaults();
   }
-  
+
   // If #markup is not empty and no theme function is set, use theme_markup.
   // This allows to specify just #markup on an element without setting the #type.
   if (!empty($elements['#markup']) && empty($elements['#theme'])) {

=== modified file 'modules/simpletest/tests/bootstrap.test'
--- modules/simpletest/tests/bootstrap.test	2009-01-31 16:50:56 +0000
+++ modules/simpletest/tests/bootstrap.test	2009-02-25 17:13:30 +0000
@@ -212,16 +212,16 @@ class HookBootExitTestCase extends Drupa
     $this->assertEqual(db_query("SELECT COUNT(*) FROM {watchdog} WHERE type = 'system_test' AND message = 'hook_boot'")->fetchField(), $calls, t('hook_boot called with normal cache.'));
     $this->assertEqual(db_query("SELECT COUNT(*) FROM {watchdog} WHERE type = 'system_test' AND message = 'hook_exit'")->fetchField(), $calls, t('hook_exit called with normal cache.'));
 
-    // Test with aggressive cache. Boot and exit should not fire since the
-    // page is cached.
-    variable_set('cache', CACHE_AGGRESSIVE);
-    $this->assertTrue(cache_get(url('', array('absolute' => TRUE)), 'cache_page'), t('Page has been cached.'));
+    // Test whether disabling boot and exit works.
+    variable_set('page_cache_fire_hooks', FALSE);
+    $cache = cache_get(url('', array('absolute' => TRUE)), 'cache_page');
+    $this->assertTrue(is_object($cache), t('Page has been cached.'));
     $this->drupalGet('');
     $this->assertEqual(db_query("SELECT COUNT(*) FROM {watchdog} WHERE type = 'system_test' AND message = 'hook_boot'")->fetchField(), $calls, t('hook_boot not called with agressive cache and a cached page.'));
     $this->assertEqual(db_query("SELECT COUNT(*) FROM {watchdog} WHERE type = 'system_test' AND message = 'hook_exit'")->fetchField(), $calls, t('hook_exit not called with agressive cache and a cached page.'));
 
-    // Test with aggressive cache and page cache cleared. Boot and exit should
-    // be called.
+    // Test whether disabling boot and exit only is in effect if page is
+    // cached.
     $this->assertTrue(db_delete('cache_page')->execute(), t('Page cache cleared.'));
     $this->drupalGet('');
     $calls++;

=== modified file 'modules/simpletest/tests/session.test'
--- modules/simpletest/tests/session.test	2009-01-19 10:46:50 +0000
+++ modules/simpletest/tests/session.test	2009-02-25 17:13:30 +0000
@@ -157,8 +157,8 @@ class SessionTestCase extends DrupalWebT
 
     variable_set('cache', CACHE_NORMAL);
 
-    // During this request the session is destroyed in drupal_page_footer(),
-    // and the session cookie is unset.
+    // During this request the session is destroyed and the session cookie is
+    // unset.
     $this->drupalGet('');
     $this->assertSessionCookie(TRUE);
     $this->assertSessionStarted(TRUE);
@@ -168,41 +168,39 @@ class SessionTestCase extends DrupalWebT
     // expires=..."
     $this->assertTrue(preg_match('/SESS\w+=deleted/', $this->drupalGetHeader('Set-Cookie')), t('Session cookie was deleted.'));
 
-    // Verify that the session cookie was actually deleted.
+    // Verify that page caching works.
     $this->drupalGet('');
-    $this->assertSessionCookie(FALSE);
-    $this->assertSessionStarted(FALSE);
+    $this->assertTrue($this->drupalGetHeader('ETag'), t('Page cached.'));
     $this->assertFalse($this->drupalGetHeader('Set-Cookie'), t('New session was not started.'));
 
     // Start a new session by setting a message.
     $this->drupalGet('session-test/set-message');
+    $this->assertText(t('A message was set.'), t('A message was set.'));
     $this->assertSessionCookie(FALSE);
     $this->assertSessionStarted(FALSE);
     $this->assertTrue($this->drupalGetHeader('Set-Cookie'), t('New session was started.'));
 
-    // Display the message.
+    // Display the message. Session cookie is destroyed at the end of the request.
     $this->drupalGet('');
     $this->assertSessionCookie(TRUE);
     $this->assertSessionStarted(TRUE);
     $this->assertSessionEmpty(FALSE);
     $this->assertFalse($this->drupalGetHeader('ETag'), t('Page was not cached.'));
     $this->assertText(t('This is a dummy message.'), t('Message was displayed.'));
+    $this->assertTrue(preg_match('/SESS\w+=deleted/', $this->drupalGetHeader('Set-Cookie')), t('Session cookie was deleted.'));
 
-    // During this request the session is destroyed in _drupal_bootstrap(),
-    // and the session cookie is unset.
+    // The next request is cached. Cached requests skip the session system.
     $this->drupalGet('');
-    $this->assertSessionCookie(TRUE);
-    $this->assertSessionStarted(TRUE);
-    $this->assertSessionEmpty(TRUE);
-    $this->assertTrue($this->drupalGetHeader('ETag'), t('Page was cached.'));
+    $this->assertSessionCookie(FALSE);
+    $this->assertSessionStarted(FALSE);
+    $this->assertTrue($this->drupalGetHeader('ETag'), t('Page cached.'));
     $this->assertNoText(t('This is a dummy message.'), t('Message was not cached.'));
-    $this->assertTrue(preg_match('/SESS\w+=deleted/', $this->drupalGetHeader('Set-Cookie')), t('Session cookie was deleted.'));
 
-    // Verify that session was destroyed.
-    $this->drupalGet('');
+    // Next, check an uncached page for no session start.
+    $this->drupalGet($this->randomName());
     $this->assertSessionCookie(FALSE);
     $this->assertSessionStarted(FALSE);
-    $this->assertTrue($this->drupalGetHeader('ETag'), t('Page was cached.'));
+    $this->assertFalse($this->drupalGetHeader('ETag'), t('Page was not cached.'));
     $this->assertFalse($this->drupalGetHeader('Set-Cookie'), t('New session was not started.'));
 
     // Verify that modifying $_SESSION without having started a session

=== modified file 'modules/simpletest/tests/session_test.module'
--- modules/simpletest/tests/session_test.module	2009-01-26 14:08:40 +0000
+++ modules/simpletest/tests/session_test.module	2009-02-25 17:13:30 +0000
@@ -52,7 +52,8 @@ function session_test_menu() {
  */
 function session_test_boot() {
   header('X-Session-Cookie: ' . intval(isset($_COOKIE[session_name()])));
-  header('X-Session-Started: ' . intval(drupal_session_is_started()));
+  // When called from hook_boot() for cached pages, the session system is not loaded.
+  header('X-Session-Started: ' . intval(function_exists('drupal_session_is_started') && drupal_session_is_started()));
   header('X-Session-Empty: ' . intval(empty($_SESSION)));
 }
 

=== modified file 'modules/system/system.admin.inc'
--- modules/system/system.admin.inc	2009-02-25 13:36:29 +0000
+++ modules/system/system.admin.inc	2009-02-25 17:13:30 +0000
@@ -1294,9 +1294,6 @@ function system_logging_settings() {
  * @see system_settings_form()
  */
 function system_performance_settings() {
-
-  $description = '<p>' . t("The normal cache mode is suitable for most sites and does not cause any side effects. The aggressive cache mode causes Drupal to skip the loading (boot) and unloading (exit) of enabled modules when serving a cached page. This results in an additional performance boost but can cause unwanted side effects.") . '</p>';
-
   // Check if the "Who's online" block is enabled.
   $online_block_enabled = db_select('block')
     ->condition('module', 'user')
@@ -1305,19 +1302,12 @@ function system_performance_settings() {
     ->countQuery()
     ->execute()
     ->fetchField();
+
+  $description = '';
   if ($online_block_enabled) {
     $description .= '<p>' . t("When caching is enabled, anonymous user sessions are only saved to the database when needed, so the \"Who's online\" block does not display the number of anonymous users.") . '</p>';
   }
 
-  $problem_modules = array_unique(array_merge(module_implements('boot'), module_implements('exit')));
-  sort($problem_modules);
-
-  if (count($problem_modules) > 0) {
-    $description .= '<p>' . t('<strong class="error">The following enabled modules are incompatible with aggressive mode caching and will not function properly: %modules</strong>', array('%modules' => implode(', ', $problem_modules))) . '.</p>';
-  }
-  else {
-    $description .= '<p>' . t('<strong class="ok">Currently, all enabled modules are compatible with the aggressive caching policy.</strong> Please note, if you use aggressive caching and enable new modules, you will need to check this page again to ensure compatibility.') . '</p>';
-  }
   $form['page_cache'] = array(
     '#type' => 'fieldset',
     '#title' => t('Page cache'),
@@ -1328,8 +1318,8 @@ function system_performance_settings() {
     '#type' => 'radios',
     '#title' => t('Caching mode'),
     '#default_value' => variable_get('cache', CACHE_DISABLED),
-    '#options' => array(CACHE_DISABLED => t('Disabled'), CACHE_NORMAL => t('Normal (recommended for production sites, no side effects)'), CACHE_AGGRESSIVE => t('Aggressive (experts only, possible side effects)')),
-    '#description' => $description
+    '#options' => array(CACHE_DISABLED => t('Disabled'), CACHE_NORMAL => t('Normal (recommended for production sites)')),
+    '#description' => $description,
   );
 
   $period = drupal_map_assoc(array(0, 60, 180, 300, 600, 900, 1800, 2700, 3600, 10800, 21600, 32400, 43200, 86400), 'format_interval');

=== modified file 'modules/system/system.install'
--- modules/system/system.install	2009-02-25 13:33:53 +0000
+++ modules/system/system.install	2009-02-25 17:38:59 +0000
@@ -3226,6 +3226,20 @@ function system_update_7019() {
 }
 
 /**
+ * CACHE_AGGRESSIVE is gone, change it to normal caching and add a warning to
+ * read the documentation.
+ */
+function system_update_7020() {
+  $ret = array();
+  if (variable_get('cache') == 2) {
+    variable_set('cache', CACHE_NORMAL);
+    $t = get_t();
+    $ret[] = array('success' => TRUE, 'query' => $t('This site previously had aggressive caching enabled, to keep this setting, add a variable override for page_cache_fire_hooks in settings.php'));
+  }
+  return $ret;
+}
+
+/**
  * @} End of "defgroup updates-6.x-to-7.x"
  * The next series of updates should start at 8000.
  */

=== modified file 'modules/user/user.module'
--- modules/user/user.module	2009-02-19 12:09:30 +0000
+++ modules/user/user.module	2009-02-25 17:13:30 +0000
@@ -1583,6 +1583,7 @@ function user_authenticate_finalize(&$ed
   // This is called before hook_user in case one of those functions fails
   // or incorrectly does a redirect which would leave the old session in place.
   drupal_session_regenerate();
+  page_cache_set_disable_cookie();
   user_module_invoke('login', $edit, $user);
 }
 

=== modified file 'sites/default/default.settings.php'
--- sites/default/default.settings.php	2009-02-18 15:19:54 +0000
+++ sites/default/default.settings.php	2009-02-25 17:13:30 +0000
@@ -46,7 +46,7 @@
 /**
  * Database settings:
  *
- * The $databases array specifies the database connection or 
+ * The $databases array specifies the database connection or
  * connections that Drupal may use.  Drupal is able to connect
  * to multiple databases, including multiple types of databases,
  * during the same request.
@@ -63,7 +63,7 @@
  *   'port' => 3306,
  * );
  *
- * The "driver" property indicates what Drupal database driver the 
+ * The "driver" property indicates what Drupal database driver the
  * connection should use.  This is usually the same as the name of the
  * database type, such as mysql or sqlite, but not always.  The other
  * properties will vary depending on the driver.  For SQLite, you must
@@ -80,7 +80,7 @@
  * A target database allows Drupal to try to send certain queries to a
  * different database if it can but fall back to the default connection if not.
  * That is useful for master/slave replication, as Drupal may try to connect
- * to a slave server when appropriate and if one is not available will simply 
+ * to a slave server when appropriate and if one is not available will simply
  * fall back to the single master server.
  *
  * The general format for the $databases array is as follows:
@@ -290,6 +290,17 @@ ini_set('session.cookie_lifetime', 20000
 # );
 
 /**
+ * Page caching behaviour.
+ *
+ * To use page caching without touching the database set cache to
+ * CACHE_NORMAL. To bypass the hook_boot() and hook_exit() implementations,
+ * set the page_cache_invoke_hooks variable to FALSE. Also see the IP blocking
+ * section below.
+ */
+# $conf['cache'] = CACHE_NORMAL;
+# $conf['page_cache_invoke_hooks'] = FALSE;
+
+/**
  *
  * IP blocking:
  *
@@ -311,3 +322,4 @@ ini_set('session.cookie_lifetime', 20000
 # $conf['blocked_ips'] = array(
 #   'a.b.c.d',
 # );
+

