=== modified file '.htaccess'
--- .htaccess	2008-12-11 04:56:37 +0000
+++ .htaccess	2009-05-11 06:03:56 +0000
@@ -66,8 +66,10 @@
   # Cache all files for 2 weeks after access (A).
   ExpiresDefault A1209600
 
-  # Do not cache dynamically generated pages.
-  ExpiresByType text/html A1
+  <Files index.php>
+    # Caching headers for dynamically generated pages are set from PHP.
+    ExpiresActive Off
+  </Files>
 </IfModule>
 
 # Various rewrite rules.

=== modified file 'includes/batch.inc'
--- includes/batch.inc	2008-12-06 09:02:33 +0000
+++ includes/batch.inc	2009-05-11 06:03:56 +0000
@@ -338,7 +338,7 @@
     // We get here if $form['#redirect'] was FALSE, or if the form is a
     // multi-step form. We save the final $form_state value to be retrieved
     // by drupal_get_form, and we redirect to the originating page.
-    $_SESSION['batch_form_state'] = $_batch['form_state'];
+    drupal_set_session('batch_form_state', $_batch['form_state']);
     drupal_goto($_batch['source_page']);
   }
 }

=== modified file 'includes/bootstrap.inc'
--- includes/bootstrap.inc	2009-04-30 00:36:53 +0000
+++ includes/bootstrap.inc	2009-05-13 19:52:02 +0000
@@ -1,5 +1,5 @@
 <?php
-// $Id: bootstrap.inc,v 1.206.2.12 2009/04/30 00:13:30 goba Exp $
+// $Id: bootstrap.inc,v 1.206.2.11 2009/02/25 13:49:54 dries Exp $
 
 /**
  * @file
@@ -545,32 +545,41 @@
 /**
  * Retrieve the current page from the cache.
  *
- * Note: we do not serve cached pages when status messages are waiting (from
- * a redirected form submission which was completed).
+ * 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-
+ * specific content that should not be cached and displayed to other users.
  *
- * @param $status_only
- *   When set to TRUE, retrieve the status of the page cache only
- *   (whether it was started in this request or not).
+ * @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. Default of TRUE added to maximize Drupal 5 compatibility.
+ * @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.
  */
