Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(244)

Side by Side Diff: runtime/bin/mime_multipart_parser.dart

Issue 11337019: Use patching for dart:io. (Closed) Base URL: https://dart.googlecode.com/svn/branches/bleeding_edge/dart
Patch Set: Address comments Created 8 years, 1 month ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
(Empty)
1 // Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file
2 // for details. All rights reserved. Use of this source code is governed by a
3 // BSD-style license that can be found in the LICENSE file.
4
5 /**
6 * Parser for MIME multipart types of data as described in RFC 2046
7 * section 5.1.1. The data to parse is supplied through the [:update:]
8 * method. As the data is parsed the following callbacks are called:
9 *
10 * [:partStart;
11 * [:headerReceived;
12 * [:headersComplete;
13 * [:partDataReceived;
14 * [:partEnd;
15 * [:error:]
16 */
17
18 class _MimeMultipartParser {
19 const int _START = 0;
20 const int _FIRST_BOUNDARY_ENDING = 111;
21 const int _FIRST_BOUNDARY_END = 112;
22 const int _BOUNDARY_ENDING = 1;
23 const int _BOUNDARY_END = 2;
24 const int _HEADER_START = 3;
25 const int _HEADER_FIELD = 4;
26 const int _HEADER_VALUE_START = 5;
27 const int _HEADER_VALUE = 6;
28 const int _HEADER_VALUE_FOLDING_OR_ENDING = 7;
29 const int _HEADER_VALUE_FOLD_OR_END = 8;
30 const int _HEADER_ENDING = 9;
31 const int _CONTENT = 10;
32 const int _LAST_BOUNDARY_DASH2 = 11;
33 const int _LAST_BOUNDARY_ENDING = 12;
34 const int _LAST_BOUNDARY_END = 13;
35 const int _DONE = 14;
36 const int _FAILURE = 15;
37
38 // Construct a new MIME multipart parser with the boundary
39 // [boundary]. The boundary should be as specified in the content
40 // type parameter, that is without the -- prefix.
41 _MimeMultipartParser(String boundary) {
42 List<int> charCodes = boundary.charCodes;
43 _boundary = new List<int>(4 + charCodes.length);
44 // Set-up the matching boundary preceding it with CRLF and two
45 // dashes.
46 _boundary[0] = _CharCode.CR;
47 _boundary[1] = _CharCode.LF;
48 _boundary[2] = _CharCode.DASH;
49 _boundary[3] = _CharCode.DASH;
50 _boundary.setRange(4, charCodes.length, charCodes);
51 _state = _START;
52 _headerField = new StringBuffer();
53 _headerValue = new StringBuffer();
54 }
55
56 int update(List<int> buffer, int offset, int count) {
57 // Current index in the data buffer. If index is negative then it
58 // is the index into the artificial prefix of the boundary string.
59 int index;
60 // Number of boundary bytes to artificially place before the supplied data.
61 int boundaryPrefix = 0;
62 // Position where content starts. Will be null if no known content
63 // start exists. Will be negative of the content starts in the
64 // boundary prefix. Will be zero or position if the content starts
65 // in the current buffer.
66 int contentStartIndex;
67
68 // Function to report content data for the current part. The data
69 // reported is from the current content start index up til the
70 // current index. As the data can be artificially prefixed with a
71 // prefix of the boundary both the content start index and index
72 // can be negative.
73 void reportData() {
74 if (partDataReceived == null) return;
75
76 if (contentStartIndex < 0) {
77 var contentLength = boundaryPrefix + index - _boundaryIndex;
78 if (contentLength <= boundaryPrefix) {
79 partDataReceived(
80 _boundary.getRange(0, contentLength));
81 } else {
82 partDataReceived(
83 _boundary.getRange(0, boundaryPrefix));
84 partDataReceived(
85 buffer.getRange(0, contentLength - boundaryPrefix));
86 }
87 } else {
88 var contentLength = index - contentStartIndex - _boundaryIndex;
89 partDataReceived(
90 buffer.getRange(contentStartIndex, contentLength));
91 }
92 }
93
94 // Prepare for processing the buffer.
95 index = offset;
96 int lastIndex = offset + count;
97 if (_state == _CONTENT && _boundaryIndex == 0) {
98 contentStartIndex = 0;
99 } else {
100 contentStartIndex = null;
101 }
102 // The data to parse might be "artificially" prefixed with a
103 // partial match of the boundary.
104 boundaryPrefix = _boundaryIndex;
105
106 while ((index < lastIndex) && _state != _FAILURE && _state != _DONE) {
107 int byte;
108 if (index < 0) {
109 byte = _boundary[boundaryPrefix + index];
110 } else {
111 byte = buffer[index];
112 }
113 switch (_state) {
114 case _START:
115 if (_toLowerCase(byte) == _toLowerCase(_boundary[_boundaryIndex])) {
116 _boundaryIndex++;
117 if (_boundaryIndex == _boundary.length) {
118 _state = _FIRST_BOUNDARY_ENDING;
119 _boundaryIndex = 0;
120 }
121 } else {
122 // Restart matching of the boundary.
123 index = index - _boundaryIndex;
124 _boundaryIndex = 0;
125 }
126 break;
127
128 case _FIRST_BOUNDARY_ENDING:
129 if (byte == _CharCode.CR) {
130 _state = _FIRST_BOUNDARY_END;
131 } else {
132 _expectWS(byte);
133 }
134 break;
135
136 case _FIRST_BOUNDARY_END:
137 _expect(byte, _CharCode.LF);
138 _state = _HEADER_START;
139 break;
140
141 case _BOUNDARY_ENDING:
142 if (byte == _CharCode.CR) {
143 _state = _BOUNDARY_END;
144 } else if (byte == _CharCode.DASH) {
145 _state = _LAST_BOUNDARY_DASH2;
146 } else {
147 _expectWS(byte);
148 }
149 break;
150
151 case _BOUNDARY_END:
152 _expect(byte, _CharCode.LF);
153 if (partEnd != null) {
154 partEnd(false);
155 }
156 _state = _HEADER_START;
157 break;
158
159 case _HEADER_START:
160 if (byte == _CharCode.CR) {
161 _state = _HEADER_ENDING;
162 } else {
163 // Start of new header field.
164 _headerField.addCharCode(_toLowerCase(byte));
165 _state = _HEADER_FIELD;
166 }
167 break;
168
169 case _HEADER_FIELD:
170 if (byte == _CharCode.COLON) {
171 _state = _HEADER_VALUE_START;
172 } else {
173 if (!_isTokenChar(byte)) {
174 throw new MimeParserException("Invalid header field name");
175 }
176 _headerField.addCharCode(_toLowerCase(byte));
177 }
178 break;
179
180 case _HEADER_VALUE_START:
181 if (byte == _CharCode.CR) {
182 _state = _HEADER_VALUE_FOLDING_OR_ENDING;
183 } else if (byte != _CharCode.SP && byte != _CharCode.HT) {
184 // Start of new header value.
185 _headerValue.addCharCode(byte);
186 _state = _HEADER_VALUE;
187 }
188 break;
189
190 case _HEADER_VALUE:
191 if (byte == _CharCode.CR) {
192 _state = _HEADER_VALUE_FOLDING_OR_ENDING;
193 } else {
194 _headerValue.addCharCode(byte);
195 }
196 break;
197
198 case _HEADER_VALUE_FOLDING_OR_ENDING:
199 _expect(byte, _CharCode.LF);
200 _state = _HEADER_VALUE_FOLD_OR_END;
201 break;
202
203 case _HEADER_VALUE_FOLD_OR_END:
204 if (byte == _CharCode.SP || byte == _CharCode.HT) {
205 _state = _HEADER_VALUE_START;
206 } else {
207 String headerField = _headerField.toString();
208 String headerValue =_headerValue.toString();
209 if (headerReceived != null) {
210 headerReceived(headerField, headerValue);
211 }
212 _headerField.clear();
213 _headerValue.clear();
214 if (byte == _CharCode.CR) {
215 _state = _HEADER_ENDING;
216 } else {
217 // Start of new header field.
218 _headerField.addCharCode(_toLowerCase(byte));
219 _state = _HEADER_FIELD;
220 }
221 }
222 break;
223
224 case _HEADER_ENDING:
225 _expect(byte, _CharCode.LF);
226 if (headersComplete != null) headersComplete();
227 _state = _CONTENT;
228 contentStartIndex = index + 1;
229 break;
230
231 case _CONTENT:
232 if (_toLowerCase(byte) == _toLowerCase(_boundary[_boundaryIndex])) {
233 _boundaryIndex++;
234 if (_boundaryIndex == _boundary.length) {
235 if (contentStartIndex != null) {
236 index++;
237 reportData();
238 index--;
239 }
240 _boundaryIndex = 0;
241 _state = _BOUNDARY_ENDING;
242 }
243 } else {
244 // Restart matching of the boundary.
245 index = index - _boundaryIndex;
246 if (contentStartIndex == null) contentStartIndex = index;
247 _boundaryIndex = 0;
248 }
249 break;
250
251 case _LAST_BOUNDARY_DASH2:
252 _expect(byte, _CharCode.DASH);
253 _state = _LAST_BOUNDARY_ENDING;
254 break;
255
256 case _LAST_BOUNDARY_ENDING:
257 if (byte == _CharCode.CR) {
258 _state = _LAST_BOUNDARY_END;
259 } else {
260 _expectWS(byte);
261 }
262 break;
263
264 case _LAST_BOUNDARY_END:
265 _expect(byte, _CharCode.LF);
266 if (partEnd != null) {
267 partEnd(true);
268 }
269 _state = _DONE;
270 break;
271
272 default:
273 // Should be unreachable.
274 assert(false);
275 break;
276 }
277
278 // Move to the next byte.
279 index++;
280 }
281
282 // Report any known content.
283 if (_state == _CONTENT && contentStartIndex != null) {
284 reportData();
285 }
286 return index - offset;
287 }
288
289 bool _isTokenChar(int byte) {
290 return byte > 31 && byte < 128 && _Const.SEPARATORS.indexOf(byte) == -1;
291 }
292
293 int _toLowerCase(int byte) {
294 final int aCode = "A".charCodeAt(0);
295 final int zCode = "Z".charCodeAt(0);
296 final int delta = "a".charCodeAt(0) - aCode;
297 return (aCode <= byte && byte <= zCode) ? byte + delta : byte;
298 }
299
300 void _expect(int val1, int val2) {
301 if (val1 != val2) {
302 throw new MimeParserException("Failed to parse multipart mime 1");
303 }
304 }
305
306 void _expectWS(int byte) {
307 if (byte != _CharCode.SP && byte != _CharCode.HT) {
308 throw new MimeParserException("Failed to parse multipart mime 2");
309 }
310 }
311
312 List<int> _boundary;
313 int _state;
314 int _boundaryIndex = 0;
315
316 StringBuffer _headerField;
317 StringBuffer _headerValue;
318
319 Function partStart;
320 Function headerReceived;
321 Function headersComplete;
322 Function partDataReceived;
323 Function partEnd;
324 }
325
326
327 class MimeParserException implements Exception {
328 const MimeParserException([String this.message = ""]);
329 String toString() => "MimeParserException: $message";
330 final String message;
331 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698