From 9e3f141701b96b6974b3386f83dc76e70a41377d Mon Sep 17 00:00:00 2001
From: Miikka Salminen <miikka.salminen@gmail.com>
Date: Wed, 6 Apr 2016 17:11:23 +0300
Subject: [PATCH] Fixed #26466 -- Added HTTP_REFERER decoding to i18n
 set_language() view.

---
 django/views/i18n.py                |  4 +++-
 tests/view_tests/tests/test_i18n.py | 12 ++++++++++++
 tests/view_tests/urls.py            |  1 +
 tests/view_tests/views.py           |  4 ++++
 4 files changed, 20 insertions(+), 1 deletion(-)

diff --git a/django/views/i18n.py b/django/views/i18n.py
index b76791d3c5..12fa1f70f6 100644
--- a/django/views/i18n.py
+++ b/django/views/i18n.py
@@ -12,7 +12,7 @@ from django.utils import six
 from django.utils._os import upath
 from django.utils.encoding import smart_text
 from django.utils.formats import get_format, get_format_modules
-from django.utils.http import is_safe_url
+from django.utils.http import is_safe_url, urlunquote
 from django.utils.translation import (
     LANGUAGE_SESSION_KEY, check_for_language, get_language, to_locale,
 )
@@ -36,6 +36,8 @@ def set_language(request):
     next = request.POST.get('next', request.GET.get('next'))
     if (next or not request.is_ajax()) and not is_safe_url(url=next, host=request.get_host()):
         next = request.META.get('HTTP_REFERER')
+        if next:
+            next = urlunquote(next)  # HTTP_REFERER may be encoded.
         if not is_safe_url(url=next, host=request.get_host()):
             next = '/'
     response = http.HttpResponseRedirect(next) if next else http.HttpResponse(status=204)
diff --git a/tests/view_tests/tests/test_i18n.py b/tests/view_tests/tests/test_i18n.py
index 1c934aaf8d..5e6e9d2f2d 100644
--- a/tests/view_tests/tests/test_i18n.py
+++ b/tests/view_tests/tests/test_i18n.py
@@ -138,6 +138,18 @@ class I18NTests(TestCase):
             self.assertEqual(language_cookie['path'], '/test/')
             self.assertEqual(language_cookie['max-age'], 3600 * 7 * 2)
 
+    def test_setlang_decodes_http_referer_url(self):
+        """
+        The set_language view decodes the HTTP_REFERER URL.
+        """
+        # The url() & view must exist for this to work as a regression test.
+        self.assertEqual(reverse('with_parameter', kwargs={'parameter': 'x'}), '/test-setlang/x/')
+        lang_code = self._get_inactive_language_code()
+        encoded_url = '/test-setlang/%C3%A4/'  # (%C3%A4 decodes to รค)
+        response = self.client.post('/i18n/setlang/', {'language': lang_code}, HTTP_REFERER=encoded_url)
+        self.assertRedirects(response, encoded_url, fetch_redirect_response=False)
+        self.assertEqual(self.client.session[LANGUAGE_SESSION_KEY], lang_code)
+
     @modify_settings(MIDDLEWARE_CLASSES={
         'append': 'django.middleware.locale.LocaleMiddleware',
     })
diff --git a/tests/view_tests/urls.py b/tests/view_tests/urls.py
index 34028727aa..a8cb756f77 100644
--- a/tests/view_tests/urls.py
+++ b/tests/view_tests/urls.py
@@ -103,4 +103,5 @@ urlpatterns += [
         name='raises_template_does_not_exist'
     ),
     url(r'^render_no_template/$', views.render_no_template, name='render_no_template'),
+    url(r'^test-setlang/(?P<parameter>[^/]+)/$', views.with_parameter, name='with_parameter'),
 ]
diff --git a/tests/view_tests/views.py b/tests/view_tests/views.py
index 203cb6149c..01c0e0e9ea 100644
--- a/tests/view_tests/views.py
+++ b/tests/view_tests/views.py
@@ -26,6 +26,10 @@ def index_page(request):
     return HttpResponse('<html><body>Dummy page</body></html>')
 
 
+def with_parameter(request, parameter):
+    return HttpResponse('ok')
+
+
 def raises(request):
     # Make sure that a callable that raises an exception in the stack frame's
     # local vars won't hijack the technical 500 response. See: