### 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; }