#732486: Make the signature of drupal_add_http_header() more sane and more robust.

From: Damien Tournoud <damien@tournoud.net>


---
 authorize.php                           |    2 +-
 bootstrap.inc                           |   30 ++++++++++--------------------
 common.inc                              |    6 +++---
 database/database.inc                   |    2 +-
 errors.inc                              |    2 +-
 aggregator/tests/aggregator_test.module |    2 +-
 image/image.module                      |    4 ++--
 update.php                              |    2 +-
 8 files changed, 20 insertions(+), 30 deletions(-)

diff --git authorize.php authorize.php
index 2e68352..3adbbee 100644
--- authorize.php
+++ authorize.php
@@ -39,7 +39,7 @@ define('MAINTENANCE_MODE', 'update');
  * Render a 403 access denied page for authorize.php
  */
 function authorize_access_denied_page() {
-  drupal_add_http_header('403 Forbidden');
+  drupal_add_http_header('Status', '403 Forbidden');
   watchdog('access denied', 'authorize.php', NULL, WATCHDOG_WARNING);
   drupal_set_title('Access denied');
   return t('You are not allowed to access this page.');
diff --git includes/bootstrap.inc includes/bootstrap.inc
index cb149cb..db8c9ea 100644
--- includes/bootstrap.inc
+++ includes/bootstrap.inc
@@ -906,32 +906,22 @@ function drupal_load($type, $name) {
  * 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".
+ *   The HTTP header name, or the special 'Status' header name.
  * @param $value
- *   The HTTP header value; if omitted, the specified header is unset.
+ *   The HTTP header value; if equal to FALSE, the specified header is unset.
+ *   If $name is 'Status', this is expected to be a status code followed by a
+ *   reason phrase, e.g. "404 Not Found".
  * @param $append
  *   Whether to append the value to an existing header or to replace it.
  */
-function drupal_add_http_header($name = NULL, $value = NULL, $append = FALSE) {
+function drupal_add_http_header($name, $value, $append = FALSE) {
   // The headers as name/value pairs.
-  $headers = &drupal_static(__FUNCTION__, array());
+  $headers = &drupal_static('drupal_http_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);
-  }
+  $name_lower = strtolower($name);
   _drupal_set_preferred_header_name($name);
 
-  if (!isset($value)) {
+  if ($value == FALSE) {
     $headers[$name_lower] = FALSE;
   }
   elseif (isset($headers[$name_lower]) && $append) {
@@ -956,7 +946,7 @@ function drupal_add_http_header($name = NULL, $value = NULL, $append = FALSE) {
  *   or NULL if the header has not been set.
  */
 function drupal_get_http_header($name = NULL) {
-  $headers = drupal_add_http_header();
+  $headers = &drupal_static('drupal_http_headers', array());
   if (isset($name)) {
     $name = strtolower($name);
     return isset($headers[$name]) ? $headers[$name] : NULL;
@@ -1006,7 +996,7 @@ function drupal_send_headers($default_headers = array(), $only_default = FALSE)
     }
   }
   foreach ($headers as $name_lower => $value) {
-    if ($name_lower == ':status') {
+    if ($name_lower == 'status') {
       header($_SERVER['SERVER_PROTOCOL'] . ' ' . $value);
     }
     // Skip headers that have been unset.
diff --git includes/common.inc includes/common.inc
index 239a979..2d3b043 100644
--- includes/common.inc
+++ includes/common.inc
@@ -2339,7 +2339,7 @@ function drupal_deliver_html_page($page_callback_result) {
     switch ($page_callback_result) {
       case MENU_NOT_FOUND:
         // Print a 404 page.
-        drupal_add_http_header('404 Not Found');
+        drupal_add_http_header('Status', '404 Not Found');
 
         watchdog('page not found', check_plain($_GET['q']), NULL, WATCHDOG_WARNING);
 
@@ -2369,7 +2369,7 @@ function drupal_deliver_html_page($page_callback_result) {
 
       case MENU_ACCESS_DENIED:
         // Print a 403 page.
-        drupal_add_http_header('403 Forbidden');
+        drupal_add_http_header('Status', '403 Forbidden');
         watchdog('access denied', check_plain($_GET['q']), NULL, WATCHDOG_WARNING);
 
         // Keep old path for reference, and to allow forms to redirect to it.
@@ -2397,7 +2397,7 @@ function drupal_deliver_html_page($page_callback_result) {
       case MENU_SITE_OFFLINE:
         // Print a 503 page.
         drupal_maintenance_theme();
-        drupal_add_http_header('503 Service unavailable');
+        drupal_add_http_header('Status', '503 Service unavailable');
         drupal_set_title(t('Site under maintenance'));
         print theme('maintenance_page', array('content' => filter_xss_admin(variable_get('maintenance_mode_message',
           t('@site is currently under maintenance. We should be back shortly. Thank you for your patience.', array('@site' => variable_get('site_name', 'Drupal')))))));
diff --git includes/database/database.inc includes/database/database.inc
index b92ee63..4a42f68 100644
--- includes/database/database.inc
+++ includes/database/database.inc
@@ -2722,7 +2722,7 @@ function _db_error_page($error = '') {
   global $db_type;
   drupal_language_initialize();
   drupal_maintenance_theme();
-  drupal_add_http_header($_SERVER['SERVER_PROTOCOL'] . ' 503 Service Unavailable');
+  drupal_add_http_header('Status', '503 Service Unavailable');
   drupal_set_title('Site offline');
 }
 
diff --git includes/errors.inc includes/errors.inc
index 42bd3a1..4e2dbdc 100644
--- includes/errors.inc
+++ includes/errors.inc
@@ -172,7 +172,7 @@ function _drupal_log_error($error, $fatal = FALSE) {
   }
 
   if ($fatal) {
-    drupal_add_http_header('500 Service unavailable (with message)');
+    drupal_add_http_header('Status', '500 Service unavailable (with message)');
   }
 
   if (isset($_SERVER['HTTP_X_REQUESTED_WITH']) && $_SERVER['HTTP_X_REQUESTED_WITH'] == 'XMLHttpRequest') {
diff --git modules/aggregator/tests/aggregator_test.module modules/aggregator/tests/aggregator_test.module
index d5293ce..a516826 100644
--- modules/aggregator/tests/aggregator_test.module
+++ modules/aggregator/tests/aggregator_test.module
@@ -40,7 +40,7 @@ function aggregator_test_feed($use_last_modified = FALSE, $use_etag = FALSE) {
   }
   // Return 304 not modified if either last modified or etag match.
   if ($last_modified == $if_modified_since || $etag == $if_none_match) {
-    drupal_add_http_header('304 Not Modified');
+    drupal_add_http_header('Status', '304 Not Modified');
     return;
   }
 
diff --git modules/image/image.module modules/image/image.module
index 6755a6d..ac06836 100644
--- modules/image/image.module
+++ modules/image/image.module
@@ -609,7 +609,7 @@ function image_style_generate() {
     if (!$lock_acquired) {
       // Tell client to retry again in 3 seconds. Currently no browsers are known
       // to support Retry-After.
-      drupal_add_http_header('503 Service Unavailable');
+      drupal_add_http_header('Status', '503 Service Unavailable');
       drupal_add_http_header('Retry-After', 3);
       print t('Image generation in progress. Try again shortly.');
       drupal_exit();
@@ -630,7 +630,7 @@ function image_style_generate() {
   }
   else {
     watchdog('image', 'Unable to generate the derived image located at %path.', array('%path' => $destination));
-    drupal_add_http_header('500 Internal Server Error');
+    drupal_add_http_header('Status', '500 Internal Server Error');
     print t('Error generating image.');
     drupal_exit();
   }
diff --git update.php update.php
index 988024e..a93fc37 100644
--- update.php
+++ update.php
@@ -237,7 +237,7 @@ function update_info_page() {
 }
 
 function update_access_denied_page() {
-  drupal_add_http_header('403 Forbidden');
+  drupal_add_http_header('Status', '403 Forbidden');
   watchdog('access denied', 'update.php', NULL, WATCHDOG_WARNING);
   drupal_set_title('Access denied');
   return '<p>Access denied. You are not authorized to access this page. Log in using either an account with the <em>administer software updates</em> permission or the site maintenance account (the account you created during installation). If you cannot log in, you will have to edit <code>settings.php</code> to bypass this access check. To do this:</p>
