arnaucube
arnaucube/comunicationLeapPublic Self-hosted repository
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};