|
1 const Cc = Components.classes; |
|
2 const Ci = Components.interfaces; |
|
3 const CC = Components.Constructor; |
|
4 |
|
5 const BinaryInputStream = CC("@mozilla.org/binaryinputstream;1", |
|
6 "nsIBinaryInputStream", |
|
7 "setInputStream"); |
|
8 |
|
9 function parseHeaders(data, start) |
|
10 { |
|
11 let headers = {}; |
|
12 |
|
13 while (true) { |
|
14 let done = false; |
|
15 let end = data.indexOf("\r\n", start); |
|
16 if (end == -1) { |
|
17 done = true; |
|
18 end = data.length; |
|
19 } |
|
20 let line = data.substring(start, end); |
|
21 start = end + 2; |
|
22 if (line == "") |
|
23 // empty line, we're done |
|
24 break; |
|
25 |
|
26 //XXX: this doesn't handle multi-line headers. do we care? |
|
27 let [name, value] = line.split(':'); |
|
28 //XXX: not normalized, should probably use nsHttpHeaders or something |
|
29 headers[name] = value.trimLeft(); |
|
30 } |
|
31 return [headers, start]; |
|
32 } |
|
33 |
|
34 function parseMultipartForm(request) |
|
35 { |
|
36 let boundary = null; |
|
37 // See if this is a multipart/form-data request, and if so, find the |
|
38 // boundary string |
|
39 if (request.hasHeader("Content-Type")) { |
|
40 var contenttype = request.getHeader("Content-Type"); |
|
41 var bits = contenttype.split(";"); |
|
42 if (bits[0] == "multipart/form-data") { |
|
43 for (var i = 1; i < bits.length; i++) { |
|
44 var b = bits[i].trimLeft(); |
|
45 if (b.indexOf("boundary=") == 0) { |
|
46 // grab everything after boundary= |
|
47 boundary = "--" + b.substring(9); |
|
48 break; |
|
49 } |
|
50 } |
|
51 } |
|
52 } |
|
53 if (boundary == null) |
|
54 return null; |
|
55 |
|
56 let body = new BinaryInputStream(request.bodyInputStream); |
|
57 let avail; |
|
58 let bytes = []; |
|
59 while ((avail = body.available()) > 0) { |
|
60 let readBytes = body.readByteArray(avail); |
|
61 for (let b of readBytes) { |
|
62 bytes.push(b); |
|
63 } |
|
64 } |
|
65 let data = ""; |
|
66 for (let b of bytes) { |
|
67 data += String.fromCharCode(b); |
|
68 } |
|
69 let formData = {}; |
|
70 let done = false; |
|
71 let start = 0; |
|
72 while (true) { |
|
73 // read first line |
|
74 let end = data.indexOf("\r\n", start); |
|
75 if (end == -1) { |
|
76 done = true; |
|
77 end = data.length; |
|
78 } |
|
79 |
|
80 let line = data.substring(start, end); |
|
81 // look for closing boundary delimiter line |
|
82 if (line == boundary + "--") { |
|
83 break; |
|
84 } |
|
85 |
|
86 if (line != boundary) { |
|
87 dump("expected boundary line but didn't find it!"); |
|
88 break; |
|
89 } |
|
90 |
|
91 // parse headers |
|
92 start = end + 2; |
|
93 let headers = null; |
|
94 [headers, start] = parseHeaders(data, start); |
|
95 |
|
96 // find next boundary string |
|
97 end = data.indexOf("\r\n" + boundary, start); |
|
98 if (end == -1) { |
|
99 dump("couldn't find next boundary string\n"); |
|
100 break; |
|
101 } |
|
102 |
|
103 // read part data, stick in formData using Content-Disposition header |
|
104 let part = data.substring(start, end); |
|
105 start = end + 2; |
|
106 |
|
107 if ("Content-Disposition" in headers) { |
|
108 let bits = headers["Content-Disposition"].split(';'); |
|
109 if (bits[0] == 'form-data') { |
|
110 for (let i = 0; i < bits.length; i++) { |
|
111 let b = bits[i].trimLeft(); |
|
112 if (b.indexOf('name=') == 0) { |
|
113 //TODO: handle non-ascii here? |
|
114 let name = b.substring(6, b.length - 1); |
|
115 //TODO: handle multiple-value properties? |
|
116 formData[name] = part; |
|
117 } |
|
118 //TODO: handle filename= ? |
|
119 //TODO: handle multipart/mixed for multi-file uploads? |
|
120 } |
|
121 } |
|
122 } |
|
123 } |
|
124 return formData; |
|
125 } |
|
126 |
|
127 function handleRequest(request, response) |
|
128 { |
|
129 if (request.method == "GET") { |
|
130 let id = null; |
|
131 for each(p in request.queryString.split('&')) { |
|
132 let [key, value] = p.split('='); |
|
133 if (key == 'id') |
|
134 id = value; |
|
135 } |
|
136 if (id == null) { |
|
137 response.setStatusLine(request.httpVersion, 400, "Bad Request"); |
|
138 response.write("Missing id parameter"); |
|
139 } |
|
140 else { |
|
141 let data = getState(id); |
|
142 if (data == "") { |
|
143 response.setStatusLine(request.httpVersion, 404, "Not Found"); |
|
144 response.write("Not Found"); |
|
145 } |
|
146 else { |
|
147 response.setHeader("Content-Type", "text/plain", false); |
|
148 response.write(data); |
|
149 } |
|
150 } |
|
151 } |
|
152 else if (request.method == "POST") { |
|
153 let formData = parseMultipartForm(request); |
|
154 |
|
155 if (formData && 'upload_file_minidump' in formData) { |
|
156 response.setHeader("Content-Type", "text/plain", false); |
|
157 |
|
158 let uuidGenerator = Cc["@mozilla.org/uuid-generator;1"] |
|
159 .getService(Ci.nsIUUIDGenerator); |
|
160 let uuid = uuidGenerator.generateUUID().toString(); |
|
161 // ditch the {}, add bp- prefix |
|
162 uuid = 'bp-' + uuid.substring(1,uuid.length-2); |
|
163 |
|
164 let d = JSON.stringify(formData); |
|
165 //dump('saving crash report ' + uuid + ': ' + d + '\n'); |
|
166 setState(uuid, d); |
|
167 |
|
168 response.write("CrashID=" + uuid + "\n"); |
|
169 } |
|
170 else { |
|
171 dump('*** crashreport.sjs: Malformed request?\n'); |
|
172 response.setStatusLine(request.httpVersion, 400, "Bad Request"); |
|
173 response.write("Missing minidump file"); |
|
174 } |
|
175 } |
|
176 else { |
|
177 response.setStatusLine(request.httpVersion, 405, "Method not allowed"); |
|
178 response.write("Can't handle HTTP method " + request.method); |
|
179 } |
|
180 } |