|
6 | 6 | and HEAD requests in a fairly straightforward manner. |
7 | 7 | """ |
8 | 8 |
|
9 | | -__version__ = "0.3.3" |
| 9 | +__version__ = "0.3.4" |
10 | 10 | __author__ = "yangyongbao@126.com" |
11 | 11 | __all__ = ["SimpleHTTPRequestHandler"] |
12 | 12 |
|
@@ -117,53 +117,82 @@ def deal_post_data(self): |
117 | 117 | remain_bytes = int(content_length) |
118 | 118 | except ValueError: |
119 | 119 | return False, "Invalid content-length header" |
| 120 | + if remain_bytes < 0: |
| 121 | + return False, "Invalid content-length header" |
120 | 122 | if remain_bytes > self.max_upload_size: |
121 | 123 | return False, "Upload exceeds the %d byte limit" % self.max_upload_size |
122 | | - line = self.rfile.readline() |
123 | | - remain_bytes -= len(line) |
124 | | - if boundary not in line: |
| 124 | + |
| 125 | + def read_upload_line(): |
| 126 | + line = self.rfile.readline() |
| 127 | + return line, len(line) |
| 128 | + |
| 129 | + def is_boundary_line(line): |
| 130 | + stripped = line.rstrip(b'\r\n') |
| 131 | + delimiter = b'--' + boundary |
| 132 | + return stripped == delimiter or stripped == delimiter + b'--' |
| 133 | + |
| 134 | + line, line_length = read_upload_line() |
| 135 | + remain_bytes -= line_length |
| 136 | + if not line or not is_boundary_line(line): |
125 | 137 | return False, "Content NOT begin with boundary" |
126 | | - line = self.rfile.readline() |
127 | | - remain_bytes -= len(line) |
128 | | - fn = re.findall(r'Content-Disposition.*name="file"; filename="(.*)"', line.decode('utf-8', 'replace')) |
| 138 | + |
| 139 | + part_headers = [] |
| 140 | + while remain_bytes > 0: |
| 141 | + line, line_length = read_upload_line() |
| 142 | + remain_bytes -= line_length |
| 143 | + if not line: |
| 144 | + return False, "Unexpected end of multipart headers" |
| 145 | + if line in (b'\r\n', b'\n'): |
| 146 | + break |
| 147 | + part_headers.append(line.decode('utf-8', 'replace')) |
| 148 | + else: |
| 149 | + return False, "Unexpected end of multipart headers" |
| 150 | + |
| 151 | + header_text = "".join(part_headers) |
| 152 | + fn = re.findall(r'Content-Disposition.*name="file"; filename="([^"]*)"', header_text) |
129 | 153 | if not fn: |
130 | 154 | return False, "Can't find out file name..." |
131 | 155 | fn = sanitize_upload_filename(fn[0]) |
132 | 156 | if not fn: |
133 | 157 | return False, "Unsafe upload file name" |
134 | 158 | path = translate_path(self.path) |
| 159 | + if not os.path.isdir(path): |
| 160 | + return False, "Upload target is not a directory" |
135 | 161 | target_path = os.path.join(path, fn) |
136 | 162 | while os.path.exists(target_path): |
137 | 163 | target_path += "_" |
138 | | - line = self.rfile.readline() |
139 | | - remain_bytes -= len(line) |
140 | | - line = self.rfile.readline() |
141 | | - remain_bytes -= len(line) |
142 | 164 | try: |
143 | 165 | out = open(target_path, 'wb') |
144 | 166 | except IOError: |
145 | 167 | return False, "Can't create file to write, do you have permission to write?" |
146 | 168 |
|
| 169 | + success = False |
147 | 170 | try: |
148 | | - pre_line = self.rfile.readline() |
149 | | - remain_bytes -= len(pre_line) |
| 171 | + pre_line = None |
150 | 172 | while remain_bytes > 0: |
151 | | - line = self.rfile.readline() |
152 | | - remain_bytes -= len(line) |
153 | | - if boundary in line: |
154 | | - pre_line = pre_line[0:-1] |
155 | | - if pre_line.endswith(b'\r'): |
| 173 | + line, line_length = read_upload_line() |
| 174 | + remain_bytes -= line_length |
| 175 | + if not line: |
| 176 | + return False, "Unexpected end of data." |
| 177 | + if is_boundary_line(line): |
| 178 | + if pre_line is not None: |
156 | 179 | pre_line = pre_line[0:-1] |
157 | | - out.write(pre_line) |
158 | | - out.close() |
| 180 | + if pre_line.endswith(b'\r'): |
| 181 | + pre_line = pre_line[0:-1] |
| 182 | + out.write(pre_line) |
| 183 | + success = True |
159 | 184 | return True, "File '%s' upload success!" % os.path.basename(target_path) |
160 | | - else: |
| 185 | + if pre_line is not None: |
161 | 186 | out.write(pre_line) |
162 | | - pre_line = line |
| 187 | + pre_line = line |
| 188 | + return False, "Unexpected end of data." |
163 | 189 | finally: |
164 | | - if not out.closed: |
165 | | - out.close() |
166 | | - return False, "Unexpected end of data." |
| 190 | + out.close() |
| 191 | + if not success: |
| 192 | + try: |
| 193 | + os.remove(target_path) |
| 194 | + except OSError: |
| 195 | + pass |
167 | 196 |
|
168 | 197 | def send_head(self): |
169 | 198 | """Common code for GET and HEAD commands. |
|
0 commit comments