Пример #1
0
    def multipart_encode(self, vars, files, boundary=None, buffer=None):
        if boundary is None:
            if isPython3:
                boundary = mimetools._make_boundary()
                boundary = boundary.replace("=", "-")
            else:
                boundary = mimetools.choose_boundary()

        if buffer is None:
            buffer = ''
        for (key, value) in vars:
            buffer += '--%s\r\n' % boundary
            buffer += 'Content-Disposition: form-data; name="%s"' % key
            buffer += '\r\n\r\n' + value + '\r\n'
        for (key, fd) in files:
            filename = fd.name.split('/')[-1]
            contenttype = mimetypes.guess_type(
                filename)[0] or 'application/octet-stream'
            buffer += '--%s\r\n' % boundary
            buffer += 'Content-Disposition: form-data; name="%s"; filename="%s"\r\n' % (
                key, filename)
            buffer += 'Content-Type: %s\r\n' % contenttype
            fd.seek(0)
            if isPython3:
                buffer = buffer.encode() + b'\r\n' + fd.read() + b'\r\n'
            else:
                buffer += '\r\n' + fd.read() + '\r\n'

        if isPython3:
            buffer += b'--%b--\r\n\r\n' % boundary.encode()
        else:
            buffer += '--%s--\r\n\r\n' % boundary
        return boundary, buffer
Пример #2
0
    def __encode_multipart_formdata(self, fields, files):
        """
        fields is a sequence of (key, value) elements for regular form fields.
        files is a sequence of (filename, filehandle) files to be uploaded
        returns (content_type, body)
        """
        BOUNDARY = _make_boundary()

        if len(files) > 0:
            fields['nFileCount'] = str(len(files))

        crlf = '\r\n'
        buf = StringIO()

        for k, v in list(fields.items()):
            if DEBUG:
                print(("field: %s: %s"% (repr(k), repr(v))))
            buf.write(crlf.join([ '--' + BOUNDARY, 'Content-disposition: form-data; name="%s"' % k, '', str(v), '' ]))

        n = 0
        for f, h in list(files.items()):
            n += 1
            buf.write(crlf.join([ '--' + BOUNDARY, 'Content-disposition: form-data; name="File%d"; filename="%s"' % ( n, f), '' ]))
            buf.write(crlf.join([ 'Content-type: application/octet-stream', '', '' ]))
            buf.write(h.read())
            buf.write(crlf)

        buf.write('--' + BOUNDARY + '--' + crlf)
        content_type = "multipart/form-data; boundary=%s" % BOUNDARY
        return content_type, buf.getvalue().encode('utf-8')
Пример #3
0
    def __init__(self,
                 ak,
                 sk,
                 type='TOP',
                 upload_endpoint=config.UPLOAD_ENDPOINT,
                 manage_endpoint=config.MANAGE_ENDPOINT):
        '''Description : 文件上传对象.

        Input : AK: 开发者的AccessKeyId

                SK: 开发者的AccessKeySecret

                type: 开发者的服务类型(即AK/SK的颁发类型,百川用户都是TOP),可不填写。

                `upload_endpoint`和`manage_endpoint`一般不需要填写,使用默认值即可。

        '''

        self.__ak = ak
        self.__sk = sk
        self.__type = type
        self.upload_endpoint = upload_endpoint
        self.manage_endpoint = manage_endpoint
        self.http_client = WantHttp()
        self.boundary = _make_boundary()
Пример #4
0
 def multipart_encode(self, vars):
     "Enconde form data (vars dict)"
     boundary = _make_boundary()
     buf = StringIO()
     for key, value in list(vars.items()):
         if not isinstance(value, IOBase):
             buf.write('--%s\r\n' % boundary)
             buf.write('Content-Disposition: form-data; name="%s"' % key)
             buf.write('\r\n\r\n' + value + '\r\n')
         else:
             fd = value
             file_size = os.fstat(fd.fileno())[stat.ST_SIZE]
             filename = os.path.basename(fd.name)
             contenttype = mimetypes.guess_type(
                 filename)[0] or 'application/octet-stream'
             buf.write('--%s\r\n' % boundary)
             buf.write(
                 'Content-Disposition: form-data; name="%s"; filename="%s"\r\n'
                 % (key, filename))
             buf.write('Content-Type: %s\r\n' % contenttype)
             # buffer += 'Content-Length: %s\r\n' % file_size
             fd.seek(0)
             buf.write('\r\n' + fd.read() + '\r\n')
     buf.write('--' + boundary + '--\r\n\r\n')
     buf = buf.getvalue()
     return boundary, buf
    def multipart_encode(self, vars, files, boundary=None, buffer=None):
        if boundary is None:
            if isPython3:
                boundary = mimetools._make_boundary()
                boundary = boundary.replace("=", "-")
            else:
                boundary = mimetools.choose_boundary()

        if buffer is None:
            buffer = ''
        for(key, value) in vars:
            buffer += '--%s\r\n' % boundary
            buffer += 'Content-Disposition: form-data; name="%s"' % key
            buffer += '\r\n\r\n' + value + '\r\n'
        for(key, fd) in files:
            filename = fd.name.split('/')[-1]
            contenttype = mimetypes.guess_type(filename)[0] or 'application/octet-stream'
            buffer += '--%s\r\n' % boundary
            buffer += 'Content-Disposition: form-data; name="%s"; filename="%s"\r\n' % (key, filename)
            buffer += 'Content-Type: %s\r\n' % contenttype
            fd.seek(0)
            if isPython3:
                buffer = buffer.encode() + b'\r\n' + fd.read() + b'\r\n'
            else:
                buffer += '\r\n' + fd.read() + '\r\n'

        if isPython3:
            buffer += b'--%b--\r\n\r\n' % boundary.encode()
        else:
            buffer += '--%s--\r\n\r\n' % boundary
        return boundary, buffer
