Wed, 31 Dec 2014 06:09:35 +0100
Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /* This Source Code Form is subject to the terms of the Mozilla Public
3 * License, v. 2.0. If a copy of the MPL was not distributed with this
4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6 /**
7 * The MIME stream separates headers and a datastream. It also allows
8 * automatic creation of the content-length header.
9 */
11 #include "ipc/IPCMessageUtils.h"
13 #include "nsCOMPtr.h"
14 #include "nsComponentManagerUtils.h"
15 #include "nsIMultiplexInputStream.h"
16 #include "nsIMIMEInputStream.h"
17 #include "nsISeekableStream.h"
18 #include "nsIStringStream.h"
19 #include "nsString.h"
20 #include "nsMIMEInputStream.h"
21 #include "nsIClassInfoImpl.h"
22 #include "nsIIPCSerializableInputStream.h"
23 #include "mozilla/ipc/InputStreamUtils.h"
25 using namespace mozilla::ipc;
27 class nsMIMEInputStream : public nsIMIMEInputStream,
28 public nsISeekableStream,
29 public nsIIPCSerializableInputStream
30 {
31 public:
32 nsMIMEInputStream();
33 virtual ~nsMIMEInputStream();
35 NS_DECL_THREADSAFE_ISUPPORTS
36 NS_DECL_NSIINPUTSTREAM
37 NS_DECL_NSIMIMEINPUTSTREAM
38 NS_DECL_NSISEEKABLESTREAM
39 NS_DECL_NSIIPCSERIALIZABLEINPUTSTREAM
41 NS_METHOD Init();
43 private:
45 void InitStreams();
47 struct ReadSegmentsState {
48 nsIInputStream* mThisStream;
49 nsWriteSegmentFun mWriter;
50 void* mClosure;
51 };
52 static NS_METHOD ReadSegCb(nsIInputStream* aIn, void* aClosure,
53 const char* aFromRawSegment, uint32_t aToOffset,
54 uint32_t aCount, uint32_t *aWriteCount);
56 nsCString mHeaders;
57 nsCOMPtr<nsIStringInputStream> mHeaderStream;
59 nsCString mContentLength;
60 nsCOMPtr<nsIStringInputStream> mCLStream;
62 nsCOMPtr<nsIInputStream> mData;
63 nsCOMPtr<nsIMultiplexInputStream> mStream;
64 bool mAddContentLength;
65 bool mStartedReading;
66 };
68 NS_IMPL_ADDREF(nsMIMEInputStream)
69 NS_IMPL_RELEASE(nsMIMEInputStream)
71 NS_IMPL_CLASSINFO(nsMIMEInputStream, nullptr, nsIClassInfo::THREADSAFE,
72 NS_MIMEINPUTSTREAM_CID)
74 NS_IMPL_QUERY_INTERFACE_CI(nsMIMEInputStream,
75 nsIMIMEInputStream,
76 nsIInputStream,
77 nsISeekableStream,
78 nsIIPCSerializableInputStream)
79 NS_IMPL_CI_INTERFACE_GETTER(nsMIMEInputStream,
80 nsIMIMEInputStream,
81 nsIInputStream,
82 nsISeekableStream)
84 nsMIMEInputStream::nsMIMEInputStream() : mAddContentLength(false),
85 mStartedReading(false)
86 {
87 }
89 nsMIMEInputStream::~nsMIMEInputStream()
90 {
91 }
93 NS_METHOD nsMIMEInputStream::Init()
94 {
95 nsresult rv = NS_OK;
96 mStream = do_CreateInstance("@mozilla.org/io/multiplex-input-stream;1",
97 &rv);
98 NS_ENSURE_SUCCESS(rv, rv);
100 mHeaderStream = do_CreateInstance("@mozilla.org/io/string-input-stream;1",
101 &rv);
102 NS_ENSURE_SUCCESS(rv, rv);
103 mCLStream = do_CreateInstance("@mozilla.org/io/string-input-stream;1", &rv);
104 NS_ENSURE_SUCCESS(rv, rv);
106 rv = mStream->AppendStream(mHeaderStream);
107 NS_ENSURE_SUCCESS(rv, rv);
109 rv = mStream->AppendStream(mCLStream);
110 NS_ENSURE_SUCCESS(rv, rv);
112 return NS_OK;
113 }
116 /* attribute boolean addContentLength; */
117 NS_IMETHODIMP
118 nsMIMEInputStream::GetAddContentLength(bool *aAddContentLength)
119 {
120 *aAddContentLength = mAddContentLength;
121 return NS_OK;
122 }
123 NS_IMETHODIMP
124 nsMIMEInputStream::SetAddContentLength(bool aAddContentLength)
125 {
126 NS_ENSURE_FALSE(mStartedReading, NS_ERROR_FAILURE);
127 mAddContentLength = aAddContentLength;
128 return NS_OK;
129 }
131 /* void addHeader ([const] in string name, [const] in string value); */
132 NS_IMETHODIMP
133 nsMIMEInputStream::AddHeader(const char *aName, const char *aValue)
134 {
135 NS_ENSURE_FALSE(mStartedReading, NS_ERROR_FAILURE);
136 mHeaders.Append(aName);
137 mHeaders.AppendLiteral(": ");
138 mHeaders.Append(aValue);
139 mHeaders.AppendLiteral("\r\n");
141 // Just in case someone somehow uses our stream, lets at least
142 // let the stream have a valid pointer. The stream will be properly
143 // initialized in nsMIMEInputStream::InitStreams
144 mHeaderStream->ShareData(mHeaders.get(), 0);
146 return NS_OK;
147 }
149 /* void setData (in nsIInputStream stream); */
150 NS_IMETHODIMP
151 nsMIMEInputStream::SetData(nsIInputStream *aStream)
152 {
153 NS_ENSURE_FALSE(mStartedReading, NS_ERROR_FAILURE);
154 // Remove the old stream if there is one
155 if (mData)
156 mStream->RemoveStream(2);
158 mData = aStream;
159 if (aStream)
160 mStream->AppendStream(mData);
161 return NS_OK;
162 }
164 // set up the internal streams
165 void nsMIMEInputStream::InitStreams()
166 {
167 NS_ASSERTION(!mStartedReading,
168 "Don't call initStreams twice without rewinding");
170 mStartedReading = true;
172 // We'll use the content-length stream to add the final \r\n
173 if (mAddContentLength) {
174 uint64_t cl = 0;
175 if (mData) {
176 mData->Available(&cl);
177 }
178 mContentLength.AssignLiteral("Content-Length: ");
179 mContentLength.AppendInt(cl);
180 mContentLength.AppendLiteral("\r\n\r\n");
181 }
182 else {
183 mContentLength.AssignLiteral("\r\n");
184 }
185 mCLStream->ShareData(mContentLength.get(), -1);
186 mHeaderStream->ShareData(mHeaders.get(), -1);
187 }
191 #define INITSTREAMS \
192 if (!mStartedReading) { \
193 InitStreams(); \
194 }
196 // Reset mStartedReading when Seek-ing to start
197 NS_IMETHODIMP
198 nsMIMEInputStream::Seek(int32_t whence, int64_t offset)
199 {
200 nsresult rv;
201 nsCOMPtr<nsISeekableStream> stream = do_QueryInterface(mStream);
202 if (whence == NS_SEEK_SET && offset == 0) {
203 rv = stream->Seek(whence, offset);
204 if (NS_SUCCEEDED(rv))
205 mStartedReading = false;
206 }
207 else {
208 INITSTREAMS;
209 rv = stream->Seek(whence, offset);
210 }
212 return rv;
213 }
215 // Proxy ReadSegments since we need to be a good little nsIInputStream
216 NS_IMETHODIMP nsMIMEInputStream::ReadSegments(nsWriteSegmentFun aWriter,
217 void *aClosure, uint32_t aCount,
218 uint32_t *_retval)
219 {
220 INITSTREAMS;
221 ReadSegmentsState state;
222 state.mThisStream = this;
223 state.mWriter = aWriter;
224 state.mClosure = aClosure;
225 return mStream->ReadSegments(ReadSegCb, &state, aCount, _retval);
226 }
228 NS_METHOD
229 nsMIMEInputStream::ReadSegCb(nsIInputStream* aIn, void* aClosure,
230 const char* aFromRawSegment,
231 uint32_t aToOffset, uint32_t aCount,
232 uint32_t *aWriteCount)
233 {
234 ReadSegmentsState* state = (ReadSegmentsState*)aClosure;
235 return (state->mWriter)(state->mThisStream,
236 state->mClosure,
237 aFromRawSegment,
238 aToOffset,
239 aCount,
240 aWriteCount);
241 }
243 /**
244 * Forward everything else to the mStream after calling InitStreams()
245 */
247 // nsIInputStream
248 NS_IMETHODIMP nsMIMEInputStream::Close(void) { INITSTREAMS; return mStream->Close(); }
249 NS_IMETHODIMP nsMIMEInputStream::Available(uint64_t *_retval) { INITSTREAMS; return mStream->Available(_retval); }
250 NS_IMETHODIMP nsMIMEInputStream::Read(char * buf, uint32_t count, uint32_t *_retval) { INITSTREAMS; return mStream->Read(buf, count, _retval); }
251 NS_IMETHODIMP nsMIMEInputStream::IsNonBlocking(bool *aNonBlocking) { INITSTREAMS; return mStream->IsNonBlocking(aNonBlocking); }
253 // nsISeekableStream
254 NS_IMETHODIMP nsMIMEInputStream::Tell(int64_t *_retval)
255 {
256 INITSTREAMS;
257 nsCOMPtr<nsISeekableStream> stream = do_QueryInterface(mStream);
258 return stream->Tell(_retval);
259 }
260 NS_IMETHODIMP nsMIMEInputStream::SetEOF(void) {
261 INITSTREAMS;
262 nsCOMPtr<nsISeekableStream> stream = do_QueryInterface(mStream);
263 return stream->SetEOF();
264 }
267 /**
268 * Factory method used by do_CreateInstance
269 */
271 nsresult
272 nsMIMEInputStreamConstructor(nsISupports *outer, REFNSIID iid, void **result)
273 {
274 *result = nullptr;
276 if (outer)
277 return NS_ERROR_NO_AGGREGATION;
279 nsMIMEInputStream *inst = new nsMIMEInputStream();
280 if (!inst)
281 return NS_ERROR_OUT_OF_MEMORY;
283 NS_ADDREF(inst);
285 nsresult rv = inst->Init();
286 if (NS_FAILED(rv)) {
287 NS_RELEASE(inst);
288 return rv;
289 }
291 rv = inst->QueryInterface(iid, result);
292 NS_RELEASE(inst);
294 return rv;
295 }
297 void
298 nsMIMEInputStream::Serialize(InputStreamParams& aParams,
299 FileDescriptorArray& aFileDescriptors)
300 {
301 MIMEInputStreamParams params;
303 if (mData) {
304 nsCOMPtr<nsIInputStream> stream = do_QueryInterface(mData);
305 MOZ_ASSERT(stream);
307 InputStreamParams wrappedParams;
308 SerializeInputStream(stream, wrappedParams, aFileDescriptors);
310 NS_ASSERTION(wrappedParams.type() != InputStreamParams::T__None,
311 "Wrapped stream failed to serialize!");
313 params.optionalStream() = wrappedParams;
314 }
315 else {
316 params.optionalStream() = mozilla::void_t();
317 }
319 params.headers() = mHeaders;
320 params.contentLength() = mContentLength;
321 params.startedReading() = mStartedReading;
322 params.addContentLength() = mAddContentLength;
324 aParams = params;
325 }
327 bool
328 nsMIMEInputStream::Deserialize(const InputStreamParams& aParams,
329 const FileDescriptorArray& aFileDescriptors)
330 {
331 if (aParams.type() != InputStreamParams::TMIMEInputStreamParams) {
332 NS_ERROR("Received unknown parameters from the other process!");
333 return false;
334 }
336 const MIMEInputStreamParams& params =
337 aParams.get_MIMEInputStreamParams();
338 const OptionalInputStreamParams& wrappedParams = params.optionalStream();
340 mHeaders = params.headers();
341 mContentLength = params.contentLength();
342 mStartedReading = params.startedReading();
344 // nsMIMEInputStream::Init() already appended mHeaderStream & mCLStream
345 mHeaderStream->ShareData(mHeaders.get(),
346 mStartedReading ? mHeaders.Length() : 0);
347 mCLStream->ShareData(mContentLength.get(),
348 mStartedReading ? mContentLength.Length() : 0);
350 nsCOMPtr<nsIInputStream> stream;
351 if (wrappedParams.type() == OptionalInputStreamParams::TInputStreamParams) {
352 stream = DeserializeInputStream(wrappedParams.get_InputStreamParams(),
353 aFileDescriptors);
354 if (!stream) {
355 NS_WARNING("Failed to deserialize wrapped stream!");
356 return false;
357 }
359 mData = stream;
361 if (NS_FAILED(mStream->AppendStream(mData))) {
362 NS_WARNING("Failed to append stream!");
363 return false;
364 }
365 }
366 else {
367 NS_ASSERTION(wrappedParams.type() == OptionalInputStreamParams::Tvoid_t,
368 "Unknown type for OptionalInputStreamParams!");
369 }
371 mAddContentLength = params.addContentLength();
373 return true;
374 }