Repository: ckan/ckan

Issue:
Requesting page with non-ASCII URL fails in Jinja template rendering
[Jinja only accepts unicode or ASCII](http://jinja.pocoo.org/docs/dev/api/#unicode)

`ckan/lib/base.py` relays requested URLs in raw format to Jinja, causing `UnicodeDecodeError: 'ascii' codec can't decode byte 0xc3 in position 20: ordinal not in range(128)` if URL or its parts are rendered to page.

This issue is visible, for example, when searching for datasets using a browser that does not auto-escape non-ASCII characters. For example, IE dies not escape while Firefox does. This bug is easily reproduced by searching datasets with "ä" as the search term using IE, or by using curl: `curl -v "http://demo.ckan.org/dataset?q=ä"`

URLs and any other data used in Jinja templates should be decoded and encoded into unicode before rendering. This is not a trivial fix, since correctly identifying an unknown encoding in all cases is borderline, if not, impossible.

One possibility would be to assume requests come in UTF-8 or ASCII and just decode everything as if it was UTF-8 (since only-ASCII UTF-8 is the same as the same text in ASCII). This would not work for other encodings like latin-1, but would be a start.

I can create a patch and pull request for the UTF-8/ASCII-only solution if it's considered acceptable. Otherwise I'd like to request comments on other options. [BeautifulSoup](http://www.crummy.com/software/BeautifulSoup/bs3/documentation.html#Beautiful%20Soup%20Gives%20You%20Unicode,%20Dammit) has one solution for a few encodings.



Discussion:
@bzar yes, please! A patch that expects only UTF-8/ASCII sounds great. We should only be passing unicode through to jinja.

Digging into this I have uncovered the following:
1. Pylons already assumes all data is UTF-8 and [decodes it into unicode by default since pylons 0.9.6](http://pylons-webframework.readthedocs.org/en/latest/tutorials/understanding_unicode.html#request-parameters)
2. The non-unicode string rendered by the template is CKAN_CURRENT_URL, defined [here](https://github.com/ckan/ckan/blob/master/ckan/config/middleware.py#L222)
3. Changing CKAN_CURRENT_URL to unicode breaks "url_for" provided by "routes" library
4. Current code base already has a fix for this that escapes the UTF-8 url into valid ASCII [here](https://github.com/ckan/ckan/blob/master/ckan/config/middleware.py#L220-L221)
5. This fix has been commented out in [this commit](https://github.com/ckan/ckan/commit/0e7fd02d80bd822c85a5d754dec1f10010273cbe) because it affected legacy templates

Should this "fix" be reverted or at least parametrized?

@bzar we should revert it. the legacy templates have been removed.

There is still support for extensions using the legacy template engine. Write the PR and we can ask @davidread to test if it breaks their legacy templates. If not, we should backport this fix too.

I've read and read this, but have lots of questions about what is meant by failure and why, and it would require me to dig a lot to understand how to test this. We've switched to Jinja templates for search page now, so it's also not much help me doing it anyway, I'm afraid. Sorry.

Testing it is as simple as requesting `/dataset?q=ä"`. Make sure the HTTP client you use doesn't auto-escape the URL (Firefox for one does this, IE and curl don't as [mentioned in pylons' manual](http://pylons-webframework.readthedocs.org/en/latest/tutorials/understanding_unicode.html#output-encoding)).

The failure is a "500 internal server error" reply caused by rendering a non-ASCII python string (not unicode object) inside a JINJA template, namely the request URL stored in CKAN_CURRENT_URL.

I'll prepare the pull request.


Resolving patch:
diff --git a/ckan/config/middleware.py b/ckan/config/middleware.py
--- a/ckan/config/middleware.py
+++ b/ckan/config/middleware.py
@@ -218,7 +218,7 @@ def __call__(self, environ, start_response):
 
             if qs:
                 # sort out weird encodings
-                #qs = urllib.quote(qs, '')
+                qs = urllib.quote(qs, '')
                 environ['CKAN_CURRENT_URL'] = '%s?%s' % (path_info, qs)
             else:
                 environ['CKAN_CURRENT_URL'] = path_info


Test patch: