Refs #33173 -- Removed use of deprecated cgi module.
https://peps.python.org/pep-0594/#cgi
This commit is contained in:
parent
02dbf1667c
commit
34e2148fc7
|
@ -1,5 +1,4 @@
|
||||||
import argparse
|
import argparse
|
||||||
import cgi
|
|
||||||
import mimetypes
|
import mimetypes
|
||||||
import os
|
import os
|
||||||
import posixpath
|
import posixpath
|
||||||
|
@ -15,6 +14,7 @@ from django.core.management.base import BaseCommand, CommandError
|
||||||
from django.core.management.utils import handle_extensions, run_formatters
|
from django.core.management.utils import handle_extensions, run_formatters
|
||||||
from django.template import Context, Engine
|
from django.template import Context, Engine
|
||||||
from django.utils import archive
|
from django.utils import archive
|
||||||
|
from django.utils.http import parse_header_parameters
|
||||||
from django.utils.version import get_docs_version
|
from django.utils.version import get_docs_version
|
||||||
|
|
||||||
|
|
||||||
|
@ -327,7 +327,7 @@ class TemplateCommand(BaseCommand):
|
||||||
# Trying to get better name from response headers
|
# Trying to get better name from response headers
|
||||||
content_disposition = headers["content-disposition"]
|
content_disposition = headers["content-disposition"]
|
||||||
if content_disposition:
|
if content_disposition:
|
||||||
_, params = cgi.parse_header(content_disposition)
|
_, params = parse_header_parameters(content_disposition)
|
||||||
guessed_filename = params.get("filename") or used_name
|
guessed_filename = params.get("filename") or used_name
|
||||||
else:
|
else:
|
||||||
guessed_filename = used_name
|
guessed_filename = used_name
|
||||||
|
|
|
@ -6,7 +6,6 @@ file upload handlers for processing.
|
||||||
"""
|
"""
|
||||||
import base64
|
import base64
|
||||||
import binascii
|
import binascii
|
||||||
import cgi
|
|
||||||
import collections
|
import collections
|
||||||
import html
|
import html
|
||||||
from urllib.parse import unquote
|
from urllib.parse import unquote
|
||||||
|
@ -20,6 +19,7 @@ from django.core.exceptions import (
|
||||||
from django.core.files.uploadhandler import SkipFile, StopFutureHandlers, StopUpload
|
from django.core.files.uploadhandler import SkipFile, StopFutureHandlers, StopUpload
|
||||||
from django.utils.datastructures import MultiValueDict
|
from django.utils.datastructures import MultiValueDict
|
||||||
from django.utils.encoding import force_str
|
from django.utils.encoding import force_str
|
||||||
|
from django.utils.regex_helper import _lazy_re_compile
|
||||||
|
|
||||||
__all__ = ("MultiPartParser", "MultiPartParserError", "InputStreamExhausted")
|
__all__ = ("MultiPartParser", "MultiPartParserError", "InputStreamExhausted")
|
||||||
|
|
||||||
|
@ -49,6 +49,8 @@ class MultiPartParser:
|
||||||
and returns a tuple of ``(MultiValueDict(POST), MultiValueDict(FILES))``.
|
and returns a tuple of ``(MultiValueDict(POST), MultiValueDict(FILES))``.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
boundary_re = _lazy_re_compile(rb"[ -~]{0,200}[!-~]")
|
||||||
|
|
||||||
def __init__(self, META, input_data, upload_handlers, encoding=None):
|
def __init__(self, META, input_data, upload_handlers, encoding=None):
|
||||||
"""
|
"""
|
||||||
Initialize the MultiPartParser object.
|
Initialize the MultiPartParser object.
|
||||||
|
@ -77,7 +79,7 @@ class MultiPartParser:
|
||||||
% force_str(content_type)
|
% force_str(content_type)
|
||||||
)
|
)
|
||||||
boundary = opts.get("boundary")
|
boundary = opts.get("boundary")
|
||||||
if not boundary or not cgi.valid_boundary(boundary):
|
if not boundary or not self.boundary_re.fullmatch(boundary):
|
||||||
raise MultiPartParserError(
|
raise MultiPartParserError(
|
||||||
"Invalid boundary in multipart: %s" % force_str(boundary)
|
"Invalid boundary in multipart: %s" % force_str(boundary)
|
||||||
)
|
)
|
||||||
|
|
|
@ -1,4 +1,3 @@
|
||||||
import cgi
|
|
||||||
import codecs
|
import codecs
|
||||||
import copy
|
import copy
|
||||||
from io import BytesIO
|
from io import BytesIO
|
||||||
|
@ -22,7 +21,7 @@ from django.utils.datastructures import (
|
||||||
)
|
)
|
||||||
from django.utils.encoding import escape_uri_path, iri_to_uri
|
from django.utils.encoding import escape_uri_path, iri_to_uri
|
||||||
from django.utils.functional import cached_property
|
from django.utils.functional import cached_property
|
||||||
from django.utils.http import is_same_domain
|
from django.utils.http import is_same_domain, parse_header_parameters
|
||||||
from django.utils.regex_helper import _lazy_re_compile
|
from django.utils.regex_helper import _lazy_re_compile
|
||||||
|
|
||||||
from .multipartparser import parse_header
|
from .multipartparser import parse_header
|
||||||
|
@ -97,7 +96,7 @@ class HttpRequest:
|
||||||
|
|
||||||
def _set_content_type_params(self, meta):
|
def _set_content_type_params(self, meta):
|
||||||
"""Set content_type, content_params, and encoding."""
|
"""Set content_type, content_params, and encoding."""
|
||||||
self.content_type, self.content_params = cgi.parse_header(
|
self.content_type, self.content_params = parse_header_parameters(
|
||||||
meta.get("CONTENT_TYPE", "")
|
meta.get("CONTENT_TYPE", "")
|
||||||
)
|
)
|
||||||
if "charset" in self.content_params:
|
if "charset" in self.content_params:
|
||||||
|
|
|
@ -366,3 +366,36 @@ def escape_leading_slashes(url):
|
||||||
if url.startswith("//"):
|
if url.startswith("//"):
|
||||||
url = "/%2F{}".format(url[2:])
|
url = "/%2F{}".format(url[2:])
|
||||||
return url
|
return url
|
||||||
|
|
||||||
|
|
||||||
|
def _parseparam(s):
|
||||||
|
while s[:1] == ";":
|
||||||
|
s = s[1:]
|
||||||
|
end = s.find(";")
|
||||||
|
while end > 0 and (s.count('"', 0, end) - s.count('\\"', 0, end)) % 2:
|
||||||
|
end = s.find(";", end + 1)
|
||||||
|
if end < 0:
|
||||||
|
end = len(s)
|
||||||
|
f = s[:end]
|
||||||
|
yield f.strip()
|
||||||
|
s = s[end:]
|
||||||
|
|
||||||
|
|
||||||
|
def parse_header_parameters(line):
|
||||||
|
"""
|
||||||
|
Parse a Content-type like header.
|
||||||
|
Return the main content-type and a dictionary of options.
|
||||||
|
"""
|
||||||
|
parts = _parseparam(";" + line)
|
||||||
|
key = parts.__next__()
|
||||||
|
pdict = {}
|
||||||
|
for p in parts:
|
||||||
|
i = p.find("=")
|
||||||
|
if i >= 0:
|
||||||
|
name = p[:i].strip().lower()
|
||||||
|
value = p[i + 1 :].strip()
|
||||||
|
if len(value) >= 2 and value[0] == value[-1] == '"':
|
||||||
|
value = value[1:-1]
|
||||||
|
value = value.replace("\\\\", "\\").replace('\\"', '"')
|
||||||
|
pdict[name] = value
|
||||||
|
return key, pdict
|
||||||
|
|
|
@ -12,6 +12,7 @@ from django.utils.http import (
|
||||||
int_to_base36,
|
int_to_base36,
|
||||||
is_same_domain,
|
is_same_domain,
|
||||||
parse_etags,
|
parse_etags,
|
||||||
|
parse_header_parameters,
|
||||||
parse_http_date,
|
parse_http_date,
|
||||||
quote_etag,
|
quote_etag,
|
||||||
url_has_allowed_host_and_scheme,
|
url_has_allowed_host_and_scheme,
|
||||||
|
@ -435,3 +436,39 @@ class EscapeLeadingSlashesTests(unittest.TestCase):
|
||||||
for url, expected in tests:
|
for url, expected in tests:
|
||||||
with self.subTest(url=url):
|
with self.subTest(url=url):
|
||||||
self.assertEqual(escape_leading_slashes(url), expected)
|
self.assertEqual(escape_leading_slashes(url), expected)
|
||||||
|
|
||||||
|
|
||||||
|
class ParseHeaderParameterTests(unittest.TestCase):
|
||||||
|
def test_basic(self):
|
||||||
|
tests = [
|
||||||
|
("text/plain", ("text/plain", {})),
|
||||||
|
("text/vnd.just.made.this.up ; ", ("text/vnd.just.made.this.up", {})),
|
||||||
|
("text/plain;charset=us-ascii", ("text/plain", {"charset": "us-ascii"})),
|
||||||
|
(
|
||||||
|
'text/plain ; charset="us-ascii"',
|
||||||
|
("text/plain", {"charset": "us-ascii"}),
|
||||||
|
),
|
||||||
|
(
|
||||||
|
'text/plain ; charset="us-ascii"; another=opt',
|
||||||
|
("text/plain", {"charset": "us-ascii", "another": "opt"}),
|
||||||
|
),
|
||||||
|
(
|
||||||
|
'attachment; filename="silly.txt"',
|
||||||
|
("attachment", {"filename": "silly.txt"}),
|
||||||
|
),
|
||||||
|
(
|
||||||
|
'attachment; filename="strange;name"',
|
||||||
|
("attachment", {"filename": "strange;name"}),
|
||||||
|
),
|
||||||
|
(
|
||||||
|
'attachment; filename="strange;name";size=123;',
|
||||||
|
("attachment", {"filename": "strange;name", "size": "123"}),
|
||||||
|
),
|
||||||
|
(
|
||||||
|
'form-data; name="files"; filename="fo\\"o;bar"',
|
||||||
|
("form-data", {"name": "files", "filename": 'fo"o;bar'}),
|
||||||
|
),
|
||||||
|
]
|
||||||
|
for header, expected in tests:
|
||||||
|
with self.subTest(header=header):
|
||||||
|
self.assertEqual(parse_header_parameters(header), expected)
|
||||||
|
|
Loading…
Reference in New Issue