-function page_get_cache($status_only = FALSE) {
-  static $status = FALSE;
+function page_get_cache($retrieve = TRUE) {
   global $user, $base_root;
+  static $ob_started = FALSE;
 
-  if ($status_only) {
-    return $status;
+  if ($user->uid || ($_SERVER['REQUEST_METHOD'] != 'GET' && $_SERVER['REQUEST_METHOD'] != 'HEAD') || count(drupal_get_messages(NULL, FALSE)) || $_SERVER['SERVER_SOFTWARE'] === 'PHP CLI') {
+    return FALSE;
   }
-  $cache = NULL;
-
-  if (!$user->uid && $_SERVER['REQUEST_METHOD'] == 'GET' && count(drupal_set_message()) == 0 && $_SERVER['SERVER_SOFTWARE'] !== 'PHP CLI') {
+  if ($retrieve) {
     $cache = cache_get($base_root . request_uri(), 'cache_page');
-
-    if (empty($cache)) {
+    if ($cache) {
+      return $cache;
+    }
+    else {
       ob_start();
-      $status = TRUE;
+      $ob_started = TRUE;
     }
   }
-
-  return $cache;
+  return $ob_started;
 }
 
 /**
@@ -618,73 +627,260 @@
 }
 
 /**
+ * Set an HTTP response header for the current page.
+ *
+ * Note: When sending a Content-Type header, always include a 'charset' type,
+ * too. This is necessary to avoid security bugs (e.g. UTF-7 XSS).
+ *
+ * @param $name
+ *   The HTTP header name, or a status code followed by a reason phrase, e.g.
+ *   "404 Not Found".
+ * @param $value
+ *   The HTTP header value; if omitted, the specified header is unset.
+ * @param $append
+ *   Whether to append the value to an existing header or to replace it.
+ */
+function drupal_set_header($name = NULL, $value = NULL, $append = FALSE) {
+  // The headers as name/value pairs.
+  static $headers = array();
+
+  if (!isset($name)) {
+    return $headers;
+  }
+
+  // Save status codes using the special key ":status".
+  if (preg_match('/^\d{3} /', $name)) {
+    $value = $name;
+    $name = $name_lower = ':status';
+  }
+  else {
+    $name_lower = strtolower($name);
+  }
+  _drupal_set_preferred_header_name($name);
+
+  if (!isset($value)) {
+    $headers[$name_lower] = FALSE;
+  }
+  elseif (isset($headers[$name_lower]) && $append) {
+    // Multiple headers with identical names may be combined using comma (RFC
+    // 2616, section 4.2).
+    $headers[$name_lower] .= ',' . $value;
+  }
+  else {
+    $headers[$name_lower] = $value;
+  }
+  drupal_send_headers(array($name => $headers[$name_lower]), TRUE);
+}
+
+/**
+ * Get the HTTP response headers for the current page.
+ *
+ * @param $name
+ *   An HTTP header name. If omitted, all headers are returned as name/value
+ *   pairs. If an array value is FALSE, the header has been unset.
+ * @return
+ *   A string containing the header value, or FALSE if the header has been set,
+ *   or NULL if the header has not been set.
+ */
+function drupal_get_header($name = NULL) {
+  $headers = drupal_set_header();
+  if (isset($name)) {
+    $name = strtolower($name);
+    return isset($headers[$name]) ? $headers[$name] : NULL;
+  }
+  else {
+    return $headers;
+  }
+}
+
+/**
+ * Header names are case-insensitive, but for maximum compatibility they should
+ * follow "common form" (see RFC 2617, section 4.2).
+ */
+function _drupal_set_preferred_header_name($name = NULL) {
+  static $header_names = array();
+
+  if (!isset($name)) {
+    return $header_names;
+  }
+  $header_names[strtolower($name)] = $name;
+}
+
+/**
+ * Send the HTTP response headers previously set using drupal_set_header().
+ * Add default headers, unless they have been replaced or unset using
+ * drupal_set_header().
+ *
+ * @param $default_headers
+ *   An array of headers as name/value pairs.
+ * @param $single
+ *   If TRUE and headers have already be sent, send only the specified header.
+ */
+function drupal_send_headers($default_headers = array(), $only_default = FALSE) {
+  static $headers_sent = FALSE;
+  $headers = drupal_get_header();
+  if ($only_default && $headers_sent) {
+    $headers = array();
+  }
+  $headers_sent = TRUE;
+
+  $header_names = _drupal_set_preferred_header_name();
+  foreach ($default_headers as $name => $value) {
+    $name_lower = strtolower($name);
+    if (!isset($headers[$name_lower])) {
+      $headers[$name_lower] = $value;
+      $header_names[$name_lower] = $name;
+    }
+  }
+  foreach ($headers as $name_lower => $value) {
+    if ($name_lower == ':status') {
+      header($_SERVER['SERVER_PROTOCOL'] . ' ' . $value);
+    }
+    // Skip headers that have been unset.
+    elseif ($value) {
+      header($header_names[$name_lower] . ': ' . $value);
+    }
+  }
+}
+
+/**
  * Set HTTP headers in preparation for a page response.
  *
- * Authenticated users are always given a 'no-cache' header, and will
- * fetch a fresh page on every request.  This prevents authenticated
- * users seeing locally cached pages that show them as logged out.
+ * Authenticated users are always given a 'no-cache' header, and will fetch a
+ * fresh page on every request. This prevents authenticated users from seeing
+ * locally cached pages.
+ *
+ * Also give each page a unique ETag. This will force clients to include both
+ * an If-Modified-Since header and an If-None-Match header when doing
+ * conditional requests for the page (required by RFC 2616, section 13.3.4),
+ * making the validation more robust. This is a workaround for a bug in Mozilla
+ * Firefox that is triggered when Drupal's caching is enabled and the user
+ * accesses Drupal via an HTTP proxy (see
+ * https://bugzilla.mozilla.org/show_bug.cgi?id=269303): When an authenticated
+ * user requests a page, and then logs out and requests the same page again,
+ * Firefox may send a conditional request based on the page that was cached
+ * locally when the user was logged in. If this page did not have an ETag
+ * header, the request only contains an If-Modified-Since header. The date will
+ * be recent, because with authenticated users the Last-Modified header always
+ * refers to the time of the request. If the user accesses Drupal via a proxy
+ * server, and the proxy already has a cached copy of the anonymous page with an
+ * older Last-Modified date, the proxy may respond with 304 Not Modified, making
+ * the client think that the anonymous and authenticated pageviews are
+ * identical.
  *
  * @see page_set_cache()
  */
 function drupal_page_header() {
-  header("Expires: Sun, 19 Nov 1978 05:00:00 GMT");
-  header("Last-Modified: ". gmdate("D, d M Y H:i:s") ." GMT");
-  header("Cache-Control: store, no-cache, must-revalidate");
-  header("Cache-Control: post-check=0, pre-check=0", FALSE);
+  static $headers_sent = FALSE;
+  if ($headers_sent) {
+    return TRUE;
+  }
+  $headers_sent = TRUE;
+
+  $default_headers = array(
+    'Expires' => 'Sun, 11 Mar 1984 12:00:00 GMT',
+    'Last-Modified' => gmdate(DATE_RFC1123, $_SERVER['REQUEST_TIME']),
+    'Cache-Control' => 'no-cache, must-revalidate, post-check=0, pre-check=0',
+    'ETag' => '"' . $_SERVER['REQUEST_TIME'] . '"',
+  );
+  drupal_send_headers($default_headers);
 }
 
 /**
  * Set HTTP headers in preparation for a cached page response.
  *
- * The general approach here is that anonymous users can keep a local
- * cache of the page, but must revalidate it on every request.  Then,
- * they are given a '304 Not Modified' response as long as they stay
- * logged out and the page has not been modified.
+ * The headers allow as much as possible in proxies and browsers without any
+ * particular knowledge about the pages. Modules can override these headers
+ * using drupal_set_header().
  *
+ * If the request is conditional (using If-Modified-Since and If-None-Match),
+ * and the conditions match those currently in the cache, a 304 Not Modified
+ * response is sent.
  */
-function drupal_page_cache_header($cache) {
-  // Set default values:
-  $last_modified = gmdate('D, d M Y H:i:s', $cache->created) .' GMT';
-  $etag = '"'. md5($last_modified) .'"';
-
-  // See if the client has provided the required HTTP headers:
-  $if_modified_since = isset($_SERVER['HTTP_IF_MODIFIED_SINCE']) ? stripslashes($_SERVER['HTTP_IF_MODIFIED_SINCE']) : FALSE;
+function drupal_page_cache_header(stdClass $cache) {
+  // Negotiate whether to use compression.
+  $page_compression = variable_get('page_compression', TRUE) && extension_loaded('zlib');
+  $return_compressed = $page_compression && isset($_SERVER['HTTP_ACCEPT_ENCODING']) && strpos($_SERVER['HTTP_ACCEPT_ENCODING'], 'gzip') !== FALSE;
+
+  // Get headers set in hook_boot(). Keys are lower-case.
+  $hook_boot_headers = drupal_get_header();
+
+  // Headers generated in this function, that may be replaced or unset using
+  // drupal_set_headers(). Keys are mixed-case.
+  $default_headers = array();
+
+  foreach ($cache->headers as $name => $value) {
+    // In the case of a 304 response, certain headers must be sent, and the
+    // remaining may not (see RFC 2616, section 10.3.5). Do not override
+    // headers set in hook_boot().
+    $name_lower = strtolower($name);
+    if (in_array($name_lower, array('content-location', 'expires', 'cache-control', 'vary')) && !isset($hook_boot_headers[$name_lower])) {
+      drupal_set_header($name, $value);
+      unset($cache->headers[$name]);
+    }
+  }
+
+  // If a cache is served from a HTTP proxy without hitting the web server,
+  // the boot and exit hooks cannot be fired, so only allow caching in
+  // proxies with aggressive caching. If the client send a session cookie, do
+  // not bother caching the page in a public proxy, because the cached copy
+  // will only be served to that particular user due to Vary: Cookie, unless
+  // the Vary header has been replaced or unset in hook_boot() (see below).
+  $max_age = variable_get('cache', CACHE_NONE) == CACHE_AGGRESSIVE && (!isset($_COOKIE[session_name()]) || isset($hook_boot_headers['vary'])) ? variable_get('cache_lifetime', 0) : 0;
+  $default_headers['Cache-Control'] = 'public, max-age=' . $max_age;
+
+  // Entity tag should change if the output changes.
+  $etag = '"' . $cache->created . '-' . intval($return_compressed) . '"';
+  header('Etag: ' . $etag);
+
+  // See if the client has provided the required HTTP headers.
+  $if_modified_since = isset($_SERVER['HTTP_IF_MODIFIED_SINCE']) ? strtotime($_SERVER['HTTP_IF_MODIFIED_SINCE']) : FALSE;
   $if_none_match = isset($_SERVER['HTTP_IF_NONE_MATCH']) ? stripslashes($_SERVER['HTTP_IF_NONE_MATCH']) : FALSE;
 
   if ($if_modified_since && $if_none_match
       && $if_none_match == $etag // etag must match
-      && $if_modified_since == $last_modified) {  // if-modified-since must match
-    header('HTTP/1.1 304 Not Modified');
-    // All 304 responses must send an etag if the 200 response for the same object contained an etag
-    header("Etag: $etag");
+      && $if_modified_since == $cache->created) {  // if-modified-since must match
+    header($_SERVER['SERVER_PROTOCOL'] . ' 304 Not Modified');
+    drupal_send_headers($default_headers);
     return;
   }
 
-  // Send appropriate response:
-  header("Last-Modified: $last_modified");
-  header("ETag: $etag");
-
-  // The following headers force validation of cache:
-  header("Expires: Sun, 19 Nov 1978 05:00:00 GMT");
-  header("Cache-Control: must-revalidate");
-
-  if (variable_get('page_compression', TRUE)) {
-    // Determine if the browser accepts gzipped data.
-    if (@strpos($_SERVER['HTTP_ACCEPT_ENCODING'], 'gzip') === FALSE && function_exists('gzencode')) {
-      // Strip the gzip header and run uncompress.
+  // Send the remaining headers.
+  foreach ($cache->headers as $name => $value) {
+    drupal_set_header($name, $value);
+  }
+
+  $default_headers['Last-Modified'] = gmdate(DATE_RFC1123, $cache->created);
+
+  // HTTP/1.0 proxies does not support the Vary header, so prevent any caching
+  // by sending an Expires date in the past. HTTP/1.1 clients ignores the
+  // Expires header if a Cache-Control: max-age= directive is specified (see RFC
+  // 2616, section 14.9.3).
+  $default_headers['Expires'] = 'Sun, 19 Nov 1978 05:00:00 GMT';
+
+  drupal_send_headers($default_headers);
+
+  // Allow HTTP proxies to cache pages for anonymous users without a session
+  // cookie. The Vary header is used to indicates the set of request-header
+  // fields that fully determines whether a cache is permitted to use the
+  // response to reply to a subsequent request for a given URL without
+  // revalidation. If a Vary header has been set in hook_boot(), it is assumed
+  // that the module knows how to cache the page.
+  if (!isset($hook_boot_headers['vary']) && !variable_get('omit_vary_cookie', FALSE)) {
+    header('Vary: Cookie');
+  }
+
+  if ($page_compression) {
+    header('Vary: Accept-Encoding', FALSE);
+    // If page_compression is enabled, the cache contains gzipped data.
+    if ($return_compressed) {
+      header('Content-Encoding: gzip');
+    }
+    else {
+      // The client does not support compression, so unzip the data in the
+      // cache. Strip the gzip header and run uncompress.
       $cache->data = gzinflate(substr(substr($cache->data, 10), 0, -8));
     }
-    elseif (function_exists('gzencode')) {
-      header('Content-Encoding: gzip');
-    }
-  }
-
-  // Send the original request's headers. We send them one after
-  // another so PHP's header() function can deal with duplicate
-  // headers.
-  $headers = explode("\n", $cache->headers);
-  foreach ($headers as $header) {
-    header($header);
   }
 
   print $cache->data;
@@ -861,7 +1057,12 @@
 function drupal_set_message($message = NULL, $type = 'status', $repeat = TRUE) {
   if ($message) {
     if (!isset($_SESSION['messages'])) {
-      $_SESSION['messages'] = array();
+      if (function_exists('drupal_set_session')) {
+        drupal_set_session('messages', array());
+      }
+      else {
+        $_SESSION['messages'] = array();
+      }
     }
 
     if (!isset($_SESSION['messages'][$type])) {
@@ -985,7 +1186,7 @@
 }
 
 function _drupal_bootstrap($phase) {
-  global $conf;
+  global $conf, $user;
 
   switch ($phase) {
 
@@ -1028,23 +1229,38 @@
     case DRUPAL_BOOTSTRAP_SESSION:
       require_once variable_get('session_inc', './includes/session.inc');
       session_set_save_handler('sess_open', 'sess_close', 'sess_read', 'sess_write', 'sess_destroy_sid', 'sess_gc');
-      session_start();
+      // If a session cookie exists, initialize the session. Otherwise the
+      // session is only started on demand in drupal_session_start(), making
+      // anonymous users not use a session cookie unless something is stored in
+      // $_SESSION. This allows HTTP proxies to cache anonymous pageviews.
+      if (isset($_COOKIE[session_name()])) {
+        drupal_session_start();
+      }
+      else {
+        $user = drupal_anonymous_user();
+      }
       break;
 
     case DRUPAL_BOOTSTRAP_LATE_PAGE_CACHE:
       // Initialize configuration variables, using values from settings.php if available.
       $conf = variable_init(isset($conf) ? $conf : array());
+
       $cache_mode = variable_get('cache', CACHE_DISABLED);
       // Get the page from the cache.
-      $cache = $cache_mode == CACHE_DISABLED ? '' : page_get_cache();
-      // If the skipping of the bootstrap hooks is not enforced, call hook_boot.
-      if (!$cache || $cache_mode != CACHE_AGGRESSIVE) {
+      $cache = $cache_mode == CACHE_DISABLED ? FALSE : page_get_cache(TRUE);
+      // If the skipping of the bootstrap hooks is not enforced, call hook_init.
+      if (!is_object($cache) || $cache_mode != CACHE_AGGRESSIVE) {
         // Load module handling.
         require_once './includes/module.inc';
         bootstrap_invoke_all('boot');
       }
       // If there is a cached page, display it.
-      if ($cache) {
+      if (is_object($cache)) {
+        // Destroy empty anonymous sessions.
+        if (drupal_session_is_started() && empty($_SESSION)) {
+          session_destroy();
+        }
+        header('X-Drupal-Cache: HIT');
         drupal_page_cache_header($cache);
         // If the skipping of the bootstrap hooks is not enforced, call hook_exit.
         if ($cache_mode != CACHE_AGGRESSIVE) {
@@ -1053,8 +1269,20 @@
         // We are done.
         exit;
       }
+  
       // Prepare for non-cached page workflow.
-      drupal_page_header();
+  
+      // If the session has not already been started and output buffering is
+      // not enabled, the HTTP headers must be sent now, including the session
+      // cookie. If output buffering is enabled, the session may be started
+      // at any time using drupal_session_start().
+      if ($cache === FALSE) {
+        drupal_page_header();
+        drupal_session_start();
+      }
+      else {
+        header('X-Drupal-Cache: MISS');
+      }
       break;
 
     case DRUPAL_BOOTSTRAP_LANGUAGE:

=== modified file 'includes/cache.inc'
--- includes/cache.inc	2009-04-30 00:36:53 +0000
+++ includes/cache.inc	2009-05-11 06:33:13 +0000
@@ -25,6 +25,10 @@
 
   $cache = db_fetch_object(db_query("SELECT data, created, headers, expire, serialized FROM {". $table ."} WHERE cid = '%s'", $cid));
   if (isset($cache->data)) {
+    if (isset($cache->headers)) {
+      $cache->headers = unserialize($cache->headers);
+    }
+
     // If the data is permanent or we're not enforcing a minimum cache lifetime
     // always return the cached data.
     if ($cache->expire == CACHE_PERMANENT || !variable_get('cache_lifetime', 0)) {
@@ -99,7 +103,9 @@
  * @param $headers
  *   A string containing HTTP header information for cached pages.
  */
-function cache_set($cid, $data, $table = 'cache', $expire = CACHE_PERMANENT, $headers = NULL) {
+function cache_set($cid, $data, $table = 'cache', $expire = CACHE_PERMANENT, $headers = array()) {
+  $headers = serialize($headers);
+  
   $serialized = 0;
   if (is_object($data) || is_array($data)) {
     $data = serialize($data);

=== modified file 'includes/common.inc'
--- includes/common.inc	2009-05-13 20:47:41 +0000
+++ includes/common.inc	2009-05-18 22:38:48 +0000
@@ -125,30 +125,23 @@
   drupal_lookup_path('wipe');
 }
 
-/**
- * Set an HTTP response header for the current page.
- *
- * Note: When sending a Content-Type header, always include a 'charset' type,
- * too. This is necessary to avoid security bugs (e.g. UTF-7 XSS).
+/*
+ * The function drupal_set_header() has been moved to includes/bootstrap.inc in Pressflow.
  */
-function drupal_set_header($header = NULL) {
-  // We use an array to guarantee there are no leading or trailing delimiters.
-  // Otherwise, header('') could get called when serving the page later, which
-  // ends HTTP headers prematurely on some PHP versions.
-  static $stored_headers = array();
-
-  if (strlen($header)) {
-    header($header);
-    $stored_headers[] = $header;
-  }
-  return implode("\n", $stored_headers);
-}
 
 /**
  * Get the HTTP response headers for the current page.
+ *
+ * This function is not called by Pressflow and remains here
+ * only for Drupal 5/6 API compatibility.
  */
 function drupal_get_headers() {
-  return drupal_set_header();
+  $headers = drupal_set_header();
+  $header_text = array();
+  foreach ($headers as $name => $value) {
+    $header_text[] .= $name . ': ' . $value;
+  }
+  return implode("\n", $header_text);
 }
 
 /**
@@ -321,9 +314,11 @@
     module_invoke_all('exit', $url);
   }
 
-  // Even though session_write_close() is registered as a shutdown function, we
-  // need all session data written to the database before redirecting.
-  session_write_close();
+  if (drupal_session_is_started()) {
+    // Even though session_write_close() is registered as a shutdown function,
+    // we need all session data written to the database before redirecting.
+    session_write_close();
+  }
 
   header('Location: '. $url, TRUE, $http_response_code);
 
@@ -1568,6 +1563,16 @@
  * react to the closing of the page by calling hook_exit().
  */
 function drupal_page_footer() {
+  global $user;
+
+  // Destroy empty anonymous sessions if possible.
+  if (!headers_sent() && drupal_session_is_started() && empty($_SESSION) && !$user->uid) {
+    session_destroy();
+  }
+  elseif (!empty($_SESSION) && !drupal_session_is_started()) {
+    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();
   }
@@ -2584,30 +2589,46 @@
 function page_set_cache() {
   global $user, $base_root;
 
-  if (!$user->uid && $_SERVER['REQUEST_METHOD'] == 'GET' && page_get_cache(TRUE)) {
-    // This will fail in some cases, see page_get_cache() for the explanation.
-    if ($data = ob_get_contents()) {
-      $cache = TRUE;
-      if (variable_get('page_compression', TRUE) && function_exists('gzencode')) {
-        // We do not store the data in case the zlib mode is deflate.
-        // This should be rarely happening.
-        if (zlib_get_coding_type() == 'deflate') {
-          $cache = FALSE;
-        }
-        else if (zlib_get_coding_type() == FALSE) {
-          $data = gzencode($data, 9, FORCE_GZIP);
-        }
-        // The remaining case is 'gzip' which means the data is
-        // already compressed and nothing left to do but to store it.
-      }
-      ob_end_flush();
-      if ($cache && $data) {
-        cache_set($base_root . request_uri(), $data, 'cache_page', CACHE_TEMPORARY, drupal_get_headers());
-      }
-    }
+  if (page_get_cache(FALSE)) {
+    $cache_page = TRUE;
+    $cache = (object) array(
+      'cid' => $base_root . request_uri(),
+      'data' => ob_get_clean(),
+      'expire' => CACHE_TEMPORARY,
+      'created' => $_SERVER['REQUEST_TIME'],
+      'headers' => array(),
+    );
+    // Restore preferred header names based on the lower-case names returned
+    // by drupal_get_header().
+    $header_names = _drupal_set_preferred_header_name();
+    foreach (drupal_get_header() as $name_lower => $value) {
+      $cache->headers[$header_names[$name_lower]] = $value;
+    }
+    if (variable_get('page_compression', TRUE) && function_exists('gzencode')) {
+      // We do not store the data in case the zlib mode is deflate. This should
+      // be rarely happening.
+      if (zlib_get_coding_type() == 'deflate') {
+        $cache_page = FALSE;
+      }
+      elseif (zlib_get_coding_type() == FALSE) {
+        $cache->data = gzencode($cache->data, 9, FORCE_GZIP);
+      }
+      // The remaining case is 'gzip' which means the data is already
+      // compressed and nothing left to do but to store it.
+    }
+    if ($cache_page && $cache->data) {
+      cache_set($cache->cid, $cache->data, 'cache_page', $cache->expire, $cache->headers);
+    }
+    drupal_page_cache_header($cache);
+  }
+  else {
+    // If output buffering was enabled during bootstrap, and the headers were
+    // not sent in the DRUPAL_BOOTSTRAP_LATE_PAGE_CACHE phase, send them now.
+    drupal_page_header();
   }
 }
 
+
 /**
  * Executes a cron run when called
  * @return

=== modified file 'includes/form.inc'
--- includes/form.inc	2009-05-13 20:47:41 +0000
+++ includes/form.inc	2009-05-18 22:38:48 +0000
@@ -2376,7 +2376,7 @@
  *   foreach ($results as $result) {
  *     $items[] = t('Loaded node %title.', array('%title' => $result));
  *   }
- *   $_SESSION['my_batch_results'] = $items;
+ *   drupal_session_set('my_batch_results', $items);
  * }
  * @endcode
  */

=== modified file 'includes/session.inc'
--- includes/session.inc	2008-12-11 18:48:10 +0000
+++ includes/session.inc	2009-05-11 06:03:56 +0000
@@ -56,11 +56,10 @@
 function sess_write($key, $value) {
   global $user;
 
-  // If saving of session data is disabled or if the client doesn't have a session,
-  // and one isn't being created ($value), do nothing. This keeps crawlers out of
-  // the session table. This reduces memory and server load, and gives more useful
-  // statistics. We can't eliminate anonymous session table rows without breaking
-  // the throttle module and the "Who's Online" block.
+  // If saving of session data is disabled, or if a new empty anonymous session
+  // has been started, do nothing. This keeps anonymous users, including
+  // crawlers, out of the session table, unless they actually have something
+  // stored in $_SESSION.
   if (!session_save_session() || ($user->uid == 0 && empty($_COOKIE[session_name()]) && empty($value))) {
     return TRUE;
   }
@@ -83,6 +82,72 @@
 }
 
 /**
+ * Propagate $_SESSION and set session cookie if not already set. This function
+ * should be called before writing to $_SESSION, usually via
+ * drupal_set_session().
+ *
+ * @param $start
+ *   If FALSE, the session is not actually started. This is only used by
+ *   drupal_session_is_started().
+ * @return
+ *   TRUE if session has already been started, or FALSE if it has not.
+ */
+function drupal_session_start($start = TRUE) {
+  static $started = FALSE;
+  if ($start && !$started) {
+    $started = TRUE;
+    session_start();
+  }
+  return $started;
+}
+
+/**
+ * Return whether a session has been started and the $_SESSION variable is
+ * available.
+ */
+function drupal_session_is_started() {
+  return drupal_session_start(FALSE);
+}
+
+/**
+ * Get a session variable.
+ *
+ * @param $name
+ *   The name of the variable to get. If not supplied, all variables are returned.
+ * @return
+ *   The value of the variable, or FALSE if the variable is not set.
+ */
+function drupal_get_session($name = NULL) {
+  if (is_null($name)) {
+    return $_SESSION;
+  }
+  elseif (isset($_SESSION[$name])) {
+    return $_SESSION[$name];
+  }
+  else {
+    return FALSE;
+  }
+}
+
+/**
+ * Set a session variable. The variable becomes accessible via $_SESSION[$name]
+ * in the current and later requests. If there is no active PHP session prior
+ * to the call, one is started automatically.
+ *
+ * Anonymous users generate less server load if their $_SESSION variable is
+ * empty, so unused entries should be unset using unset($_SESSION['foo']).
+ *
+ * @param $name
+ *   The name of the variable to set.
+ * @param $value
+ *   The value to set.
+ */
+function drupal_set_session($name, $value) {
+  drupal_session_start();
+  $_SESSION[$name] = $value;
+}
+
+/**
  * Called when an anonymous user becomes authenticated or vice-versa.
  */
 function sess_regenerate() {
@@ -127,6 +192,9 @@
  */
 function sess_destroy_sid($sid) {
   db_query("DELETE FROM {sessions} WHERE sid = '%s'", $sid);
+  // Unset cookie.
+  extract(session_get_cookie_params());
+  setcookie(session_name(), '', time() - 3600, $path, $domain, $secure, $httponly);
 }
 
 /**

=== modified file 'modules/comment/comment.module'
--- modules/comment/comment.module	2009-05-13 20:47:41 +0000
+++ modules/comment/comment.module	2009-05-18 22:38:48 +0000
@@ -1668,9 +1668,9 @@
     $user = $account;
   }
   else {
-    $_SESSION['comment_mode'] = $mode;
-    $_SESSION['comment_sort'] = $order;
-    $_SESSION['comment_comments_per_page'] = $comments_per_page;
+    drupal_set_session('comment_mode', $mode);
+    drupal_set_session('comment_sort', $order);
+    drupal_set_session('comment_comments_per_page', $comments_per_page);
   }
 }
 

=== added directory 'modules/cookie_cache_bypass'
=== added file 'modules/cookie_cache_bypass/cookie_cache_bypass.info'
--- modules/cookie_cache_bypass/cookie_cache_bypass.info	1970-01-01 00:00:00 +0000
+++ modules/cookie_cache_bypass/cookie_cache_bypass.info	2009-04-02 20:35:48 +0000
@@ -0,0 +1,3 @@
+name = Cookie Cache Bypass
+description = Sets a cookie on form submission directing a reverse proxy to temporarily not serve cached pages for an anonymous user that just submitted content.
+core = 6.x

=== added file 'modules/cookie_cache_bypass/cookie_cache_bypass.module'
--- modules/cookie_cache_bypass/cookie_cache_bypass.module	1970-01-01 00:00:00 +0000
+++ modules/cookie_cache_bypass/cookie_cache_bypass.module	2009-04-02 20:35:48 +0000
@@ -0,0 +1,10 @@
+<?php
+
+function cookie_cache_bypass_form_alter(&$form, $form_state, $form_id) {
+  $form['#submit'][] = 'cookie_cache_bypass_submit';
+}
+
+function cookie_cache_bypass_submit() {
+  $lifetime = variable_get('cache_lifetime', 300);
+  setcookie('NO_CACHE', 'Y', $_SERVER['REQUEST_TIME'] + ($lifetime + 300), '/');
+}

=== modified file 'modules/dblog/dblog.admin.inc'
--- modules/dblog/dblog.admin.inc	2008-12-06 09:02:33 +0000
+++ modules/dblog/dblog.admin.inc	2009-05-11 06:03:56 +0000
@@ -317,13 +317,16 @@
   switch ($op) {
     case t('Filter'):
       foreach ($filters as $name => $filter) {
+        if (empty($_SESSION['dblog_overview_filter'])) {
+          drupal_set_session('dblog_overview_filter', array());
+        }
         if (isset($form_state['values'][$name])) {
           $_SESSION['dblog_overview_filter'][$name] = $form_state['values'][$name];
         }
       }
       break;
     case t('Reset'):
-      $_SESSION['dblog_overview_filter'] = array();
+      drupal_set_session('dblog_overview_filter', array());
       break;
   }
   return 'admin/reports/dblog';

=== modified file 'modules/node/node.admin.inc'
--- modules/node/node.admin.inc	2008-12-11 04:56:37 +0000
+++ modules/node/node.admin.inc	2009-05-11 06:03:56 +0000
@@ -323,6 +323,9 @@
         $flat_options = form_options_flatten($filters[$filter]['options']);
 
         if (isset($flat_options[$form_state['values'][$filter]])) {
+          if (empty($_SESSION['node_overview_filter'])) {
+            drupal_set_session('node_overview_filter', array());
+          }
           $_SESSION['node_overview_filter'][] = array($filter, $form_state['values'][$filter]);
         }
       }
@@ -331,7 +334,7 @@
       array_pop($_SESSION['node_overview_filter']);
       break;
     case t('Reset'):
-      $_SESSION['node_overview_filter'] = array();
+      drupal_set_session($_SESSION['node_overview_filter'], array());
       break;
   }
 }

=== modified file 'modules/openid/openid.module'
--- modules/openid/openid.module	2009-04-30 00:36:53 +0000
+++ modules/openid/openid.module	2009-05-11 06:33:13 +0000
@@ -162,6 +162,10 @@
     return;
   }
 
+  if (empty($_SESSION['openid'])) {
+    drupal_set_session('openid', array());
+  }
+  
   // Store discovered information in the users' session so we don't have to rediscover.
   $_SESSION['openid']['service'] = $services[0];
   // Store the claimed id

=== modified file 'modules/user/user.admin.inc'
--- modules/user/user.admin.inc	2008-12-06 09:02:33 +0000
+++ modules/user/user.admin.inc	2009-05-11 06:03:56 +0000
@@ -101,6 +101,9 @@
         // Merge an array of arrays into one if necessary.
         $options = $filter == 'permission' ? call_user_func_array('array_merge', $filters[$filter]['options']) : $filters[$filter]['options'];
         if (isset($options[$form_state['values'][$filter]])) {
+          if (empty($_SESSION['user_overview_filter'])) {
+            drupal_set_session('user_overview_filter', array());
+          }
           $_SESSION['user_overview_filter'][] = array($filter, $form_state['values'][$filter]);
         }
       }
@@ -109,7 +112,7 @@
       array_pop($_SESSION['user_overview_filter']);
       break;
     case t('Reset'):
-      $_SESSION['user_overview_filter'] = array();
+      drupal_set_session('user_overview_filter', array());
       break;
     case t('Update'):
       return;

=== modified file 'modules/user/user.module'
--- modules/user/user.module	2009-04-30 00:36:53 +0000
+++ modules/user/user.module	2009-05-11 06:33:13 +0000
@@ -1372,6 +1372,10 @@
   // Regenerate the session ID to prevent against session fixation attacks.
   sess_regenerate();
   user_module_invoke('login', $edit, $user);
+  if (variable_get('reverse_proxy', 0)) {
+    // If we have a reverse proxy, set a special cookie so the proxy would pass through the requests.
+    setcookie('LOGGED_IN', 'Y', $_SERVER['REQUEST_TIME'] + ini_get('session.cookie_lifetime'), '/');
+  }
 }
 
 /**

=== modified file 'modules/user/user.pages.inc'
--- modules/user/user.pages.inc	2008-12-06 09:02:33 +0000
+++ modules/user/user.pages.inc	2009-01-29 17:10:33 +0000
@@ -146,6 +146,12 @@
 
   watchdog('user', 'Session closed for %name.', array('%name' => $user->name));
 
+  if (variable_get('reverse_proxy', 0)) {
+    // If we are behind a reverse proxy, unset the logged in cookie by giving it
+    // an empty value and a time in the past
+    setcookie('LOGGED_IN', '', $_SERVER['REQUEST_TIME'] - 86400, '/');
+  }
+
   // Destroy the current session:
   session_destroy();
   module_invoke_all('user', 'logout', NULL, $user);

=== modified file 'sites/default/default.settings.php'
--- sites/default/default.settings.php	2008-12-06 09:02:33 +0000
+++ sites/default/default.settings.php	2009-01-29 17:10:33 +0000
@@ -197,6 +197,27 @@
  * logging, statistics and access management systems; if you are unsure
  * about this setting, do not have a reverse proxy, or Drupal operates in
  * a shared hosting environment, this setting should be set to disabled.
+ *
+ * Configuration for Squid
+ *   ...
+ *   acl cookie_logged_in_set rep_header Set-Cookie LOGGED_IN=Y
+ *   cache deny cookie_logged_in_set
+ *   acl cookie_logged_in_out rep_header Cookie LOGGED_IN=Y
+ *   cache deny cookie_logged_in_out
+ *   acl cookie_logged_in     req_header Cookie LOGGED_IN=Y
+ *   cache deny cookie_logged_in
+ *   ...
+ *
+ * Configuration for Varnish
+ *
+ *    sub vcl_recv {
+ *      ...
+ *      if (req.http.Cookie && req.http.Cookie ~ "LOGGED_IN=Y") {
+ *        pass;
+ *      }
+ *      ...
+ *    }
+ *
  */
 #   'reverse_proxy' => TRUE,
 /**

=== modified file 'update.php'
--- update.php	2009-04-30 00:36:53 +0000
+++ update.php	2009-05-11 06:54:00 +0000
@@ -617,6 +617,7 @@
 
 // Access check:
 if (!empty($update_free_access) || $user->uid == 1) {
+  drupal_session_start();
 
   include_once './includes/install.inc';
   include_once './includes/batch.inc';

