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