Exemple #1
0
def _decode_string(s, charset):
    try:
        return s.decode(charset)
    except LookupError:
        raise RequestError('unknown charset %r' % charset)
    except UnicodeDecodeError:
        raise RequestError('invalid %r encoded string' % charset)
Exemple #2
0
 def _process_multipart_body(self, mimeinput, charset):
     headers = io.BytesIO()
     lines = mimeinput.readpart()
     for line in lines:
         headers.write(line)
         if line == b'\r\n':
             break
     headers.seek(0)
     headers = email.message_from_binary_file(headers)
     ctype, ctype_params = parse_header(headers.get('content-type', ''))
     if ctype and 'charset' in ctype_params:
         charset = ctype_params['charset']
     cdisp, cdisp_params = parse_header(
         headers.get('content-disposition', ''))
     if not cdisp:
         raise RequestError('expected Content-Disposition header')
     name = cdisp_params.get('name')
     filename = cdisp_params.get('filename')
     if not (cdisp == 'form-data' and name):
         raise RequestError('expected Content-Disposition: form-data'
                            'with a "name" parameter: got %r' %
                            headers.get('content-disposition', ''))
     # FIXME: should really to handle Content-Transfer-Encoding and other
     # MIME complexity here.  See RFC2048 for the full horror story.
     if filename:
         # it might be large file upload so use a temporary file
         upload = Upload(filename, ctype, charset)
         upload.receive(lines)
         _add_field_value(self.form, name, upload)
     else:
         value = _decode_string(b''.join(lines), charset or self.charset)
         _add_field_value(self.form, name, value)
Exemple #3
0
def parse_query(qs, charset):
    """(qs: string) -> {key:string, string|[string]}

    Parse a query given as a string argument and return a dictionary.
    """
    fields = {}
    for chunk in qs.split('&'):
        if not chunk:
            continue
        if '=' not in chunk:
            name = chunk
            value = ''
        else:
            name, value = chunk.split('=', 1)
        try:
            name = urllib.parse.unquote_plus(name,
                                             encoding=charset,
                                             errors='strict')
            value = urllib.parse.unquote_plus(value,
                                              encoding=charset,
                                              errors='strict')
        except LookupError:
            raise RequestError('unknown charset %r' % charset)
        except UnicodeDecodeError:
            raise RequestError('invalid %r encoded string' % charset)
        _add_field_value(fields, name, value)
    return fields
Exemple #4
0
def _decode_string(s, charset):
    if charset == 'iso-8859-1' == quixote.DEFAULT_CHARSET:
        # To avoid breaking applications that are not Unicode-safe, return
        # a str instance in this case.
        return s
    try:
        return s.decode(charset)
    except LookupError:
        raise RequestError('unknown charset %r' % charset)
    except UnicodeDecodeError:
        raise RequestError('invalid %r encoded string' % charset)
Exemple #5
0
 def _process_multipart(self, length, params):
     boundary = params.get('boundary')
     if not boundary:
         raise RequestError('multipart/form-data missing boundary')
     charset = params.get('charset')
     mimeinput = MIMEInput(self.stdin, boundary, length)
     try:
         for line in mimeinput.readpart():
             pass  # discard lines up to first boundary
         while mimeinput.moreparts():
             self._process_multipart_body(mimeinput, charset)
     except EOFError:
         raise RequestError('unexpected end of multipart/form-data')
Exemple #6
0
    def parse_content_type(self):
        full_ctype = self.get_header('Content-Type')
        if full_ctype is None:
            raise RequestError("no Content-Type header")

        (ctype, ctype_params) = parse_header(full_ctype)
        boundary = ctype_params.get('boundary')

        if not (ctype == "multipart/form-data" and boundary):
            raise RequestError("expected Content-Type: multipart/form-data "
                               "with a 'boundary' parameter: got %r"
                               % full_ctype)

        return (ctype, boundary)
Exemple #7
0
def parse_content_disposition(full_cdisp):
    (cdisp, cdisp_params) = parse_header(full_cdisp)
    name = cdisp_params.get('name')
    if not (cdisp == 'form-data' and name):
        raise RequestError('expected Content-Disposition: form-data '
                           'with a "name" parameter: got %r' % full_cdisp)
    return (name, cdisp_params.get('filename'))
