=== modified file 'includes/form.inc'
--- includes/form.inc	2007-04-23 17:00:36 +0000
+++ includes/form.inc	2007-04-24 03:42:25 +0000
@@ -46,56 +46,67 @@
  *   The rendered form.
  */
 function drupal_get_form($form_id) {
-  // In multi-step form scenarios, the incoming $_POST values are not
-  // necessarily intended for the current form. We need to build
-  // a copy of the previously built form for validation and processing,
-  // then go on to the one that was requested if everything works.
-
-  $form_build_id = md5(mt_rand());
-  if (isset($_POST['form_build_id']) && isset($_SESSION['form'][$_POST['form_build_id']]['args']) && $_POST['form_id'] == $form_id) {
-    // There's a previously stored multi-step form. We should handle
-    // IT first.
-    $stored = TRUE;
-    $args = $_SESSION['form'][$_POST['form_build_id']]['args'];
-    $form = call_user_func_array('drupal_retrieve_form', $args);
-    $form['#build_id'] = $_POST['form_build_id'];
+  global $user;
+  $results = array();
+
+  $package['store'] = NULL;
+  $expire = max(ini_get('session.cookie_lifetime'), 86400);
+  // If the incoming $_POST values have a form_build_id, we'll check the
+  // cache for a copy of the form in question. If it's there, we don't
+  // have to rebuild the form to proceed. In addition, if there is a 'data
+  // store' that was cached from previous steps, we'll retrieve it and
+  // use it in a little bit.
+  if (isset($_POST['form_id']) && $_POST['form_id'] == $form_id && !empty($_POST['form_build_id'])) {
+    if ($cached = cache_get('form_'. $user->uid .'_'. $_POST['form_build_id'], 'cache_form', $expire)) {
+      $form = unserialize($cached->data);
+      if ($cached = cache_get('store_'. $_POST['form_build_id'], 'cache_form')) {
+        $package['store'] = unserialize($cached->data);
+      }
+    }
   }
-  else {
-    // We're coming in fresh; build things as they would be. If the
-    // form's #multistep flag is set, store the build parameters so
-    // the same form can be reconstituted for validation.
-    $args = func_get_args();
+
+  // If the previous bit of code didn't result in a populated $form
+  // object, we need to construct the form from scratch.
+  $args = func_get_args();
+  if (!isset($form)) {
     $form = call_user_func_array('drupal_retrieve_form', $args);
-    if (isset($form['#multistep']) && $form['#multistep']) {
-      // Clean up old multistep form session data.
-      _drupal_clean_form_sessions();
-      $_SESSION['form'][$form_build_id] = array('timestamp' => time(), 'args' => $args);
-      $form['#build_id'] = $form_build_id;
-    }
-    $stored = FALSE;
-  }
-
-  // Process the form, submit it, and store any errors if necessary.
-  drupal_process_form($args[0], $form);
-
-  if ($stored && !form_get_errors()) {
-    // If it's a stored form and there were no errors, we processed the
-    // stored form successfully. Now we need to build the form that was
-    // actually requested. We always pass in the current $_POST values
-    // to the builder function, as values from one stage of a multistep
-    // form can determine how subsequent steps are displayed.
-    $args = func_get_args();
-    $args[] = $_POST;
+    $form_build_id = md5(mt_rand());
+    $form['#build_id'] = $form_build_id;
+    drupal_prepare_form($form_id, $form, $package['store']);
+    if (!empty($form['#cache'])) {
+      cache_set('form_'. $user->uid .'_'. $form_build_id, serialize($form), 'cache_form', $expire);
+    }
+  }
+  $form['#post'] = $_POST;
+
+  // Now that we know we have a form, we'll process it (validating,
+  // submitting, and handling the results returned by its submission
+  // handlers. Submit handlers accumulate data in the package by
+  // altering the $package variable, which is passed into them by
+  // reference.
+  drupal_process_form($form_id, $form, $package);
+
+  if (isset($package['store'])) {
+    $args[] = $package['store'];
     $form = call_user_func_array('drupal_retrieve_form', $args);
-    unset($_SESSION['form'][$_POST['form_build_id']]);
-    if (isset($form['#multistep']) && $form['#multistep']) {
-      $_SESSION['form'][$form_build_id] = array('timestamp' => time(), 'args' => $args);
-      $form['#build_id'] = $form_build_id;
-    }
-    drupal_prepare_form($args[0], $form);
+
+    $form_build_id = md5(mt_rand());
+    $form['#build_id'] = $form_build_id;
+    drupal_prepare_form($form_id, $form, $package['store']);
+
+    cache_set('store_'. $form_build_id, serialize($package['store']), 'cache_form', $expire);
+    cache_set('form_'. $user->uid .'_'. $form_build_id, serialize($form), 'cache_form', $expire);
+
+    $_POST = array();
+    $form['#post'] = array();
+
+    // Process the form, submit it, and store any errors if necessary.
+    drupal_process_form($form_id, $form, $package);
   }
 
-  return drupal_render_form($args[0], $form);
+  // If we haven't redirected to a new location, we want to render
+  // whatever form array is currently in hand.
+  return drupal_render_form($form_id, $form);
 }
 
 
@@ -131,8 +142,6 @@ function _drupal_clean_form_sessions() {
  *   when it is submitted by a user.
  * @param ...
  *   Any additional arguments needed by the form building function.
- * @return
- *   Any form validation errors encountered.
  *
  * For example:
  *
@@ -149,18 +158,17 @@ function _drupal_clean_form_sessions() {
  * $values['name'] = 'robo-user';
  * drupal_execute('story_node_form', $values, $node);
  */
-function drupal_execute($form_id, $form_values) {
+function drupal_execute($form_id, $form_values, &$package) {
   $args = func_get_args();
-
-  $form_id = array_shift($args);
-  $form_values = array_shift($args);
+  $args = array_slice($args, 3);
   array_unshift($args, $form_id);
-
-  if (isset($form_values)) {
-    $form = call_user_func_array('drupal_retrieve_form', $args);
-    $form['#post'] = $form_values;
-    return drupal_process_form($form_id, $form);
+  if (isset($package['store'])) {
+    $args[] = $package['store'];
   }
+  $form = call_user_func_array('drupal_retrieve_form', $args);
+  $form['#post'] = $form_values;
+  drupal_prepare_form($form_id, $form, $package['store']);
+  drupal_process_form($form_id, $form, $package);
 }
 
 /**
@@ -235,8 +243,8 @@ function drupal_retrieve_form($form_id) 
  * @return
  *   The path to redirect the user to upon completion.
  */
-function drupal_process_form($form_id, &$form) {
-  global $form_values, $form_submitted, $user, $form_button_counter;
+function drupal_process_form($form_id, &$form, &$package) {
+  global $form_values, $form_submitted, $user, $form_button_counter, $user;
   static $saved_globals = array();
   // In some scenarios, this function can be called recursively. Pushing any pre-existing
   // $form_values and form submission data lets us start fresh without clobbering work done
@@ -247,16 +255,22 @@ function drupal_process_form($form_id, &
   $form_submitted = FALSE;
   $form_button_counter = array(0, 0);
 
-  drupal_prepare_form($form_id, $form);
-  if (($form['#programmed']) || (!empty($_POST) && (($_POST['form_id'] == $form_id)))) {
-    drupal_validate_form($form_id, $form);
+  $form = form_builder($form_id, $form);
+  if ((!empty($form['#programmed'])) || (!empty($form['#post']) && (($form['#post']['form_id'] == $form_id)))) {
+    $old_uid = $user->uid;
+    drupal_validate_form($form_id, $form, $package);
     // IE does not send a button value when there is only one submit button (and no non-submit buttons)
     // and you submit by pressing enter.
     // In that case we accept a submission without button values.
     if ((($form['#programmed']) || $form_submitted || (!$form_button_counter[0] && $form_button_counter[1])) && !form_get_errors()) {
-      $redirect = drupal_submit_form($form_id, $form);
+      $package['redirect'] = NULL;
+      drupal_submit_form($form, $package);
       if (!$form['#programmed']) {
-        drupal_redirect_form($form, $redirect);
+        if (variable_get('cache', CACHE_DISABLED) == CACHE_DISABLED || $user->uid) {
+          cache_clear_all('form_'. $old_uid .'_'. $form_values['form_build_id'], 'cache_form');
+          cache_clear_all('store_'. $old_uid .'_'. $form_values['form_build_id'], 'cache_form');
+        }
+        drupal_redirect_form($form, $package['redirect']);
       }
     }
   }
@@ -264,9 +278,6 @@ function drupal_process_form($form_id, &
   // We've finished calling functions that alter the global values, so we can
   // restore the ones that were there before this function was called.
   list($form_values, $form_submitted, $form_button_counter) = array_pop($saved_globals);
-  if (isset($redirect)) {
-    return $redirect;
-  }
 }
 
 /**
@@ -280,24 +291,15 @@ function drupal_process_form($form_id, &
  * @param $form
  *   An associative array containing the structure of the form.
  */
-function drupal_prepare_form($form_id, &$form) {
+function drupal_prepare_form($form_id, &$form, $store) {
   global $user;
 
   $form['#type'] = 'form';
-  if (!isset($form['#skip_duplicate_check'])) {
+  //if (!isset($form['#skip_duplicate_check'])) {
     $form['#skip_duplicate_check'] = FALSE;
-  }
-
-  if (!isset($form['#post'])) {
-    $form['#post'] = $_POST;
-    $form['#programmed'] = FALSE;
-  }
-  else {
-    $form['#programmed'] = TRUE;
-  }
+  //}
+  $form['#programmed'] = isset($form['#post']);
 
-  // In multi-step form scenarios, this id is used to identify
-  // a unique instance of a particular form for retrieval.
   if (isset($form['#build_id'])) {
     $form['form_build_id'] = array(
       '#type' => 'hidden',
@@ -330,7 +332,11 @@ function drupal_prepare_form($form_id, &
 
 
   if (isset($form_id)) {
-    $form['form_id'] = array('#type' => 'hidden', '#value' => $form_id, '#id' => form_clean_id("edit-$form_id"));
+    $form['form_id'] = array(
+      '#type' => 'hidden',
+      '#value' => $form_id,
+      '#id' => form_clean_id("edit-$form_id"),
+    );
   }
   if (!isset($form['#id'])) {
     $form['#id'] = form_clean_id($form_id);
@@ -340,20 +346,18 @@ function drupal_prepare_form($form_id, &
 
   if (!isset($form['#validate'])) {
     if (function_exists($form_id .'_validate')) {
-      $form['#validate'] = array($form_id .'_validate' => array());
+      $form['#validate'] = array($form_id .'_validate');
     }
   }
 
   if (!isset($form['#submit'])) {
     if (function_exists($form_id .'_submit')) {
       // We set submit here so that it can be altered.
-      $form['#submit'] = array($form_id .'_submit' => array());
+      $form['#submit'] = array($form_id .'_submit');
     }
   }
 
-  drupal_alter('form', $form, $form_id);
-
-  $form = form_builder($form_id, $form);
+  drupal_alter('form', $form, $form_id, $store);
 }
 
 
@@ -368,7 +372,7 @@ function drupal_prepare_form($form_id, &
  *   An associative array containing the structure of the form.
  *
  */
-function drupal_validate_form($form_id, $form) {
+function drupal_validate_form($form_id, $form, &$package) {
   global $form_values;
   static $validated_forms = array();
 
@@ -385,12 +389,12 @@ function drupal_validate_form($form_id, 
     }
   }
 
-  if (!$form['#programmed'] && !$form['#skip_duplicate_check'] && isset($_SESSION['last_submitted']['hash']) && $_SESSION['last_submitted']['hash'] == md5(serialize($form['form_id']['#post']))) {
+  if (!$form['#programmed'] && !$form['#skip_duplicate_check'] && isset($_SESSION['last_submitted']['hash']) && $_SESSION['last_submitted']['hash'] == md5(serialize($form['#post']))) {
     // This is a repeat submission.
     drupal_redirect_form(NULL, $_SESSION['last_submitted']['destination']);
   }
 
-  _form_validate($form, $form_id);
+  _form_validate($form, $package, $form_id);
   $validated_forms[$form_id] = TRUE;
 }
 
@@ -398,45 +402,33 @@ function drupal_validate_form($form_id, 
  * Processes user-submitted form data from a global variable using
  * the submit functions defined in a structured form array.
  *
- * @param $form_id
- *   A unique string identifying the form for validation, submission,
- *   theming, and hook_form_alter functions.
  * @param $form
  *   An associative array containing the structure of the form.
+ * @param $package
+ *   Eaton needs to document this.
  * @return
  *   A string containing the path of the page to display when processing
  *   is complete.
  *
  */
-function drupal_submit_form($form_id, $form) {
+function drupal_submit_form($form, &$package) {
   global $form_values;
-  $default_args = array($form_id, &$form_values);
   $submitted = FALSE;
-  $goto = NULL;
 
   if (isset($form['#submit'])) {
-    foreach ($form['#submit'] as $function => $args) {
+    foreach ($form['#submit'] as $function) {
       if (function_exists($function)) {
-        $args = array_merge($default_args, (array) $args);
-        // Since we can only redirect to one page, only the last redirect
-        // will work.
-        $redirect = call_user_func_array($function, $args);
+        $function($form_values, $form, $package);
         $submitted = TRUE;
-        if (isset($redirect)) {
-          $goto = $redirect;
-        }
       }
     }
   }
+
   // Successful submit. Hash this form's POST and store the hash in the
   // session. We'll use this hash later whenever this user submits another
   // form to make sure no identical forms get submitted twice.
   if ($submitted && !$form['#skip_duplicate_check']) {
-    $_SESSION['last_submitted'] = array('destination' => $goto, 'hash' => md5(serialize($form['form_id']['#post'])));
-  }
-
-  if (isset($goto)) {
-    return $goto;
+    $_SESSION['last_submitted'] = array('destination' => $package['redirect'], 'hash' => md5(serialize($form['#post'])));
   }
 }
 
@@ -517,11 +509,11 @@ function drupal_redirect_form($form, $re
  *   A unique string identifying the form for validation, submission,
  *   theming, and hook_form_alter functions.
  */
-function _form_validate($elements, $form_id = NULL) {
+function _form_validate($elements, &$package, $form_id = NULL) {
   // Recurse through all children.
   foreach (element_children($elements) as $key) {
     if (isset($elements[$key]) && $elements[$key]) {
-      _form_validate($elements[$key]);
+      _form_validate($elements[$key], $package);
     }
   }
   /* Validate the current input */
@@ -564,16 +556,21 @@ function _form_validate($elements, $form
       }
     }
 
-    // Call user-defined validators.
-    if (isset($elements['#validate'])) {
-      foreach ($elements['#validate'] as $function => $args) {
-        $args = array_merge(array($elements), $args);
-        // For the full form we hand over a copy of $form_values.
-        if (isset($form_id)) {
-          $args = array_merge(array($form_id, $GLOBALS['form_values']), $args);
+    // Call user-defined form level validators.
+    if (isset($form_id)) {
+      if (isset($elements['#validate'])) {
+        foreach ($elements['#validate'] as $function) {
+          if (function_exists($function))  {
+            $function($GLOBALS['form_values'], $elements, $package);
+          }
         }
+      }
+    }
+    // Call user-defined element validators.
+    elseif (isset($elements['#element_validate'])) {
+      foreach ($elements['#element_validate'] as $function) {
         if (function_exists($function))  {
-          call_user_func_array($function, $args);
+          $function($elements, $package);
         }
       }
     }
@@ -1117,7 +1114,7 @@ function expand_password_confirm($elemen
     '#title' => t('Confirm password'),
     '#value' => $element['#value']['pass2'],
   );
-  $element['#validate'] = array('password_confirm_validate' => array());
+  $element['#element_validate'] = array('password_confirm_validate');
   $element['#tree'] = TRUE;
 
   if (isset($element['#size'])) {

