Bug 470455 - test_database_sync_embed_visits.js leaks, r=sdwilsh
[wine-gecko.git] / netwerk / base / src / nsMIMEInputStream.cpp
blob8593d87f757088d8a458a1948ac9c471a578b6f9
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
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/
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.
15 * The Original Code is frightening to behold.
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.
22 * Contributor(s):
23 * Jonas Sicking <sicking@bigfoot.com>
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.
37 * ***** END LICENSE BLOCK ***** */
39 /**
40 * The MIME stream separates headers and a datastream. It also allows
41 * automatic creation of the content-length header.
44 #include "nsCOMPtr.h"
45 #include "nsComponentManagerUtils.h"
46 #include "nsIMultiplexInputStream.h"
47 #include "nsIMIMEInputStream.h"
48 #include "nsISeekableStream.h"
49 #include "nsIStringStream.h"
50 #include "nsString.h"
52 class nsMIMEInputStream : public nsIMIMEInputStream,
53 public nsISeekableStream
55 public:
56 nsMIMEInputStream();
57 virtual ~nsMIMEInputStream();
59 NS_DECL_ISUPPORTS
60 NS_DECL_NSIINPUTSTREAM
61 NS_DECL_NSIMIMEINPUTSTREAM
62 NS_DECL_NSISEEKABLESTREAM
64 NS_METHOD Init();
66 private:
68 void InitStreams();
70 struct ReadSegmentsState {
71 nsIInputStream* mThisStream;
72 nsWriteSegmentFun mWriter;
73 void* mClosure;
75 static NS_METHOD ReadSegCb(nsIInputStream* aIn, void* aClosure,
76 const char* aFromRawSegment, PRUint32 aToOffset,
77 PRUint32 aCount, PRUint32 *aWriteCount);
79 nsCString mHeaders;
80 nsCOMPtr<nsIStringInputStream> mHeaderStream;
82 nsCString mContentLength;
83 nsCOMPtr<nsIStringInputStream> mCLStream;
85 nsCOMPtr<nsIInputStream> mData;
86 nsCOMPtr<nsIMultiplexInputStream> mStream;
87 PRPackedBool mAddContentLength;
88 PRPackedBool mStartedReading;
91 NS_IMPL_THREADSAFE_ISUPPORTS3(nsMIMEInputStream,
92 nsIMIMEInputStream,
93 nsIInputStream,
94 nsISeekableStream)
96 nsMIMEInputStream::nsMIMEInputStream() : mAddContentLength(PR_FALSE),
97 mStartedReading(PR_FALSE)
101 nsMIMEInputStream::~nsMIMEInputStream()
105 NS_METHOD nsMIMEInputStream::Init()
107 nsresult rv = NS_OK;
108 mStream = do_CreateInstance("@mozilla.org/io/multiplex-input-stream;1",
109 &rv);
110 NS_ENSURE_SUCCESS(rv, rv);
112 mHeaderStream = do_CreateInstance("@mozilla.org/io/string-input-stream;1",
113 &rv);
114 NS_ENSURE_SUCCESS(rv, rv);
115 mCLStream = do_CreateInstance("@mozilla.org/io/string-input-stream;1", &rv);
116 NS_ENSURE_SUCCESS(rv, rv);
118 rv = mStream->AppendStream(mHeaderStream);
119 NS_ENSURE_SUCCESS(rv, rv);
121 rv = mStream->AppendStream(mCLStream);
122 NS_ENSURE_SUCCESS(rv, rv);
124 return NS_OK;
128 /* attribute boolean addContentLength; */
129 NS_IMETHODIMP
130 nsMIMEInputStream::GetAddContentLength(PRBool *aAddContentLength)
132 *aAddContentLength = mAddContentLength;
133 return NS_OK;
135 NS_IMETHODIMP
136 nsMIMEInputStream::SetAddContentLength(PRBool aAddContentLength)
138 NS_ENSURE_FALSE(mStartedReading, NS_ERROR_FAILURE);
139 mAddContentLength = aAddContentLength;
140 return NS_OK;
143 /* void addHeader ([const] in string name, [const] in string value); */
144 NS_IMETHODIMP
145 nsMIMEInputStream::AddHeader(const char *aName, const char *aValue)
147 NS_ENSURE_FALSE(mStartedReading, NS_ERROR_FAILURE);
148 mHeaders.Append(aName);
149 mHeaders.AppendLiteral(": ");
150 mHeaders.Append(aValue);
151 mHeaders.AppendLiteral("\r\n");
153 // Just in case someone somehow uses our stream, lets at least
154 // let the stream have a valid pointer. The stream will be properly
155 // initialized in nsMIMEInputStream::InitStreams
156 mHeaderStream->ShareData(mHeaders.get(), 0);
158 return NS_OK;
161 /* void setData (in nsIInputStream stream); */
162 NS_IMETHODIMP
163 nsMIMEInputStream::SetData(nsIInputStream *aStream)
165 NS_ENSURE_FALSE(mStartedReading, NS_ERROR_FAILURE);
166 // Remove the old stream if there is one
167 if (mData)
168 mStream->RemoveStream(2);
170 mData = aStream;
171 if (aStream)
172 mStream->AppendStream(mData);
173 return NS_OK;
176 // set up the internal streams
177 void nsMIMEInputStream::InitStreams()
179 NS_ASSERTION(!mStartedReading,
180 "Don't call initStreams twice without rewinding");
182 mStartedReading = PR_TRUE;
184 // We'll use the content-length stream to add the final \r\n
185 if (mAddContentLength) {
186 PRUint32 cl = 0;
187 if (mData) {
188 mData->Available(&cl);
190 mContentLength.AssignLiteral("Content-Length: ");
191 mContentLength.AppendInt((PRInt32)cl);
192 mContentLength.AppendLiteral("\r\n\r\n");
194 else {
195 mContentLength.AssignLiteral("\r\n");
197 mCLStream->ShareData(mContentLength.get(), -1);
198 mHeaderStream->ShareData(mHeaders.get(), -1);
203 #define INITSTREAMS \
204 if (!mStartedReading) { \
205 InitStreams(); \
208 // Reset mStartedReading when Seek-ing to start
209 NS_IMETHODIMP
210 nsMIMEInputStream::Seek(PRInt32 whence, PRInt64 offset)
212 nsresult rv;
213 nsCOMPtr<nsISeekableStream> stream = do_QueryInterface(mStream);
214 if (whence == NS_SEEK_SET && LL_EQ(offset, LL_Zero())) {
215 rv = stream->Seek(whence, offset);
216 if (NS_SUCCEEDED(rv))
217 mStartedReading = PR_FALSE;
219 else {
220 INITSTREAMS;
221 rv = stream->Seek(whence, offset);
224 return rv;
227 // Proxy ReadSegments since we need to be a good little nsIInputStream
228 NS_IMETHODIMP nsMIMEInputStream::ReadSegments(nsWriteSegmentFun aWriter,
229 void *aClosure, PRUint32 aCount,
230 PRUint32 *_retval)
232 INITSTREAMS;
233 ReadSegmentsState state;
234 state.mThisStream = this;
235 state.mWriter = aWriter;
236 state.mClosure = aClosure;
237 return mStream->ReadSegments(ReadSegCb, &state, aCount, _retval);
240 NS_METHOD
241 nsMIMEInputStream::ReadSegCb(nsIInputStream* aIn, void* aClosure,
242 const char* aFromRawSegment,
243 PRUint32 aToOffset, PRUint32 aCount,
244 PRUint32 *aWriteCount)
246 ReadSegmentsState* state = (ReadSegmentsState*)aClosure;
247 return (state->mWriter)(state->mThisStream,
248 state->mClosure,
249 aFromRawSegment,
250 aToOffset,
251 aCount,
252 aWriteCount);
256 * Forward everything else to the mStream after calling InitStreams()
259 // nsIInputStream
260 NS_IMETHODIMP nsMIMEInputStream::Close(void) { INITSTREAMS; return mStream->Close(); }
261 NS_IMETHODIMP nsMIMEInputStream::Available(PRUint32 *_retval) { INITSTREAMS; return mStream->Available(_retval); }
262 NS_IMETHODIMP nsMIMEInputStream::Read(char * buf, PRUint32 count, PRUint32 *_retval) { INITSTREAMS; return mStream->Read(buf, count, _retval); }
263 NS_IMETHODIMP nsMIMEInputStream::IsNonBlocking(PRBool *aNonBlocking) { INITSTREAMS; return mStream->IsNonBlocking(aNonBlocking); }
265 // nsISeekableStream
266 NS_IMETHODIMP nsMIMEInputStream::Tell(PRInt64 *_retval)
268 INITSTREAMS;
269 nsCOMPtr<nsISeekableStream> stream = do_QueryInterface(mStream);
270 return stream->Tell(_retval);
272 NS_IMETHODIMP nsMIMEInputStream::SetEOF(void) {
273 INITSTREAMS;
274 nsCOMPtr<nsISeekableStream> stream = do_QueryInterface(mStream);
275 return stream->SetEOF();
280 * Factory method used by do_CreateInstance
283 NS_METHOD
284 nsMIMEInputStreamConstructor(nsISupports *outer, REFNSIID iid, void **result)
286 *result = nsnull;
288 if (outer)
289 return NS_ERROR_NO_AGGREGATION;
291 nsMIMEInputStream *inst;
292 NS_NEWXPCOM(inst, nsMIMEInputStream);
293 if (!inst)
294 return NS_ERROR_OUT_OF_MEMORY;
296 NS_ADDREF(inst);
298 nsresult rv = inst->Init();
299 if (NS_FAILED(rv)) {
300 NS_RELEASE(inst);
301 return rv;
304 rv = inst->QueryInterface(iid, result);
305 NS_RELEASE(inst);
307 return rv;