def __init__(self, data): self.data = data self.boundary = choose_boundary() if 'audio' in data: self.input_name = 'audio' self.input_file = data.pop('audio') if 'document' in data: self.input_name = 'document' self.input_file = data.pop('document') if 'photo' in data: self.input_name = 'photo' self.input_file = data.pop('photo') if 'video' in data: self.input_name = 'video' self.input_file = data.pop('video') if 'voice' in data: self.input_name = 'voice' self.input_file = data.pop('voice') if isinstance(self.input_file, file): self.input_file_content = self.input_file.read() self.filename = os.path.basename(self.input_file.name) self.mimetype = mimetypes.guess_type(self.filename)[0] or \ DEFAULT_MIME_TYPE if 'http' in self.input_file: self.input_file_content = urlopen(self.input_file).read() self.mimetype = InputFile.is_image(self.input_file_content) self.filename = self.mimetype.replace('/', '.')
def __init__(self, data): self.data = data self.boundary = choose_boundary() for t in FILE_TYPES: if t in data: self.input_name = t self.input_file = data.pop(t) break else: raise TelegramError('Unknown inputfile type') if hasattr(self.input_file, 'read'): self.filename = None self.input_file_content = self.input_file.read() if 'filename' in data: self.filename = self.data.pop('filename') elif hasattr(self.input_file, 'name'): # on py2.7, pylint fails to understand this properly # pylint: disable=E1101 self.filename = os.path.basename(self.input_file.name) try: self.mimetype = self.is_image(self.input_file_content) if not self.filename or '.' not in self.filename: self.filename = self.mimetype.replace('/', '.') except TelegramError: if self.filename: self.mimetype = mimetypes.guess_type( self.filename)[0] or DEFAULT_MIME_TYPE else: self.mimetype = DEFAULT_MIME_TYPE
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
def __init__(self, data): self.data = data self.boundary = choose_boundary() if 'audio' in data: self.input_name = 'audio' self.input_file = data.pop('audio') if 'document' in data: self.input_name = 'document' self.input_file = data.pop('document') if 'photo' in data: self.input_name = 'photo' self.input_file = data.pop('photo') if 'sticker' in data: self.input_name = 'sticker' self.input_file = data.pop('sticker') if 'video' in data: self.input_name = 'video' self.input_file = data.pop('video') if 'voice' in data: self.input_name = 'voice' self.input_file = data.pop('voice') if 'certificate' in data: self.input_name = 'certificate' self.input_file = data.pop('certificate') if str(self.input_file).startswith('http'): from_url = True self.input_file = urlopen(self.input_file) else: from_url = False if hasattr(self.input_file, 'read') or from_url: self.filename = None self.input_file_content = self.input_file.read() if 'filename' in data: self.filename = self.data.pop('filename') elif hasattr(self.input_file, 'name'): # on py2.7, pylint fails to understand this properly # pylint: disable=E1101 self.filename = os.path.basename(self.input_file.name) elif from_url: self.filename = os.path.basename(self.input_file.url) \ .split('?')[0].split('&')[0] try: self.mimetype = InputFile.is_image(self.input_file_content) if not self.filename or '.' not in self.filename: self.filename = self.mimetype.replace('/', '.') except TelegramError: self.mimetype = mimetypes.guess_type(self.filename)[0] or \ DEFAULT_MIME_TYPE
def post_files(self, url, json_data, files=None): if not files or not isinstance(files, dict): raise ValueError('One or more files is required. Files should be ' 'passed as a dictionary of filename: file-like-' 'object.') boundary = choose_boundary() form_files = [] for i, (filename, file_obj) in enumerate(files.items()): try: data = file_obj.read() except AttributeError: data = bytes(file_obj) mimetype = mimetypes.guess_type(filename)[0] form_files.append(( 'file_%s' % i, filename, mimetype or 'application/octet-stream', data)) part_boundary = '--' + boundary parts = [ part_boundary, 'Content-Disposition: form-data; name="data"', '', json.dumps(json_data)] for field_name, filename, mimetype, data in form_files: parts.extend(( part_boundary, 'Content-Disposition: file; name="%s"; filename="%s"' % ( field_name, filename), 'Content-Type: %s' % mimetype, '', data)) parts.append('--' + boundary + '--') parts.append('') headers = {'Content-Type': 'multipart/form-data; boundary=%s' % boundary} if self.key: headers['key'] = self.key data = '\r\n'.join(parts) if not isinstance(data, bytes): data = data.encode('utf-8') request = Request(self.get_full_url(url), data=data, headers=headers) return json.loads(urlopen(request).read())
def __init__(self, data): self.data = data self.boundary = choose_boundary() if 'audio' in data: self.input_name = 'audio' self.input_file = data.pop('audio') elif 'document' in data: self.input_name = 'document' self.input_file = data.pop('document') elif 'photo' in data: self.input_name = 'photo' self.input_file = data.pop('photo') elif 'sticker' in data: self.input_name = 'sticker' self.input_file = data.pop('sticker') elif 'video' in data: self.input_name = 'video' self.input_file = data.pop('video') elif 'voice' in data: self.input_name = 'voice' self.input_file = data.pop('voice') elif 'certificate' in data: self.input_name = 'certificate' self.input_file = data.pop('certificate') else: raise TelegramError('Unknown inputfile type') if hasattr(self.input_file, 'read'): self.filename = None self.input_file_content = self.input_file.read() if 'filename' in data: self.filename = self.data.pop('filename') elif hasattr(self.input_file, 'name'): # on py2.7, pylint fails to understand this properly # pylint: disable=E1101 self.filename = os.path.basename(self.input_file.name) try: self.mimetype = self.is_image(self.input_file_content) if not self.filename or '.' not in self.filename: self.filename = self.mimetype.replace('/', '.') except TelegramError: self.mimetype = mimetypes.guess_type( self.filename)[0] or DEFAULT_MIME_TYPE
def __init__(self, data): self.data = data self.boundary = choose_boundary() for t in FILE_TYPES: if t in data: self.input_name = t self.input_file = data.pop(t) break else: raise TelegramError('Unknown inputfile type') if hasattr(self.input_file, 'read'): self.filename = None self.input_file_content = self.input_file.read() if 'filename' in data: self.filename = self.data.pop('filename') elif (hasattr(self.input_file, 'name') and not isinstance(self.input_file.name, int) and # py3 self.input_file.name != '<fdopen>'): # py2 # on py2.7, pylint fails to understand this properly # pylint: disable=E1101 self.filename = os.path.basename(self.input_file.name) try: self.mimetype = self.is_image(self.input_file_content) if not self.filename or '.' not in self.filename: self.filename = self.mimetype.replace('/', '.') except TelegramError: if self.filename: self.mimetype = mimetypes.guess_type( self.filename)[0] or DEFAULT_MIME_TYPE else: self.mimetype = DEFAULT_MIME_TYPE if sys.version_info < (3,): if isinstance(self.filename, unicode): # flake8: noqa pylint: disable=E0602 self.filename = self.filename.encode('utf-8', 'replace')
def __init__(self): self.form_fields = [] self.files = [] self.boundary = choose_boundary() return
def multipart_encode(self, v_vars, files, boundary=None, buf=None): if six.PY3: if boundary is None: boundary = choose_boundary() if buf is None: buf = io.BytesIO() for(key, value) in v_vars: buf.write(b'--' + boundary.encode("utf-8") + b'\r\n') buf.write( b'Content-Disposition: form-data; name="' + key.encode("utf-8") + b'"' ) buf.write(b'\r\n\r\n' + value.encode("utf-8") + b'\r\n') for(key, fd) in files: try: filename = fd.name.split('/')[-1] except AttributeError: # Spoof a file name if the object doesn't have one. # This is designed to catch when the user submits # a StringIO object filename = 'temp.pdf' contenttype = mimetypes.guess_type(filename)[0] or \ b'application/octet-stream' buf.write(b'--' + boundary.encode("utf-8") + b'\r\n') buf.write( b'Content-Disposition: form-data; ' + b'name="' + key.encode("utf-8") + b'"; ' + b'filename="' + filename.encode("utf-8") + b'"\r\n' ) buf.write( b'Content-Type: ' + contenttype.encode("utf-8") + b'\r\n' ) fd.seek(0) buf.write( b'\r\n' + fd.read() + b'\r\n' ) buf.write(b'--') buf.write(boundary.encode("utf-8")) buf.write(b'--\r\n\r\n') buf = buf.getvalue() return boundary, buf else: if boundary is None: boundary = choose_boundary() if buf is None: buf = io.StringIO() for(key, value) in v_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: try: filename = fd.name.split('/')[-1] except AttributeError: # Spoof a file name if the object doesn't have one. # This is designed to catch when the user submits # a StringIO object filename = 'temp.pdf' 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 _serve_fileobj(fileobj, content_type, content_length, debug=False): """Internal. Set response.body to the given file object, perhaps ranged.""" response = cherrypy.serving.response # HTTP/1.0 didn't have Range/Accept-Ranges headers, or the 206 code request = cherrypy.serving.request if request.protocol >= (1, 1): response.headers["Accept-Ranges"] = "bytes" r = httputil.get_ranges(request.headers.get('Range'), content_length) if r == []: response.headers['Content-Range'] = "bytes */%s" % content_length message = "Invalid Range (first-byte-pos greater than Content-Length)" if debug: cherrypy.log(message, 'TOOLS.STATIC') raise cherrypy.HTTPError(416, message) if r: if len(r) == 1: # Return a single-part response. start, stop = r[0] if stop > content_length: stop = content_length r_len = stop - start if debug: cherrypy.log('Single part; start: %r, stop: %r' % (start, stop), 'TOOLS.STATIC') response.status = "206 Partial Content" response.headers['Content-Range'] = ( "bytes %s-%s/%s" % (start, stop - 1, content_length)) response.headers['Content-Length'] = r_len fileobj.seek(start) response.body = file_generator_limited(fileobj, r_len) else: # Return a multipart/byteranges response. response.status = "206 Partial Content" try: # Python 3 from email.generator import _make_boundary as choose_boundary except ImportError: # Python 2 from mimetools import choose_boundary boundary = choose_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 ntob("\r\n") for start, stop in r: if debug: cherrypy.log('Multipart; start: %r, stop: %r' % (start, stop), 'TOOLS.STATIC') yield ntob("--" + boundary, 'ascii') yield ntob("\r\nContent-type: %s" % content_type, 'ascii') yield ntob("\r\nContent-range: bytes %s-%s/%s\r\n\r\n" % (start, stop - 1, content_length), 'ascii') fileobj.seek(start) for chunk in file_generator_limited(fileobj, stop-start): yield chunk yield ntob("\r\n") # Final boundary yield ntob("--" + boundary + "--", 'ascii') # Apache compatibility: yield ntob("\r\n") response.body = file_ranges() return response.body else: if debug: cherrypy.log('No byteranges requested', 'TOOLS.STATIC') # Set Content-Length and use an iterable (file object) # this way CP won't load the whole file in memory response.headers['Content-Length'] = content_length response.body = fileobj return response.body
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: mod_since = None if mod_since is not None: if self.aq_parent._p_mtime: last_mod = int(self.aq_parent._p_mtime) else: last_mod = int(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.aq_parent._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.aq_parent._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, StringType): 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 = choose_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.aq_parent._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('\r\n--%s\r\n' % boundary) RESPONSE.write('Content-Type: %s\r\n' % self.content_type) RESPONSE.write( 'Content-Range: bytes %d-%d/%d\r\n\r\n' % (start, end - 1, self.size)) if isinstance(data, StringType): 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('\r\n--%s--\r\n' % boundary) return True