Repository: encode/django-rest-framework

Issue:
Support encoded filename as per rfc6266 in FileUploadParser.



Discussion:

Resolving patch:
diff --git a/rest_framework/parsers.py b/rest_framework/parsers.py
--- a/rest_framework/parsers.py
+++ b/rest_framework/parsers.py
@@ -11,7 +11,7 @@
 from django.http.multipartparser import MultiPartParser as DjangoMultiPartParser
 from django.http.multipartparser import MultiPartParserError, parse_header, ChunkIter
 from django.utils import six
-from rest_framework.compat import etree, yaml, force_text
+from rest_framework.compat import etree, yaml, force_text, urlparse
 from rest_framework.exceptions import ParseError
 from rest_framework import renderers
 import json
@@ -290,6 +290,22 @@ def get_filename(self, stream, media_type, parser_context):
         try:
             meta = parser_context['request'].META
             disposition = parse_header(meta['HTTP_CONTENT_DISPOSITION'].encode('utf-8'))
-            return force_text(disposition[1]['filename'])
+            filename_parm = disposition[1]
+            if 'filename*' in filename_parm:
+                return self.get_encoded_filename(filename_parm)
+            return force_text(filename_parm['filename'])
         except (AttributeError, KeyError):
             pass
+
+    def get_encoded_filename(self, filename_parm):
+        """
+        Handle encoded filenames per RFC6266. See also:
+        http://tools.ietf.org/html/rfc2231#section-4
+        """
+        encoded_filename = force_text(filename_parm['filename*'])
+        try:
+            charset, lang, filename = encoded_filename.split('\'', 2)
+            filename = urlparse.unquote(filename)
+        except (ValueError, LookupError):
+            filename = force_text(filename_parm['filename'])
+        return filename


Test patch:
diff --git a/tests/test_parsers.py b/tests/test_parsers.py
--- a/tests/test_parsers.py
+++ b/tests/test_parsers.py
@@ -1,3 +1,5 @@
+# -*- coding: utf-8 -*-
+
 from __future__ import unicode_literals
 from rest_framework.compat import StringIO
 from django import forms
@@ -113,3 +115,25 @@ def test_get_filename(self):
         parser = FileUploadParser()
         filename = parser.get_filename(self.stream, None, self.parser_context)
         self.assertEqual(filename, 'file.txt')
+
+    def test_get_encoded_filename(self):
+        parser = FileUploadParser()
+
+        self.__replace_content_disposition('inline; filename*=utf-8\'\'ÀĥƦ.txt')
+        filename = parser.get_filename(self.stream, None, self.parser_context)
+        self.assertEqual(filename, 'ÀĥƦ.txt')
+
+        self.__replace_content_disposition('inline; filename=fallback.txt; filename*=utf-8\'\'ÀĥƦ.txt')
+        filename = parser.get_filename(self.stream, None, self.parser_context)
+        self.assertEqual(filename, 'ÀĥƦ.txt')
+
+        self.__replace_content_disposition('inline; filename=fallback.txt; filename*=utf-8\'en-us\'ÀĥƦ.txt')
+        filename = parser.get_filename(self.stream, None, self.parser_context)
+        self.assertEqual(filename, 'ÀĥƦ.txt')
+
+        self.__replace_content_disposition('inline; filename=fallback.txt; filename*=utf-8--ÀĥƦ.txt')
+        filename = parser.get_filename(self.stream, None, self.parser_context)
+        self.assertEqual(filename, 'fallback.txt')
+
+    def __replace_content_disposition(self, disposition):
+        self.parser_context['request'].META['HTTP_CONTENT_DISPOSITION'] = disposition