Пример #6
0
        def _handle_multipart(self, msg):
            subparts = msg.get_payload()
            if subparts is None:
                subparts = []
            elif isinstance(subparts, basestring):
                self._fp.write(subparts)
                return
            elif not isinstance(subparts, list):
                subparts = [subparts]

            msgtexts = [self._flatten_submessage(part) for part in subparts]

            alltext = '\r\n'.join(msgtexts)

            no_boundary = object()
            boundary = msg.get_boundary(failobj=no_boundary)
            if boundary is no_boundary:
                boundary = _make_boundary(alltext)
                msg.set_boundary(boundary)

            if msg.preamble is not None:
                self._fp.write(msg.preamble)
                self._fp.write('\r\n')
            self._fp.write('--' + boundary)

            for body_part in msgtexts:
                self._fp.write('\r\n')
                self._fp.write(body_part)
                self._fp.write('\r\n--' + boundary)
            self._fp.write('--\r\n')

            if msg.epilogue is not None:
                self._fp.write('\r\n')
                self._fp.write(msg.epilogue)
    def _encode_form_data(self, fields, files):
        boundary = _make_boundary()
        parts = []

        for name, value in fields:
            parts.append(b("--%s" % boundary))
            parts.append(b("Content-Disposition: form-data; name=\"%s\""
                                                                   % name))
            parts.append(b("Content-Type: text/plain"))
            parts.append(b(""))
            parts.append(b(value))

        for name, fp in files:
            filename = basename(fp.name)
            mimetype = _get_content_type(filename)
            fp.seek(0)

            parts.append(b("--%s" % boundary))
            parts.append(b("Content-Disposition: file; name=\"%s\"; " \
                           "filename=\"%s\"" % (name, filename)))
            parts.append(b("Content-Type: %s" % mimetype))
            parts.append(b(""))
            parts.append(fp.read())

        parts.append(b("--%s--" % boundary))
        data = b(NEWLINE).join(parts)

        return boundary, data
Пример #8
0
        def _handle_multipart(self, msg):
            subparts = msg.get_payload()
            if subparts is None:
                subparts = []
            elif isinstance(subparts, basestring):
                self._fp.write(subparts)
                return
            elif not isinstance(subparts, list):
                subparts = [subparts]

            msgtexts = [self._flatten_submessage(part) for part in subparts]

            alltext = '\r\n'.join(msgtexts)

            no_boundary = object()
            boundary = msg.get_boundary(failobj=no_boundary)
            if boundary is no_boundary:
                boundary = _make_boundary(alltext)
                msg.set_boundary(boundary)

            if msg.preamble is not None:
                self._fp.write(msg.preamble)
                self._fp.write('\r\n')
            self._fp.write('--' + boundary)

            for body_part in msgtexts:
                self._fp.write('\r\n')
                self._fp.write(body_part)
                self._fp.write('\r\n--' + boundary)
            self._fp.write('--\r\n')

            if msg.epilogue is not None:
                self._fp.write('\r\n')
                self._fp.write(msg.epilogue)
Пример #9
0
 def _handle_multipart(self, msg):
     # The trick here is to write out each part separately, merge the all
     # together, and then make sure that the boundary we've chosen isn't
     # present in the payload.
     msgtexts = []
     subparts = msg.get_payload()
     if subparts is None:
         subparts = []
     elif isinstance(subparts, basestring):
         # e.g. a non-strict parse of a message with no starting boundary
         self._fp.write(subparts)
         return
     elif not isinstance(subparts, list):
         # Scalar payload
         subparts = [subparts]
     for part in subparts:
         s = StringIO()
         g = self.clone(s)
         g.flatten(part, unixfrom=False)
         msgtexts.append(s.getvalue())
     # BAW: What about boundaries that are wrapped in double-quotes?
     boundary = msg.get_boundary()
     if not boundary:
         # Create a boundary that doesn't appear in any of the
         # message texts.
         alltext = NL.join(msgtexts)
         boundary = _make_boundary(alltext)
         msg.set_boundary(boundary)
     # If there's a preamble, write it out, with a trailing CRLF
     if msg.preamble is not None:
         if self._mangle_from_:
             preamble = fcre.sub('>From ', msg.preamble)
         else:
             preamble = msg.preamble
         print >> self._fp, preamble
     # dash-boundary transport-padding CRLF
     print >> self._fp, '--' + boundary
     # body-part
     if msgtexts:
         self._fp.write(msgtexts.pop(0))
     # *encapsulation
     # --> delimiter transport-padding
     # --> CRLF body-part
     for body_part in msgtexts:
         # delimiter transport-padding CRLF
         print >> self._fp, '\n--' + boundary
         # body-part
         self._fp.write(body_part)
     # close-delimiter transport-padding
     self._fp.write('\n--' + boundary + '--' + NL)
     if msg.epilogue is not None:
         if self._mangle_from_:
             epilogue = fcre.sub('>From ', msg.epilogue)
         else:
             epilogue = msg.epilogue
         self._fp.write(epilogue)
Пример #10
0
    def __create_request_parts(self, files):
        request_list = []
        boundary = _make_boundary()
        content_length = 0

        boundary_string = b'--%s\r\n' % (boundary)
        for fieldname, value in iter_fields(files):
            content_length += len(boundary_string)

            if isinstance(value, tuple):
                filename, data = value
                content_disposition_string = (
                    ('Content-Disposition: form-data; name="%s"; '
                     'filename="%s"\r\n' %
                     (fieldname, filename)) + ('Content-Type: %s\r\n\r\n' %
                                               (guess_content_type(filename))))

            else:
                data = value
                content_disposition_string = (
                    ('Content-Disposition: form-data; name="%s"\r\n' %
                     (fieldname)) + 'Content-Type: text/plain\r\n\r\n')
            request_list.append(
                BytesIO(str(boundary_string + content_disposition_string)))
            content_length += len(content_disposition_string)
            if hasattr(data, 'read'):
                data_stream = data
            else:
                data_stream = BytesIO(str(data))

            data_stream.seek(0, 2)
            data_size = data_stream.tell()
            data_stream.seek(0)

            request_list.append(data_stream)
            content_length += data_size

            end_string = b'\r\n'
            request_list.append(BytesIO(end_string))
            content_length += len(end_string)

        request_list.append(BytesIO(b'--%s--\r\n' % (boundary)))
        content_length += len(boundary_string)

        # There's a bug in httplib.py that generates a UnicodeDecodeError on binary uploads if
        # there are *any* unicode strings passed into headers as part of the requests call.
        # For this reason all strings are explicitly converted to non-unicode at this point.
        self.content_type_header = {
            b'Content-Type': b'multipart/form-data; boundary=%s' % boundary
        }
        self.content_length_header = {b'Content-Length': str(content_length)}
        self._body_parts = request_list

        self._content_length = content_length + 2
Пример #11
0
    def __encode_multipart_formdata(self, fields, files):
        """
        fields is a sequence of (key, value) elements for regular form fields.
        files is a sequence of (filename, filehandle) files to be uploaded
        returns (content_type, body)
        """
        BOUNDARY = _make_boundary()

        if len(files) > 0:
            fields['nFileCount'] = str(len(files))

        crlf = '\r\n'
        buf = BytesIO()

        for k, v in fields.items():
            if DEBUG:
                print("field: %s: %s"% (repr(k), repr(v)))
            lines = [
                '--' + BOUNDARY,
                'Content-disposition: form-data; name="%s"' % k,
                '',
                str(v),
                '',
            ]
            buf.write(crlf.join(lines).encode('utf-8'))

        n = 0
        for f, h in files.items():
            n += 1
            lines = [
                '--' + BOUNDARY,
                'Content-disposition: form-data; name="File%d"; '
                    'filename="%s"' % (n, f),
                '',
            ]
            buf.write(crlf.join(lines).encode('utf-8'))
            lines = [
                'Content-type: application/octet-stream',
                '',
                '',
            ]
            buf.write(crlf.join(lines).encode('utf-8'))
            buf.write(h.read())
            buf.write(crlf.encode('utf-8'))

        buf.write(('--' + BOUNDARY + '--' + crlf).encode('utf-8'))
        content_type = "multipart/form-data; boundary=%s" % BOUNDARY
        return content_type, buf.getvalue()
Пример #12
0
    def __create_request_parts(self, files):
        request_list = []
        boundary = _make_boundary()
        content_length = 0

        boundary_string = '--%s\r\n' % (boundary)
        for fieldname, value in iter_fields(files):
            content_length += len(boundary_string)

            if isinstance(value, tuple):
                filename, data = value
                content_disposition_string = (('Content-Disposition: form-data; name="%s"; ''filename="%s"\r\n' % (fieldname, filename))
                                            + ('Content-Type: %s\r\n\r\n' % (guess_content_type(filename))))

            else:
                data = value
                content_disposition_string =  (('Content-Disposition: form-data; name="%s"\r\n' % (fieldname))
                                            + 'Content-Type: text/plain\r\n\r\n')
            request_list.append(BytesIO(str.encode(boundary_string + content_disposition_string)))
            content_length += len(content_disposition_string)
            if hasattr(data, 'read'):
                data_stream = data
            else:
                data_stream = BytesIO(str.encode(data))

            data_stream.seek(0,2)
            data_size = data_stream.tell()
            data_stream.seek(0)

            request_list.append(data_stream)
            content_length += data_size

            end_string = b'\r\n'
            request_list.append(BytesIO(end_string))
            content_length += len(end_string)

        request_list.append(BytesIO(str.encode('--%s--\r\n' % (boundary))))
        content_length += len(boundary_string)

        # There's a bug in httplib.py that generates a UnicodeDecodeError on binary uploads if
        # there are *any* unicode strings passed into headers as part of the requests call.
        # For this reason all strings are explicitly converted to non-unicode at this point.
        self.content_type_header = {b'Content-Type': str.encode('multipart/form-data; boundary=%s' % boundary)}
        self.content_length_header = {b'Content-Length': str.encode(str(content_length))}
        self._body_parts = request_list

        self._content_length = content_length + 2
Пример #13
0
	def encode_multipart_formdata(fields, files):
		BOUNDARY = _make_boundary()
		CRLF = '\r\n'
		L = []
		for (key, value) in fields:
			L.append('--' + BOUNDARY)
			L.append('Content-Disposition: form-data; name="%s"' % key)
			L.append('')
			L.append(value)
		for (key, filename, value) in files:
			L.append('--' + BOUNDARY)
			L.append('Content-Disposition: form-data; name="%s"; filename="%s"' % (key, filename))
			L.append('Content-Type: %s' % mimetypes.guess_type(filename)[0] or 'application/octet-stream')
			L.append('')
			L.append(value)
		L.append('--' + BOUNDARY + '--')
		L.append('')
		body = CRLF.join(L)
		content_type = 'multipart/form-data; boundary=%s' % BOUNDARY
		return content_type, body
Пример #14
0
def encode_multipart_formdata(fields, files):
    BOUNDARY = _make_boundary()
    CRLF = ('\r\n').encode()
    L = []
    if fields is not None:
        for key in fields:
            L.append(('--' + BOUNDARY).encode())
            L.append(('Content-Disposition: form-data; name="%s"' % key).encode())
            L.append(('').encode())
            L.append(fields[key].encode())
    for (key, filename, value) in files:
        L.append(('--' + BOUNDARY).encode())
        L.append(('Content-Disposition: form-data; name="%s"; filename="%s"' % (key, filename)).encode())
        L.append(('Content-Type: %s' % get_content_type(filename)).encode())
        L.append(('').encode())
        L.append(value)
    L.append(('--' + BOUNDARY + '--').encode())
    L.append(('').encode())
    body = CRLF.join(L)
    content_type = 'multipart/form-data; boundary=%s' % BOUNDARY
    return content_type, body
Пример #15
0
 def multipart_encode(self, these_vars, files, boundary = None, buf = None):
     if boundary is None:
         boundary = _make_boundary(os.getuid + os.getpid())
     if buf is None:
         buf = StringIO()
     for(key, value) in these_vars:
         buf.write('--%s\r\n' % boundary)
         buf.write('Content-Disposition: form-data; name="%s"' % key)
         buf.write('\r\n\r\n' + value + '\r\n')
     for(key, fd) in files:
         file_size = os.fstat(fd.fileno())[stat.ST_SIZE]
         filename = fd.name.split('/')[-1]
         contenttype = mimetypes.guess_type(filename)[0] or 'application/octet-stream'
         buf.write('--%s\r\n' % boundary)
         buf.write('Content-Disposition: form-data; name="%s"; filename="%s"\r\n' % (key, filename))
         buf.write('Content-Type: %s\r\n' % contenttype)
         # buffer += 'Content-Length: %s\r\n' % file_size
         fd.seek(0)
         buf.write('\r\n' + fd.read() + '\r\n')
     buf.write('--' + boundary + '--\r\n\r\n')
     buf = buf.getvalue()
     return boundary, buf
Пример #16
0
 def encode_multipart_formdata(fields, files):
     BOUNDARY = _make_boundary()
     CRLF = '\r\n'
     L = []
     for (key, value) in fields:
         L.append('--' + BOUNDARY)
         L.append('Content-Disposition: form-data; name="%s"' % key)
         L.append('')
         L.append(value)
     for (key, filename, value) in files:
         L.append('--' + BOUNDARY)
         L.append(
             'Content-Disposition: form-data; name="%s"; filename="%s"' %
             (key, filename))
         L.append('Content-Type: %s' % mimetypes.guess_type(filename)[0]
                  or 'application/octet-stream')
         L.append('')
         L.append(value)
     L.append('--' + BOUNDARY + '--')
     L.append('')
     body = CRLF.join(L)
     content_type = 'multipart/form-data; boundary=%s' % BOUNDARY
     return content_type, body
Пример #17
0
    def fetch_form(self, form, submit=None):
        values = []
        form_elements = find_form_elements(form)
        if submit is None:
            submits = [_submit for _submit in form_elements if _submit.get('type', '').lower() in ('submit', 'image')]
            if len(submits) == 1:
                submit = submits[0]
            elif submits:
                raise MechanizeError('multiple submit buttons exist. specify any one')
        collect_form_values(values, form_elements, submit)
        method = form.get('method').upper()
        enctype = form.get('enctype', FORM_URLENCODE_MIME_TYPE)
        url = urljoin(self.location, form.get('action'))

        logger.debug('submit_form: method=%s, url=%s, enctype=%s, values=%r' % (method, url, enctype, values))

        if method.upper() == 'GET':
            encoding = self.encoding
            queries = urlencoded_form_data(values)
            final_url = urljoin(url, '?' + queries)
            return self.create_loader(urllib2.Request(final_url))
        else:
            headers = None
            if enctype == FORM_URLENCODE_MIME_TYPE:
                data = encode_urlencoded_form_data(values, self.encoding)
                headers = {
                    'Content-Type': FORM_URLENCODE_MIME_TYPE
                    }
            else:
                boundary = _make_boundary()
                data = encode_mime_multipart_form_data(values, self.encoding, boundary)
                headers = {
                    'Content-Type': '%s; boundary=%s' % (FORM_MULTIPART_MIME_TYPE,
                                                       boundary)
                    }
            return self.create_loader(urllib2.Request(url, data=data, headers=headers))
Пример #18
0
def serve_file(request, response, path, type=None, disposition=None,
               name=None):
    """Set status, headers, and body in order to serve the given file.

    The Content-Type header will be set to the type arg, if provided.
    If not provided, the Content-Type will be guessed by the file extension
    of the 'path' argument.

    If disposition is not None, the Content-Disposition header will be set
    to "<disposition>; filename=<name>". If name is None, it will be set
    to the basename of path. If disposition is None, no Content-Disposition
    header will be written.
    """

    if not os.path.isabs(path):
        raise ValueError("'%s' is not an absolute path." % path)

    try:
        st = os.stat(path)
    except OSError:
        return notfound(request, response)

    # Check if path is a directory.
    if stat.S_ISDIR(st.st_mode):
        # Let the caller deal with it as they like.
        return notfound(request, response)

    # Set the Last-Modified response header, so that
    # modified-since validation code can work.
    response.headers['Last-Modified'] = formatdate(
        st.st_mtime, usegmt=True
    )

    result = validate_since(request, response)
    if result is not None:
        return result

    if type is None:
        # Set content-type based on filename extension
        ext = ""
        i = path.rfind('.')
        if i != -1:
            ext = path[i:].lower()
        type = mimetypes.types_map.get(ext, "text/plain")
    response.headers['Content-Type'] = type

    if disposition is not None:
        if name is None:
            name = os.path.basename(path)
        cd = '%s; filename="%s"' % (disposition, name)
        response.headers["Content-Disposition"] = cd

    # Set Content-Length and use an iterable (file object)
    #   this way CP won't load the whole file in memory
    c_len = st.st_size
    bodyfile = open(path, 'rb')

    # HTTP/1.0 didn't have Range/Accept-Ranges headers, or the 206 code
    if request.protocol >= (1, 1):
        response.headers["Accept-Ranges"] = "bytes"
        r = get_ranges(request.headers.get('Range'), c_len)
        if r == []:
            response.headers['Content-Range'] = "bytes */%s" % c_len
            return httperror(request, response, 416)
        if r:
            if len(r) == 1:
                # Return a single-part response.
                start, stop = r[0]
                r_len = stop - start
                response.status = 206
                response.headers['Content-Range'] = (
                    "bytes %s-%s/%s" % (start, stop - 1, c_len)
                )
                response.headers['Content-Length'] = r_len
                bodyfile.seek(start)
                response.body = bodyfile.read(r_len)
            else:
                # Return a multipart/byteranges response.
                response.status = 206
                boundary = _make_boundary()
                ct = "multipart/byteranges; boundary=%s" % boundary
                response.headers['Content-Type'] = ct
                if "Content-Length" in response.headers:
                    # Delete Content-Length header so finalize() recalcs it.
                    del response.headers["Content-Length"]

                def file_ranges():
                    # Apache compatibility:
                    yield "\r\n"

                    for start, stop in r:
                        yield "--" + boundary
                        yield "\r\nContent-type: %s" % type
                        yield ("\r\nContent-range: bytes %s-%s/%s\r\n\r\n"
                               % (start, stop - 1, c_len))
                        bodyfile.seek(start)
                        yield bodyfile.read(stop - start)
                        yield "\r\n"
                    # Final boundary
                    yield "--" + boundary + "--"

                    # Apache compatibility:
                    yield "\r\n"
                response.body = file_ranges()
        else:
            response.headers['Content-Length'] = c_len
            response.body = bodyfile
    else:
        response.headers['Content-Length'] = c_len
        response.body = bodyfile

    return response
