? boost-325813.patch
Index: boost.admin.debug.inc
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/boost/Attic/boost.admin.debug.inc,v
retrieving revision 1.1.2.1
diff -u -p -r1.1.2.1 boost.admin.debug.inc
--- boost.admin.debug.inc	15 Jan 2011 09:07:39 -0000	1.1.2.1
+++ boost.admin.debug.inc	21 Feb 2011 09:27:27 -0000
@@ -21,5 +21,7 @@ function boost_admin_debug_settings() {
     '#description' => t('Only use for debugging purposes as this can fill up watchdog fairly quickly.'),
   );
 
+  // reset htaccess on submit;
+  $form['#submit'][] = 'boost_form_submit_handler';
   return system_settings_form($form);
 }
Index: boost.admin.expiration.inc
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/boost/Attic/boost.admin.expiration.inc,v
retrieving revision 1.1.2.1
diff -u -p -r1.1.2.1 boost.admin.expiration.inc
--- boost.admin.expiration.inc	13 Feb 2011 09:45:07 -0000	1.1.2.1
+++ boost.admin.expiration.inc	21 Feb 2011 09:27:27 -0000
@@ -27,5 +27,7 @@ function boost_admin_expiration_settings
     '#description' => t(''),
   );
 
+  // reset htaccess on submit;
+  $form['#submit'][] = 'boost_form_submit_handler';
   return system_settings_form($form);
 }
Index: boost.admin.filesystem.inc
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/boost/Attic/boost.admin.filesystem.inc,v
retrieving revision 1.1.2.2
diff -u -p -r1.1.2.2 boost.admin.filesystem.inc
--- boost.admin.filesystem.inc	14 Feb 2011 01:34:08 -0000	1.1.2.2
+++ boost.admin.filesystem.inc	21 Feb 2011 09:27:27 -0000
@@ -30,5 +30,7 @@ function boost_admin_filesystem_settings
     '#default_value' => variable_get('boost_char', BOOST_CHAR),
   );
 
+  // reset htaccess on submit;
+  $form['#submit'][] = 'boost_form_submit_handler';
   return system_settings_form($form);
 }
