You are here

function _captcha_get_posted_captcha_info in CAPTCHA 7

Same name and namespace in other branches
  1. 8 captcha.module \_captcha_get_posted_captcha_info()
  2. 6.2 captcha.module \_captcha_get_posted_captcha_info()

Helper function for getting the posted CAPTCHA info (posted form_id and CAPTCHA sessions ID) from a form in case it is posted.

This function hides the form processing mess for several use cases an browser bug workarounds. For example: $element['#post'] can typically be used to get the posted form_id and captcha_sid, but in the case of node preview situations (with correct CAPTCHA response) that does not work and we can get them from $form_state['clicked_button']['#post']. However with Internet Explorer 7, the latter does not work either when submitting the forms (with only one text field) with the enter key (see http://drupal.org/node/534168), in which case we also have to check $form_state['buttons']['button']['0']['#post'].

@todo for Drupal 7 version: is this IE7 workaround still needed?

Parameters

array $element: the CAPTCHA element.

array $form_state: the form state structure to extract the info from.

string $this_form_id: the form ID of the form we are currently processing (which is not necessarily the form that was posted).

Return value

array an array with $posted_form_id and $post_captcha_sid (with NULL values if the values could not be found, e.g. for a fresh form).

1 call to _captcha_get_posted_captcha_info()
captcha_element_process in ./captcha.module
Process callback for CAPTCHA form element.

File

./captcha.module, line 624
This module enables basic CAPTCHA functionality: administrators can add a CAPTCHA to desired forms that users without the 'skip CAPTCHA' permission (typically anonymous visitors) have to solve.

Code

function _captcha_get_posted_captcha_info($element, $form_state, $this_form_id) {

  //Handle Ajax scenarios
  if (!empty($form_state['rebuild_info'])) {
    if (!empty($form_state['captcha_info']['posted_form_id'])) {
      $posted_form_id = $form_state['captcha_info']['posted_form_id'];
    }
    else {
      $posted_form_id = $form_state['input']['form_id'];
    }
    $posted_captcha_sid = $form_state['captcha_info']['captcha_sid'];
  }
  else {
    if ($form_state['submitted'] && isset($form_state['captcha_info'])) {

      // We are handling (or rebuilding) an already submitted form,
      // so we already determined the posted form ID and CAPTCHA session ID
      // for this form (from before submitting). Reuse this info.
      $posted_form_id = $form_state['captcha_info']['posted_form_id'];
      $posted_captcha_sid = $form_state['captcha_info']['captcha_sid'];
    }
    else {

      // We have to determine the posted form ID and CAPTCHA session ID
      // from the post data.
      // Because we possibly use raw post data here,
      // we should be extra cautious and filter this data.
      $posted_form_id = isset($form_state['input']['form_id']) ? preg_replace("/[^a-z0-9_]/", "", (string) $form_state['input']['form_id']) : NULL;
      $posted_captcha_sid = isset($form_state['input']['captcha_sid']) ? (int) $form_state['input']['captcha_sid'] : NULL;
      $posted_captcha_token = !empty($form_state['input']['captcha_token']) ? preg_replace("/[^a-zA-Z0-9]/", "", (string) $form_state['input']['captcha_token']) : NULL;
      if ($posted_form_id == $this_form_id) {

        // Check if the posted CAPTCHA token is valid for the posted CAPTCHA
        // session ID. Note that we could just check the validity of the CAPTCHA
        // token and extract the CAPTCHA session ID from that (without looking at
        // the actual posted CAPTCHA session ID). However, here we check both
        // the posted CAPTCHA token and session ID: it is a bit more stringent
        // and the database query should also be more efficient (because there is
        // an index on the CAPTCHA session ID).
        if ($posted_captcha_sid != NULL) {
          $expected_captcha_token = db_query("SELECT token FROM {captcha_sessions} WHERE csid = :csid", array(
            ':csid' => $posted_captcha_sid,
          ))
            ->fetchField();
          if ($expected_captcha_token !== $posted_captcha_token) {
            if (empty($form_state['input']['captcha_cacheable'])) {
              drupal_set_message(t('CAPTCHA session reuse attack detected.'), 'error');
            }

            // Invalidate the CAPTCHA session.
            $posted_captcha_sid = NULL;
          }

          // Invalidate CAPTCHA token to avoid reuse.
          db_update('captcha_sessions')
            ->fields(array(
            'token' => NULL,
          ))
            ->condition('csid', $posted_captcha_sid)
            ->execute();
        }
      }
      else {

        // The CAPTCHA session ID is specific to the posted form.
        // Return NULL, so a new session will be generated for this other form.
        $posted_captcha_sid = NULL;
      }
    }
  }
  return array(
    $posted_form_id,
    $posted_captcha_sid,
  );
}