Пример #19
0
    def _handle_multipart(self, msg):
        """
        A multipart handling implementation that addresses issue #14983.

        This is just a copy of the parent's method which fixes the following
        bug: http://bugs.python.org/issue14983 (see the line marked with
        "(***)").

        :param msg: The multipart message to be handled.
        :type msg: email.message.Message
        """
        # The trick here is to write out each part separately, merge them all
        # together, and then make sure that the boundary we've chosen isn't
        # present in the payload.
        msgtexts = []
        subparts = msg.get_payload()
        if subparts is None:
            subparts = []
        elif isinstance(subparts, basestring):
            # e.g. a non-strict parse of a message with no starting boundary.
            self._fp.write(subparts)
            return
        elif not isinstance(subparts, list):
            # Scalar payload
            subparts = [subparts]
        for part in subparts:
            s = StringIO()
            g = self.clone(s)
            g.flatten(part, unixfrom=False)
            msgtexts.append(s.getvalue())
        # BAW: What about boundaries that are wrapped in double-quotes?
        boundary = msg.get_boundary()
        if not boundary:
            # Create a boundary that doesn't appear in any of the
            # message texts.
            alltext = NL.join(msgtexts)
            boundary = _make_boundary(alltext)
            msg.set_boundary(boundary)
        # If there's a preamble, write it out, with a trailing CRLF
        if msg.preamble is not None:
            preamble = msg.preamble
            if self._mangle_from_:
                preamble = fcre.sub('>From ', msg.preamble)
            self._fp.write(preamble + '\n')
        # dash-boundary transport-padding CRLF
        self._fp.write('--' + boundary + '\n')
        # body-part
        if msgtexts:
            self._fp.write(msgtexts.pop(0))
        # *encapsulation
        # --> delimiter transport-padding
        # --> CRLF body-part
        for body_part in msgtexts:
            # delimiter transport-padding CRLF
            self._fp.write('\n--' + boundary + '\n')
            # body-part
            self._fp.write(body_part)
        # close-delimiter transport-padding
        self._fp.write('\n--' + boundary + '--' + '\n')  # (***) Solve #14983
        if msg.epilogue is not None:
            self._fp.write('\n')
            epilogue = msg.epilogue
            if self._mangle_from_:
                epilogue = fcre.sub('>From ', msg.epilogue)
            self._fp.write(epilogue)
Пример #20
0
def gen_boundary():
    return _make_boundary()
Пример #21
0
 def __init__(self):
     self.files = []
     self.boundary = _make_boundary()
