diff -r 6641e941ef1e -r ff1a9aa48cfd app/django/http/__init__.py --- a/app/django/http/__init__.py Tue Oct 14 12:36:55 2008 +0000 +++ b/app/django/http/__init__.py Tue Oct 14 16:00:59 2008 +0000 @@ -1,4 +1,5 @@ import os +import re from Cookie import SimpleCookie, CookieError from pprint import pformat from urllib import urlencode @@ -9,13 +10,16 @@ except ImportError: from cgi import parse_qsl -from django.utils.datastructures import MultiValueDict, FileDict +from django.utils.datastructures import MultiValueDict, ImmutableList from django.utils.encoding import smart_str, iri_to_uri, force_unicode - +from django.http.multipartparser import MultiPartParser +from django.conf import settings +from django.core.files import uploadhandler from utils import * RESERVED_CHARS="!*'();:@&=+$,/?%#[]" +absolute_http_url_re = re.compile(r"^https?://", re.I) class Http404(Exception): pass @@ -25,10 +29,12 @@ # The encoding used in GET/POST dicts. None means use default setting. _encoding = None + _upload_handlers = [] def __init__(self): self.GET, self.POST, self.COOKIES, self.META, self.FILES = {}, {}, {}, {}, {} self.path = '' + self.path_info = '' self.method = None def __repr__(self): @@ -36,17 +42,6 @@ (pformat(self.GET), pformat(self.POST), pformat(self.COOKIES), pformat(self.META)) - def __getitem__(self, key): - for d in (self.POST, self.GET): - if key in d: - return d[key] - raise KeyError, "%s not found in either POST or GET" % key - - def has_key(self, key): - return key in self.GET or key in self.POST - - __contains__ = has_key - def get_host(self): """Returns the HTTP host using the environment or request headers.""" # We try three options, in order of decreasing preference. @@ -57,8 +52,8 @@ else: # Reconstruct the host using the algorithm from PEP 333. host = self.META['SERVER_NAME'] - server_port = self.META['SERVER_PORT'] - if server_port != (self.is_secure() and 443 or 80): + server_port = str(self.META['SERVER_PORT']) + if server_port != (self.is_secure() and '443' or '80'): host = '%s:%s' % (host, server_port) return host @@ -73,7 +68,7 @@ """ if not location: location = self.get_full_path() - if not ':' in location: + if not absolute_http_url_re.match(location): current_uri = '%s://%s%s' % (self.is_secure() and 'https' or 'http', self.get_host(), self.path) location = urljoin(current_uri, location) @@ -102,39 +97,31 @@ encoding = property(_get_encoding, _set_encoding) -def parse_file_upload(header_dict, post_data): - """Returns a tuple of (POST QueryDict, FILES MultiValueDict).""" - import email, email.Message - from cgi import parse_header - raw_message = '\r\n'.join(['%s:%s' % pair for pair in header_dict.items()]) - raw_message += '\r\n\r\n' + post_data - msg = email.message_from_string(raw_message) - POST = QueryDict('', mutable=True) - FILES = MultiValueDict() - for submessage in msg.get_payload(): - if submessage and isinstance(submessage, email.Message.Message): - name_dict = parse_header(submessage['Content-Disposition'])[1] - # name_dict is something like {'name': 'file', 'filename': 'test.txt'} for file uploads - # or {'name': 'blah'} for POST fields - # We assume all uploaded files have a 'filename' set. - if 'filename' in name_dict: - assert type([]) != type(submessage.get_payload()), "Nested MIME messages are not supported" - if not name_dict['filename'].strip(): - continue - # IE submits the full path, so trim everything but the basename. - # (We can't use os.path.basename because that uses the server's - # directory separator, which may not be the same as the - # client's one.) - filename = name_dict['filename'][name_dict['filename'].rfind("\\")+1:] - FILES.appendlist(name_dict['name'], FileDict({ - 'filename': filename, - 'content-type': 'Content-Type' in submessage and submessage['Content-Type'] or None, - 'content': submessage.get_payload(), - })) - else: - POST.appendlist(name_dict['name'], submessage.get_payload()) - return POST, FILES + def _initialize_handlers(self): + self._upload_handlers = [uploadhandler.load_handler(handler, self) + for handler in settings.FILE_UPLOAD_HANDLERS] + + def _set_upload_handlers(self, upload_handlers): + if hasattr(self, '_files'): + raise AttributeError("You cannot set the upload handlers after the upload has been processed.") + self._upload_handlers = upload_handlers + def _get_upload_handlers(self): + if not self._upload_handlers: + # If thre are no upload handlers defined, initialize them from settings. + self._initialize_handlers() + return self._upload_handlers + + upload_handlers = property(_get_upload_handlers, _set_upload_handlers) + + def parse_file_upload(self, META, post_data): + """Returns a tuple of (POST QueryDict, FILES MultiValueDict).""" + self.upload_handlers = ImmutableList( + self.upload_handlers, + warning = "You cannot alter upload handlers after the upload has been processed." + ) + parser = MultiPartParser(META, post_data, self.upload_handlers, self.encoding) + return parser.parse() class QueryDict(MultiValueDict): """ @@ -144,6 +131,11 @@ Values retrieved from this class are converted from the given encoding (DEFAULT_CHARSET by default) to unicode. """ + # These are both reset in __init__, but is specified here at the class + # level so that unpickling will have valid values + _mutable = True + _encoding = None + def __init__(self, query_string, mutable=False, encoding=None): MultiValueDict.__init__(self) if not encoding: @@ -152,12 +144,24 @@ from django.conf import settings encoding = settings.DEFAULT_CHARSET self.encoding = encoding - self._mutable = True for key, value in parse_qsl((query_string or ''), True): # keep_blank_values=True self.appendlist(force_unicode(key, encoding, errors='replace'), force_unicode(value, encoding, errors='replace')) self._mutable = mutable + def _get_encoding(self): + if self._encoding is None: + # *Important*: do not import settings at the module level because + # of the note in core.handlers.modpython. + from django.conf import settings + self._encoding = settings.DEFAULT_CHARSET + return self._encoding + + def _set_encoding(self, value): + self._encoding = value + + encoding = property(_get_encoding, _set_encoding) + def _assert_mutable(self): if not self._mutable: raise AttributeError("This QueryDict instance is immutable") @@ -207,8 +211,13 @@ def update(self, other_dict): self._assert_mutable() f = lambda s: str_to_unicode(s, self.encoding) - d = dict([(f(k), f(v)) for k, v in other_dict.items()]) - MultiValueDict.update(self, d) + if hasattr(other_dict, 'lists'): + for key, valuelist in other_dict.lists(): + for value in valuelist: + MultiValueDict.update(self, {f(key): f(value)}) + else: + d = dict([(f(k), f(v)) for k, v in other_dict.items()]) + MultiValueDict.update(self, d) def pop(self, key, *args): self._assert_mutable() @@ -448,3 +457,4 @@ return unicode(s, encoding, 'replace') else: return s +