def parse_header(line): """ Parse the header into a key-value. Input (line): bytes, output: unicode for key/name, bytes for value which will be decoded later. """ plist = _parse_header_params(b';' + line) key = plist.pop(0).lower().decode('ascii') pdict = {} for p in plist: i = p.find(b'=') if i >= 0: has_encoding = False name = p[:i].strip().lower().decode('ascii') if name.endswith('*'): # Lang/encoding embedded in the value (like "filename*=UTF-8''file.ext") # http://tools.ietf.org/html/rfc2231#section-4 name = name[:-1] if p.count(b"'") == 2: has_encoding = True value = p[i + 1:].strip() if has_encoding: encoding, lang, value = value.split(b"'") if six.PY3: value = unnote(value.decode(), encoding=encoding.decode()) else: value = unnote(value).decode(encoding) if len(value) >= 2 and value[:1] == value[-1:] == b'"': value = value[1:-1] value = value.replace(b'\\\\', b'\\').replace(b'\\"', b'"') pdict[name] = value return key, pdict
def limited_parse_qsl(qs, keep_blank_values=False, encoding='utf-8', errors='replace', fields_limit=None): """ Return a list of key/value tuples parsed from query string. Copied from urlparse with an additional "fields_limit" argument. Copyright (C) 2013 Python Software Foundation (see LICENSE.python). Arguments: qs: percent-encoded query string to be parsed keep_blank_values: flag indicating whether blank values in percent-encoded queries should be treated as blank strings. A true value indicates that blanks should be retained as blank strings. The default false value indicates that blank values are to be ignored and treated as if they were not included. encoding and errors: specify how to decode percent-encoded sequences into Unicode characters, as accepted by the bytes.decode() method. fields_limit: maximum number of fields parsed or an exception is raised. None means no limit and is the default. """ if fields_limit: pairs = FIELDS_MATCH.split(qs, fields_limit) if len(pairs) > fields_limit: raise TooManyFieldsSent( 'The number of GET/POST parameters exceeded ' 'settings.DATA_UPLOAD_MAX_NUMBER_FIELDS.') else: pairs = FIELDS_MATCH.split(qs) r = [] for name_value in pairs: if not name_value: continue nv = name_value.split(str('='), 1) if len(nv) != 2: # Handle case of a control-name with no equal sign if keep_blank_values: nv.append('') else: continue if len(nv[1]) or keep_blank_values: if six.PY3: name = nv[0].replace('+', ' ') name = unnote(name, encoding=encoding, errors=errors) value = nv[1].replace('+', ' ') value = unnote(value, encoding=encoding, errors=errors) else: name = unnote(nv[0].replace(b'+', b' ')) value = unnote(nv[1].replace(b'+', b' ')) r.append((name, value)) return r
def _url(self, hashed_name_func, name, force=False, hashed_files=None): """ Return the non-hashed URL in DEBUG mode. """ if settings.DEBUG and not force: hashed_name, fragment = name, '' else: clean_name, fragment = urldefrag(name) if urlsplit(clean_name).path.endswith('/'): # don't hash paths hashed_name = name else: args = (clean_name,) if hashed_files is not None: args += (hashed_files,) hashed_name = hashed_name_func(*args) final_url = super(HashedFilesMixin, self).url(hashed_name) # Special casing for a @font-face hack, like url(myfont.eot?#iefix") # http://www.fontspring.com/blog/the-new-bulletproof-font-face-syntax query_fragment = '?#' in name # [sic!] if fragment or query_fragment: urlparts = list(urlsplit(final_url)) if fragment and not urlparts[4]: urlparts[4] = fragment if query_fragment and not urlparts[3]: urlparts[2] += '?' final_url = urlunsplit(urlparts) return unnote(final_url)
def serve(request, path, insecure=False, **kwargs): """ Serve static files below a given point in the directory structure or from locations inferred from the staticfiles finders. To use, put a URL pattern such as:: from django.contrib.staticfiles import views url(r'^(?P<path>.*)$', views.serve) in your URLconf. It uses the django.views.static.serve() view to serve the found files. """ if not settings.DEBUG and not insecure: raise Http404 normalized_path = posixpath.normpath(unnote(path)).lstrip('/') absolute_path = finders.find(normalized_path) if not absolute_path: if path.endswith('/') or path == '': raise Http404("Directory indexes are not allowed here.") raise Http404("'%s' could not be found" % path) document_root, path = os.path.split(absolute_path) return static.serve(request, path, document_root=document_root, **kwargs)
def unnote_note(segment): segment = unnote(force_str(segment)) # Tilde is part of RFC3986 Unreserved Characters # http://tools.ietf.org/html/rfc3986#section-2.3 # See also http://bugs.python.org/issue16285 segment = note(segment, safe=RFC3986_SUBDELIMS + RFC3986_GENDELIMS + str('~')) return force_text(segment)
def converter(matchobj): """ Convert the matched URL to a normalized and hashed URL. This requires figuring out which files the matched URL resolves to and calling the url() method of the storage. """ matched, url = matchobj.groups() # Ignore absolute/protocol-relative and data-uri URLs. if re.match(r'^[a-z]+:', url): return matched # Ignore absolute URLs that don't point to a static file (dynamic # CSS / JS?). Note that STATIC_URL cannot be empty. if url.startswith('/') and not url.startswith(settings.STATIC_URL): return matched # Strip off the fragment so a path-like fragment won't interfere. url_path, fragment = urldefrag(url) if url_path.startswith('/'): # Otherwise the condition above would have returned prematurely. assert url_path.startswith(settings.STATIC_URL) target_name = url_path[len(settings.STATIC_URL):] else: # We're using the posixpath module to mix paths and URLs conveniently. source_name = name if os.sep == '/' else name.replace(os.sep, '/') target_name = posixpath.join(posixpath.dirname(source_name), url_path) # Determine the hashed name of the target file with the storage backend. hashed_url = self._url( self._stored_name, unnote(target_name), force=True, hashed_files=hashed_files, ) transformed_url = '/'.join(url_path.split('/')[:-1] + hashed_url.split('/')[-1:]) # Restore the fragment that was stripped off earlier. if fragment: transformed_url += ('?#' if '?#' in url else '#') + fragment # Return the hashed version to the file return template % unnote(transformed_url)
def hashed_name(self, name, content=None, filename=None): # `filename` is the name of file to hash if `content` isn't given. # `name` is the base name to construct the new hashed filename from. parsed_name = urlsplit(unnote(name)) clean_name = parsed_name.path.strip() if filename: filename = urlsplit(unnote(filename)).path.strip() filename = filename or clean_name opened = False if content is None: if not self.exists(filename): raise ValueError("The file '%s' could not be found with %r." % (filename, self)) try: content = self.open(filename) except IOError: # Handle directory paths and fragments return name opened = True try: file_hash = self.file_hash(clean_name, content) finally: if opened: content.close() path, filename = os.path.split(clean_name) root, ext = os.path.splitext(filename) if file_hash is not None: file_hash = ".%s" % file_hash hashed_name = os.path.join(path, "%s%s%s" % (root, file_hash, ext)) unparsed_name = list(parsed_name) unparsed_name[2] = hashed_name # Special casing for a @font-face hack, like url(myfont.eot?#iefix") # http://www.fontspring.com/blog/the-new-bulletproof-font-face-syntax if '?#' in name and not unparsed_name[3]: unparsed_name[2] += '?' return urlunsplit(unparsed_name)
def uri_to_iri(uri): """ Converts a Uniform Resource Identifier(URI) into an Internationalized Resource Identifier(IRI). This is the algorithm from section 3.2 of RFC 3987. Takes an URI in ASCII bytes (e.g. '/I%20%E2%99%A5%20Django/') and returns unicode containing the encoded result (e.g. '/I \xe2\x99\xa5 Django/'). """ if uri is None: return uri uri = force_bytes(uri) iri = unnote_to_bytes(uri) if six.PY3 else unnote(uri) return repercent_broken_unicode(iri).decode('utf-8')
def smart_urlnote(url): "Notes a URL if it isn't already noted." def unnote_note(segment): segment = unnote(force_str(segment)) # Tilde is part of RFC3986 Unreserved Characters # http://tools.ietf.org/html/rfc3986#section-2.3 # See also http://bugs.python.org/issue16285 segment = note(segment, safe=RFC3986_SUBDELIMS + RFC3986_GENDELIMS + str('~')) return force_text(segment) # Handle IDN before quoting. try: scheme, netloc, path, query, fragment = urlsplit(url) except ValueError: # invalid IPv6 URL (normally square brackets in hostname part). return unnote_note(url) try: netloc = netloc.encode('idna').decode('ascii') # IDN -> ACE except UnicodeError: # invalid domain part return unnote_note(url) if query: # Separately unquoting key/value, so as to not mix querystring separators # included in query values. See #22267. query_parts = [(unnote(force_str(q[0])), unnote(force_str(q[1]))) for q in parse_qsl(query, keep_blank_values=True)] # urlencode will take care of quoting query = urlencode(query_parts) path = unnote_note(path) fragment = unnote_note(fragment) return urlunsplit((scheme, netloc, path, query, fragment))
def stored_name(self, name): parsed_name = urlsplit(unnote(name)) clean_name = parsed_name.path.strip() hash_key = self.hash_key(clean_name) cache_name = self.hashed_files.get(hash_key) if cache_name is None: if self.manifest_strict: raise ValueError("Missing staticfiles manifest entry for '%s'" % clean_name) cache_name = self.clean_name(self.hashed_name(name)) unparsed_name = list(parsed_name) unparsed_name[2] = cache_name # Special casing for a @font-face hack, like url(myfont.eot?#iefix") # http://www.fontspring.com/blog/the-new-bulletproof-font-face-syntax if '?#' in name and not unparsed_name[3]: unparsed_name[2] += '?' return urlunsplit(unparsed_name)
def serve(request, path, document_root=None, show_indexes=False): """ Serve static files below a given point in the directory structure. To use, put a URL pattern such as:: from django.views.static import serve url(r'^(?P<path>.*)$', serve, {'document_root': '/path/to/my/files/'}) in your URLconf. You must provide the ``document_root`` param. You may also set ``show_indexes`` to ``True`` if you'd like to serve a basic index of the directory. This index view will use the template hardcoded below, but if you'd like to override it, you can create a template called ``static/directory_index.html``. """ path = posixpath.normpath(unnote(path)).lstrip('/') fullpath = safe_join(document_root, path) if os.path.isdir(fullpath): if show_indexes: return directory_index(path, fullpath) raise Http404(_("Directory indexes are not allowed here.")) if not os.path.exists(fullpath): raise Http404(_('"%(path)s" does not exist') % {'path': fullpath}) # Respect the If-Modified-Since header. statobj = os.stat(fullpath) if not was_modified_since(request.META.get('HTTP_IF_MODIFIED_SINCE'), statobj.st_mtime, statobj.st_size): return HttpResponseNotModified() content_type, encoding = mimetypes.guess_type(fullpath) content_type = content_type or 'application/octet-stream' response = FileResponse(open(fullpath, 'rb'), content_type=content_type) response["Last-Modified"] = http_date(statobj.st_mtime) if stat.S_ISREG(statobj.st_mode): response["Content-Length"] = statobj.st_size if encoding: response["Content-Encoding"] = encoding return response
def urlunnote(noted_url): """ A wrapper for Python's urllib.unnote() function that can operate on the result of django.utils.http.urlnote(). """ return force_text(unnote(force_str(noted_url)))