michael@0: /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ michael@0: /* This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: /** michael@0: * The MIME stream separates headers and a datastream. It also allows michael@0: * automatic creation of the content-length header. michael@0: */ michael@0: michael@0: #include "ipc/IPCMessageUtils.h" michael@0: michael@0: #include "nsCOMPtr.h" michael@0: #include "nsComponentManagerUtils.h" michael@0: #include "nsIMultiplexInputStream.h" michael@0: #include "nsIMIMEInputStream.h" michael@0: #include "nsISeekableStream.h" michael@0: #include "nsIStringStream.h" michael@0: #include "nsString.h" michael@0: #include "nsMIMEInputStream.h" michael@0: #include "nsIClassInfoImpl.h" michael@0: #include "nsIIPCSerializableInputStream.h" michael@0: #include "mozilla/ipc/InputStreamUtils.h" michael@0: michael@0: using namespace mozilla::ipc; michael@0: michael@0: class nsMIMEInputStream : public nsIMIMEInputStream, michael@0: public nsISeekableStream, michael@0: public nsIIPCSerializableInputStream michael@0: { michael@0: public: michael@0: nsMIMEInputStream(); michael@0: virtual ~nsMIMEInputStream(); michael@0: michael@0: NS_DECL_THREADSAFE_ISUPPORTS michael@0: NS_DECL_NSIINPUTSTREAM michael@0: NS_DECL_NSIMIMEINPUTSTREAM michael@0: NS_DECL_NSISEEKABLESTREAM michael@0: NS_DECL_NSIIPCSERIALIZABLEINPUTSTREAM michael@0: michael@0: NS_METHOD Init(); michael@0: michael@0: private: michael@0: michael@0: void InitStreams(); michael@0: michael@0: struct ReadSegmentsState { michael@0: nsIInputStream* mThisStream; michael@0: nsWriteSegmentFun mWriter; michael@0: void* mClosure; michael@0: }; michael@0: static NS_METHOD ReadSegCb(nsIInputStream* aIn, void* aClosure, michael@0: const char* aFromRawSegment, uint32_t aToOffset, michael@0: uint32_t aCount, uint32_t *aWriteCount); michael@0: michael@0: nsCString mHeaders; michael@0: nsCOMPtr mHeaderStream; michael@0: michael@0: nsCString mContentLength; michael@0: nsCOMPtr mCLStream; michael@0: michael@0: nsCOMPtr mData; michael@0: nsCOMPtr mStream; michael@0: bool mAddContentLength; michael@0: bool mStartedReading; michael@0: }; michael@0: michael@0: NS_IMPL_ADDREF(nsMIMEInputStream) michael@0: NS_IMPL_RELEASE(nsMIMEInputStream) michael@0: michael@0: NS_IMPL_CLASSINFO(nsMIMEInputStream, nullptr, nsIClassInfo::THREADSAFE, michael@0: NS_MIMEINPUTSTREAM_CID) michael@0: michael@0: NS_IMPL_QUERY_INTERFACE_CI(nsMIMEInputStream, michael@0: nsIMIMEInputStream, michael@0: nsIInputStream, michael@0: nsISeekableStream, michael@0: nsIIPCSerializableInputStream) michael@0: NS_IMPL_CI_INTERFACE_GETTER(nsMIMEInputStream, michael@0: nsIMIMEInputStream, michael@0: nsIInputStream, michael@0: nsISeekableStream) michael@0: michael@0: nsMIMEInputStream::nsMIMEInputStream() : mAddContentLength(false), michael@0: mStartedReading(false) michael@0: { michael@0: } michael@0: michael@0: nsMIMEInputStream::~nsMIMEInputStream() michael@0: { michael@0: } michael@0: michael@0: NS_METHOD nsMIMEInputStream::Init() michael@0: { michael@0: nsresult rv = NS_OK; michael@0: mStream = do_CreateInstance("@mozilla.org/io/multiplex-input-stream;1", michael@0: &rv); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: mHeaderStream = do_CreateInstance("@mozilla.org/io/string-input-stream;1", michael@0: &rv); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: mCLStream = do_CreateInstance("@mozilla.org/io/string-input-stream;1", &rv); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: rv = mStream->AppendStream(mHeaderStream); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: rv = mStream->AppendStream(mCLStream); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: michael@0: /* attribute boolean addContentLength; */ michael@0: NS_IMETHODIMP michael@0: nsMIMEInputStream::GetAddContentLength(bool *aAddContentLength) michael@0: { michael@0: *aAddContentLength = mAddContentLength; michael@0: return NS_OK; michael@0: } michael@0: NS_IMETHODIMP michael@0: nsMIMEInputStream::SetAddContentLength(bool aAddContentLength) michael@0: { michael@0: NS_ENSURE_FALSE(mStartedReading, NS_ERROR_FAILURE); michael@0: mAddContentLength = aAddContentLength; michael@0: return NS_OK; michael@0: } michael@0: michael@0: /* void addHeader ([const] in string name, [const] in string value); */ michael@0: NS_IMETHODIMP michael@0: nsMIMEInputStream::AddHeader(const char *aName, const char *aValue) michael@0: { michael@0: NS_ENSURE_FALSE(mStartedReading, NS_ERROR_FAILURE); michael@0: mHeaders.Append(aName); michael@0: mHeaders.AppendLiteral(": "); michael@0: mHeaders.Append(aValue); michael@0: mHeaders.AppendLiteral("\r\n"); michael@0: michael@0: // Just in case someone somehow uses our stream, lets at least michael@0: // let the stream have a valid pointer. The stream will be properly michael@0: // initialized in nsMIMEInputStream::InitStreams michael@0: mHeaderStream->ShareData(mHeaders.get(), 0); michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: /* void setData (in nsIInputStream stream); */ michael@0: NS_IMETHODIMP michael@0: nsMIMEInputStream::SetData(nsIInputStream *aStream) michael@0: { michael@0: NS_ENSURE_FALSE(mStartedReading, NS_ERROR_FAILURE); michael@0: // Remove the old stream if there is one michael@0: if (mData) michael@0: mStream->RemoveStream(2); michael@0: michael@0: mData = aStream; michael@0: if (aStream) michael@0: mStream->AppendStream(mData); michael@0: return NS_OK; michael@0: } michael@0: michael@0: // set up the internal streams michael@0: void nsMIMEInputStream::InitStreams() michael@0: { michael@0: NS_ASSERTION(!mStartedReading, michael@0: "Don't call initStreams twice without rewinding"); michael@0: michael@0: mStartedReading = true; michael@0: michael@0: // We'll use the content-length stream to add the final \r\n michael@0: if (mAddContentLength) { michael@0: uint64_t cl = 0; michael@0: if (mData) { michael@0: mData->Available(&cl); michael@0: } michael@0: mContentLength.AssignLiteral("Content-Length: "); michael@0: mContentLength.AppendInt(cl); michael@0: mContentLength.AppendLiteral("\r\n\r\n"); michael@0: } michael@0: else { michael@0: mContentLength.AssignLiteral("\r\n"); michael@0: } michael@0: mCLStream->ShareData(mContentLength.get(), -1); michael@0: mHeaderStream->ShareData(mHeaders.get(), -1); michael@0: } michael@0: michael@0: michael@0: michael@0: #define INITSTREAMS \ michael@0: if (!mStartedReading) { \ michael@0: InitStreams(); \ michael@0: } michael@0: michael@0: // Reset mStartedReading when Seek-ing to start michael@0: NS_IMETHODIMP michael@0: nsMIMEInputStream::Seek(int32_t whence, int64_t offset) michael@0: { michael@0: nsresult rv; michael@0: nsCOMPtr stream = do_QueryInterface(mStream); michael@0: if (whence == NS_SEEK_SET && offset == 0) { michael@0: rv = stream->Seek(whence, offset); michael@0: if (NS_SUCCEEDED(rv)) michael@0: mStartedReading = false; michael@0: } michael@0: else { michael@0: INITSTREAMS; michael@0: rv = stream->Seek(whence, offset); michael@0: } michael@0: michael@0: return rv; michael@0: } michael@0: michael@0: // Proxy ReadSegments since we need to be a good little nsIInputStream michael@0: NS_IMETHODIMP nsMIMEInputStream::ReadSegments(nsWriteSegmentFun aWriter, michael@0: void *aClosure, uint32_t aCount, michael@0: uint32_t *_retval) michael@0: { michael@0: INITSTREAMS; michael@0: ReadSegmentsState state; michael@0: state.mThisStream = this; michael@0: state.mWriter = aWriter; michael@0: state.mClosure = aClosure; michael@0: return mStream->ReadSegments(ReadSegCb, &state, aCount, _retval); michael@0: } michael@0: michael@0: NS_METHOD michael@0: nsMIMEInputStream::ReadSegCb(nsIInputStream* aIn, void* aClosure, michael@0: const char* aFromRawSegment, michael@0: uint32_t aToOffset, uint32_t aCount, michael@0: uint32_t *aWriteCount) michael@0: { michael@0: ReadSegmentsState* state = (ReadSegmentsState*)aClosure; michael@0: return (state->mWriter)(state->mThisStream, michael@0: state->mClosure, michael@0: aFromRawSegment, michael@0: aToOffset, michael@0: aCount, michael@0: aWriteCount); michael@0: } michael@0: michael@0: /** michael@0: * Forward everything else to the mStream after calling InitStreams() michael@0: */ michael@0: michael@0: // nsIInputStream michael@0: NS_IMETHODIMP nsMIMEInputStream::Close(void) { INITSTREAMS; return mStream->Close(); } michael@0: NS_IMETHODIMP nsMIMEInputStream::Available(uint64_t *_retval) { INITSTREAMS; return mStream->Available(_retval); } michael@0: NS_IMETHODIMP nsMIMEInputStream::Read(char * buf, uint32_t count, uint32_t *_retval) { INITSTREAMS; return mStream->Read(buf, count, _retval); } michael@0: NS_IMETHODIMP nsMIMEInputStream::IsNonBlocking(bool *aNonBlocking) { INITSTREAMS; return mStream->IsNonBlocking(aNonBlocking); } michael@0: michael@0: // nsISeekableStream michael@0: NS_IMETHODIMP nsMIMEInputStream::Tell(int64_t *_retval) michael@0: { michael@0: INITSTREAMS; michael@0: nsCOMPtr stream = do_QueryInterface(mStream); michael@0: return stream->Tell(_retval); michael@0: } michael@0: NS_IMETHODIMP nsMIMEInputStream::SetEOF(void) { michael@0: INITSTREAMS; michael@0: nsCOMPtr stream = do_QueryInterface(mStream); michael@0: return stream->SetEOF(); michael@0: } michael@0: michael@0: michael@0: /** michael@0: * Factory method used by do_CreateInstance michael@0: */ michael@0: michael@0: nsresult michael@0: nsMIMEInputStreamConstructor(nsISupports *outer, REFNSIID iid, void **result) michael@0: { michael@0: *result = nullptr; michael@0: michael@0: if (outer) michael@0: return NS_ERROR_NO_AGGREGATION; michael@0: michael@0: nsMIMEInputStream *inst = new nsMIMEInputStream(); michael@0: if (!inst) michael@0: return NS_ERROR_OUT_OF_MEMORY; michael@0: michael@0: NS_ADDREF(inst); michael@0: michael@0: nsresult rv = inst->Init(); michael@0: if (NS_FAILED(rv)) { michael@0: NS_RELEASE(inst); michael@0: return rv; michael@0: } michael@0: michael@0: rv = inst->QueryInterface(iid, result); michael@0: NS_RELEASE(inst); michael@0: michael@0: return rv; michael@0: } michael@0: michael@0: void michael@0: nsMIMEInputStream::Serialize(InputStreamParams& aParams, michael@0: FileDescriptorArray& aFileDescriptors) michael@0: { michael@0: MIMEInputStreamParams params; michael@0: michael@0: if (mData) { michael@0: nsCOMPtr stream = do_QueryInterface(mData); michael@0: MOZ_ASSERT(stream); michael@0: michael@0: InputStreamParams wrappedParams; michael@0: SerializeInputStream(stream, wrappedParams, aFileDescriptors); michael@0: michael@0: NS_ASSERTION(wrappedParams.type() != InputStreamParams::T__None, michael@0: "Wrapped stream failed to serialize!"); michael@0: michael@0: params.optionalStream() = wrappedParams; michael@0: } michael@0: else { michael@0: params.optionalStream() = mozilla::void_t(); michael@0: } michael@0: michael@0: params.headers() = mHeaders; michael@0: params.contentLength() = mContentLength; michael@0: params.startedReading() = mStartedReading; michael@0: params.addContentLength() = mAddContentLength; michael@0: michael@0: aParams = params; michael@0: } michael@0: michael@0: bool michael@0: nsMIMEInputStream::Deserialize(const InputStreamParams& aParams, michael@0: const FileDescriptorArray& aFileDescriptors) michael@0: { michael@0: if (aParams.type() != InputStreamParams::TMIMEInputStreamParams) { michael@0: NS_ERROR("Received unknown parameters from the other process!"); michael@0: return false; michael@0: } michael@0: michael@0: const MIMEInputStreamParams& params = michael@0: aParams.get_MIMEInputStreamParams(); michael@0: const OptionalInputStreamParams& wrappedParams = params.optionalStream(); michael@0: michael@0: mHeaders = params.headers(); michael@0: mContentLength = params.contentLength(); michael@0: mStartedReading = params.startedReading(); michael@0: michael@0: // nsMIMEInputStream::Init() already appended mHeaderStream & mCLStream michael@0: mHeaderStream->ShareData(mHeaders.get(), michael@0: mStartedReading ? mHeaders.Length() : 0); michael@0: mCLStream->ShareData(mContentLength.get(), michael@0: mStartedReading ? mContentLength.Length() : 0); michael@0: michael@0: nsCOMPtr stream; michael@0: if (wrappedParams.type() == OptionalInputStreamParams::TInputStreamParams) { michael@0: stream = DeserializeInputStream(wrappedParams.get_InputStreamParams(), michael@0: aFileDescriptors); michael@0: if (!stream) { michael@0: NS_WARNING("Failed to deserialize wrapped stream!"); michael@0: return false; michael@0: } michael@0: michael@0: mData = stream; michael@0: michael@0: if (NS_FAILED(mStream->AppendStream(mData))) { michael@0: NS_WARNING("Failed to append stream!"); michael@0: return false; michael@0: } michael@0: } michael@0: else { michael@0: NS_ASSERTION(wrappedParams.type() == OptionalInputStreamParams::Tvoid_t, michael@0: "Unknown type for OptionalInputStreamParams!"); michael@0: } michael@0: michael@0: mAddContentLength = params.addContentLength(); michael@0: michael@0: return true; michael@0: }