? path_redirect_goto.patch
Index: path_redirect.module
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/path_redirect/path_redirect.module,v
retrieving revision 1.3.2.14
diff -u -F^f -r1.3.2.14 path_redirect.module
--- path_redirect.module	16 Dec 2007 19:52:46 -0000	1.3.2.14
+++ path_redirect.module	17 Dec 2007 14:24:46 -0000
@@ -8,7 +8,7 @@
 function path_redirect_help($section) {
   switch ($section) {
     case 'admin/build/path_redirect':
-      return t("<p>Here you can set up URL redirecting for this site. Any existing or non-existing path within this site can redirect to any internal or external URL. </p>");      
+      return t("<p>Here you can set up URL redirecting for this site. Any existing or non-existing path within this site can redirect to any internal or external URL. </p>");
     case 'admin/build/path_redirect/'. arg(2):
     case 'admin/build/path_redirect/edit/'. arg(3):
       return t("<p>The <strong>from</strong> path must be an internal Drupal path in the form of 'node/123', 'admin/logs', or 'taxonomy/term/123'. The <strong>to</strong> path can be either an internal Drupal path as above or a complete external URL such as http://www.example.com/. Furthermore, the <strong>to</strong> path may contain query arguments (such as 'page=2') and fragment anchors, to make it possible to redirect to 'admin/user?page=1#help'. Most redirects will not contain queries or anchors.</p>");
@@ -17,7 +17,7 @@ function path_redirect_help($section) {
 
 /**
  * Implementation of hook_init
- * 
+ *
  * Early checking of URL requested.
  * If a match is found, user is redirected using drupal_goto()
  *
@@ -30,9 +30,15 @@ function path_redirect_init() {
   }
   $r = db_fetch_object(db_query("SELECT redirect, query, fragment, type FROM {path_redirect} WHERE path = '%s' OR path = '%s'", $query, utf8_encode($query)));
   if ($r) {
-    // if there's a result found, do the redirect
-    unset($_REQUEST['destination']);
-    drupal_goto($r->redirect, ($r->query ? $r->query: NULL), ($r->fragment ? $r->fragment : NULL), $r->type);
+    if (function_exists('drupal_goto')) {
+      // if there's a result found, do the redirect
+      unset($_REQUEST['destination']);
+      drupal_goto($r->redirect, ($r->query ? $r->query: NULL), ($r->fragment ? $r->fragment : NULL), $r->type);
+    }
+    else {
+      // page caching is turned on so drupal_goto() (common.inc) hasn't been loaded
+      path_redirect_goto($r->redirect, ($r->query ? $r->query: NULL), ($r->fragment ? $r->fragment : NULL), $r->type);
+    }
   }
 }
 
@@ -64,7 +70,7 @@ function path_redirect_menu($may_cache) 
       'access' => $access,
       'weight' => 2,
       'type' => MENU_LOCAL_TASK,
-    );    
+    );
   }
   else {
     if (arg(0) == 'admin' && arg(1) == 'build' && arg(2) == 'path_redirect' && arg(3) == 'edit') {
@@ -77,7 +83,7 @@ function path_redirect_menu($may_cache) 
         'weight' => 9,
       );
     }
-    
+
   }
   return $items;
 }
@@ -128,7 +134,7 @@ function path_redirect_admin($rid = FALS
     array('data' => t('Type'), 'field' => 'type'),
     array('data' => t('Operations'), 'colspan' => '3')
   );
-  
+
   $result = pager_query('SELECT rid, path, redirect, query, fragment, type FROM {path_redirect}'. tablesort_sql($header), 50);
   $count = db_num_rows($result);
   $types = path_redirect_error_list();
@@ -153,18 +159,18 @@ function path_redirect_admin($rid = FALS
   else {
     $output .= '<p>'. t('No entries found.') .'</p>';
   }
-  
+
   $output .= '<p>'. l(t('Add New Redirect'), 'admin/build/path_redirect/new') .'</p>';
-  
+
   $output .= theme('pager');
-  
+
   return $output;
-  
+
 }
 
 function path_redirect_edit($edit = array()) {
   $default_type = 301;
-  
+
   if (!empty($edit)) {
     $form['rid'] = array(
       '#type' => 'hidden',
@@ -176,7 +182,7 @@ function path_redirect_edit($edit = arra
     // set up the default
     $edit['type'] = $default_type;
   }
-  
+
   $form['path'] = array(
     '#type' => 'textfield',
     '#title' => t('From'),
@@ -184,7 +190,7 @@ function path_redirect_edit($edit = arra
     '#maxlength' => 255,
     '#default_value' => drupal_get_path_alias($edit['path']),
   );
-  
+
   $form['redirect'] = array(
     '#type' => 'item',
     '#prefix' => '<div class="container-inline">',
@@ -192,47 +198,47 @@ function path_redirect_edit($edit = arra
     '#title' => t('To'),
     '#description' => '<div style="display:block">'. t('Enter a Drupal path, path alias, or external URL to redirect to. Use %front to redirect to the front page.  Enter (optional) queries after "?" and (optional) anchor after "#". Most redirects will not contain queries or fragment anchors.', array('%front' => '<front>')) .'</div>',
   );
-  
+
   $form['redirect']['redirect'] = array(
     '#type' => 'textfield',
     '#size' => 30,
     '#maxlength' => 255,
     '#default_value' => drupal_get_path_alias($edit['redirect']),
   );
-  
+
   $form['redirect'][] = array(
     '#value' => '?',
   );
-  
+
   $form['redirect']['query'] = array(
     '#type' => 'textfield',
     '#size' => 12,
     '#maxlength' => 50,
     '#default_value' => $edit['query'],
   );
-  
+
   $form['redirect'][] = array(
     '#value' => '#',
   );
-  
+
   $form['redirect']['fragment'] = array(
     '#type' => 'textfield',
     '#size' => 12,
     '#maxlength' => 50,
     '#default_value' => $edit['fragment'],
   );
-  
+
   $form[] = array(
     '#value' => "<p> </p>", // little bit of extra space
   );
-  
+
   $form['type'] = array(
     '#type' => 'fieldset',
     '#title' => t('Redirect Type'),
     '#collapsible' => true,
     '#collapsed' => ($edit['type'] == $default_type),
   );
-  
+
   foreach (path_redirect_error_list() as $key => $info) {
     $form['type'][]['type'] = array(
       '#type' => 'radio',
@@ -246,12 +252,12 @@ function path_redirect_edit($edit = arra
     '#type' => 'markup',
     '#value' => t('<p>Find more information about http redirect codes <a href="http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.3">here</a>.</p>'),
   );
-  
+
   $form['submit'] = array(
     '#type' => 'submit',
     '#value' => t('Submit'),
   );
-  
+
   return $form;
 }
 
@@ -259,7 +265,7 @@ function path_redirect_edit_validate($fo
   if (trim($form_values['path']) == '') {
     form_set_error('path', t('You must enter a <strong>from</strong> path.'));
   }
-  
+
   //check that the from url is valid and contains no # or ?
   elseif (strstr($form_values['path'], '#')) {
     form_set_error('path', t('You cannot redirect from a fragment anchor.'));
@@ -270,22 +276,22 @@ function path_redirect_edit_validate($fo
   elseif (!valid_url($form_values['path'])) {
     form_set_error('path', t('The redirect <strong>from</strong> path does not appear valid. This must be a local Drupal path.'));
   }
-  
+
   if (!valid_url($form_values['redirect']) && !valid_url($form_values['redirect'], TRUE) && $form_values['redirect'] != '<front>') {
     form_set_error('redirect', t('The redirect <strong>to</strong> path does not appear valid.'));
   }
-  
+
   $form_values['path'] = drupal_get_normal_path($form_values['path']);
   if ($form_values['redirect'] == '<front>') {
     $form_values['redirect'] = variable_get('site_frontpage', 'node');
   }
   $form_values['redirect'] = drupal_get_normal_path($form_values['redirect']);
-  
+
   //check that there there are no redirect loops
   if ($form_values['path'] === $form_values['redirect']) {
     form_set_error('redirect', t('You are attempting to redirect the page to itself. This will result in an infinite loop -- so don\'t do it!'));
   }
-  
+
   // Allow spaces in "from" path
   $form_values['path'] = str_replace("+", " ", $form_values['path']);
 }
@@ -360,12 +366,36 @@ function path_redirect_delete_confirm_su
   drupal_goto('admin/build/path_redirect');
 }
 
+/**
+ * This is a copy of drupal_goto() redesigned for use during the bootstrap
+ */
+function path_redirect_goto($path = '', $query = NULL, $fragment = NULL, $http_response_code = 302) {
+
+  $url = url($path, $query, $fragment, TRUE);
+  // Remove newlines from the URL to avoid header injection attacks.
+  $url = str_replace(array("\n", "\r"), '', $url);
+
+  // Before the redirect, allow modules to react to the end of the page request.
+  bootstrap_invoke_all('exit');
+
+  // 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);
+
+  // The "Location" header sends a REDIRECT status code to the http
+  // daemon. In some cases this can go wrong, so we make sure none
+  // of the code below the drupal_goto() call gets executed when we redirect.
+  exit();
+}
+
 
 /**
  * Return an array of 300-range error codes
  * placed here for clarity
  */
-function path_redirect_error_list() {   
+function path_redirect_error_list() {
   $errors = array(
     300 => array('title' => t('300 Multiple Choices'), 'description' => t('The request is ambiguous and needs clarification as to which resource was requested.')),
     301 => array('title' => t('301 Moved Permanently'), 'description' => t('Moved Permanently. The resource has permanently moved elsewhere, the response indicates where it has gone to. <strong>Recommended.</strong>')),
