def redirect(req, location, permanent=0, text=None): """ A convenience function to provide redirection """ if not isinstance(location, str): raise TypeError("location must be of type str") if req.sent_bodyct: raise IOError("Cannot redirect after headers have already been sent.") req.err_headers_out["Location"] = location if permanent: req.status = apache.HTTP_MOVED_PERMANENTLY else: req.status = apache.HTTP_MOVED_TEMPORARILY if text is None: req.write('<p>The document has moved' ' <a href="%s">here</a></p>\n' % location) else: req.write(text) raise apache.SERVER_RETURN(apache.DONE)
def __init__(self, req, keep_blank_values=0, strict_parsing=0, file_callback=None, field_callback=None): # # Whenever readline is called ALWAYS use the max size EVEN when # not expecting a long line. - this helps protect against # malformed content from exhausting memory. # self.list = FieldList() # always process GET-style parameters if req.args: pairs = parse_qsl(req.args, keep_blank_values) for pair in pairs: self.add_field(pair[0], pair[1]) if req.method != "POST": return try: clen = int(req.headers_in["content-length"]) except (KeyError, ValueError): # absent content-length is not acceptable raise apache.SERVER_RETURN(apache.HTTP_LENGTH_REQUIRED) if "content-type" not in req.headers_in: ctype = b"application/x-www-form-urlencoded" else: ctype = req.headers_in["content-type"].encode("latin1") if not isinstance(ctype, bytes): raise TypeError("ctype must be of type bytes") if ctype.startswith(b"application/x-www-form-urlencoded"): v = req.read(clen) if not isinstance(v, bytes): raise TypeError("req.read() must return bytes") pairs = parse_qsl(v, keep_blank_values) for pair in pairs: self.add_field(pair[0], pair[1]) return if not ctype.startswith(b"multipart/"): # we don't understand this content-type raise apache.SERVER_RETURN(apache.HTTP_NOT_IMPLEMENTED) # figure out boundary try: i = ctype.lower().rindex(b"boundary=") boundary = ctype[i + 9:] if len(boundary) >= 2 and boundary[:1] == boundary[-1:] == b'"': boundary = boundary[1:-1] boundary = re.compile(b"--" + re.escape(boundary) + b"(--)?\r?\n") except ValueError: raise apache.SERVER_RETURN(apache.HTTP_BAD_REQUEST) # read until boundary self.read_to_boundary(req, boundary, None) end_of_stream = False while not end_of_stream: ## parse headers ctype, type_options = b"text/plain", {} disp, disp_options = None, {} headers = apache.make_table() line = req.readline(readBlockSize) if not isinstance(line, bytes): raise TypeError("req.readline() must return bytes") match = boundary.match(line) if (not line) or match: # we stop if we reached the end of the stream or a stop # boundary (which means '--' after the boundary) we # continue to the next part if we reached a simple # boundary in either case this would mean the entity is # malformed, but we're tolerating it anyway. end_of_stream = (not line) or (match.group(1) is not None) continue skip_this_part = False while line not in (b'\r', b'\r\n'): nextline = req.readline(readBlockSize) while nextline and nextline[:1] in [b' ', b'\t']: line = line + nextline nextline = req.readline(readBlockSize) # we read the headers until we reach an empty line # NOTE : a single \n would mean the entity is malformed, but # we're tolerating it anyway h, v = line.split(b":", 1) headers.add( h, v) # mp_table accepts bytes, but always returns str h = h.lower() if h == b"content-disposition": disp, disp_options = parse_header(v) elif h == b"content-type": ctype, type_options = parse_header(v) # # NOTE: FIX up binary rubbish sent as content type # from Microsoft IE 6.0 when sending a file which # does not have a suffix. # if ctype.find(b'/') == -1: ctype = b'application/octet-stream' line = nextline match = boundary.match(line) if (not line) or match: # we stop if we reached the end of the stream or a # stop boundary (which means '--' after the # boundary) we continue to the next part if we # reached a simple boundary in either case this # would mean the entity is malformed, but we're # tolerating it anyway. skip_this_part = True end_of_stream = (not line) or (match.group(1) is not None) break if skip_this_part: continue if b"name" in disp_options: name = disp_options[b"name"] else: name = None # create a file object # is this a file? filename = None if b"filename" in disp_options: filename = disp_options[b"filename"] if file_callback and isinstance(file_callback, collections.Callable): file = file_callback(filename) else: file = tempfile.TemporaryFile("w+b") else: if field_callback and isinstance(field_callback, collections.Callable): file = field_callback() else: file = BytesIO() # read it in self.read_to_boundary(req, boundary, file) file.seek(0) # make a Field if filename: field = Field(name) field.filename = filename else: field = StringField(file.read()) field.name = name field.file = file field.type = PY2 and ctype or ctype.decode('latin1') field.type_options = type_options field.disposition = PY2 and disp or disp.decode('latin1') field.disposition_options = disp_options field.headers = headers self.list.append(field)