Index: boost.admin.htaccess.inc
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/boost/Attic/boost.admin.htaccess.inc,v
retrieving revision 1.1.2.3
diff -u -p -r1.1.2.3 boost.admin.htaccess.inc
--- boost.admin.htaccess.inc	13 Feb 2011 09:45:07 -0000	1.1.2.3
+++ boost.admin.htaccess.inc	21 Feb 2011 09:27:28 -0000
@@ -17,24 +17,10 @@ define('BOOST_SERVER_NAME_HTTP_HOST', '%
 define('BOOST_DOCUMENT_ROOT', '%{DOCUMENT_ROOT}');
 
 /**
- * Default etag settings.
- */
-define('BOOST_APACHE_ETAG', 3);
-
-/**
- * Default header setttings
- */
-define('BOOST_APACHE_XHEADER', 1);
-
-/**
  * Default setting for SSL pages
  */
 define('BOOST_SSL_BYPASS', TRUE);
 
-/**
- * Default setting for forcing all content to be utf8
- */
-define('BOOST_FORCE_UTF8', TRUE);
 
 /**
  * Form builder; Configure boost settings.
@@ -126,19 +112,21 @@ function boost_admin_htaccess_settings()
     '#default_value' => variable_get('boost_ssl_bypass', BOOST_SSL_BYPASS),
     '#description'   => t('Ticking this is recommended if you use the securepages module.'),
   );
-  $form['htaccess']['boost_force_utf8'] = array(
+  $form['htaccess']['boost_add_default_charset'] = array(
     '#type'          => 'checkbox',
+    '#title'         => t('Add "AddDefaultCharset X" to the htaccess rules'),
+    '#default_value' => variable_get('boost_add_default_charset', BOOST_ADD_DEFAULT_CHARSET),
+    '#description'   => t('Depending on your i18n settings you might want this disabled or enabled. X is set below'),
+  );
+  $form['htaccess']['boost_charset_type'] = array(
+    '#type'          => 'textfield',
     '#title'         => t('Add "AddDefaultCharset utf-8" to the htaccess rules'),
-    '#default_value' => variable_get('boost_force_utf8', BOOST_FORCE_UTF8),
+    '#default_value' => variable_get('boost_charset_type', BOOST_CHARSET_TYPE),
     '#description'   => t('Depending on your i18n settings you might want this disabled or enabled.'),
   );
-//   $form['htaccess']['boost_ignore_htaccess_warning'] = array(
-//     '#type'          => 'checkbox',
-//     '#title'         => t('Ignore .htaccess warning'),
-//     '#default_value' => variable_get('boost_ignore_htaccess_warning', FALSE),
-//     '#description'   => t('Do not warn about missing or modified boost rules in the .htaccess file on the <a href="!link">status report page</a>. Enable this if you have a good reason to modify the boost rules in .htaccess.', array('!link' => url('admin/reports/status'))),
-//   );
 
+  // reset htaccess on submit;
+  $form['#submit'][] = 'boost_form_submit_handler';
   return system_settings_form($form);
 }
 
@@ -162,8 +150,9 @@ function boost_admin_htaccess_generation
   # RewriteBase / </tt></pre> and above <pre><tt>  # Pass all requests not referring directly to files in the filesystem to
   # index.php. Clean URLs are handled in drupal_environment_initialize().</tt></pre><br />Note that the generated rules' settings can be configure at !link.", array('!link' => l('admin/config/development/performance/boost/htaccess-settings', 'admin/config/development/performance/boost/htaccess-settings'))),
   );
-//   $form['#submit'][] = 'boost_admin_htaccess_page_submit';
 
+  // reset htaccess on submit;
+  $form['#submit'][] = 'boost_form_submit_handler';
   return $form;
 }
 
Index: boost.admin.inc
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/boost/boost.admin.inc,v
retrieving revision 1.2.2.1
diff -u -p -r1.2.2.1 boost.admin.inc
--- boost.admin.inc	13 Feb 2011 09:45:07 -0000	1.2.2.1
+++ boost.admin.inc	21 Feb 2011 09:27:28 -0000
@@ -127,5 +127,7 @@ function boost_admin_settings() {
     $form['cache_types'][$title]['#collapsed'] = $collapsed;
   }
 
+  // reset htaccess on submit;
+  $form['#submit'][] = 'boost_form_submit_handler';
   return system_settings_form($form);
 }
Index: boost.install
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/boost/boost.install,v
retrieving revision 1.3.2.3
diff -u -p -r1.3.2.3 boost.install
--- boost.install	13 Feb 2011 09:45:07 -0000	1.3.2.3
+++ boost.install	21 Feb 2011 09:27:28 -0000
@@ -13,6 +13,7 @@
  * Implements hook_enable().
  */
 function boost_enable() {
+  boost_htaccess_cache_dir_put();
 }
 
 /**
Index: boost.module
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/boost/boost.module,v
retrieving revision 1.5.2.6
diff -u -p -r1.5.2.6 boost.module
--- boost.module	13 Feb 2011 09:45:07 -0000	1.5.2.6
+++ boost.module	21 Feb 2011 09:27:29 -0000
@@ -48,6 +48,27 @@ define('BOOST_IGNORE_FLUSH', TRUE);
 define('BOOST_EXPIRE_CRON', TRUE);
 
 /**
+ * Default etag settings.
+ */
+define('BOOST_APACHE_ETAG', 3);
+
+/**
+ * Default header setttings.
+ */
+define('BOOST_APACHE_XHEADER', 1);
+
+/**
+ * Default setting for forcing all content to be the charset defined below.
+ */
+define('BOOST_ADD_DEFAULT_CHARSET', TRUE);
+
+/**
+ * Default for content charset.
+ */
+define('BOOST_CHARSET_TYPE', 'utf-8');
+
+
+/**
  * Implements hook_menu().
  */
 function boost_menu() {
@@ -207,7 +228,7 @@ function boost_exit($destination = NULL)
     // Attach extension to filename.
     $_boost['filename'] .= '.' . $_boost['matched_header_info']['extension'];
     // Write to file.
-    boost_write_file($_boost['directory'], $_boost['filename'], $data);
+    boost_write_file($_boost['filename'], $data);
   }
 }
 
@@ -1043,29 +1064,47 @@ function boost_print_r($data) {
 }
 
 /**
- * Write to a file.
+ * Write to a file. Ensures write is atomic via rename operation.
  *
- * @param $directory
- *  relative directory.
  * @param $filename
  *  relative filename.
  * @param $data
  *  data to write to the file.
  */
