? .DS_Store
? .htaccess
? includes/filetransfer
? sites/default/files
? sites/default/settings.php
Index: includes/common.inc
===================================================================
RCS file: /cvs/drupal/drupal/includes/common.inc,v
retrieving revision 1.1160
diff -u -p -r1.1160 common.inc
--- includes/common.inc	4 May 2010 14:55:22 -0000	1.1160
+++ includes/common.inc	7 May 2010 17:21:39 -0000
@@ -2371,6 +2371,15 @@ function drupal_deliver_html_page($page_
         break;
 
       case MENU_ACCESS_DENIED:
+        // There are a few cases where we don't want to present a 403 error,
+        // for example, a logged-in user trying to get to ?q=user/login. That
+        // should be handled more gracefully by going to an appropriate path.
+        $login_path = drupal_get_login_paths($_GET['q']);
+        if (!empty($login_path['authenticated_redirect']) && $_GET['q'] != $login_path['authenticated_redirect']) {
+          drupal_set_message(t('The page you requested is for anonymous users, but you are already logged in, so you have been redirected to this page.'));
+          drupal_goto($login_path['authenticated_redirect']);
+        }
+
         // Print a 403 page.
         drupal_add_http_header('Status', '403 Forbidden');
         watchdog('access denied', check_plain($_GET['q']), NULL, WATCHDOG_WARNING);
@@ -2466,6 +2475,32 @@ function drupal_exit($destination = NULL
   exit;
 }
 
+
+ /**
+ * Collect and optionally check a login path.
+ *
+ * @param $path
+ *   An optional Drupal path.
+ * @return
+ *   If the $path is defined as a key in hook_login_paths() or
+ *   hook_login_paths_alter() then return its value, otherwise return FALSE.
+ *   If the $path parameter is ommitted, then return every login path as defined
+ *   by the hooks mentioned above.
+ */
+function drupal_get_login_paths($path = NULL) {
+  $login_paths = module_invoke_all('login_paths');
+  drupal_alter('login_paths', $login_paths);
+  if (isset($path)) {
+    foreach ($login_paths as $login_path => $settings) {
+      if (drupal_match_path($path, $login_path)) {
+        return $settings;
+      }
+    }
+    return FALSE;
+  }
+  return $login_paths;
+}
+
 /**
  * Form an associative array from a linear array.
  *
Index: includes/menu.inc
===================================================================
RCS file: /cvs/drupal/drupal/includes/menu.inc,v
retrieving revision 1.391
diff -u -p -r1.391 menu.inc
--- includes/menu.inc	6 May 2010 05:59:30 -0000	1.391
+++ includes/menu.inc	7 May 2010 17:21:39 -0000
@@ -3364,9 +3364,13 @@ function _menu_site_is_offline($check_on
       }
     }
     else {
-      // Anonymous users get a FALSE at the login prompt, TRUE otherwise.
+      // Return TRUE if this is an anonymous user unless they are at a login
+      // path, in which case return FALSE to allow them to visit the page.
+      // if drupal_get_login_paths()[['maintenance_mode_allow_access'] is TRUE,
+      // return FALSE from this function, meaning to allow access.
       if (user_is_anonymous()) {
-        return ($_GET['q'] != 'user' && $_GET['q'] != 'user/login');
+        $login_path = drupal_get_login_paths($_GET['q']);
+        return empty($login_path['maintenance_mode_allow_access']);
       }
       // Logged in users are unprivileged here, so they are logged out.
       if (!$check_only) {
Index: modules/openid/openid.module
===================================================================
RCS file: /cvs/drupal/drupal/modules/openid/openid.module,v
retrieving revision 1.87
diff -u -p -r1.87 openid.module
--- modules/openid/openid.module	5 May 2010 16:51:30 -0000	1.87
+++ modules/openid/openid.module	7 May 2010 17:21:40 -0000
@@ -39,6 +39,13 @@ function openid_menu() {
 }
 
 /**
+ * Implements hook_login_paths().
+ */
+function openid_login_paths() {
+  return array('openid/authenticate' => array('maintenance_mode_allow_access' => TRUE));
+}
+
+/**
  * Implements hook_help().
  */
 function openid_help($path, $arg) {
Index: modules/simpletest/drupal_web_test_case.php
===================================================================
RCS file: /cvs/drupal/drupal/modules/simpletest/drupal_web_test_case.php,v
retrieving revision 1.216
diff -u -p -r1.216 drupal_web_test_case.php
--- modules/simpletest/drupal_web_test_case.php	6 May 2010 05:59:31 -0000	1.216
+++ modules/simpletest/drupal_web_test_case.php	7 May 2010 17:21:40 -0000
@@ -1083,10 +1083,11 @@ class DrupalWebTestCase extends DrupalTe
    * Logs a user out of the internal browser, then check the login page to confirm logout.
    */
   protected function drupalLogout() {
-    // Make a request to the logout page, and redirect to the user page, the
+    // Make a request to the logout page, and then to the user page, the
     // idea being if you were properly logged out you should be seeing a login
     // screen.
-    $this->drupalGet('user/logout', array('query' => array('destination' => 'user')));
+    $this->drupalGet('user/logout');
+    $this->drupalGet('user');
     $pass = $this->assertField('name', t('Username field found.'), t('Logout'));
     $pass = $pass && $this->assertField('pass', t('Password field found.'), t('Logout'));
 
Index: modules/simpletest/tests/menu_test.module
===================================================================
RCS file: /cvs/drupal/drupal/modules/simpletest/tests/menu_test.module,v
retrieving revision 1.14
diff -u -p -r1.14 menu_test.module
--- modules/simpletest/tests/menu_test.module	26 Apr 2010 14:06:23 -0000	1.14
+++ modules/simpletest/tests/menu_test.module	7 May 2010 17:21:40 -0000
@@ -189,6 +189,12 @@ function menu_test_menu() {
     'type' => MENU_LOCAL_TASK,
   );
 
+  $items['menu_login_callback'] = array(
+    'title' => 'Used as a login path',
+    'page callback' => 'menu_login_callback',
+    'access callback' => TRUE,
+  );
+
   return $items;
 }
 
@@ -329,3 +335,18 @@ function menu_test_static_variable($valu
   }
   return $variable;
 }
+
+/**
+ * Implements hook_login_paths().
+ */
+function menu_test_login_paths() {
+  // Allow access to ?q=menu_login_callback even if in maintenance mode.
+  return array('menu_login_callback' => array('maintenance_mode_allow_access' => TRUE));
+}
+
+/**
+ * Menu callback to be used as a login path.
+ */
+function menu_login_callback() {
+  return 'This is menu_login_callback().';
+}
Index: modules/system/system.api.php
===================================================================
RCS file: /cvs/drupal/drupal/modules/system/system.api.php,v
retrieving revision 1.166
diff -u -p -r1.166 system.api.php
--- modules/system/system.api.php	5 May 2010 07:02:13 -0000	1.166
+++ modules/system/system.api.php	7 May 2010 17:21:41 -0000
@@ -3888,6 +3888,61 @@ function hook_countries_alter(&$countrie
 }
 
 /**
+ * Define paths as necessary for user login.
+ *
+ * This hook is called to determine whether special action is to be taken
+ * when a path is a login path. For example, if the path should be available
+ * to anonymous users even though the site is in maintenance mode, or if
+ * a 403 error (such as with user/login for an authenticated user) should be
+ * handled with a redirect.
+ *
+ * In core this is used to allow access to 'user/login' and 'user' and
+ * 'openid/authenticate' when the site is in maintenance mode, and also used
+ * to redirect an authenticated user to the more appropriate 'user' when
+ * they land on 'user/login' and would otherwise get a 403 error.
+ *
+ * @return
+ *   An array of arrays keyed by Drupal paths. Each value is an associative
+ *   array keyed by the type of behavior to prescribe.
+ *   'authenticated_redirect' means that an authenticated user should be
+ *   redirected to the named path instead of being given a 403.
+ *   'maintenance_mode_allow_access' means that an anonymous user should be
+ *   allowed to access this path even if the site is in maintenance mode.
+ *
+ * @see user_login_paths()
+ * @see openid_login_paths()
+ * @see drupal_get_login_paths()
+ */
+function hook_login_paths() {
+  return array(
+    'mymodule/authenticate' => array(
+      'authenticated_redirect' => 'mymodule_logged_in',
+      'maintenance_mode_allow_access' => TRUE,
+    ),
+    'mymodule/something' => array(
+      'authenticated_redirect' => 'user',
+      'maintenance_mode_allow_access' => TRUE,
+    ),
+  );
+}
+
+/**
+ * Alter login_paths created by hook_login_paths().
+ *
+ * @param $login_paths
+ *   An array of path => redirect_path or path => TRUE pairs created by
+ *   hook_login_paths().
+ */
+function hook_login_paths_alter(&$login_paths) {
+  $login_paths['user/register'] = array(
+    // Redirect an authenticated user to a different page.
+    'authenticated_redirect' => 'helper_page/naive_user',
+    // But still do not allow access to this page in maintenance mode.
+    'maintenance_mode_allow_access' => FALSE,
+  );
+}
+
+/**
  * Provide information on available file transfer backends.
  *
  * File transfer backends are used by modules to transfer files from remote
Index: modules/system/system.test
===================================================================
RCS file: /cvs/drupal/drupal/modules/system/system.test,v
retrieving revision 1.124
diff -u -p -r1.124 system.test
--- modules/system/system.test	20 Apr 2010 09:48:06 -0000	1.124
+++ modules/system/system.test	7 May 2010 17:21:41 -0000
@@ -708,8 +708,6 @@ class SiteMaintenanceTestCase extends Dr
     $this->assertText($offline_message);
     $this->drupalGet('user/register');
     $this->assertText($offline_message);
-    $this->drupalGet('user/password');
-    $this->assertText($offline_message);
 
     // Verify that user is able to log in.
     $this->drupalGet('user');
@@ -742,6 +740,23 @@ class SiteMaintenanceTestCase extends Dr
     $this->drupalLogout();
     $this->drupalGet('');
     $this->assertRaw($offline_message, t('Found the site offline message.'));
+
+    // Verify that custom site offline message is not displayed on user/password.
+    $this->drupalGet('user/password');
+    $this->assertText(t('Username or e-mail address'), t('Anonymous users can access user/password'));
+    
+    // Submit password reset form.
+    $edit = array(
+      'name' => $this->user->name,
+    );
+    $this->drupalPost('user/password', $edit, t('E-mail new password'));
+    $mails = $this->drupalGetMails();
+    $start = strpos($mails[0]['body'], 'user/reset/'. $this->user->uid);
+    $path = substr($mails[0]['body'], $start, 66 + strlen($this->user->uid));
+
+    // Log in with temporary login link.
+    $this->drupalPost($path, array(), t('Log in'));
+    $this->assertText($user_message);
   }
 }
 
Index: modules/user/user.module
===================================================================
RCS file: /cvs/drupal/drupal/modules/user/user.module,v
retrieving revision 1.1168
diff -u -p -r1.1168 user.module
--- modules/user/user.module	6 May 2010 05:59:31 -0000	1.1168
+++ modules/user/user.module	7 May 2010 17:21:42 -0000
@@ -1690,6 +1690,34 @@ function user_menu() {
 }
 
 /**
+ * Implements hook_login_paths().
+ */
+function user_login_paths() {
+  return array(
+    'user' => array(
+      'maintenance_mode_allow_access' => TRUE,
+    ),
+    'user/login' => array(
+      // If user is logged in, redirect to 'user' instead of giving 403.
+      'authenticated_redirect' => 'user',
+      // Allow access even if we are in maintenance mode.
+      'maintenance_mode_allow_access' => TRUE,
+    ),
+    'user/register' => array(
+      // Authenticated user should be redirected to user edit page.
+      'authenticated_redirect' => 'user/' . $GLOBALS['user']->uid . '/edit',
+    ),
+    // Password reset form bypasses maintenance mode.
+    'user/password' => array(
+      'maintenance_mode_allow_access' => TRUE,
+    ),
+    'user/reset/*' => array(
+      'maintenance_mode_allow_access' => TRUE,
+    ),
+  );
+}
+
+/**
  * Implements hook_init().
  */
 function user_init() {