Exemple #8
0
 def _process_urlencoded(self, length, params):
     query = self.stdin.read(length)
     if len(query) != length:
         raise RequestError('unexpected end of request body')
     # Use the declared charset if it's provided (most browser's don't
     # provide it to avoid breaking old HTTP servers).
     charset = params.get('charset', self.charset)
     self.form.update(parse_query(query, charset))
Exemple #9
0
    def parse_content_disposition(self, full_cdisp):
        (cdisp, cdisp_params) = parse_header(full_cdisp)
        name = cdisp_params.get("name")

        if not (cdisp == "form-data" and name):
            raise RequestError("expected Content-Disposition: form-data "
                               "with a 'name' parameter: got %r" % full_cdisp)

        return (name, cdisp_params.get("filename"))
Exemple #10
0
 def _q_index(self, request):
     if self._type == 'ticket':
         if len(self.parts) >= 2:
             proj_name = '/'.join(self.parts[:-1])
             target_id = self.parts[-1]
             if proj_name and target_id.isdigit():
                 self._proj_name = proj_name
                 self._target_id = target_id
                 self._user = request.user
                 self._cancel = request.get_form_var("cancel")
                 return self.mute()
     return RequestError("Invalie args!")
Exemple #11
0
 def process_inputs(self):
     length = self.environ.get('CONTENT_LENGTH') or "0"
     try:
         length = int(length)
     except ValueError:
         raise RequestError('invalid content-length header')
     if self._stdin is None:
         # We must consume entire request body as some clients and
         # middleware expect that.  We cannot rely on the application to
         # read it completely (e.g. if there is some PublishError raised).
         if length < 20000:
             fp = io.BytesIO()
         else:
             fp = tempfile.TemporaryFile("w+b")
         remaining = length
         while remaining > 0:
             s = self.stdin.read(min(remaining, 10000))
             if not s:
                 raise RequestError('unexpected end of request body')
             fp.write(s)
             remaining -= len(s)
         fp.seek(0)
         self._stdin = self.stdin
         self.stdin = fp
     else:
         # In the case of a database conflict, process_inputs() might
         # be called more than once.  In this case, there is no need
         # to buffer stdin but reset the form data and input file.
         self.stdin.seek(0)
         self.form.clear()
     query = self.get_query()
     if query:
         self.form.update(parse_query(query, self.charset))
     ctype = self.environ.get("CONTENT_TYPE")
     if ctype:
         ctype, ctype_params = parse_header(ctype)
         if ctype == 'application/x-www-form-urlencoded':
             self._process_urlencoded(length, ctype_params)
         elif ctype == 'multipart/form-data':
             self._process_multipart(length, ctype_params)
Exemple #12
0
    def check_length_read(self, file):
        # Parse Content-Length header.
        # XXX if we want to worry about disk free space, this should
        # be done *before* parsing the body!
        clen = self.get_header("Content-Length")
        if clen is not None:
            clen = int(clen)

        total_bytes = file.get_bytesread()
        if total_bytes != clen:
            raise RequestError(
                "upload request length mismatch: expected %d bytes, got %d"
                % (clen, total_bytes))
Exemple #13
0
 def process_inputs(self):
     query = self.get_query()
     if query:
         self.form.update(parse_query(query, self.charset))
     length = self.environ.get('CONTENT_LENGTH') or "0"
     try:
         length = int(length)
     except ValueError:
         raise RequestError('invalid content-length header')
     read_body = length > 0
     ctype = self.environ.get("CONTENT_TYPE")
     if ctype:
         ctype, ctype_params = parse_header(ctype)
         if ctype == 'application/x-www-form-urlencoded':
             self._process_urlencoded(length, ctype_params)
             read_body = False
         elif ctype == 'multipart/form-data':
             self._process_multipart(length, ctype_params)
             read_body = False
     if read_body:
         # We must consume entire request body as some clients and
         # middleware expect that.  We cannot rely on the application to
         # read it completely (e.g. if there is some PublishError raised).
         if length < 20000:
             fp = StringIO()
         else:
             fp = tempfile.TemporaryFile("w+b")
         remaining = length
         while remaining > 0:
             s = self.stdin.read(min(remaining, 10000))
             if not s:
                 raise RequestError('unexpected end of request body')
             fp.write(s)
             remaining -= len(s)
         fp.seek(0)
         self._stdin = self.stdin
         self.stdin = fp
