1 : /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 : /* ***** BEGIN LICENSE BLOCK *****
3 : * Version: MPL 1.1/GPL 2.0/LGPL 2.1
4 : *
5 : * The contents of this file are subject to the Mozilla Public License Version
6 : * 1.1 (the "License"); you may not use this file except in compliance with
7 : * the License. You may obtain a copy of the License at
8 : * http://www.mozilla.org/MPL/
9 : *
10 : * Software distributed under the License is distributed on an "AS IS" basis,
11 : * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
12 : * for the specific language governing rights and limitations under the
13 : * License.
14 : *
15 : * The Original Code is frightening to behold.
16 : *
17 : * The Initial Developer of the Original Code is
18 : * Jonas Sicking.
19 : * Portions created by the Initial Developer are Copyright (C) 2001
20 : * the Initial Developer. All Rights Reserved.
21 : *
22 : * Contributor(s):
23 : * Jonas Sicking <sicking@bigfoot.com>
24 : *
25 : * Alternatively, the contents of this file may be used under the terms of
26 : * either the GNU General Public License Version 2 or later (the "GPL"), or
27 : * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
28 : * in which case the provisions of the GPL or the LGPL are applicable instead
29 : * of those above. If you wish to allow use of your version of this file only
30 : * under the terms of either the GPL or the LGPL, and not to allow others to
31 : * use your version of this file under the terms of the MPL, indicate your
32 : * decision by deleting the provisions above and replace them with the notice
33 : * and other provisions required by the GPL or the LGPL. If you do not delete
34 : * the provisions above, a recipient may use your version of this file under
35 : * the terms of any one of the MPL, the GPL or the LGPL.
36 : *
37 : * ***** END LICENSE BLOCK ***** */
38 :
39 : /**
40 : * The MIME stream separates headers and a datastream. It also allows
41 : * automatic creation of the content-length header.
42 : */
43 :
44 : #include "IPC/IPCMessageUtils.h"
45 : #include "mozilla/net/NeckoMessageUtils.h"
46 :
47 : #include "nsCOMPtr.h"
48 : #include "nsComponentManagerUtils.h"
49 : #include "nsIMultiplexInputStream.h"
50 : #include "nsIMIMEInputStream.h"
51 : #include "nsISeekableStream.h"
52 : #include "nsIStringStream.h"
53 : #include "nsString.h"
54 : #include "nsMIMEInputStream.h"
55 : #include "nsIIPCSerializable.h"
56 : #include "nsIClassInfoImpl.h"
57 :
58 : class nsMIMEInputStream : public nsIMIMEInputStream,
59 : public nsISeekableStream,
60 : public nsIIPCSerializable
61 : {
62 : public:
63 : nsMIMEInputStream();
64 : virtual ~nsMIMEInputStream();
65 :
66 : NS_DECL_ISUPPORTS
67 : NS_DECL_NSIINPUTSTREAM
68 : NS_DECL_NSIMIMEINPUTSTREAM
69 : NS_DECL_NSISEEKABLESTREAM
70 : NS_DECL_NSIIPCSERIALIZABLE
71 :
72 : NS_METHOD Init();
73 :
74 : private:
75 :
76 : void InitStreams();
77 :
78 : struct ReadSegmentsState {
79 : nsIInputStream* mThisStream;
80 : nsWriteSegmentFun mWriter;
81 : void* mClosure;
82 : };
83 : static NS_METHOD ReadSegCb(nsIInputStream* aIn, void* aClosure,
84 : const char* aFromRawSegment, PRUint32 aToOffset,
85 : PRUint32 aCount, PRUint32 *aWriteCount);
86 :
87 : nsCString mHeaders;
88 : nsCOMPtr<nsIStringInputStream> mHeaderStream;
89 :
90 : nsCString mContentLength;
91 : nsCOMPtr<nsIStringInputStream> mCLStream;
92 :
93 : nsCOMPtr<nsIInputStream> mData;
94 : nsCOMPtr<nsIMultiplexInputStream> mStream;
95 : bool mAddContentLength;
96 : bool mStartedReading;
97 : };
98 :
99 8 : NS_IMPL_THREADSAFE_ADDREF(nsMIMEInputStream)
100 8 : NS_IMPL_THREADSAFE_RELEASE(nsMIMEInputStream)
101 :
102 : NS_IMPL_CLASSINFO(nsMIMEInputStream, NULL, nsIClassInfo::THREADSAFE,
103 : NS_MIMEINPUTSTREAM_CID)
104 :
105 12 : NS_IMPL_QUERY_INTERFACE4_CI(nsMIMEInputStream,
106 : nsIMIMEInputStream,
107 : nsIInputStream,
108 : nsISeekableStream,
109 1 : nsIIPCSerializable)
110 1 : NS_IMPL_CI_INTERFACE_GETTER4(nsMIMEInputStream,
111 : nsIMIMEInputStream,
112 : nsIInputStream,
113 : nsISeekableStream,
114 1 : nsIIPCSerializable)
115 :
116 1 : nsMIMEInputStream::nsMIMEInputStream() : mAddContentLength(false),
117 1 : mStartedReading(false)
118 : {
119 1 : }
120 :
121 2 : nsMIMEInputStream::~nsMIMEInputStream()
122 : {
123 4 : }
124 :
125 1 : NS_METHOD nsMIMEInputStream::Init()
126 : {
127 1 : nsresult rv = NS_OK;
128 : mStream = do_CreateInstance("@mozilla.org/io/multiplex-input-stream;1",
129 1 : &rv);
130 1 : NS_ENSURE_SUCCESS(rv, rv);
131 :
132 : mHeaderStream = do_CreateInstance("@mozilla.org/io/string-input-stream;1",
133 1 : &rv);
134 1 : NS_ENSURE_SUCCESS(rv, rv);
135 1 : mCLStream = do_CreateInstance("@mozilla.org/io/string-input-stream;1", &rv);
136 1 : NS_ENSURE_SUCCESS(rv, rv);
137 :
138 1 : rv = mStream->AppendStream(mHeaderStream);
139 1 : NS_ENSURE_SUCCESS(rv, rv);
140 :
141 1 : rv = mStream->AppendStream(mCLStream);
142 1 : NS_ENSURE_SUCCESS(rv, rv);
143 :
144 1 : return NS_OK;
145 : }
146 :
147 :
148 : /* attribute boolean addContentLength; */
149 : NS_IMETHODIMP
150 0 : nsMIMEInputStream::GetAddContentLength(bool *aAddContentLength)
151 : {
152 0 : *aAddContentLength = mAddContentLength;
153 0 : return NS_OK;
154 : }
155 : NS_IMETHODIMP
156 1 : nsMIMEInputStream::SetAddContentLength(bool aAddContentLength)
157 : {
158 1 : NS_ENSURE_FALSE(mStartedReading, NS_ERROR_FAILURE);
159 1 : mAddContentLength = aAddContentLength;
160 1 : return NS_OK;
161 : }
162 :
163 : /* void addHeader ([const] in string name, [const] in string value); */
164 : NS_IMETHODIMP
165 1 : nsMIMEInputStream::AddHeader(const char *aName, const char *aValue)
166 : {
167 1 : NS_ENSURE_FALSE(mStartedReading, NS_ERROR_FAILURE);
168 1 : mHeaders.Append(aName);
169 1 : mHeaders.AppendLiteral(": ");
170 1 : mHeaders.Append(aValue);
171 1 : mHeaders.AppendLiteral("\r\n");
172 :
173 : // Just in case someone somehow uses our stream, lets at least
174 : // let the stream have a valid pointer. The stream will be properly
175 : // initialized in nsMIMEInputStream::InitStreams
176 1 : mHeaderStream->ShareData(mHeaders.get(), 0);
177 :
178 1 : return NS_OK;
179 : }
180 :
181 : /* void setData (in nsIInputStream stream); */
182 : NS_IMETHODIMP
183 1 : nsMIMEInputStream::SetData(nsIInputStream *aStream)
184 : {
185 1 : NS_ENSURE_FALSE(mStartedReading, NS_ERROR_FAILURE);
186 : // Remove the old stream if there is one
187 1 : if (mData)
188 0 : mStream->RemoveStream(2);
189 :
190 1 : mData = aStream;
191 1 : if (aStream)
192 1 : mStream->AppendStream(mData);
193 1 : return NS_OK;
194 : }
195 :
196 : // set up the internal streams
197 1 : void nsMIMEInputStream::InitStreams()
198 : {
199 1 : NS_ASSERTION(!mStartedReading,
200 : "Don't call initStreams twice without rewinding");
201 :
202 1 : mStartedReading = true;
203 :
204 : // We'll use the content-length stream to add the final \r\n
205 1 : if (mAddContentLength) {
206 1 : PRUint32 cl = 0;
207 1 : if (mData) {
208 1 : mData->Available(&cl);
209 : }
210 1 : mContentLength.AssignLiteral("Content-Length: ");
211 1 : mContentLength.AppendInt((PRInt32)cl);
212 1 : mContentLength.AppendLiteral("\r\n\r\n");
213 : }
214 : else {
215 0 : mContentLength.AssignLiteral("\r\n");
216 : }
217 1 : mCLStream->ShareData(mContentLength.get(), -1);
218 1 : mHeaderStream->ShareData(mHeaders.get(), -1);
219 1 : }
220 :
221 :
222 :
223 : #define INITSTREAMS \
224 : if (!mStartedReading) { \
225 : InitStreams(); \
226 : }
227 :
228 : // Reset mStartedReading when Seek-ing to start
229 : NS_IMETHODIMP
230 0 : nsMIMEInputStream::Seek(PRInt32 whence, PRInt64 offset)
231 : {
232 : nsresult rv;
233 0 : nsCOMPtr<nsISeekableStream> stream = do_QueryInterface(mStream);
234 0 : if (whence == NS_SEEK_SET && LL_EQ(offset, LL_Zero())) {
235 0 : rv = stream->Seek(whence, offset);
236 0 : if (NS_SUCCEEDED(rv))
237 0 : mStartedReading = false;
238 : }
239 : else {
240 0 : INITSTREAMS;
241 0 : rv = stream->Seek(whence, offset);
242 : }
243 :
244 0 : return rv;
245 : }
246 :
247 : // Proxy ReadSegments since we need to be a good little nsIInputStream
248 0 : NS_IMETHODIMP nsMIMEInputStream::ReadSegments(nsWriteSegmentFun aWriter,
249 : void *aClosure, PRUint32 aCount,
250 : PRUint32 *_retval)
251 : {
252 0 : INITSTREAMS;
253 : ReadSegmentsState state;
254 0 : state.mThisStream = this;
255 0 : state.mWriter = aWriter;
256 0 : state.mClosure = aClosure;
257 0 : return mStream->ReadSegments(ReadSegCb, &state, aCount, _retval);
258 : }
259 :
260 : NS_METHOD
261 0 : nsMIMEInputStream::ReadSegCb(nsIInputStream* aIn, void* aClosure,
262 : const char* aFromRawSegment,
263 : PRUint32 aToOffset, PRUint32 aCount,
264 : PRUint32 *aWriteCount)
265 : {
266 0 : ReadSegmentsState* state = (ReadSegmentsState*)aClosure;
267 : return (state->mWriter)(state->mThisStream,
268 : state->mClosure,
269 : aFromRawSegment,
270 : aToOffset,
271 : aCount,
272 0 : aWriteCount);
273 : }
274 :
275 : /**
276 : * Forward everything else to the mStream after calling InitStreams()
277 : */
278 :
279 : // nsIInputStream
280 0 : NS_IMETHODIMP nsMIMEInputStream::Close(void) { INITSTREAMS; return mStream->Close(); }
281 2 : NS_IMETHODIMP nsMIMEInputStream::Available(PRUint32 *_retval) { INITSTREAMS; return mStream->Available(_retval); }
282 4 : NS_IMETHODIMP nsMIMEInputStream::Read(char * buf, PRUint32 count, PRUint32 *_retval) { INITSTREAMS; return mStream->Read(buf, count, _retval); }
283 0 : NS_IMETHODIMP nsMIMEInputStream::IsNonBlocking(bool *aNonBlocking) { INITSTREAMS; return mStream->IsNonBlocking(aNonBlocking); }
284 :
285 : // nsISeekableStream
286 0 : NS_IMETHODIMP nsMIMEInputStream::Tell(PRInt64 *_retval)
287 : {
288 0 : INITSTREAMS;
289 0 : nsCOMPtr<nsISeekableStream> stream = do_QueryInterface(mStream);
290 0 : return stream->Tell(_retval);
291 : }
292 0 : NS_IMETHODIMP nsMIMEInputStream::SetEOF(void) {
293 0 : INITSTREAMS;
294 0 : nsCOMPtr<nsISeekableStream> stream = do_QueryInterface(mStream);
295 0 : return stream->SetEOF();
296 : }
297 :
298 :
299 : /**
300 : * Factory method used by do_CreateInstance
301 : */
302 :
303 : nsresult
304 1 : nsMIMEInputStreamConstructor(nsISupports *outer, REFNSIID iid, void **result)
305 : {
306 1 : *result = nsnull;
307 :
308 1 : if (outer)
309 0 : return NS_ERROR_NO_AGGREGATION;
310 :
311 1 : nsMIMEInputStream *inst = new nsMIMEInputStream();
312 1 : if (!inst)
313 0 : return NS_ERROR_OUT_OF_MEMORY;
314 :
315 1 : NS_ADDREF(inst);
316 :
317 1 : nsresult rv = inst->Init();
318 1 : if (NS_FAILED(rv)) {
319 0 : NS_RELEASE(inst);
320 0 : return rv;
321 : }
322 :
323 1 : rv = inst->QueryInterface(iid, result);
324 1 : NS_RELEASE(inst);
325 :
326 1 : return rv;
327 : }
328 :
329 : bool
330 0 : nsMIMEInputStream::Read(const IPC::Message *aMsg, void **aIter)
331 : {
332 : using IPC::ReadParam;
333 :
334 0 : if (!ReadParam(aMsg, aIter, &mHeaders) ||
335 0 : !ReadParam(aMsg, aIter, &mContentLength) ||
336 0 : !ReadParam(aMsg, aIter, &mStartedReading))
337 0 : return false;
338 :
339 : // nsMIMEInputStream::Init() already appended mHeaderStream & mCLStream
340 0 : mHeaderStream->ShareData(mHeaders.get(),
341 0 : mStartedReading? mHeaders.Length() : 0);
342 0 : mCLStream->ShareData(mContentLength.get(),
343 0 : mStartedReading? mContentLength.Length() : 0);
344 :
345 0 : IPC::InputStream inputStream;
346 0 : if (!ReadParam(aMsg, aIter, &inputStream))
347 0 : return false;
348 :
349 0 : nsCOMPtr<nsIInputStream> stream(inputStream);
350 0 : mData = stream;
351 0 : if (stream) {
352 0 : nsresult rv = mStream->AppendStream(mData);
353 0 : if (NS_FAILED(rv))
354 0 : return false;
355 : }
356 :
357 0 : if (!ReadParam(aMsg, aIter, &mAddContentLength))
358 0 : return false;
359 :
360 0 : return true;
361 : }
362 :
363 : void
364 0 : nsMIMEInputStream::Write(IPC::Message *aMsg)
365 : {
366 : using IPC::WriteParam;
367 :
368 0 : WriteParam(aMsg, mHeaders);
369 0 : WriteParam(aMsg, mContentLength);
370 0 : WriteParam(aMsg, mStartedReading);
371 :
372 0 : IPC::InputStream inputStream(mData);
373 0 : WriteParam(aMsg, inputStream);
374 :
375 0 : WriteParam(aMsg, mAddContentLength);
376 0 : }
|