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.