Back to index

lightning-sunbird  0.9+nobinonly
nsMIMEInputStream.cpp
Go to the documentation of this file.
00001 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
00002 /* ***** BEGIN LICENSE BLOCK *****
00003  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
00004  *
00005  * The contents of this file are subject to the Mozilla Public License Version
00006  * 1.1 (the "License"); you may not use this file except in compliance with
00007  * the License. You may obtain a copy of the License at
00008  * http://www.mozilla.org/MPL/
00009  *
00010  * Software distributed under the License is distributed on an "AS IS" basis,
00011  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
00012  * for the specific language governing rights and limitations under the
00013  * License.
00014  *
00015  * The Original Code is frightening to behold.
00016  *
00017  * The Initial Developer of the Original Code is
00018  * Jonas Sicking.
00019  * Portions created by the Initial Developer are Copyright (C) 2001
00020  * the Initial Developer. All Rights Reserved.
00021  *
00022  * Contributor(s):
00023  *   Jonas Sicking <sicking@bigfoot.com>
00024  *
00025  * Alternatively, the contents of this file may be used under the terms of
00026  * either the GNU General Public License Version 2 or later (the "GPL"), or
00027  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
00028  * in which case the provisions of the GPL or the LGPL are applicable instead
00029  * of those above. If you wish to allow use of your version of this file only
00030  * under the terms of either the GPL or the LGPL, and not to allow others to
00031  * use your version of this file under the terms of the MPL, indicate your
00032  * decision by deleting the provisions above and replace them with the notice
00033  * and other provisions required by the GPL or the LGPL. If you do not delete
00034  * the provisions above, a recipient may use your version of this file under
00035  * the terms of any one of the MPL, the GPL or the LGPL.
00036  *
00037  * ***** END LICENSE BLOCK ***** */
00038 
00044 #include "nsCOMPtr.h"
00045 #include "nsComponentManagerUtils.h"
00046 #include "nsIMultiplexInputStream.h"
00047 #include "nsIMIMEInputStream.h"
00048 #include "nsISeekableStream.h"
00049 #include "nsIStringStream.h"
00050 
00051 class nsMIMEInputStream : public nsIMIMEInputStream,
00052                           public nsISeekableStream
00053 {
00054 public:
00055     nsMIMEInputStream();
00056     virtual ~nsMIMEInputStream();
00057 
00058     NS_DECL_ISUPPORTS
00059     NS_DECL_NSIINPUTSTREAM
00060     NS_DECL_NSIMIMEINPUTSTREAM
00061     NS_DECL_NSISEEKABLESTREAM
00062     
00063     NS_METHOD Init();
00064 
00065 private:
00066 
00067     void InitStreams();
00068 
00069     struct ReadSegmentsState {
00070         nsIInputStream* mThisStream;
00071         nsWriteSegmentFun mWriter;
00072         void* mClosure;
00073     };
00074     static NS_METHOD ReadSegCb(nsIInputStream* aIn, void* aClosure,
00075                                const char* aFromRawSegment, PRUint32 aToOffset,
00076                                PRUint32 aCount, PRUint32 *aWriteCount);
00077 
00078     nsCString mHeaders;
00079     nsCOMPtr<nsIStringInputStream> mHeaderStream;
00080     
00081     nsCString mContentLength;
00082     nsCOMPtr<nsIStringInputStream> mCLStream;
00083     
00084     nsCOMPtr<nsIInputStream> mData;
00085     nsCOMPtr<nsIMultiplexInputStream> mStream;
00086     PRPackedBool mAddContentLength;
00087     PRPackedBool mStartedReading;
00088 };
00089 
00090 NS_IMPL_THREADSAFE_ISUPPORTS3(nsMIMEInputStream,
00091                               nsIMIMEInputStream,
00092                               nsIInputStream,
00093                               nsISeekableStream)
00094 
00095 nsMIMEInputStream::nsMIMEInputStream() : mAddContentLength(PR_FALSE),
00096                                          mStartedReading(PR_FALSE)
00097 {
00098 }
00099 
00100 nsMIMEInputStream::~nsMIMEInputStream()
00101 {
00102 }
00103 
00104 NS_METHOD nsMIMEInputStream::Init()
00105 {
00106     nsresult rv = NS_OK;
00107     mStream = do_CreateInstance("@mozilla.org/io/multiplex-input-stream;1",
00108                                 &rv);
00109     NS_ENSURE_SUCCESS(rv, rv);
00110 
00111     mHeaderStream = do_CreateInstance("@mozilla.org/io/string-input-stream;1",
00112                                       &rv);
00113     NS_ENSURE_SUCCESS(rv, rv);
00114     mCLStream = do_CreateInstance("@mozilla.org/io/string-input-stream;1", &rv);
00115     NS_ENSURE_SUCCESS(rv, rv);
00116 
00117     nsCOMPtr<nsIInputStream> headerStream = do_QueryInterface(mHeaderStream);
00118     nsCOMPtr<nsIInputStream> clStream = do_QueryInterface(mCLStream);
00119     rv = mStream->AppendStream(headerStream);
00120     NS_ENSURE_SUCCESS(rv, rv);
00121     rv = mStream->AppendStream(clStream);
00122     NS_ENSURE_SUCCESS(rv, rv);
00123 
00124     return NS_OK;
00125 }
00126 
00127 
00128 /* attribute boolean addContentLength; */
00129 NS_IMETHODIMP
00130 nsMIMEInputStream::GetAddContentLength(PRBool *aAddContentLength)
00131 {
00132     *aAddContentLength = mAddContentLength;
00133     return NS_OK;
00134 }
00135 NS_IMETHODIMP
00136 nsMIMEInputStream::SetAddContentLength(PRBool aAddContentLength)
00137 {
00138     NS_ENSURE_FALSE(mStartedReading, NS_ERROR_FAILURE);
00139     mAddContentLength = aAddContentLength;
00140     return NS_OK;
00141 }
00142 
00143 /* void addHeader ([const] in string name, [const] in string value); */
00144 NS_IMETHODIMP
00145 nsMIMEInputStream::AddHeader(const char *aName, const char *aValue)
00146 {
00147     NS_ENSURE_FALSE(mStartedReading, NS_ERROR_FAILURE);
00148     mHeaders.Append(aName);
00149     mHeaders.AppendLiteral(": ");
00150     mHeaders.Append(aValue);
00151     mHeaders.AppendLiteral("\r\n");
00152 
00153     // Just in case someone somehow uses our stream, lets at least
00154     // let the stream have a valid pointer. The stream will be properly
00155     // initialized in nsMIMEInputStream::InitStreams
00156     mHeaderStream->ShareData(mHeaders.get(), 0);
00157 
00158     return NS_OK;
00159 }
00160 
00161 /* void setData (in nsIInputStream stream); */
00162 NS_IMETHODIMP
00163 nsMIMEInputStream::SetData(nsIInputStream *aStream)
00164 {
00165     NS_ENSURE_FALSE(mStartedReading, NS_ERROR_FAILURE);
00166     // Remove the old stream if there is one
00167     if (mData)
00168         mStream->RemoveStream(2);
00169 
00170     mData = aStream;
00171     if (aStream)
00172         mStream->AppendStream(mData);
00173     return NS_OK;
00174 }
00175 
00176 // set up the internal streams
00177 void nsMIMEInputStream::InitStreams()
00178 {
00179     NS_ASSERTION(!mStartedReading,
00180                  "Don't call initStreams twice without rewinding");
00181 
00182     mStartedReading = PR_TRUE;
00183 
00184     // We'll use the content-length stream to add the final \r\n
00185     if (mAddContentLength) {
00186         PRUint32 cl = 0;
00187         if (mData) {
00188             mData->Available(&cl);
00189         }
00190         mContentLength.AssignLiteral("Content-Length: ");
00191         mContentLength.AppendInt((PRInt32)cl);
00192         mContentLength.AppendLiteral("\r\n\r\n");
00193     }
00194     else {
00195         mContentLength.AssignLiteral("\r\n");
00196     }
00197     mCLStream->ShareData(mContentLength.get(), -1);
00198     mHeaderStream->ShareData(mHeaders.get(), -1);
00199 }
00200 
00201 
00202 
00203 #define INITSTREAMS         \
00204 if (!mStartedReading) {     \
00205     InitStreams();          \
00206 }
00207 
00208 // Reset mStartedReading when Seek-ing to start
00209 NS_IMETHODIMP
00210 nsMIMEInputStream::Seek(PRInt32 whence, PRInt64 offset)
00211 {
00212     nsresult rv;
00213     nsCOMPtr<nsISeekableStream> stream = do_QueryInterface(mStream);
00214     if (whence == NS_SEEK_SET && LL_EQ(offset, LL_Zero())) {
00215         rv = stream->Seek(whence, offset);
00216         if (NS_SUCCEEDED(rv))
00217             mStartedReading = PR_FALSE;
00218     }
00219     else {
00220         INITSTREAMS;
00221         rv = stream->Seek(whence, offset);
00222     }
00223 
00224     return rv;
00225 }
00226 
00227 // Proxy ReadSegments since we need to be a good little nsIInputStream
00228 NS_IMETHODIMP nsMIMEInputStream::ReadSegments(nsWriteSegmentFun aWriter,
00229                                               void *aClosure, PRUint32 aCount,
00230                                               PRUint32 *_retval)
00231 {
00232     INITSTREAMS;
00233     ReadSegmentsState state;
00234     state.mThisStream = this;
00235     state.mWriter = aWriter;
00236     state.mClosure = aClosure;
00237     return mStream->ReadSegments(ReadSegCb, &state, aCount, _retval);
00238 }
00239 
00240 NS_METHOD
00241 nsMIMEInputStream::ReadSegCb(nsIInputStream* aIn, void* aClosure,
00242                              const char* aFromRawSegment,
00243                              PRUint32 aToOffset, PRUint32 aCount,
00244                              PRUint32 *aWriteCount)
00245 {
00246     ReadSegmentsState* state = (ReadSegmentsState*)aClosure;
00247     return  (state->mWriter)(state->mThisStream,
00248                              state->mClosure,
00249                              aFromRawSegment,
00250                              aToOffset,
00251                              aCount,
00252                              aWriteCount);
00253 }
00254 
00259 // nsIInputStream
00260 NS_IMETHODIMP nsMIMEInputStream::Close(void) { INITSTREAMS; return mStream->Close(); }
00261 NS_IMETHODIMP nsMIMEInputStream::Available(PRUint32 *_retval) { INITSTREAMS; return mStream->Available(_retval); }
00262 NS_IMETHODIMP nsMIMEInputStream::Read(char * buf, PRUint32 count, PRUint32 *_retval) { INITSTREAMS; return mStream->Read(buf, count, _retval); }
00263 NS_IMETHODIMP nsMIMEInputStream::IsNonBlocking(PRBool *aNonBlocking) { INITSTREAMS; return mStream->IsNonBlocking(aNonBlocking); }
00264 
00265 // nsISeekableStream
00266 NS_IMETHODIMP nsMIMEInputStream::Tell(PRInt64 *_retval)
00267 {
00268     INITSTREAMS;
00269     nsCOMPtr<nsISeekableStream> stream = do_QueryInterface(mStream);
00270     return stream->Tell(_retval);
00271 }
00272 NS_IMETHODIMP nsMIMEInputStream::SetEOF(void) {
00273     INITSTREAMS;
00274     nsCOMPtr<nsISeekableStream> stream = do_QueryInterface(mStream);
00275     return stream->SetEOF();
00276 }
00277 
00278 
00283 NS_METHOD
00284 nsMIMEInputStreamConstructor(nsISupports *outer, REFNSIID iid, void **result)
00285 {
00286     *result = nsnull;
00287 
00288     if (outer)
00289         return NS_ERROR_NO_AGGREGATION;
00290 
00291     nsMIMEInputStream *inst;
00292     NS_NEWXPCOM(inst, nsMIMEInputStream);
00293     if (!inst)
00294         return NS_ERROR_OUT_OF_MEMORY;
00295 
00296     NS_ADDREF(inst);
00297 
00298     nsresult rv = inst->Init();
00299     if (NS_FAILED(rv)) {
00300         NS_RELEASE(inst);
00301         return rv;
00302     }
00303 
00304     rv = inst->QueryInterface(iid, result);
00305     NS_RELEASE(inst);
00306 
00307     return rv;
00308 }