-function boost_write_file($directory, $filename, &$data) {
-  if (!is_file($filename)) {
-    // Create directory if it doesn't exist.
-    if (!boost_mkdir($directory)) {
-      return FALSE;
-    }
-    // Save data to a file.
-    if (file_put_contents($filename, $data, LOCK_EX) === FALSE) {
-      watchdog('boost', 'Could not create the file %file on your system', array('%file' => $filename), WATCHDOG_ERROR);
-      return FALSE;
+function boost_write_file($filename, &$data) {
+  // Create directory if it doesn't exist.
+  $directory = dirname($filename);
+  if (!boost_mkdir($directory)) {
+    return FALSE;
+  }
+
+  // Save data to a temp file.
+  // file_unmanaged_save_data does not use rename.
+  $tempname = drupal_tempnam($directory, 'boost');
+  if (file_put_contents($tempname, $data) === FALSE) {
+    watchdog('boost', 'Could not create the file %file on your system', array('%file' => $tempname), WATCHDOG_ERROR);
+    @unlink($tempname);
+    return FALSE;
+  }
+
+  // Move temp file to real filename; windows can not do a rename replace.
+  if (@rename($tempname, $filename) === FALSE) {
+    $oldname = $tempname. 'old';
+    if (@rename($filename, $oldname) !== FALSE) {
+      if (@rename($tempname, $filename) === FALSE) {
+        watchdog('boost', 'Could not rename the file %file on your system', array('%file' => $filename), WATCHDOG_ERROR);
+        @unlink($tempname);
+        @rename($oldname, $filename);
+        return FALSE;
+      }
+      else {
+        @unlink($oldname);
+      }
     }
-    // chmod file so webserver can send it out.
-    drupal_chmod($filename);
   }
+
+  // chmod file so webserver can send it out.
+  drupal_chmod($filename);
   return TRUE;
 }
 
@@ -1078,7 +1117,7 @@ function boost_write_file($directory, $f
 function boost_mkdir($directory) {
   global $_boost;
 
-  // Only do something if it's not a dir
+  // Only do something if it's not a dir.
   if (!is_dir($directory)) {
     if (!boost_in_cache_dir($directory)) {
       return FALSE;
@@ -1176,3 +1215,115 @@ function boost_deliver_html_page($page_c
     watchdog('boost', boost_print_r($_boost), array(), WATCHDOG_DEBUG);
   }
 }
+
+/**
+ * Always run these functions after a form submit from boost.
+ */
+function boost_form_submit_handler() {
+  register_shutdown_function('boost_htaccess_cache_dir_put');
+}
+
+/**
+ * Overwrite old htaccess rules with new ones.
+ */
+function boost_htaccess_cache_dir_put() {
+  global $_boost;
+  boost_write_file($_boost['base_dir'] . '.htaccess', boost_htaccess_cache_dir_generate());
+}
+
+/**
+ * Generate htaccess rules for the cache directory.
+ */
+function boost_htaccess_cache_dir_generate() {
+  $char_type = variable_get('boost_charset_type', BOOST_CHARSET_TYPE);
+  $etag = variable_get('boost_apache_etag', BOOST_APACHE_ETAG);
+
+  // Go through every storage type getting data needed to build htaccess file.
+  $gzip = FALSE;
+  $data = array();
+  $files = array();
+  $types = boost_get_storage_types();
+  foreach ($types as $title => $content_types) {
+    foreach ($content_types as $type => $values) {
+      if ($values['enabled']) {
+        $forcetype = '\.' . $values['extension'];
+        if ($values['gzip']) {
+          $forcetype .= '(?:\.gz)?$';
+          $gzip = TRUE;
+        }
+        else {
+          $forcetype .= '$';
+        }
+        $files[$values['extension']] = $values['extension'];
+        $data[$values['extension']] = array(
+          'type' => $type,
+          'forcetype' => $forcetype,
+        );
+      }
+    }
+  }
+  if (empty($data) || empty($files)) {
+    return FALSE;
+  }
+
+  // Add in default charset
+  $string = '';
+  if (variable_get('boost_add_default_charset', BOOST_ADD_DEFAULT_CHARSET)) {
+    $string .= "AddDefaultCharset " . $char_type . "\n";
+  }
+
+  // Set FileETag
+  if ($etag == 1) {
+    $string .= "FileETag None\n";
+  }
+  elseif ($etag == 2) {
+    $string .= "FileETag All\n";
+  }
+  elseif ($etag == 3) {
+    $string .= "FileETag MTime Size\n";
+  }
+
+  // Set html expiration time to the past and put in boost header if desired.
+  $files = implode('|' , $files);
+  if ($gzip) {
+    $files .= '(?:\.gz)?';
+  }
+  $string .= "<FilesMatch \"\.($files)$\">\n";
+  $string .= "  <IfModule mod_expires.c>\n";
+  $string .= "    ExpiresDefault A0\n";
+  $string .= "  </IfModule>\n";
+  $string .= "  <IfModule mod_headers.c>\n";
+  $string .= "    Header set Expires \"Sun, 19 Nov 1978 05:00:00 GMT\"\n";
+  $string .= "    Header set Cache-Control \"no-store, no-cache, must-revalidate, post-check=0, pre-check=0\"\n";
+  if (variable_get('boost_apache_xheader', BOOST_APACHE_XHEADER) > 0) {
+    $string .= "    Header set X-Header \"Boost\"\n";
+  }
+  $string .= "  </IfModule>\n";
+  $string .= "</FilesMatch>\n";
+
+  // Set charset and content encoding.
+  $string .= "<IfModule mod_mime.c>\n";
+  foreach ($data as $extension => $values) {
+    $string .= "  AddCharset " . $char_type . " ." . $extension . "\n";
+  }
+  $string .= $gzip ? "  AddEncoding gzip .gz\n" : '';
+  $string .= "</IfModule>\n";
+
+  // Fix for versions of apache that do not respect the T='' RewriteRule
+  foreach ($data as $extension => $values) {
+    $forcetype = $values['forcetype'];
+    $type = $values['type'];
+    $string .= "<FilesMatch \"$forcetype\">\n";
+    $string .= "  ForceType " . $type . "\n";
+    $string .= "</FilesMatch>\n";
+  }
+
+  // Make sure files can not execute in the cache dir.
+  $string .= "\n";
+  $string .= "SetHandler Drupal_Security_Do_Not_Remove_See_SA_2006_006\n";
+  $string .= "Options None\n";
+  $string .= "Options +FollowSymLinks\n";
+  $string .= "\n";
+
+  return $string;
+}
