### Eclipse Workspace Patch 1.0
#P drupal.5.1
Index: includes/form.inc
===================================================================
RCS file: /cvs/drupal/drupal/includes/form.inc,v
retrieving revision 1.174.2.3
diff -u -r1.174.2.3 form.inc
--- includes/form.inc	29 Jan 2007 21:51:53 -0000	1.174.2.3
+++ includes/form.inc	30 Jan 2007 14:16:36 -0000
@@ -66,10 +66,14 @@
     // form's #multistep flag is set, store the build parameters so
     // the same form can be reconstituted for validation.
     $args = func_get_args();
+
+    // Clean up old form session data (sometimes).
+    // I need suggestions for this. How do we best do cleanup without wasting resources?
+    if (rand(0, 99) == 0) {
+      _drupal_clean_form_sessions();
+    }
     $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;
     }
@@ -105,13 +109,27 @@
  * $_SESSION['form'] array.
  */
 function _drupal_clean_form_sessions() {
+  // clean multistep tokens every 23.5 hours.
+  $expire = time() - 84600;
   if (isset($_SESSION['form'])) {
     foreach ($_SESSION['form'] as $build_id => $data) {
-      if ($data['timestamp'] < (time() - 84600)) {
+      if ($data['timestamp'] < $expire) {
         unset($_SESSION['form'][$build_id]);
       }
     }
   }
+  // The multiple submission protection bloats the session rather quickly.
+  // Since the session token is needed in order to submit a form, we need
+  // to hold onto them long enough that someone with a form in an open browser
+  // can submit the form in a reasonable time.
+  $expire = time() - 60 * 120; // 2 hours.
+  if (isset($_SESSION['prevent_multiple'])) {
+    foreach ($_SESSION['prevent_multiple'] as $token => $data) {
+      if ($data['timestamp'] < $expire) {
+        unset($_SESSION['prevent_multiple'][$token]);
+      }
+    }
+  }
 }
 
 
@@ -331,7 +349,25 @@
     );
   }
 
-
+  if (!$form['#programmed']) {
+    // Add a token to the form so that we can prevent multiple submissions.
+    if (!isset($form['#prevent_multiple'])) {
+      $form['#prevent_multiple'] = array('#type' => 'value', '#value' => TRUE);
+    }
+    // If the form is being built for the first time, generate a token.
+    if ($form['#prevent_multiple']) {
+      if (!isset($form['#post']['prevent_multiple'])) {
+        $multi_token = md5(mt_rand());
+        // time() is used for garbage collection purposes.
+        $_SESSION['prevent_multiple'][$multi_token] = array('timestamp' => time());
+        // This goes to the browser.
+        $form['prevent_multiple'] = array('#type' => 'hidden', '#value' => $multi_token);
+      }
+      else {
+        $form['prevent_multiple'] = array('#type' => 'hidden', '#value' => check_plain($form['#post']['prevent_multiple']));
+      }
+    }
+  }
   if (isset($form_id)) {
     $form['form_id'] = array('#type' => 'hidden', '#value' => $form_id, '#id' => form_clean_id("edit-$form_id"));
   }
@@ -397,6 +433,25 @@
     }
   }
 
+  // If a form is supposed to be submitted only once, there will be a token in
+  // the session for any legitimate attempts to submit. In the absence of this
+  // form token, set an error.
+  $prevent_multiple_token = $form_values['prevent_multiple'];
+  if (!$form['#programmed'] && $form['#prevent_multiple']['#value']) {
+    if (!isset($_SESSION['prevent_multiple']) || !is_array($_SESSION['prevent_multiple']) || !isset($_SESSION['prevent_multiple'][$prevent_multiple_token])) {
+      // We've failed validation because the form was never set up properly.
+      form_set_error('form_token', t('Validation error, please try again. If this error persists, please contact the site administrator.'));
+    }
+    else {
+      // Check to see if the form has been submitted.
+      if (isset($_SESSION['prevent_multiple'][$form_values['prevent_multiple']]) && isset($_SESSION['prevent_multiple'][$form_values['prevent_multiple']]['destination'])) {
+        // The form has been submitted before so we send the user to the destination of that submission.
+        $destination = $_SESSION['prevent_multiple'][$form_values['prevent_multiple']]['destination'];
+        drupal_redirect_form(NULL, $destination);
+      }
+    }
+  }
+
   _form_validate($form, $form_id);
   $validated_forms[$form_id] = TRUE;
 }
@@ -432,6 +487,13 @@
       }
     }
   }
+
+  // We save the destination in the $_SESSION for this form so that we can send
+  // users there if the form gets submitted multiple times.
+  if ($form['#prevent_multiple']) {
+    $_SESSION['prevent_multiple'][$form_values['prevent_multiple']]['destination'] = $goto;
+  }
+
   return $goto;
 }
 
