1
2 """Request filter to handle multipart uploads from buggy Flash clients."""
3
4 __all__ = [
5 'SafeMultipartFilter',
6 'MultipartWrapper',
7 ]
8
9 import logging
10 import re
11
12 import cherrypy
13 from turbogears import config
14
15
16 log = logging.getLogger('turbogears.filters')
17
18
19
21 r"""Wraps a file-like object, returning '' when Content-Length is reached.
22
23 The cgi module's logic for reading multipart MIME messages doesn't
24 allow the parts to know when the Content-Length for the entire message
25 has been reached, and doesn't allow for multipart-MIME messages that
26 omit the trailing CRLF (Flash 8's FileReference.upload(url), for example,
27 does this). The read_lines_to_outerboundary function gets stuck in a loop
28 until the socket times out.
29
30 This rfile wrapper simply monitors the incoming stream. When a read is
31 attempted past the Content-Length, it returns an empty string rather
32 than timing out (of course, if the last read *overlaps* the C-L, you'll
33 get the last bit of data up to C-L, and then the next read will return
34 an empty string).
35
36 """
38 self.rfile = rfile
39 self.clen = clen
40 self.bytes_read = 0
41
42 - def read(self, size=None):
43 if self.clen:
44
45 if self.bytes_read >= self.clen:
46 return ''
47
48
49 new_bytes_read = self.bytes_read + size
50 if new_bytes_read > self.clen:
51 size = self.clen - self.bytes_read
52
53 data = self.rfile.read(size)
54 self.bytes_read += len(data)
55 return data
56
58 if size is not None:
59 if self.clen:
60
61 if self.bytes_read >= self.clen:
62 return ''
63
64
65 new_bytes_read = self.bytes_read + size
66 if new_bytes_read > self.clen:
67 size = self.clen - self.bytes_read
68
69 data = self.rfile.readline(size)
70 self.bytes_read += len(data)
71 return data
72
73
74
75 res = []
76 size = 256
77 while True:
78 if self.clen:
79
80 if self.bytes_read >= self.clen:
81 return ''.join(res)
82
83
84 new_bytes_read = self.bytes_read + size
85 if new_bytes_read > self.clen:
86 size = self.clen - self.bytes_read
87
88 data = self.rfile.readline(size)
89 self.bytes_read += len(data)
90 res.append(data)
91
92 if len(data) < size or data[-1:] == "\n":
93 return ''.join(res)
94
96
97 total = 0
98 lines = []
99 line = self.readline()
100 while line:
101 lines.append(line)
102 total += len(line)
103 if 0 < sizehint <= total:
104 break
105 line = self.readline()
106 return lines
107
110
113
115 if self.clen:
116
117 if self.bytes_read >= self.clen:
118 return ''
119
120 data = self.rfile.next()
121 self.bytes_read += len(data)
122 return data
123
124
126 """CherryPy filter to handle buggy multipart requests from Flash clients."""
127
128 flash_ua_rx = re.compile(r'(Shockwave|Adobe)\s+Flash.*')
129
131 if not config.get("safempfilter.on", False):
132 return
133 log.debug("Using FlashMultipartFilter for request %s.",
134 cherrypy.request.path)
135 ct = cherrypy.request.headers.get('Content-Type', '')
136 ua = cherrypy.request.headers.get('User-Agent', '')
137
138 if ct.startswith('multipart/') and self.flash_ua_rx.match(ua):
139 log.debug("Detected Flash client multipart request. "
140 "Using MulipartWrapper to read request")
141 clen = cherrypy.request.headers.get('Content-Length', '0')
142 try:
143 clen = int(clen)
144 except ValueError:
145 return
146 cherrypy.request.rfile = MultipartWrapper(
147 cherrypy.request.rfile, clen)
148