Пример #22
0
    def _range_request_handler(self, REQUEST, RESPONSE):
        # HTTP Range header handling: return True if we've served a range
        # chunk out of our data.
        range = REQUEST.get_header('Range', None)
        request_range = REQUEST.get_header('Request-Range', None)
        if request_range is not None:
            # Netscape 2 through 4 and MSIE 3 implement a draft version
            # Later on, we need to serve a different mime-type as well.
            range = request_range
        if_range = REQUEST.get_header('If-Range', None)
        if range is not None:
            ranges = HTTPRangeSupport.parseRange(range)

            if if_range is not None:
                # Only send ranges if the data isn't modified, otherwise send
                # the whole object. Support both ETags and Last-Modified dates!
                if len(if_range) > 1 and if_range[:2] == 'ts':
                    # ETag:
                    if if_range != self.http__etag():
                        # Modified, so send a normal response. We delete
                        # the ranges, which causes us to skip to the 200
                        # response.
                        ranges = None
                else:
                    # Date
                    date = if_range.split(';')[0]
                    try:
                        mod_since = int(DateTime(date).timeTime())
                    except Exception:
                        mod_since = None
                    if mod_since is not None:
                        if self._p_mtime:
                            last_mod = int(self._p_mtime)
                        else:
                            last_mod = 0
                        if last_mod > mod_since:
                            # Modified, so send a normal response. We delete
                            # the ranges, which causes us to skip to the 200
                            # response.
                            ranges = None

            if ranges:
                # Search for satisfiable ranges.
                satisfiable = 0
                for start, end in ranges:
                    if start < self.size:
                        satisfiable = 1
                        break

                if not satisfiable:
                    RESPONSE.setHeader(
                        'Content-Range', 'bytes */%d' % self.size)
                    RESPONSE.setHeader('Accept-Ranges', 'bytes')
                    RESPONSE.setHeader(
                        'Last-Modified', rfc1123_date(self._p_mtime))
                    RESPONSE.setHeader('Content-Type', self.content_type)
                    RESPONSE.setHeader('Content-Length', self.size)
                    RESPONSE.setStatus(416)
                    return True

                ranges = HTTPRangeSupport.expandRanges(ranges, self.size)

                if len(ranges) == 1:
                    # Easy case, set extra header and return partial set.
                    start, end = ranges[0]
                    size = end - start

                    RESPONSE.setHeader(
                        'Last-Modified', rfc1123_date(self._p_mtime))
                    RESPONSE.setHeader('Content-Type', self.content_type)
                    RESPONSE.setHeader('Content-Length', size)
                    RESPONSE.setHeader('Accept-Ranges', 'bytes')
                    RESPONSE.setHeader(
                        'Content-Range',
                        'bytes %d-%d/%d' % (start, end - 1, self.size))
                    RESPONSE.setStatus(206)  # Partial content

                    data = self.data
                    if isinstance(data, binary_type):
                        RESPONSE.write(data[start:end])
                        return True

                    # Linked Pdata objects. Urgh.
                    pos = 0
                    while data is not None:
                        l = len(data.data)
                        pos = pos + l
                        if pos > start:
                            # We are within the range
                            lstart = l - (pos - start)

                            if lstart < 0:
                                lstart = 0

                            # find the endpoint
                            if end <= pos:
                                lend = l - (pos - end)

                                # Send and end transmission
                                RESPONSE.write(data[lstart:lend])
                                break

                            # Not yet at the end, transmit what we have.
                            RESPONSE.write(data[lstart:])

                        data = data.next

                    return True

                else:
                    boundary = _make_boundary()

                    # Calculate the content length
                    size = (8 + len(boundary) +  # End marker length
                            len(ranges) * (  # Constant lenght per set
                            49 + len(boundary) + len(self.content_type) +
                            len('%d' % self.size)))
                    for start, end in ranges:
                        # Variable length per set
                        size = (size + len('%d%d' % (start, end - 1)) +
                                end - start)

                    # Some clients implement an earlier draft of the spec, they
                    # will only accept x-byteranges.
                    draftprefix = (request_range is not None) and 'x-' or ''

                    RESPONSE.setHeader('Content-Length', size)
                    RESPONSE.setHeader('Accept-Ranges', 'bytes')
                    RESPONSE.setHeader(
                        'Last-Modified', rfc1123_date(self._p_mtime))
                    RESPONSE.setHeader(
                        'Content-Type',
                        'multipart/%sbyteranges; boundary=%s' % (
                            draftprefix, boundary))
                    RESPONSE.setStatus(206)  # Partial content

                    data = self.data
                    # The Pdata map allows us to jump into the Pdata chain
                    # arbitrarily during out-of-order range searching.
                    pdata_map = {}
                    pdata_map[0] = data

                    for start, end in ranges:
                        RESPONSE.write(
                            b'\r\n--' + boundary.encode('ascii') + b'\r\n')
                        RESPONSE.write(
                            b'Content-Type: ' +
                            self.content_type.encode('ascii') + b'\r\n')
                        RESPONSE.write(
                            b'Content-Range: bytes ' +
                            str(start).encode('ascii') +
                            b'-' +
                            str(end - 1).encode('ascii') +
                            b'/' +
                            str(self.size).encode('ascii') +
                            b'\r\n\r\n')

                        if isinstance(data, binary_type):
                            RESPONSE.write(data[start:end])

                        else:
                            # Yippee. Linked Pdata objects. The following
                            # calculations allow us to fast-forward through the
                            # Pdata chain without a lot of dereferencing if we
                            # did the work already.
                            first_size = len(pdata_map[0].data)
                            if start < first_size:
                                closest_pos = 0
                            else:
                                closest_pos = (
                                    ((start - first_size) >> 16 << 16) +
                                    first_size)
                            pos = min(closest_pos, max(pdata_map.keys()))
                            data = pdata_map[pos]

                            while data is not None:
                                l = len(data.data)
                                pos = pos + l
                                if pos > start:
                                    # We are within the range
                                    lstart = l - (pos - start)

                                    if lstart < 0:
                                        lstart = 0

                                    # find the endpoint
                                    if end <= pos:
                                        lend = l - (pos - end)

                                        # Send and loop to next range
                                        RESPONSE.write(data[lstart:lend])
                                        break

                                    # Not yet at the end,
                                    # transmit what we have.
                                    RESPONSE.write(data[lstart:])

                                data = data.next
                                # Store a reference to a Pdata chain link
                                # so we don't have to deref during
                                # this request again.
                                pdata_map[pos] = data

                    # Do not keep the link references around.
                    del pdata_map

                    RESPONSE.write(
                        b'\r\n--' + boundary.encode('ascii') + b'--\r\n')
                    return True
