michael@0: const Cc = Components.classes; michael@0: const Ci = Components.interfaces; michael@0: const CC = Components.Constructor; michael@0: michael@0: const BinaryInputStream = CC("@mozilla.org/binaryinputstream;1", michael@0: "nsIBinaryInputStream", michael@0: "setInputStream"); michael@0: michael@0: function parseHeaders(data, start) michael@0: { michael@0: let headers = {}; michael@0: michael@0: while (true) { michael@0: let done = false; michael@0: let end = data.indexOf("\r\n", start); michael@0: if (end == -1) { michael@0: done = true; michael@0: end = data.length; michael@0: } michael@0: let line = data.substring(start, end); michael@0: start = end + 2; michael@0: if (line == "") michael@0: // empty line, we're done michael@0: break; michael@0: michael@0: //XXX: this doesn't handle multi-line headers. do we care? michael@0: let [name, value] = line.split(':'); michael@0: //XXX: not normalized, should probably use nsHttpHeaders or something michael@0: headers[name] = value.trimLeft(); michael@0: } michael@0: return [headers, start]; michael@0: } michael@0: michael@0: function parseMultipartForm(request) michael@0: { michael@0: let boundary = null; michael@0: // See if this is a multipart/form-data request, and if so, find the michael@0: // boundary string michael@0: if (request.hasHeader("Content-Type")) { michael@0: var contenttype = request.getHeader("Content-Type"); michael@0: var bits = contenttype.split(";"); michael@0: if (bits[0] == "multipart/form-data") { michael@0: for (var i = 1; i < bits.length; i++) { michael@0: var b = bits[i].trimLeft(); michael@0: if (b.indexOf("boundary=") == 0) { michael@0: // grab everything after boundary= michael@0: boundary = "--" + b.substring(9); michael@0: break; michael@0: } michael@0: } michael@0: } michael@0: } michael@0: if (boundary == null) michael@0: return null; michael@0: michael@0: let body = new BinaryInputStream(request.bodyInputStream); michael@0: let avail; michael@0: let bytes = []; michael@0: while ((avail = body.available()) > 0) { michael@0: let readBytes = body.readByteArray(avail); michael@0: for (let b of readBytes) { michael@0: bytes.push(b); michael@0: } michael@0: } michael@0: let data = ""; michael@0: for (let b of bytes) { michael@0: data += String.fromCharCode(b); michael@0: } michael@0: let formData = {}; michael@0: let done = false; michael@0: let start = 0; michael@0: while (true) { michael@0: // read first line michael@0: let end = data.indexOf("\r\n", start); michael@0: if (end == -1) { michael@0: done = true; michael@0: end = data.length; michael@0: } michael@0: michael@0: let line = data.substring(start, end); michael@0: // look for closing boundary delimiter line michael@0: if (line == boundary + "--") { michael@0: break; michael@0: } michael@0: michael@0: if (line != boundary) { michael@0: dump("expected boundary line but didn't find it!"); michael@0: break; michael@0: } michael@0: michael@0: // parse headers michael@0: start = end + 2; michael@0: let headers = null; michael@0: [headers, start] = parseHeaders(data, start); michael@0: michael@0: // find next boundary string michael@0: end = data.indexOf("\r\n" + boundary, start); michael@0: if (end == -1) { michael@0: dump("couldn't find next boundary string\n"); michael@0: break; michael@0: } michael@0: michael@0: // read part data, stick in formData using Content-Disposition header michael@0: let part = data.substring(start, end); michael@0: start = end + 2; michael@0: michael@0: if ("Content-Disposition" in headers) { michael@0: let bits = headers["Content-Disposition"].split(';'); michael@0: if (bits[0] == 'form-data') { michael@0: for (let i = 0; i < bits.length; i++) { michael@0: let b = bits[i].trimLeft(); michael@0: if (b.indexOf('name=') == 0) { michael@0: //TODO: handle non-ascii here? michael@0: let name = b.substring(6, b.length - 1); michael@0: //TODO: handle multiple-value properties? michael@0: formData[name] = part; michael@0: } michael@0: //TODO: handle filename= ? michael@0: //TODO: handle multipart/mixed for multi-file uploads? michael@0: } michael@0: } michael@0: } michael@0: } michael@0: return formData; michael@0: } michael@0: michael@0: function handleRequest(request, response) michael@0: { michael@0: if (request.method == "GET") { michael@0: let id = null; michael@0: for each(p in request.queryString.split('&')) { michael@0: let [key, value] = p.split('='); michael@0: if (key == 'id') michael@0: id = value; michael@0: } michael@0: if (id == null) { michael@0: response.setStatusLine(request.httpVersion, 400, "Bad Request"); michael@0: response.write("Missing id parameter"); michael@0: } michael@0: else { michael@0: let data = getState(id); michael@0: if (data == "") { michael@0: response.setStatusLine(request.httpVersion, 404, "Not Found"); michael@0: response.write("Not Found"); michael@0: } michael@0: else { michael@0: response.setHeader("Content-Type", "text/plain", false); michael@0: response.write(data); michael@0: } michael@0: } michael@0: } michael@0: else if (request.method == "POST") { michael@0: let formData = parseMultipartForm(request); michael@0: michael@0: if (formData && 'upload_file_minidump' in formData) { michael@0: response.setHeader("Content-Type", "text/plain", false); michael@0: michael@0: let uuidGenerator = Cc["@mozilla.org/uuid-generator;1"] michael@0: .getService(Ci.nsIUUIDGenerator); michael@0: let uuid = uuidGenerator.generateUUID().toString(); michael@0: // ditch the {}, add bp- prefix michael@0: uuid = 'bp-' + uuid.substring(1,uuid.length-2); michael@0: michael@0: let d = JSON.stringify(formData); michael@0: //dump('saving crash report ' + uuid + ': ' + d + '\n'); michael@0: setState(uuid, d); michael@0: michael@0: response.write("CrashID=" + uuid + "\n"); michael@0: } michael@0: else { michael@0: dump('*** crashreport.sjs: Malformed request?\n'); michael@0: response.setStatusLine(request.httpVersion, 400, "Bad Request"); michael@0: response.write("Missing minidump file"); michael@0: } michael@0: } michael@0: else { michael@0: response.setStatusLine(request.httpVersion, 405, "Method not allowed"); michael@0: response.write("Can't handle HTTP method " + request.method); michael@0: } michael@0: }