Line data Source code
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/. */
5 :
6 : /**
7 : * The MIME stream separates headers and a datastream. It also allows
8 : * automatic creation of the content-length header.
9 : */
10 :
11 : #include "ipc/IPCMessageUtils.h"
12 :
13 : #include "nsCOMPtr.h"
14 : #include "nsComponentManagerUtils.h"
15 : #include "nsIHttpHeaderVisitor.h"
16 : #include "nsIMIMEInputStream.h"
17 : #include "nsISeekableStream.h"
18 : #include "nsString.h"
19 : #include "nsMIMEInputStream.h"
20 : #include "nsIClassInfoImpl.h"
21 : #include "nsIIPCSerializableInputStream.h"
22 : #include "mozilla/Move.h"
23 : #include "mozilla/ipc/InputStreamUtils.h"
24 :
25 : using namespace mozilla::ipc;
26 : using mozilla::Maybe;
27 : using mozilla::Move;
28 :
29 : class nsMIMEInputStream : public nsIMIMEInputStream,
30 : public nsISeekableStream,
31 : public nsIIPCSerializableInputStream
32 : {
33 : virtual ~nsMIMEInputStream();
34 :
35 : public:
36 : nsMIMEInputStream();
37 :
38 : NS_DECL_THREADSAFE_ISUPPORTS
39 : NS_DECL_NSIINPUTSTREAM
40 : NS_DECL_NSIMIMEINPUTSTREAM
41 : NS_DECL_NSISEEKABLESTREAM
42 : NS_DECL_NSIIPCSERIALIZABLEINPUTSTREAM
43 :
44 : private:
45 :
46 : void InitStreams();
47 :
48 0 : struct MOZ_STACK_CLASS ReadSegmentsState {
49 : nsCOMPtr<nsIInputStream> mThisStream;
50 : nsWriteSegmentFun mWriter;
51 : void* mClosure;
52 : };
53 : static nsresult ReadSegCb(nsIInputStream* aIn, void* aClosure,
54 : const char* aFromRawSegment, uint32_t aToOffset,
55 : uint32_t aCount, uint32_t *aWriteCount);
56 :
57 : bool IsIPCSerializable() const;
58 :
59 : nsTArray<HeaderEntry> mHeaders;
60 :
61 : nsCOMPtr<nsIInputStream> mStream;
62 : bool mStartedReading;
63 : };
64 :
65 0 : NS_IMPL_ADDREF(nsMIMEInputStream)
66 0 : NS_IMPL_RELEASE(nsMIMEInputStream)
67 :
68 3 : NS_IMPL_CLASSINFO(nsMIMEInputStream, nullptr, nsIClassInfo::THREADSAFE,
69 : NS_MIMEINPUTSTREAM_CID)
70 :
71 0 : NS_INTERFACE_MAP_BEGIN(nsMIMEInputStream)
72 0 : NS_INTERFACE_MAP_ENTRY(nsIMIMEInputStream)
73 0 : NS_INTERFACE_MAP_ENTRY(nsIInputStream)
74 0 : NS_INTERFACE_MAP_ENTRY(nsISeekableStream)
75 0 : NS_INTERFACE_MAP_ENTRY_CONDITIONAL(nsIIPCSerializableInputStream,
76 : IsIPCSerializable())
77 0 : NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIMIMEInputStream)
78 0 : NS_IMPL_QUERY_CLASSINFO(nsMIMEInputStream)
79 0 : NS_INTERFACE_MAP_END
80 :
81 0 : NS_IMPL_CI_INTERFACE_GETTER(nsMIMEInputStream,
82 : nsIMIMEInputStream,
83 : nsIInputStream,
84 : nsISeekableStream)
85 :
86 0 : nsMIMEInputStream::nsMIMEInputStream() : mStartedReading(false)
87 : {
88 0 : }
89 :
90 0 : nsMIMEInputStream::~nsMIMEInputStream()
91 : {
92 0 : }
93 :
94 : NS_IMETHODIMP
95 0 : nsMIMEInputStream::GetAddContentLength(bool *aAddContentLength)
96 : {
97 0 : *aAddContentLength = true;
98 0 : return NS_OK;
99 : }
100 : NS_IMETHODIMP
101 0 : nsMIMEInputStream::SetAddContentLength(bool aAddContentLength)
102 : {
103 0 : NS_ENSURE_FALSE(mStartedReading, NS_ERROR_FAILURE);
104 0 : if (!aAddContentLength) {
105 : // Content-Length is automatically added by the channel when setting the
106 : // upload stream, so setting this to false has no practical effect.
107 0 : return NS_ERROR_FAILURE;
108 : }
109 0 : return NS_OK;
110 : }
111 :
112 : NS_IMETHODIMP
113 0 : nsMIMEInputStream::AddHeader(const char *aName, const char *aValue)
114 : {
115 0 : NS_ENSURE_FALSE(mStartedReading, NS_ERROR_FAILURE);
116 :
117 0 : HeaderEntry* entry = mHeaders.AppendElement();
118 0 : entry->name().Append(aName);
119 0 : entry->value().Append(aValue);
120 :
121 0 : return NS_OK;
122 : }
123 :
124 : NS_IMETHODIMP
125 0 : nsMIMEInputStream::VisitHeaders(nsIHttpHeaderVisitor *visitor)
126 : {
127 : nsresult rv;
128 :
129 0 : for (auto& header : mHeaders) {
130 0 : rv = visitor->VisitHeader(header.name(), header.value());
131 0 : if (NS_FAILED(rv)) {
132 0 : return rv;
133 : }
134 : }
135 0 : return NS_OK;
136 : }
137 :
138 : NS_IMETHODIMP
139 0 : nsMIMEInputStream::SetData(nsIInputStream *aStream)
140 : {
141 0 : NS_ENSURE_FALSE(mStartedReading, NS_ERROR_FAILURE);
142 :
143 0 : nsCOMPtr<nsISeekableStream> seekable = do_QueryInterface(aStream);
144 0 : if (!seekable) {
145 0 : return NS_ERROR_INVALID_ARG;
146 : }
147 :
148 0 : mStream = aStream;
149 0 : return NS_OK;
150 : }
151 :
152 : NS_IMETHODIMP
153 0 : nsMIMEInputStream::GetData(nsIInputStream **aStream)
154 : {
155 0 : NS_ENSURE_ARG_POINTER(aStream);
156 0 : *aStream = mStream;
157 0 : NS_IF_ADDREF(*aStream);
158 0 : return NS_OK;
159 : }
160 :
161 : // set up the internal streams
162 0 : void nsMIMEInputStream::InitStreams()
163 : {
164 0 : NS_ASSERTION(!mStartedReading,
165 : "Don't call initStreams twice without rewinding");
166 :
167 0 : mStartedReading = true;
168 0 : }
169 :
170 :
171 :
172 : #define INITSTREAMS \
173 : if (!mStartedReading) { \
174 : NS_ENSURE_TRUE(mStream, NS_ERROR_UNEXPECTED); \
175 : InitStreams(); \
176 : }
177 :
178 : // Reset mStartedReading when Seek-ing to start
179 : NS_IMETHODIMP
180 0 : nsMIMEInputStream::Seek(int32_t whence, int64_t offset)
181 : {
182 0 : NS_ENSURE_TRUE(mStream, NS_ERROR_UNEXPECTED);
183 :
184 : nsresult rv;
185 0 : nsCOMPtr<nsISeekableStream> stream = do_QueryInterface(mStream);
186 :
187 0 : if (whence == NS_SEEK_SET && offset == 0) {
188 0 : rv = stream->Seek(whence, offset);
189 0 : if (NS_SUCCEEDED(rv))
190 0 : mStartedReading = false;
191 : }
192 : else {
193 0 : INITSTREAMS;
194 0 : rv = stream->Seek(whence, offset);
195 : }
196 :
197 0 : return rv;
198 : }
199 :
200 : // Proxy ReadSegments since we need to be a good little nsIInputStream
201 0 : NS_IMETHODIMP nsMIMEInputStream::ReadSegments(nsWriteSegmentFun aWriter,
202 : void *aClosure, uint32_t aCount,
203 : uint32_t *_retval)
204 : {
205 0 : INITSTREAMS;
206 0 : ReadSegmentsState state;
207 0 : state.mThisStream = this;
208 0 : state.mWriter = aWriter;
209 0 : state.mClosure = aClosure;
210 0 : return mStream->ReadSegments(ReadSegCb, &state, aCount, _retval);
211 : }
212 :
213 : nsresult
214 0 : nsMIMEInputStream::ReadSegCb(nsIInputStream* aIn, void* aClosure,
215 : const char* aFromRawSegment,
216 : uint32_t aToOffset, uint32_t aCount,
217 : uint32_t *aWriteCount)
218 : {
219 0 : ReadSegmentsState* state = (ReadSegmentsState*)aClosure;
220 0 : return (state->mWriter)(state->mThisStream,
221 : state->mClosure,
222 : aFromRawSegment,
223 : aToOffset,
224 : aCount,
225 0 : aWriteCount);
226 : }
227 :
228 : /**
229 : * Forward everything else to the mStream after calling InitStreams()
230 : */
231 :
232 : // nsIInputStream
233 0 : NS_IMETHODIMP nsMIMEInputStream::Close(void) { INITSTREAMS; return mStream->Close(); }
234 0 : NS_IMETHODIMP nsMIMEInputStream::Available(uint64_t *_retval) { INITSTREAMS; return mStream->Available(_retval); }
235 0 : NS_IMETHODIMP nsMIMEInputStream::Read(char * buf, uint32_t count, uint32_t *_retval) { INITSTREAMS; return mStream->Read(buf, count, _retval); }
236 0 : NS_IMETHODIMP nsMIMEInputStream::IsNonBlocking(bool *aNonBlocking) { INITSTREAMS; return mStream->IsNonBlocking(aNonBlocking); }
237 :
238 : // nsISeekableStream
239 0 : NS_IMETHODIMP nsMIMEInputStream::Tell(int64_t *_retval)
240 : {
241 0 : INITSTREAMS;
242 0 : nsCOMPtr<nsISeekableStream> stream = do_QueryInterface(mStream);
243 0 : return stream->Tell(_retval);
244 : }
245 0 : NS_IMETHODIMP nsMIMEInputStream::SetEOF(void) {
246 0 : INITSTREAMS;
247 0 : nsCOMPtr<nsISeekableStream> stream = do_QueryInterface(mStream);
248 0 : return stream->SetEOF();
249 : }
250 :
251 :
252 : /**
253 : * Factory method used by do_CreateInstance
254 : */
255 :
256 : nsresult
257 0 : nsMIMEInputStreamConstructor(nsISupports *outer, REFNSIID iid, void **result)
258 : {
259 0 : *result = nullptr;
260 :
261 0 : if (outer)
262 0 : return NS_ERROR_NO_AGGREGATION;
263 :
264 0 : RefPtr<nsMIMEInputStream> inst = new nsMIMEInputStream();
265 0 : if (!inst)
266 0 : return NS_ERROR_OUT_OF_MEMORY;
267 :
268 0 : return inst->QueryInterface(iid, result);
269 : }
270 :
271 : void
272 0 : nsMIMEInputStream::Serialize(InputStreamParams& aParams,
273 : FileDescriptorArray& aFileDescriptors)
274 : {
275 0 : MIMEInputStreamParams params;
276 :
277 0 : if (mStream) {
278 0 : InputStreamParams wrappedParams;
279 0 : InputStreamHelper::SerializeInputStream(mStream, wrappedParams,
280 0 : aFileDescriptors);
281 :
282 0 : NS_ASSERTION(wrappedParams.type() != InputStreamParams::T__None,
283 : "Wrapped stream failed to serialize!");
284 :
285 0 : params.optionalStream() = wrappedParams;
286 : }
287 : else {
288 0 : params.optionalStream() = mozilla::void_t();
289 : }
290 :
291 0 : params.headers() = mHeaders;
292 0 : params.startedReading() = mStartedReading;
293 :
294 0 : aParams = params;
295 0 : }
296 :
297 : bool
298 0 : nsMIMEInputStream::Deserialize(const InputStreamParams& aParams,
299 : const FileDescriptorArray& aFileDescriptors)
300 : {
301 0 : if (aParams.type() != InputStreamParams::TMIMEInputStreamParams) {
302 0 : NS_ERROR("Received unknown parameters from the other process!");
303 0 : return false;
304 : }
305 :
306 : const MIMEInputStreamParams& params =
307 0 : aParams.get_MIMEInputStreamParams();
308 0 : const OptionalInputStreamParams& wrappedParams = params.optionalStream();
309 :
310 0 : mHeaders = params.headers();
311 0 : mStartedReading = params.startedReading();
312 :
313 0 : if (wrappedParams.type() == OptionalInputStreamParams::TInputStreamParams) {
314 0 : nsCOMPtr<nsIInputStream> stream;
315 : stream =
316 0 : InputStreamHelper::DeserializeInputStream(wrappedParams.get_InputStreamParams(),
317 0 : aFileDescriptors);
318 0 : if (!stream) {
319 0 : NS_WARNING("Failed to deserialize wrapped stream!");
320 0 : return false;
321 : }
322 :
323 0 : mStream = stream;
324 : }
325 : else {
326 0 : NS_ASSERTION(wrappedParams.type() == OptionalInputStreamParams::Tvoid_t,
327 : "Unknown type for OptionalInputStreamParams!");
328 : }
329 :
330 0 : return true;
331 : }
332 :
333 : Maybe<uint64_t>
334 0 : nsMIMEInputStream::ExpectedSerializedLength()
335 : {
336 0 : nsCOMPtr<nsIIPCSerializableInputStream> serializable = do_QueryInterface(mStream);
337 0 : return serializable ? serializable->ExpectedSerializedLength() : Nothing();
338 : }
339 :
340 : bool
341 0 : nsMIMEInputStream::IsIPCSerializable() const
342 : {
343 : // If SetData() or Deserialize() has not be called yet, mStream is null.
344 0 : if (!mStream) {
345 0 : return true;
346 : }
347 :
348 0 : nsCOMPtr<nsIIPCSerializableInputStream> serializable = do_QueryInterface(mStream);
349 0 : return !!serializable;
350 : }
|