HTTP Referer overwritten when used in FormAPI.

dobson - May 15, 2006 - 18:41
Project:Drupal
Version:5.7
Component:forms system
Category:bug report
Priority:normal
Assigned:Unassigned
Status:by design
Description

Here is my test code:

function mymodule_testform(){
  $form = array();
  //$form['#redirect'] = $_SERVER['HTTP_REFERER'];
  $form['then_go_to'] = array('#type'=>'hidden', '#value'=>$_SERVER['HTTP_REFERER']);
  $form['submit'] = array('#type'=>'submit',  '#value'=>'Submit Form');

  drupal_set_message('set: '.$form['then_go_to']['#value']);

  return drupal_get_form('testform', $form);
}

function testform_submit($form_id, &$form_values){
  drupal_set_message('get '.print_r($form_values['then_go_to'],true));
  // return $form_values['then_go_to'];
  drupal_goto($form_values['then_go_to']);
}

When the form is rendered for the first time, the message printed is:

set: <MY REFERER>

When I submit, however, the message is:

    * set: <FORM URL>
    * get <FORM URL>
    * set: <FORM URL>

It seems that the form render function is being called once again before the submit code, and resetting the referer to the form URL. I have tried to set $form['#redirect'], and I have tried to return the URL from the submit function, with the same results.

#1

Morbus Iff - May 15, 2006 - 19:08

Just commenting to ping me.

#2

chx - May 15, 2006 - 19:17
Status:active» won't fix

Pardon me, but how this is a Drupal issue? HTTP_REFERER is the previous page. In this case, the previous before the POST request.

#3

Morbus Iff - May 15, 2006 - 20:04
Status:won't fix» active

Chx: please take two deep breaths and actually try to understand, mmkay?

  1. User is at www.disobey.com. User clicks link.
  2. User is now at demo.com/drupal/form. Referer is "http://www.disobey.com".
  3. Referer, "http://www.disobey.com" is set in $form['whee']. The intent is to return the user to the page they came from (disobey.com) once the form has been submitted (ie., return "http://disobey.com/" from formname_submit).
  4. $form['whee'] continues to remain disobey.com in form_alter().
  5. By the time $form['whee'] becomes $form_values['whee'] in form_submit(), it is now set to the URL of the form.

The only way it could become set to the URL of the form is if the form generating code (where $form['whee'] was set to disobey.com) was re-run at a later step in the process (somewhere between _alter and _submit), when the referer has, in fact, changed. This has nothing to do with "OHMYGOD, LEARN2HTTP" - the same problem would appearently occur with epoch timestamps: they'd be set once on the GET, and then again on the POST which can be a huUuGe difference. Same with a zillion other dynamic form fields that should not change.

As you've mentioned in IRC, if your *only* soluton, that you know ACTUALLY works because you tested it before responding, is to change the #action of the form to something else, then by all means say as much and close.

#4

chx - May 15, 2006 - 20:22
Status:active» by design

I have not tested anything and if that's the case, then I am re-closing because there is no way we can avoid (in 4.7, at least) to rebuild the form tree as form API needs it. For 4.8 we might save it to session.

#5

dobson - May 15, 2006 - 22:30

Followup for those who may come after in search of a solution: there appear to be several clues for finding ways around this particular design.

1) $form['#action'],
2) checking for POST vs GET,
3) $_SESSION variables,
4) wait for 4.8 :),
And probably a few more ( certainly not $form['#redirect'] as of 4.7.0 ).

Good luck.

Oh, and while I'm here already, I might as well mention that the comment preview button below this post isn't previewing (I'd report it properly but I'm nervous to start a new thread on drupal.org unless A: I have a bloody good reason to, and B: It's important enough for me to have taken the time to ask for help in phrasing my post in a manner which will not result in my being ignored while still being acceptable to the drupal community).

Oh, and seeing as I'm already here, I may as well mention that the breadcrumb above this 'New Comment' page reads: 'Home » Downloads » HTTP Referer overwritten when used in FormAPI'. I am of course not sure, but I might have posted to 'Home » Downloads » Drupal project » Drupal » issues'. See above.

I totally love Drupal, btw! Drupal and #drupal-support with its cargo of friendly, helpful people are the best in the west.

#6

greg.harvey - April 8, 2008 - 13:48
Version:4.7.0» 5.7
Status:by design» active

*BUMP*

This is still the case? I'm using Drupal 5.7 and finding the EXACT same behaviour. And strangely, I can find no other discussion of it. Whatever happened to this issue? The implication was it would be revisited, but it seems that never happened and it's still not clear how you get around it. Any takers?

#7

Morbus Iff - April 8, 2008 - 14:01

Greg: the way to move this forward would be to test it under Drupal 6 and, if it still exists, state as much here and set the version to 7.x-dev. No more work is being done, save for security fixes, on Drupal 5 OR 6.

#8

chx - April 8, 2008 - 14:03
Status:active» by design

this is still by design. Drupal 6 has a #cache and Drupal 5 has #multistep that stores the form tree though.

#9

greg.harvey - April 8, 2008 - 15:42

Thanks guys. Achieved what I wanted with nodeapi in the end. Look forward to getting in to Drupal 6, but no can do until a few more modules I need are in a more stable state. =(

Won't be long now!

#10

greg.harvey - May 28, 2009 - 11:52

In case anyone has a similar solution and chances on this thread, I wrote up my solution here as a comment: http://drupal.org/node/134000#comment-799369

(Which was deleted ... wtf? Sometimes I wonder why I bother...)

#11

davidhunt - May 28, 2009 - 03:57

Word on the street is that this idea - redirecting back to referer - is an unreliable one because of the referer_uri() call; you may not get one at all, and it could be faked or insecure. If you're linking internally, it's probably better to pass a variable on to the form function with the redirect path in it. However, if none of that is sufficient to dissuade you from this path, you may benefit from the ideas below. I decided to save a variable to $_SESSION with the referer - and since I had several different forms, I broke it out into a separate function:

function _form_redirect() {
  if (!$_SESSION['formredirect'] || $_SESSION['formredirect']['time'] < (time() - 600)) {
    $redirect = check_url(referer_uri());
    $_SESSION['formredirect'] = array('url' => $redirect, 'time' => time());
  }
  return $_SESSION['formredirect']['url'];
}

That way I could call that function in form creation:
function foo_form() {
  ...
  $form['#redirect'] = _form_redirect();
}

And I unset the $_SESSION variable before returning the redirect in the submit function:
function foo_form_submit($form_id, $form_values) {
  unset($_SESSION['formredirect']);
  ...
  return $form_values['#redirect'];
}

As it turns out, since I think all my links are going to be internal, I'm probably going to put another parameter into the function that calls confirm_form instead of doing the above; however, since I spent some time on this, I figured it might possibly help someone else out if you need to redirect back to an external page.

 
 

Drupal is a registered trademark of Dries Buytaert.