Code

Ticket #1484: 1484.m-r.4.diff

File 1484.m-r.4.diff, 6.4 KB (added by Maniac <Maniac@…>, 8 years ago)

Patch 4 for magic-removal

Line 
1Index: django/http/__init__.py
2===================================================================
3--- django/http/__init__.py     (revision 2623)
4+++ django/http/__init__.py     (working copy)
5@@ -2,6 +2,8 @@
6 from pprint import pformat
7 from urllib import urlencode
8 from django.utils.datastructures import MultiValueDict
9+import cgi
10+from StringIO import StringIO
11 
12 try:
13     # The mod_python version is more efficient, so try importing it first.
14@@ -35,35 +37,60 @@
15     def get_full_path(self):
16         return ''
17 
18-def parse_file_upload(header_dict, post_data):
19+class FileDict(dict):
20+    "Keeps uploaded file as file-like object and reads its content on demand"
21+    def __getitem__(self, name):
22+        if name=='content' and not 'content' in self:
23+            self['file'].seek(0, 2)
24+            size = self['file'].tell()
25+            self['file'].seek(0, 0)
26+            self['content']=self['file'].read(size)
27+        return dict.__getitem__(self, name)
28+
29+    def __deepcopy__(self, memo={}):
30+        self['content'] # make sure file content is loaded
31+        import copy
32+        result = self.__class__()
33+        memo[id(self)] = result
34+        for key, value in dict.items(self):
35+            dict.__setitem__(result, copy.deepcopy(key, memo), copy.deepcopy(value, memo))
36+        return result
37+
38+class FieldStorage(cgi.FieldStorage):
39+    "cgi.FieldStorage with ability to store files on disk or in memory"
40+    def make_file(self, binary=None):
41+        from django.conf.settings import STORE_UPLOAD_ON_DISK
42+        if STORE_UPLOAD_ON_DISK:
43+            return cgi.FieldStorage.make_file(self, binary)
44+        else:
45+            return StringIO()
46+
47+def parse_file_upload(post_stream, environ):
48     "Returns a tuple of (POST MultiValueDict, FILES MultiValueDict)"
49-    import email, email.Message
50-    from cgi import parse_header
51-    raw_message = '\r\n'.join(['%s:%s' % pair for pair in header_dict.items()])
52-    raw_message += '\r\n\r\n' + post_data
53-    msg = email.message_from_string(raw_message)
54+    fs = FieldStorage(post_stream, environ=environ)
55     POST = MultiValueDict()
56     FILES = MultiValueDict()
57-    for submessage in msg.get_payload():
58-        if isinstance(submessage, email.Message.Message):
59-            name_dict = parse_header(submessage['Content-Disposition'])[1]
60-            # name_dict is something like {'name': 'file', 'filename': 'test.txt'} for file uploads
61-            # or {'name': 'blah'} for POST fields
62-            # We assume all uploaded files have a 'filename' set.
63-            if name_dict.has_key('filename'):
64-                assert type([]) != type(submessage.get_payload()), "Nested MIME messages are not supported"
65-                if not name_dict['filename'].strip():
66+    for key in fs.keys():
67+        # We can't use FieldStorage.getlist to get contents of a
68+        # field as list because for file fields it returns only filenames
69+        if type(fs[key]) == type([]):
70+            field_list = fs[key]
71+        else:
72+            field_list = [fs[key]]
73+        for field in field_list:
74+            if hasattr(field, 'filename') and field.filename is not None:
75+                if not field.filename.strip():
76                     continue
77                 # IE submits the full path, so trim everything but the basename.
78                 # (We can't use os.path.basename because it expects Linux paths.)
79-                filename = name_dict['filename'][name_dict['filename'].rfind("\\")+1:]
80-                FILES.appendlist(name_dict['name'], {
81+                filename = field.filename[field.filename.rfind("\\")+1:]
82+                FILES.appendlist(key, FileDict({
83                     'filename': filename,
84-                    'content-type': (submessage.has_key('Content-Type') and submessage['Content-Type'] or None),
85-                    'content': submessage.get_payload(),
86-                })
87+                    'content-type': field.type,
88+                    'file': field.file,
89+                }))
90             else:
91-                POST.appendlist(name_dict['name'], submessage.get_payload())
92+                POST.appendlist(key, field.value)
93     return POST, FILES
94 
95 class QueryDict(MultiValueDict):
96Index: django/conf/global_settings.py
97===================================================================
98--- django/conf/global_settings.py      (revision 2623)
99+++ django/conf/global_settings.py      (working copy)
100@@ -204,6 +204,10 @@
101 # Hint: you really don't!
102 TRANSACTIONS_MANAGED = False
103 
104+# Whether to store uploaded files in temp files rather than in memory.
105+# Storing files on disk may be necessary for accepting large files.
106+STORE_UPLOAD_ON_DISK = False
107+
108 ##############
109 # MIDDLEWARE #
110 ##############
111Index: django/core/handlers/wsgi.py
112===================================================================
113--- django/core/handlers/wsgi.py        (revision 2623)
114+++ django/core/handlers/wsgi.py        (working copy)
115@@ -69,9 +69,7 @@
116         # Populates self._post and self._files
117         if self.environ['REQUEST_METHOD'] == 'POST':
118             if self.environ.get('CONTENT_TYPE', '').startswith('multipart'):
119-                header_dict = dict([(k, v) for k, v in self.environ.items() if k.startswith('HTTP_')])
120-                header_dict['Content-Type'] = self.environ.get('CONTENT_TYPE', '')
121-                self._post, self._files = http.parse_file_upload(header_dict, self.raw_post_data)
122+                self._post, self._files = http.parse_file_upload(self.environ['wsgi.input'], self.environ)
123             else:
124                 self._post, self._files = http.QueryDict(self.raw_post_data), datastructures.MultiValueDict()
125         else:
126Index: django/core/handlers/modpython.py
127===================================================================
128--- django/core/handlers/modpython.py   (revision 2623)
129+++ django/core/handlers/modpython.py   (working copy)
130@@ -26,7 +26,10 @@
131     def _load_post_and_files(self):
132         "Populates self._post and self._files"
133         if self._req.headers_in.has_key('content-type') and self._req.headers_in['content-type'].startswith('multipart'):
134-            self._post, self._files = http.parse_file_upload(self._req.headers_in, self.raw_post_data)
135+            environ = dict(self.META)
136+            environ['CONTENT_LENGTH'] = environ['HTTP_CONTENT_LENGTH']
137+            environ['CONTENT_TYPE'] = environ['HTTP_CONTENT_TYPE']
138+            self._post, self._files = http.parse_file_upload(self._req, environ)
139         else:
140             self._post, self._files = http.QueryDict(self.raw_post_data), datastructures.MultiValueDict()
141