comunicationLeap/node_modules/formidable/lib/multipart_parser.js
node_modules/formidable/lib/multipart_parser.jsRaw
1var Buffer = require('buffer').Buffer,
2 s = 0,
3 S =
4 { PARSER_UNINITIALIZED: s++,
5 START: s++,
6 START_BOUNDARY: s++,
7 HEADER_FIELD_START: s++,
8 HEADER_FIELD: s++,
9 HEADER_VALUE_START: s++,
10 HEADER_VALUE: s++,
11 HEADER_VALUE_ALMOST_DONE: s++,
12 HEADERS_ALMOST_DONE: s++,
13 PART_DATA_START: s++,
14 PART_DATA: s++,
15 PART_END: s++,
16 END: s++
17 },
18
19 f = 1,
20 F =
21 { PART_BOUNDARY: f,
22 LAST_BOUNDARY: f *= 2
23 },
24
25 LF = 10,
26 CR = 13,
27 SPACE = 32,
28 HYPHEN = 45,
29 COLON = 58,
30 A = 97,
31 Z = 122,
32
33 lower = function(c) {
34 return c | 0x20;
35 };
36
37for (s in S) {
38 exports[s] = S[s];
39}
40
41function MultipartParser() {
42 this.boundary = null;
43 this.boundaryChars = null;
44 this.lookbehind = null;
45 this.state = S.PARSER_UNINITIALIZED;
46
47 this.index = null;
48 this.flags = 0;
49};
50exports.MultipartParser = MultipartParser;
51
52MultipartParser.stateToString = function(stateNumber) {
53 for (var state in S) {
54 var number = S[state];
55 if (number === stateNumber) return state;
56 }
57};
58
59MultipartParser.prototype.initWithBoundary = function(str) {
60 this.boundary = new Buffer(str.length+4);
61 this.boundary.write('\r\n--', 'ascii', 0);
62 this.boundary.write(str, 'ascii', 4);
63 this.lookbehind = new Buffer(this.boundary.length+8);
64 this.state = S.START;
65
66 this.boundaryChars = {};
67 for (var i = 0; i < this.boundary.length; i++) {
68 this.boundaryChars[this.boundary[i]] = true;
69 }
70};
71
72MultipartParser.prototype.write = function(buffer) {
73 var self = this,
74 i = 0,
75 len = buffer.length,
76 prevIndex = this.index,
77 index = this.index,
78 state = this.state,
79 flags = this.flags,
80 lookbehind = this.lookbehind,
81 boundary = this.boundary,
82 boundaryChars = this.boundaryChars,
83 boundaryLength = this.boundary.length,
84 boundaryEnd = boundaryLength - 1,
85 bufferLength = buffer.length,
86 c,
87 cl,
88
89 mark = function(name) {
90 self[name+'Mark'] = i;
91 },
92 clear = function(name) {
93 delete self[name+'Mark'];
94 },
95 callback = function(name, buffer, start, end) {
96 if (start !== undefined && start === end) {
97 return;
98 }
99
100 var callbackSymbol = 'on'+name.substr(0, 1).toUpperCase()+name.substr(1);
101 if (callbackSymbol in self) {
102 self[callbackSymbol](buffer, start, end);
103 }
104 },
105 dataCallback = function(name, clear) {
106 var markSymbol = name+'Mark';
107 if (!(markSymbol in self)) {
108 return;
109 }
110
111 if (!clear) {
112 callback(name, buffer, self[markSymbol], buffer.length);
113 self[markSymbol] = 0;
114 } else {
115 callback(name, buffer, self[markSymbol], i);
116 delete self[markSymbol];
117 }
118 };
119
120 for (i = 0; i < len; i++) {
121 c = buffer[i];
122 switch (state) {
123 case S.PARSER_UNINITIALIZED:
124 return i;
125 case S.START:
126 index = 0;
127 state = S.START_BOUNDARY;
128 case S.START_BOUNDARY:
129 if (index == boundary.length - 2) {
130 if (c != CR) {
131 return i;
132 }
133 index++;
134 break;
135 } else if (index - 1 == boundary.length - 2) {
136 if (c != LF) {
137 return i;
138 }
139 index = 0;
140 callback('partBegin');
141 state = S.HEADER_FIELD_START;
142 break;
143 }
144
145 if (c != boundary[index+2]) {
146 index = -2;
147 }
148 if (c == boundary[index+2]) {
149 index++;
150 }
151 break;
152 case S.HEADER_FIELD_START:
153 state = S.HEADER_FIELD;
154 mark('headerField');
155 index = 0;
156 case S.HEADER_FIELD:
157 if (c == CR) {
158 clear('headerField');
159 state = S.HEADERS_ALMOST_DONE;
160 break;
161 }
162
163 index++;
164 if (c == HYPHEN) {
165 break;
166 }
167
168 if (c == COLON) {
169 if (index == 1) {
170 // empty header field
171 return i;
172 }
173 dataCallback('headerField', true);
174 state = S.HEADER_VALUE_START;
175 break;
176 }
177
178 cl = lower(c);
179 if (cl < A || cl > Z) {
180 return i;
181 }
182 break;
183 case S.HEADER_VALUE_START:
184 if (c == SPACE) {
185 break;
186 }
187
188 mark('headerValue');
189 state = S.HEADER_VALUE;
190 case S.HEADER_VALUE:
191 if (c == CR) {
192 dataCallback('headerValue', true);
193 callback('headerEnd');
194 state = S.HEADER_VALUE_ALMOST_DONE;
195 }
196 break;
197 case S.HEADER_VALUE_ALMOST_DONE:
198 if (c != LF) {
199 return i;
200 }
201 state = S.HEADER_FIELD_START;
202 break;
203 case S.HEADERS_ALMOST_DONE:
204 if (c != LF) {
205 return i;
206 }
207
208 callback('headersEnd');
209 state = S.PART_DATA_START;
210 break;
211 case S.PART_DATA_START:
212 state = S.PART_DATA;
213 mark('partData');
214 case S.PART_DATA:
215 prevIndex = index;
216
217 if (index == 0) {
218 // boyer-moore derrived algorithm to safely skip non-boundary data
219 i += boundaryEnd;
220 while (i < bufferLength && !(buffer[i] in boundaryChars)) {
221 i += boundaryLength;
222 }
223 i -= boundaryEnd;
224 c = buffer[i];
225 }
226
227 if (index < boundary.length) {
228 if (boundary[index] == c) {
229 if (index == 0) {
230 dataCallback('partData', true);
231 }
232 index++;
233 } else {
234 index = 0;
235 }
236 } else if (index == boundary.length) {
237 index++;
238 if (c == CR) {
239 // CR = part boundary
240 flags |= F.PART_BOUNDARY;
241 } else if (c == HYPHEN) {
242 // HYPHEN = end boundary
243 flags |= F.LAST_BOUNDARY;
244 } else {
245 index = 0;
246 }
247 } else if (index - 1 == boundary.length) {
248 if (flags & F.PART_BOUNDARY) {
249 index = 0;
250 if (c == LF) {
251 // unset the PART_BOUNDARY flag
252 flags &= ~F.PART_BOUNDARY;
253 callback('partEnd');
254 callback('partBegin');
255 state = S.HEADER_FIELD_START;
256 break;
257 }
258 } else if (flags & F.LAST_BOUNDARY) {
259 if (c == HYPHEN) {
260 callback('partEnd');
261 callback('end');
262 state = S.END;
263 } else {
264 index = 0;
265 }
266 } else {
267 index = 0;
268 }
269 }
270
271 if (index > 0) {
272 // when matching a possible boundary, keep a lookbehind reference
273 // in case it turns out to be a false lead
274 lookbehind[index-1] = c;
275 } else if (prevIndex > 0) {
276 // if our boundary turned out to be rubbish, the captured lookbehind
277 // belongs to partData
278 callback('partData', lookbehind, 0, prevIndex);
279 prevIndex = 0;
280 mark('partData');
281
282 // reconsider the current character even so it interrupted the sequence
283 // it could be the beginning of a new sequence
284 i--;
285 }
286
287 break;
288 case S.END:
289 break;
290 default:
291 return i;
292 }
293 }
294
295 dataCallback('headerField');
296 dataCallback('headerValue');
297 dataCallback('partData');
298
299 this.index = index;
300 this.state = state;
301 this.flags = flags;
302
303 return len;
304};
305
306MultipartParser.prototype.end = function() {
307 var callback = function(self, name) {
308 var callbackSymbol = 'on'+name.substr(0, 1).toUpperCase()+name.substr(1);
309 if (callbackSymbol in self) {
310 self[callbackSymbol]();
311 }
312 };
313 if ((this.state == S.HEADER_FIELD_START && this.index == 0) ||
314 (this.state == S.PART_DATA && this.index == this.boundary.length)) {
315 callback(this, 'partEnd');
316 callback(this, 'end');
317 } else if (this.state != S.END) {
318 return new Error('MultipartParser.end(): stream ended unexpectedly: ' + this.explain());
319 }
320};
321
322MultipartParser.prototype.explain = function() {
323 return 'state = ' + MultipartParser.stateToString(this.state);
324};