Ticket #8593: 8593-dont-lowercase-uploaded-filenames-r10727.diff

File 8593-dont-lowercase-uploaded-filenames-r10727.diff, 7.3 KB (added by Ramiro Morales, 10 years ago)

Updated patch so it applies after file_storage tests switch from doctests to unit tests in r10707

  • django/utils/_os.py

    diff --git a/django/utils/_os.py b/django/utils/_os.py
    a b  
    2121            path = join(os.getcwdu(), path)
    2222        return normpath(path)
    2323
     24if os.name == 'nt':
     25    _normcase = lambda s: s.replace("/", "\\")
     26else:
     27    _normcase = normcase
     28
    2429def safe_join(base, *paths):
    2530    """
    2631    Joins one or more path components to the base path component intelligently.
     
    2934    The final path must be located inside of the base path component (otherwise
    3035    a ValueError is raised).
    3136    """
    32     # We need to use normcase to ensure we don't false-negative on case
    33     # insensitive operating systems (like Windows).
    3437    base = force_unicode(base)
    3538    paths = [force_unicode(p) for p in paths]
    36     final_path = normcase(abspathu(join(base, *paths)))
    37     base_path = normcase(abspathu(base))
     39    final_path = abspathu(join(base, *paths))
     40    fp = normcase(final_path)
     41    final_path = _normcase(final_path)
     42
     43    base_path = abspathu(base)
     44    bp = normcase(base_path)
     45    base_path = _normcase(base_path)
     46
    3847    base_path_len = len(base_path)
    3948    # Ensure final_path starts with base_path and that the next character after
    40     # the final path is os.sep (or nothing, in which case final_path must be
    41     # equal to base_path).
    42     if not final_path.startswith(base_path) \
    43        or final_path[base_path_len:base_path_len+1] not in ('', sep):
     49    # the base path portion of final_path is os.sep (or nothing, in which case
     50    # final_path must be equal to base_path).
     51    # We need to use normcase in these checks to ensure we don't false-negative
     52    # on case insensitive operating systems (like Windows).
     53    if not fp.startswith(bp) \
     54       or fp[base_path_len:base_path_len+1] not in ('', sep):
    4455        raise ValueError('the joined path is located outside of the base path'
    4556                         ' component')
    4657    return final_path
  • tests/regressiontests/file_storage/tests.py

    diff --git a/tests/regressiontests/file_storage/tests.py b/tests/regressiontests/file_storage/tests.py
    a b  
    3232        self.temp_dir = tempfile.mktemp()
    3333        os.makedirs(self.temp_dir)
    3434        self.storage = self.storage_class(location=self.temp_dir)
    35    
     35
     36        # Set up a second temporary directory with a unique mixed case name
     37        self.temp_dir2 = tempfile.mktemp()
     38        dn, bn = os.path.split(self.temp_dir2)
     39        bn = bn[0].swapcase() + bn[1:-1] + bn[-1].swapcase()
     40        self.temp_dir2 = os.path.join(dn, bn)
     41        os.makedirs(self.temp_dir2)
     42
    3643    def tearDown(self):
    3744        os.rmdir(self.temp_dir)
    38        
     45        os.rmdir(self.temp_dir2)
     46
    3947    def test_file_access_options(self):
    4048        """
    4149        Standard file access options are available, and work as expected.
     
    6169        self.assertRaises(SuspiciousOperation, self.storage.exists, '..')
    6270        self.assertRaises(SuspiciousOperation, self.storage.exists, '/etc/passwd')
    6371
     72    def test_file_storage_preservers_filename_case(self):
     73        """The storage backend should preserve case of filenames."""
     74        # Create a storage backend associated with the mixed case name directory
     75        temp_storage = self.storage_class(location=self.temp_dir2)
     76        # Ask that storage backend to store a file with a mixed case filename
     77        mixed_case = 'CaSe_SeNsItIvE'
     78        file = temp_storage.open(mixed_case, 'w')
     79        file.write('storage contents')
     80        file.close()
     81        self.assertEqual(os.path.join(self.temp_dir2, mixed_case), temp_storage.path(mixed_case))
     82        temp_storage.delete(mixed_case)
     83
    6484class CustomStorage(FileSystemStorage):
    6585    def get_available_name(self, name):
    6686        """
  • tests/regressiontests/file_uploads/tests.py

    diff --git a/tests/regressiontests/file_uploads/tests.py b/tests/regressiontests/file_uploads/tests.py
    a b  
    11#! -*- coding: utf-8 -*-
    22import os
     3import os.path
    34import errno
    45import shutil
    56import unittest
     
    251252            # CustomUploadError is the error that should have been raised
    252253            self.assertEqual(err.__class__, uploadhandler.CustomUploadError)
    253254
     255    def test_filename_case_preservation(self):
     256        """
     257        The storage backend shouldn't mess with the case of the filenames
     258        uploaded.
     259        """
     260        # Synthetize the contents of a file upload with a mixed
     261        # case filename so we don't have to carry such a file
     262        # in the Django tests source code tree
     263        OUR_BOUNDARY='oUrBoUnDaRyStRiNg'
     264        post_data = [
     265                '--%s' % OUR_BOUNDARY,
     266                'Content-Disposition: form-data; name="file_field"; filename="MiXeD_cAsE.txt"',
     267                'Content-Type: application/octet-stream',
     268                '',
     269                'file contents\n'
     270                '',
     271                '--%s--\r\n' % OUR_BOUNDARY,
     272                ]
     273        response = self.client.post('/file_uploads/filename_case/', '\r\n'.join(post_data),
     274                'multipart/form-data; boundary=%s' % OUR_BOUNDARY)
     275        self.assertEqual(response.status_code, 200)
     276        id = int(response.content)
     277        obj = FileModel.objects.get(pk=id)
     278        # The name of the file uploaded and the file stored in the server-side
     279        # shouldn't differ
     280        self.assertEqual(os.path.basename(obj.testfile.path), 'MiXeD_cAsE.txt')
     281
    254282class DirectoryCreationTests(unittest.TestCase):
    255283    """
    256284    Tests for error handling during directory creation
  • tests/regressiontests/file_uploads/urls.py

    diff --git a/tests/regressiontests/file_uploads/urls.py b/tests/regressiontests/file_uploads/urls.py
    a b  
    1010    (r'^quota/broken/$',    views.file_upload_quota_broken),
    1111    (r'^getlist_count/$',   views.file_upload_getlist_count),
    1212    (r'^upload_errors/$',   views.file_upload_errors),
     13    (r'^filename_case/$',   views.file_upload_filename_case_view),
    1314)
  • tests/regressiontests/file_uploads/views.py

    diff --git a/tests/regressiontests/file_uploads/views.py b/tests/regressiontests/file_uploads/views.py
    a b  
    111111def file_upload_errors(request):
    112112    request.upload_handlers.insert(0, ErroringUploadHandler())
    113113    return file_upload_echo(request)
     114
     115def file_upload_filename_case_view(request):
     116    # Adding the file to the database should succeed
     117    file = request.FILES['file_field']
     118    obj = FileModel()
     119    obj.testfile.save(file.name, file)
     120
     121    return HttpResponse('%d' % obj.pk)
     122
  • tests/regressiontests/templates/tests.py

    diff --git a/tests/regressiontests/templates/tests.py b/tests/regressiontests/templates/tests.py
    a b  
    146146        if os.path.normcase('/TEST') == os.path.normpath('/test'):
    147147            template_dirs = ['/dir1', '/DIR2']
    148148            test_template_sources('index.html', template_dirs,
    149                                   ['/dir1/index.html', '/dir2/index.html'])
     149                                  ['/dir1/index.html', '/DIR2/index.html'])
    150150            test_template_sources('/DIR1/index.HTML', template_dirs,
    151                                   ['/dir1/index.html'])
     151                                  ['/DIR1/index.HTML'])
    152152
    153153    def test_token_smart_split(self):
    154154        # Regression test for #7027
Back to Top