Пример #23
0
def serve_file(request,
               response,
               path,
               type=None,
               disposition=None,
               name=None):
    """Set status, headers, and body in order to serve the given file.

    The Content-Type header will be set to the type arg, if provided.
    If not provided, the Content-Type will be guessed by the file extension
    of the 'path' argument.

    If disposition is not None, the Content-Disposition header will be set
    to "<disposition>; filename=<name>". If name is None, it will be set
    to the basename of path. If disposition is None, no Content-Disposition
    header will be written.
    """

    if not os.path.isabs(path):
        raise ValueError("'%s' is not an absolute path." % path)

    try:
        st = os.stat(path)
    except OSError:
        return notfound(request, response)

    # Check if path is a directory.
    if stat.S_ISDIR(st.st_mode):
        # Let the caller deal with it as they like.
        return notfound(request, response)

    # Set the Last-Modified response header, so that
    # modified-since validation code can work.
    response.headers['Last-Modified'] = formatdate(st.st_mtime, usegmt=True)

    result = validate_since(request, response)
    if result is not None:
        return result

    if type is None:
        # Set content-type based on filename extension
        ext = os.path.splitext(path)[-1].lower()
        type = mimetypes.types_map.get(ext, "text/plain")
    response.headers['Content-Type'] = type

    if disposition is not None:
        if name is None:
            name = os.path.basename(path)
        cd = '%s; filename="%s"' % (disposition, name)
        response.headers["Content-Disposition"] = cd

    # Set Content-Length and use an iterable (file object)
    #   this way CP won't load the whole file in memory
    c_len = st.st_size
    bodyfile = open(path, 'rb')

    # HTTP/1.0 didn't have Range/Accept-Ranges headers, or the 206 code
    if request.protocol >= (1, 1):
        response.headers["Accept-Ranges"] = "bytes"
        r = get_ranges(request.headers.get('Range'), c_len)
        if r == []:
            response.headers['Content-Range'] = "bytes */%s" % c_len
            return httperror(request, response, 416)
        if r:
            if len(r) == 1:
                # Return a single-part response.
                start, stop = r[0]
                r_len = stop - start
                response.status = 206
                response.headers['Content-Range'] = ("bytes %s-%s/%s" %
                                                     (start, stop - 1, c_len))
                response.headers['Content-Length'] = r_len
                bodyfile.seek(start)
                response.body = bodyfile.read(r_len)
            else:
                # Return a multipart/byteranges response.
                response.status = 206
                boundary = _make_boundary()
                ct = "multipart/byteranges; boundary=%s" % boundary
                response.headers['Content-Type'] = ct
                if "Content-Length" in response.headers:
                    # Delete Content-Length header so finalize() recalcs it.
                    del response.headers["Content-Length"]

                def file_ranges():
                    # Apache compatibility:
                    yield "\r\n"

                    for start, stop in r:
                        yield "--" + boundary
                        yield "\r\nContent-type: %s" % type
                        yield ("\r\nContent-range: bytes %s-%s/%s\r\n\r\n" %
                               (start, stop - 1, c_len))
                        bodyfile.seek(start)
                        yield bodyfile.read(stop - start)
                        yield "\r\n"
                    # Final boundary
                    yield "--" + boundary + "--"

                    # Apache compatibility:
                    yield "\r\n"

                response.body = file_ranges()
        else:
            response.headers['Content-Length'] = c_len
            response.body = bodyfile
    else:
        response.headers['Content-Length'] = c_len
        response.body = bodyfile

    return response
Пример #24
0
    def _handle_multipart(self, msg):
        """
        A multipart handling implementation that addresses issue #14983.

        This is just a copy of the parent's method which fixes the following
        bug: http://bugs.python.org/issue14983 (see the line marked with
        "(***)").

        :param msg: The multipart message to be handled.
        :type msg: email.message.Message
        """
        # The trick here is to write out each part separately, merge them all
        # together, and then make sure that the boundary we've chosen isn't
        # present in the payload.
        msgtexts = []
        subparts = msg.get_payload()
        if subparts is None:
            subparts = []
        elif isinstance(subparts, basestring):
            # e.g. a non-strict parse of a message with no starting boundary.
            self._fp.write(subparts)
            return
        elif not isinstance(subparts, list):
            # Scalar payload
            subparts = [subparts]
        for part in subparts:
            s = StringIO()
            g = self.clone(s)
            g.flatten(part, unixfrom=False)
            msgtexts.append(s.getvalue())
        # BAW: What about boundaries that are wrapped in double-quotes?
        boundary = msg.get_boundary()
        if not boundary:
            # Create a boundary that doesn't appear in any of the
            # message texts.
            alltext = NL.join(msgtexts)
            boundary = _make_boundary(alltext)
            msg.set_boundary(boundary)
        # If there's a preamble, write it out, with a trailing CRLF
        if msg.preamble is not None:
            preamble = msg.preamble
            if self._mangle_from_:
                preamble = fcre.sub('>From ', msg.preamble)
            self._fp.write(preamble + '\n')
        # dash-boundary transport-padding CRLF
        self._fp.write('--' + boundary + '\n')
        # body-part
        if msgtexts:
            self._fp.write(msgtexts.pop(0))
        # *encapsulation
        # --> delimiter transport-padding
        # --> CRLF body-part
        for body_part in msgtexts:
            # delimiter transport-padding CRLF
            self._fp.write('\n--' + boundary + '\n')
            # body-part
            self._fp.write(body_part)
        # close-delimiter transport-padding
        self._fp.write('\n--' + boundary + '--' + '\n')  # (***) Solve #14983
        if msg.epilogue is not None:
            self._fp.write('\n')
            epilogue = msg.epilogue
            if self._mangle_from_:
                epilogue = fcre.sub('>From ', msg.epilogue)
            self._fp.write(epilogue)