Exemple #14
0
 def process_inputs(self):
     query = self.get_query()
     if query:
         self.form.update(parse_query(query, self.DEFAULT_CHARSET))
     length = self.environ.get('CONTENT_LENGTH') or "0"
     try:
         length = int(length)
     except ValueError:
         raise RequestError('invalid content-length header')
     ctype = self.environ.get("CONTENT_TYPE")
     if ctype:
         ctype, ctype_params = parse_header(ctype)
         if ctype == 'application/x-www-form-urlencoded':
             self._process_urlencoded(length, ctype_params)
         elif ctype == 'multipart/form-data':
             self._process_multipart(length, ctype_params)
Exemple #15
0
 def parse_body(self, file, boundary):
     total_bytes = 0                 # total bytes read from 'file'
     done = 0
     while not done:
         headers = Message(file)
         cdisp = headers.get('content-disposition')
         if not cdisp:
             raise RequestError("expected Content-Disposition header "
                                "in body sub-part")
         (name, filename) = self.parse_content_disposition(cdisp)
         if filename:
             content_type = headers.get('content-type')
             done = self.handle_upload(name, filename, file,
                                       boundary, content_type)
         else:
             done = self.handle_regular_var(name, file, boundary)
Exemple #16
0
    def __init__(self, stdin, environ, seekable=False):
        self.stdin = stdin
        self._stdin = None  # set after stdin is buffered to temp file
        self.body_is_seekable = seekable
        self.environ = environ
        self.form = {}
        self.session = None
        self.charset = self.DEFAULT_CHARSET or quixote.DEFAULT_CHARSET
        self.response = HTTPResponse()

        length = environ.get('CONTENT_LENGTH') or 0
        try:
            self._content_length = int(length)
        except ValueError:
            raise RequestError('invalid content-length header')

        # The strange treatment of SERVER_PORT_SECURE is because IIS
        # sets this environment variable to "0" for non-SSL requests
        # (most web servers -- well, Apache at least -- simply don't set
        # it in that case).
        if (environ.get('HTTPS', 'off').lower() in ('on', 'yes', '1')
                or environ.get('SERVER_PORT_SECURE', '0') != '0'):
            self.scheme = "https"
        else:
            self.scheme = "http"

        k = self.environ.get('HTTP_COOKIE', '')
        if k:
            self.cookies = parse_cookies(k)
        else:
            self.cookies = {}

        # IIS breaks PATH_INFO because it leaves in the path to
        # the script, so SCRIPT_NAME is "/cgi-bin/q.py" and PATH_INFO
        # is "/cgi-bin/q.py/foo/bar".  The following code fixes
        # PATH_INFO to the expected value "/foo/bar".
        web_server = environ.get('SERVER_SOFTWARE', 'unknown')
        if web_server.find('Microsoft-IIS') != -1:
            script = environ['SCRIPT_NAME']
            path = environ['PATH_INFO']
            if path.startswith(script):
                path = path[len(script):]
                self.environ['PATH_INFO'] = path
Exemple #17
0
 def make_body_seekable(self):
     """Ensure that 'stdin' is a seekable file object."""
     if self.body_is_seekable:
         self.stdin.seek(0)
         return
     if self._content_length < 20000:
         fp = io.BytesIO()
     else:
         fp = tempfile.TemporaryFile("w+b")
     remaining = self._content_length
     while remaining > 0:
         s = self.stdin.read(min(remaining, 10000))
         if not s:
             raise RequestError('unexpected end of request body')
         fp.write(s)
         remaining -= len(s)
     fp.seek(0)
     self._stdin = self.stdin
     self.stdin = fp
     self.body_is_seekable = True
Exemple #18
0
 def _process_urlencoded(self, length, params):
     query = self.stdin.read(length)
     if len(query) != length:
         raise RequestError('unexpected end of request body')
     charset = params.get('charset', self.DEFAULT_CHARSET)
     self.form.update(parse_query(query, charset))
Exemple #19
0
def _q_lookup(request, type_):
    user = request.user
    if (type_ in ALLOWED_MUTE_TYPE) and user:
        return MuteUI(type_)
    else:
        return RequestError('Can not parse your request')