Index: includes/form.inc
===================================================================
RCS file: /cvs/drupal/drupal/includes/form.inc,v
retrieving revision 1.174.2.1
diff -u -F^f -r1.174.2.1 form.inc
--- includes/form.inc	23 Jan 2007 19:18:11 -0000	1.174.2.1
+++ includes/form.inc	27 Jan 2007 15:21:20 -0000
@@ -65,10 +65,12 @@ function drupal_get_form($form_id) {
     // 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 multistep form session data.
+    _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;
     }
@@ -104,13 +106,24 @@ function drupal_get_form($form_id) {
  * $_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]);
       }
     }
   }
+  // Protect against multiple submissions for an hour.
+  $expire = time() - (60 * 60);
+  if (isset($_SESSION['prevent_multiple'])) {
+    foreach ($_SESSION['prevent_multiple'] as $token => $timestamp) {
+      if ($timestamp < $expire) {
+        unset($_SESSION['prevent_multiple'][$token]);
+      }
+    }
+  }
 }
 
 
@@ -330,6 +343,16 @@ function drupal_prepare_form($form_id, &
     );
   }
 
+  // If the form aims to prevent multiple submissions and is being newly built (no $form['#post']),
+  // build a random token to identify the form. Otherwise the form is being submitted, so use the
+  // existing prevent_multiple token.
+  if (isset($form['#prevent_multiple']) && $form['#prevent_multiple']) {
+    $form['prevent_multiple'] = array(
+      '#type' => 'hidden',
+      '#value' => isset($form['#post']['prevent_multiple']) ? $form['#post']['prevent_multiple'] : md5(mt_rand()),
+    );
+  }
+
 
   if (isset($form_id)) {
     $form['form_id'] = array('#type' => 'hidden', '#value' => $form_id, '#id' => form_clean_id("edit-$form_id"));
@@ -396,6 +419,12 @@ function drupal_validate_form($form_id, 
     }
   }
 
+  if (isset($form['#prevent_multiple']) && $form['#prevent_multiple'] && isset($_SESSION['prevent_multiple']) && is_array($_SESSION['prevent_multiple'])) {
+    if (in_array($form_values['prevent_multiple'], array_keys($_SESSION['prevent_multiple']))) {
+      form_set_error('prevent_multiple', t('This form has already been submitted.'));
+    }
+  }
+
   _form_validate($form, $form_id);
   $validated_forms[$form_id] = TRUE;
 }
@@ -418,6 +447,10 @@ function drupal_submit_form($form_id, $f
   global $form_values;
   $default_args = array($form_id, &$form_values);
 
+  if (isset($form['#prevent_multiple']) && $form['#prevent_multiple']) {
+    $_SESSION['prevent_multiple'][$form_values['prevent_multiple']] = time();
+  }
+
   if (isset($form['#submit'])) {
     foreach ($form['#submit'] as $function => $args) {
       if (function_exists($function)) {
Index: modules/comment/comment.module
===================================================================
RCS file: /cvs/drupal/drupal/modules/comment/comment.module,v
retrieving revision 1.520.2.1
diff -u -F^f -r1.520.2.1 comment.module
--- modules/comment/comment.module	23 Jan 2007 19:11:43 -0000	1.520.2.1
+++ modules/comment/comment.module	27 Jan 2007 15:21:21 -0000
@@ -1546,7 +1546,8 @@ function comment_form($edit, $title = NU
 
   $form['preview'] = array('#type' => 'button', '#value' => t('Preview comment'), '#weight' => 19);
   $form['#token'] = 'comment'. $edit['nid'] . $edit['pid'];
-
+  $form['#prevent_multiple'] = TRUE;
+  
   // Only show post button if preview is optional or if we are in preview mode.
   // We show the post button in preview mode even if there are form errors so that
   // optional form elements (e.g., captcha) can be updated in preview mode.
@@ -2005,4 +2006,3 @@ function int2vancode($i = 0) {
 function vancode2int($c = '00') {
   return base_convert(substr($c, 1), 36, 10);
 }
-
Index: modules/node/node.module
===================================================================
RCS file: /cvs/drupal/drupal/modules/node/node.module,v
retrieving revision 1.776
diff -u -F^f -r1.776 node.module
--- modules/node/node.module	14 Jan 2007 02:12:29 -0000	1.776
+++ modules/node/node.module	27 Jan 2007 15:21:22 -0000
@@ -1975,6 +1975,8 @@ function node_form($node, $form_values =
   // Set the id of the top-level form tag
   $form['#id'] = 'node-form';
 
+  $form['#prevent_multiple'] = TRUE;
+
   /**
    * Basic node information.
    * These elements are just values so they are not even sent to the client.