Пример #25
0
    def _range_request_handler(self, REQUEST, RESPONSE):
        # HTTP Range header handling: return True if we've served a range
        # chunk out of our data.
        range = REQUEST.get_header('Range', None)
        request_range = REQUEST.get_header('Request-Range', None)
        if request_range is not None:
            # Netscape 2 through 4 and MSIE 3 implement a draft version
            # Later on, we need to serve a different mime-type as well.
            range = request_range
        if_range = REQUEST.get_header('If-Range', None)
        if range is not None:
            ranges = HTTPRangeSupport.parseRange(range)

            if if_range is not None:
                # Only send ranges if the data isn't modified, otherwise send
                # the whole object. Support both ETags and Last-Modified dates!
                if len(if_range) > 1 and if_range[:2] == 'ts':
                    # ETag:
                    if if_range != self.http__etag():
                        # Modified, so send a normal response. We delete
                        # the ranges, which causes us to skip to the 200
                        # response.
                        ranges = None
                else:
                    # Date
                    date = if_range.split(';')[0]
                    try:
                        mod_since = int(DateTime(date).timeTime())
                    except Exception:
                        mod_since = None
                    if mod_since is not None:
                        if self._p_mtime:
                            last_mod = int(self._p_mtime)
                        else:
                            last_mod = 0
                        if last_mod > mod_since:
                            # Modified, so send a normal response. We delete
                            # the ranges, which causes us to skip to the 200
                            # response.
                            ranges = None

            if ranges:
                # Search for satisfiable ranges.
                satisfiable = 0
                for start, end in ranges:
                    if start < self.size:
                        satisfiable = 1
                        break

                if not satisfiable:
                    RESPONSE.setHeader('Content-Range',
                                       'bytes */%d' % self.size)
                    RESPONSE.setHeader('Accept-Ranges', 'bytes')
                    RESPONSE.setHeader('Last-Modified',
                                       rfc1123_date(self._p_mtime))
                    RESPONSE.setHeader('Content-Type', self.content_type)
                    RESPONSE.setHeader('Content-Length', self.size)
                    RESPONSE.setStatus(416)
                    return True

                ranges = HTTPRangeSupport.expandRanges(ranges, self.size)

                if len(ranges) == 1:
                    # Easy case, set extra header and return partial set.
                    start, end = ranges[0]
                    size = end - start

                    RESPONSE.setHeader('Last-Modified',
                                       rfc1123_date(self._p_mtime))
                    RESPONSE.setHeader('Content-Type', self.content_type)
                    RESPONSE.setHeader('Content-Length', size)
                    RESPONSE.setHeader('Accept-Ranges', 'bytes')
                    RESPONSE.setHeader(
                        'Content-Range',
                        'bytes %d-%d/%d' % (start, end - 1, self.size))
                    RESPONSE.setStatus(206)  # Partial content

                    data = self.data
                    if isinstance(data, binary_type):
                        RESPONSE.write(data[start:end])
                        return True

                    # Linked Pdata objects. Urgh.
                    pos = 0
                    while data is not None:
                        length = len(data.data)
                        pos = pos + length
                        if pos > start:
                            # We are within the range
                            lstart = length - (pos - start)

                            if lstart < 0:
                                lstart = 0

                            # find the endpoint
                            if end <= pos:
                                lend = length - (pos - end)

                                # Send and end transmission
                                RESPONSE.write(data[lstart:lend])
                                break

                            # Not yet at the end, transmit what we have.
                            RESPONSE.write(data[lstart:])

                        data = data.next

                    return True

                else:
                    boundary = _make_boundary()

                    # Calculate the content length
                    size = (
                        8 + len(boundary)  # End marker length
                        + len(ranges) * (  # Constant lenght per set
                            49 + len(boundary) + len(self.content_type) +
                            len('%d' % self.size)))
                    for start, end in ranges:
                        # Variable length per set
                        size = (size + len('%d%d' % (start, end - 1)) + end -
                                start)

                    # Some clients implement an earlier draft of the spec, they
                    # will only accept x-byteranges.
                    draftprefix = (request_range is not None) and 'x-' or ''

                    RESPONSE.setHeader('Content-Length', size)
                    RESPONSE.setHeader('Accept-Ranges', 'bytes')
                    RESPONSE.setHeader('Last-Modified',
                                       rfc1123_date(self._p_mtime))
                    RESPONSE.setHeader(
                        'Content-Type',
                        'multipart/%sbyteranges; boundary=%s' % (
                            draftprefix,
                            boundary,
                        ))
                    RESPONSE.setStatus(206)  # Partial content

                    data = self.data
                    # The Pdata map allows us to jump into the Pdata chain
                    # arbitrarily during out-of-order range searching.
                    pdata_map = {}
                    pdata_map[0] = data

                    for start, end in ranges:
                        RESPONSE.write(b'\r\n--' + boundary.encode('ascii') +
                                       b'\r\n')
                        RESPONSE.write(b'Content-Type: ' +
                                       self.content_type.encode('ascii') +
                                       b'\r\n')
                        RESPONSE.write(b'Content-Range: bytes ' +
                                       str(start).encode('ascii') + b'-' +
                                       str(end - 1).encode('ascii') + b'/' +
                                       str(self.size).encode('ascii') +
                                       b'\r\n\r\n')

                        if isinstance(data, binary_type):
                            RESPONSE.write(data[start:end])

                        else:
                            # Yippee. Linked Pdata objects. The following
                            # calculations allow us to fast-forward through the
                            # Pdata chain without a lot of dereferencing if we
                            # did the work already.
                            first_size = len(pdata_map[0].data)
                            if start < first_size:
                                closest_pos = 0
                            else:
                                closest_pos = ((
                                    (start - first_size) >> 16 << 16) +
                                               first_size)
                            pos = min(closest_pos, max(pdata_map.keys()))
                            data = pdata_map[pos]

                            while data is not None:
                                length = len(data.data)
                                pos = pos + length
                                if pos > start:
                                    # We are within the range
                                    lstart = length - (pos - start)

                                    if lstart < 0:
                                        lstart = 0

                                    # find the endpoint
                                    if end <= pos:
                                        lend = length - (pos - end)

                                        # Send and loop to next range
                                        RESPONSE.write(data[lstart:lend])
                                        break

                                    # Not yet at the end,
                                    # transmit what we have.
                                    RESPONSE.write(data[lstart:])

                                data = data.next
                                # Store a reference to a Pdata chain link
                                # so we don't have to deref during
                                # this request again.
                                pdata_map[pos] = data

                    # Do not keep the link references around.
                    del pdata_map

                    RESPONSE.write(b'\r\n--' + boundary.encode('ascii') +
                                   b'--\r\n')
                    return True
Пример #26
0
 def __init__(self):
     self.files = []
     self.boundary = _make_boundary()