formparser.py (7606B)
1 #Embedded file name: C:\BuildAgent\work\20d73f1b24eed06e\eve\release\V24.01\carbon\common\lib\werkzeug\formparser.py 2 import re 3 from cStringIO import StringIO 4 from tempfile import TemporaryFile 5 from itertools import chain, repeat 6 from werkzeug._internal import _decode_unicode, _empty_stream 7 _empty_string_iter = repeat('') 8 _multipart_boundary_re = re.compile('^[ -~]{0,200}[!-~]$') 9 _supported_multipart_encodings = frozenset(['base64', 'quoted-printable']) 10 11 def default_stream_factory(total_content_length, filename, content_type, content_length = None): 12 if total_content_length > 512000: 13 return TemporaryFile('wb+') 14 return StringIO() 15 16 17 def parse_form_data(environ, stream_factory = None, charset = 'utf-8', errors = 'ignore', max_form_memory_size = None, max_content_length = None, cls = None, silent = True): 18 content_type, extra = parse_options_header(environ.get('CONTENT_TYPE', '')) 19 try: 20 content_length = int(environ['CONTENT_LENGTH']) 21 except (KeyError, ValueError): 22 content_length = 0 23 24 if cls is None: 25 cls = MultiDict 26 if max_content_length is not None and content_length > max_content_length: 27 raise RequestEntityTooLarge() 28 stream = _empty_stream 29 files = () 30 if content_type == 'multipart/form-data': 31 try: 32 form, files = parse_multipart(environ['wsgi.input'], extra.get('boundary'), content_length, stream_factory, charset, errors, max_form_memory_size=max_form_memory_size) 33 except ValueError as e: 34 if not silent: 35 raise 36 form = cls() 37 else: 38 form = cls(form) 39 40 elif content_type == 'application/x-www-form-urlencoded' or content_type == 'application/x-url-encoded': 41 if max_form_memory_size is not None and content_length > max_form_memory_size: 42 raise RequestEntityTooLarge() 43 form = url_decode(environ['wsgi.input'].read(content_length), charset, errors=errors, cls=cls) 44 else: 45 form = cls() 46 stream = LimitedStream(environ['wsgi.input'], content_length) 47 return (stream, form, cls(files)) 48 49 50 def _fix_ie_filename(filename): 51 if filename[1:3] == ':\\' or filename[:2] == '\\\\': 52 return filename.split('\\')[-1] 53 return filename 54 55 56 def _line_parse(line): 57 if line[-2:] == '\r\n': 58 return (line[:-2], True) 59 if line[-1:] in '\r\n': 60 return (line[:-1], True) 61 return (line, False) 62 63 64 def _find_terminator(iterator): 65 for line in iterator: 66 if not line: 67 break 68 line = line.strip() 69 if line: 70 return line 71 72 return '' 73 74 75 def is_valid_multipart_boundary(boundary): 76 return _multipart_boundary_re.match(boundary) is not None 77 78 79 def parse_multipart(file, boundary, content_length, stream_factory = None, charset = 'utf-8', errors = 'ignore', buffer_size = 10240, max_form_memory_size = None): 80 if stream_factory is None: 81 stream_factory = default_stream_factory 82 if not boundary: 83 raise ValueError('Missing boundary') 84 if not is_valid_multipart_boundary(boundary): 85 raise ValueError('Invalid boundary: %s' % boundary) 86 if len(boundary) > buffer_size: 87 raise ValueError('Boundary longer than buffer size') 88 total_content_length = content_length 89 next_part = '--' + boundary 90 last_part = next_part + '--' 91 form = [] 92 files = [] 93 in_memory = 0 94 file = LimitedStream(file, content_length) 95 iterator = chain(make_line_iter(file, buffer_size=buffer_size), _empty_string_iter) 96 try: 97 terminator = _find_terminator(iterator) 98 if terminator != next_part: 99 raise ValueError('Expected boundary at start of multipart data') 100 while terminator != last_part: 101 headers = parse_multipart_headers(iterator) 102 disposition = headers.get('content-disposition') 103 if disposition is None: 104 raise ValueError('Missing Content-Disposition header') 105 disposition, extra = parse_options_header(disposition) 106 name = extra.get('name') 107 transfer_encoding = headers.get('content-transfer-encoding') 108 try_decode = transfer_encoding is not None and transfer_encoding in _supported_multipart_encodings 109 filename = extra.get('filename') 110 if filename is None: 111 is_file = False 112 container = [] 113 _write = container.append 114 guard_memory = max_form_memory_size is not None 115 else: 116 content_type = headers.get('content-type') 117 content_type = parse_options_header(content_type)[0] or 'text/plain' 118 is_file = True 119 guard_memory = False 120 if filename is not None: 121 filename = _fix_ie_filename(_decode_unicode(filename, charset, errors)) 122 try: 123 content_length = int(headers['content-length']) 124 except (KeyError, ValueError): 125 content_length = 0 126 127 container = stream_factory(total_content_length, content_type, filename, content_length) 128 _write = container.write 129 buf = '' 130 for line in iterator: 131 if not line: 132 raise ValueError('unexpected end of stream') 133 if line[:2] == '--': 134 terminator = line.rstrip() 135 if terminator in (next_part, last_part): 136 break 137 if try_decode: 138 try: 139 line = line.decode(transfer_encoding) 140 except: 141 raise ValueError('could not decode transfer encoded chunk') 142 143 if buf: 144 _write(buf) 145 buf = '' 146 if line[-2:] == '\r\n': 147 buf = '\r\n' 148 cutoff = -2 149 else: 150 buf = line[-1] 151 cutoff = -1 152 _write(line[:cutoff]) 153 if guard_memory: 154 in_memory += len(line) 155 if in_memory > max_form_memory_size: 156 from werkzeug.exceptions import RequestEntityTooLarge 157 raise RequestEntityTooLarge() 158 else: 159 raise ValueError('unexpected end of part') 160 161 if is_file: 162 container.seek(0) 163 files.append((name, FileStorage(container, filename, name, content_type, content_length, headers))) 164 else: 165 form.append((name, _decode_unicode(''.join(container), charset, errors))) 166 167 finally: 168 file.exhaust() 169 170 return (form, files) 171 172 173 def parse_multipart_headers(iterable): 174 result = [] 175 for line in iterable: 176 line, line_terminated = _line_parse(line) 177 if not line_terminated: 178 raise ValueError('unexpected end of line in multipart header') 179 if not line: 180 break 181 elif line[0] in ' \t' and result: 182 key, value = result[-1] 183 result[-1] = (key, value + '\n ' + line[1:]) 184 else: 185 parts = line.split(':', 1) 186 if len(parts) == 2: 187 result.append((parts[0].strip(), parts[1].strip())) 188 189 return Headers.linked(result) 190 191 192 from werkzeug.urls import url_decode 193 from werkzeug.wsgi import LimitedStream, make_line_iter 194 from werkzeug.exceptions import RequestEntityTooLarge 195 from werkzeug.datastructures import Headers, FileStorage, MultiDict 196 from werkzeug.http import parse_options_header