views:

481

answers:

4

I'm having some issues with passing an email address in a url to a symfony app.

The url looks like

example.com/unsubscribe/email/[email protected]

It will always result in a sfError404Exception, except when the period is removed. After doing some googling around, the only solution I've yet seen is that htaccess is bypassing the url because of the period present. However, when I add the suggested fix to htaccess, like so:

# we skip all files with .something
RewriteCond %{REQUEST_URI} \..+$
RewriteCond %{REQUEST_URI} !@.+    #skip email address
RewriteCond %{REQUEST_URI} \.epl$
RewriteCond %{REQUEST_URI} !\.html$ 
RewriteCond %{REQUEST_URI} !\.rhtml$
RewriteRule .* - [L]

I get the same 404. It also returns 404 when I use the front controller directly in the url (example.com/index.php/unsubscribe/email/[email protected]). I've tried putting the escaped version directly into the address bar, eg example.com/unsubscribe/me%40example%2Ecom and this works, but only in firefox, nowhere else.

I've spent about 2 hours in forum answer googling hell at this point and I'm running out of ideas.

Any thoughts?

Thanks.

Update: Here is the relevant section of the routing.yml:

unsubscribeform:
  url:  /unsubscribe/email/:email
  param: { module: subscribe, action: index }

Update: Stack trace ... looks like its not getting any route information to go on to me

404 | Not Found | sfError404Exception
Empty module and/or action after parsing the URL "/unsubscribe/email/[email protected]" (/).
stack trace

1. at ()
  in SF_SYMFONY_LIB_DIR/controller/sfFrontWebController.class.php line 44 ...
          41.
          42.       if (empty($moduleName) || empty($actionName))
          43.       {
          44.         throw new sfError404Exception(sprintf('Empty module and/or action after parsing the URL "%s" (%s/%s).', $request->getPathInfo(), $moduleName, $actionName));
          45.       }
          46.
          47.       // make the first request
2. at sfFrontWebController->dispatch()
  in SF_SYMFONY_LIB_DIR/util/sfContext.class.php line 159 ...
         156.    */
         157.   public function dispatch()
         158.   {
         159.     $this->getController()->dispatch();
         160.   }
         161.
         162.   /**
3. at sfContext->dispatch()
  in /home/web/htdocs/index.php line 10 ...
           7. require_once(SF_ROOT_DIR.DIRECTORY_SEPARATOR.'config'.DIRECTORY_SEPARATOR.'ProjectConfiguration.class.php');
           8.
           9. $configuration = ProjectConfiguration::getApplicationConfiguration(SF_APP, SF_ENVIRONMENT, SF_DEBUG);
          10. sfContext::createInstance($configuration)->dispatch();

11.

A: 

how is your routing.yml could you show us?

i tried it without any problems, here is the log output

for /group/list/data/[email protected] with parameters array ( 'data' => '[email protected]', 'module' => 'group', 'action' => 'list',)

metoikos
added to the question body
pivotal
A: 

Your issue isn't with Rewrite Rules. Considering that Symfony is throwing the exception then the request is making it to Symfony.

Can you post the traceback for the exception? If you have sf_logging_enabled it should log some pretty useful info for debugging the routing.

Cixate
I've added the trace to the question body.
pivotal
Hm, looks like it gets the URL ok. Does sf_logging_enabled provide any details about why your route failed?
Cixate
+2  A: 

I don't know what you are doing in Symfony but it may help to be clear that the following is not a valid URL:

example.com/unsubscribe/email/[email protected]

What you almost certainly want (and this is true for all browsers!) is:

http://example.com/unsubscribe/email/me%40example.com

Note: The @ symbol is not safe and must be encoded, the . symbol is safe however (RFC1738). If you don't escape the @ symbol it will almost certainly cause big trouble so (escaping the . almost certainly won't, but you don't need to so I wouldn't).

Problems will occur with not escaping it because @ is reserved as a separator when passing authentication parameters (e.g. http://username%[email protected]/url/). Some URL parsers will work out that you really meant to type %40 if @ occurs after the domain in the URL, but others won't.

Rather than just encoding the @ symbol statically you should use one of PHP's URL encoding functions on the email address( e.g. "$emailAddress = urlencode($emailAddress);") to ensure other characters in the address are also escaped properly. Don't be tempted to leave this till later or 'after you get it working' do it from the start and save yourself and the end users a headache! :-)

NB: There are several ways to encode URL's in PHP, so you will need to read through the documentation page for urlencode() and compare it with other approaches like rawurlencode() to be sure it's what you really want in your case.

Iain Collins
Thanks, I had been planning on using an escaped version of the "@", but sadly that doesn't seem to be the issue for me - whether the ampersand is escaped, unescaped, or completely absent, the period still manages to throw off symfony :/
pivotal
Ah interesting! I think you nave nailed it there. I had a look and it seems like this happens to other people and is a 'known issue' - seems like the .htaccess configurations that come with Symfony (or that they are recommending) cause the . to be interpreted!It seems to be covered in replies to a post here:http://www.mail-archive.com/[email protected]/msg08689.htmlAs I don't know how Symfony I can't give a copy+paste example but the suggestions of how to change .htaccess to fix it in the replies make sense - good luck! 8)
Iain Collins
+1  A: 

By default, Symfony treats . and / as parameter separators.
That makes it very easy to match a url like so:

/some/path/:param.:ext

But doesn't help with email addresses.

Fortunately, you can override the . separator by specifying your own pattern.
Just add the requirements line below to your routing:

unsubscribeform:
  url:  /unsubscribe/email/:email
  param: { module: subscribe, action: index }
  requirements: { email: .+ }

The .+ in the requirement is a regular expression matching anything. The .matches any character, and the + means match one-or-more.

(Tested in Symfony 1.4)

kenneth
+1 Nice answer!
Arms