1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/netwerk/base/src/nsMIMEInputStream.cpp Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,374 @@ 1.4 +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ 1.5 +/* This Source Code Form is subject to the terms of the Mozilla Public 1.6 + * License, v. 2.0. If a copy of the MPL was not distributed with this 1.7 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.8 + 1.9 +/** 1.10 + * The MIME stream separates headers and a datastream. It also allows 1.11 + * automatic creation of the content-length header. 1.12 + */ 1.13 + 1.14 +#include "ipc/IPCMessageUtils.h" 1.15 + 1.16 +#include "nsCOMPtr.h" 1.17 +#include "nsComponentManagerUtils.h" 1.18 +#include "nsIMultiplexInputStream.h" 1.19 +#include "nsIMIMEInputStream.h" 1.20 +#include "nsISeekableStream.h" 1.21 +#include "nsIStringStream.h" 1.22 +#include "nsString.h" 1.23 +#include "nsMIMEInputStream.h" 1.24 +#include "nsIClassInfoImpl.h" 1.25 +#include "nsIIPCSerializableInputStream.h" 1.26 +#include "mozilla/ipc/InputStreamUtils.h" 1.27 + 1.28 +using namespace mozilla::ipc; 1.29 + 1.30 +class nsMIMEInputStream : public nsIMIMEInputStream, 1.31 + public nsISeekableStream, 1.32 + public nsIIPCSerializableInputStream 1.33 +{ 1.34 +public: 1.35 + nsMIMEInputStream(); 1.36 + virtual ~nsMIMEInputStream(); 1.37 + 1.38 + NS_DECL_THREADSAFE_ISUPPORTS 1.39 + NS_DECL_NSIINPUTSTREAM 1.40 + NS_DECL_NSIMIMEINPUTSTREAM 1.41 + NS_DECL_NSISEEKABLESTREAM 1.42 + NS_DECL_NSIIPCSERIALIZABLEINPUTSTREAM 1.43 + 1.44 + NS_METHOD Init(); 1.45 + 1.46 +private: 1.47 + 1.48 + void InitStreams(); 1.49 + 1.50 + struct ReadSegmentsState { 1.51 + nsIInputStream* mThisStream; 1.52 + nsWriteSegmentFun mWriter; 1.53 + void* mClosure; 1.54 + }; 1.55 + static NS_METHOD ReadSegCb(nsIInputStream* aIn, void* aClosure, 1.56 + const char* aFromRawSegment, uint32_t aToOffset, 1.57 + uint32_t aCount, uint32_t *aWriteCount); 1.58 + 1.59 + nsCString mHeaders; 1.60 + nsCOMPtr<nsIStringInputStream> mHeaderStream; 1.61 + 1.62 + nsCString mContentLength; 1.63 + nsCOMPtr<nsIStringInputStream> mCLStream; 1.64 + 1.65 + nsCOMPtr<nsIInputStream> mData; 1.66 + nsCOMPtr<nsIMultiplexInputStream> mStream; 1.67 + bool mAddContentLength; 1.68 + bool mStartedReading; 1.69 +}; 1.70 + 1.71 +NS_IMPL_ADDREF(nsMIMEInputStream) 1.72 +NS_IMPL_RELEASE(nsMIMEInputStream) 1.73 + 1.74 +NS_IMPL_CLASSINFO(nsMIMEInputStream, nullptr, nsIClassInfo::THREADSAFE, 1.75 + NS_MIMEINPUTSTREAM_CID) 1.76 + 1.77 +NS_IMPL_QUERY_INTERFACE_CI(nsMIMEInputStream, 1.78 + nsIMIMEInputStream, 1.79 + nsIInputStream, 1.80 + nsISeekableStream, 1.81 + nsIIPCSerializableInputStream) 1.82 +NS_IMPL_CI_INTERFACE_GETTER(nsMIMEInputStream, 1.83 + nsIMIMEInputStream, 1.84 + nsIInputStream, 1.85 + nsISeekableStream) 1.86 + 1.87 +nsMIMEInputStream::nsMIMEInputStream() : mAddContentLength(false), 1.88 + mStartedReading(false) 1.89 +{ 1.90 +} 1.91 + 1.92 +nsMIMEInputStream::~nsMIMEInputStream() 1.93 +{ 1.94 +} 1.95 + 1.96 +NS_METHOD nsMIMEInputStream::Init() 1.97 +{ 1.98 + nsresult rv = NS_OK; 1.99 + mStream = do_CreateInstance("@mozilla.org/io/multiplex-input-stream;1", 1.100 + &rv); 1.101 + NS_ENSURE_SUCCESS(rv, rv); 1.102 + 1.103 + mHeaderStream = do_CreateInstance("@mozilla.org/io/string-input-stream;1", 1.104 + &rv); 1.105 + NS_ENSURE_SUCCESS(rv, rv); 1.106 + mCLStream = do_CreateInstance("@mozilla.org/io/string-input-stream;1", &rv); 1.107 + NS_ENSURE_SUCCESS(rv, rv); 1.108 + 1.109 + rv = mStream->AppendStream(mHeaderStream); 1.110 + NS_ENSURE_SUCCESS(rv, rv); 1.111 + 1.112 + rv = mStream->AppendStream(mCLStream); 1.113 + NS_ENSURE_SUCCESS(rv, rv); 1.114 + 1.115 + return NS_OK; 1.116 +} 1.117 + 1.118 + 1.119 +/* attribute boolean addContentLength; */ 1.120 +NS_IMETHODIMP 1.121 +nsMIMEInputStream::GetAddContentLength(bool *aAddContentLength) 1.122 +{ 1.123 + *aAddContentLength = mAddContentLength; 1.124 + return NS_OK; 1.125 +} 1.126 +NS_IMETHODIMP 1.127 +nsMIMEInputStream::SetAddContentLength(bool aAddContentLength) 1.128 +{ 1.129 + NS_ENSURE_FALSE(mStartedReading, NS_ERROR_FAILURE); 1.130 + mAddContentLength = aAddContentLength; 1.131 + return NS_OK; 1.132 +} 1.133 + 1.134 +/* void addHeader ([const] in string name, [const] in string value); */ 1.135 +NS_IMETHODIMP 1.136 +nsMIMEInputStream::AddHeader(const char *aName, const char *aValue) 1.137 +{ 1.138 + NS_ENSURE_FALSE(mStartedReading, NS_ERROR_FAILURE); 1.139 + mHeaders.Append(aName); 1.140 + mHeaders.AppendLiteral(": "); 1.141 + mHeaders.Append(aValue); 1.142 + mHeaders.AppendLiteral("\r\n"); 1.143 + 1.144 + // Just in case someone somehow uses our stream, lets at least 1.145 + // let the stream have a valid pointer. The stream will be properly 1.146 + // initialized in nsMIMEInputStream::InitStreams 1.147 + mHeaderStream->ShareData(mHeaders.get(), 0); 1.148 + 1.149 + return NS_OK; 1.150 +} 1.151 + 1.152 +/* void setData (in nsIInputStream stream); */ 1.153 +NS_IMETHODIMP 1.154 +nsMIMEInputStream::SetData(nsIInputStream *aStream) 1.155 +{ 1.156 + NS_ENSURE_FALSE(mStartedReading, NS_ERROR_FAILURE); 1.157 + // Remove the old stream if there is one 1.158 + if (mData) 1.159 + mStream->RemoveStream(2); 1.160 + 1.161 + mData = aStream; 1.162 + if (aStream) 1.163 + mStream->AppendStream(mData); 1.164 + return NS_OK; 1.165 +} 1.166 + 1.167 +// set up the internal streams 1.168 +void nsMIMEInputStream::InitStreams() 1.169 +{ 1.170 + NS_ASSERTION(!mStartedReading, 1.171 + "Don't call initStreams twice without rewinding"); 1.172 + 1.173 + mStartedReading = true; 1.174 + 1.175 + // We'll use the content-length stream to add the final \r\n 1.176 + if (mAddContentLength) { 1.177 + uint64_t cl = 0; 1.178 + if (mData) { 1.179 + mData->Available(&cl); 1.180 + } 1.181 + mContentLength.AssignLiteral("Content-Length: "); 1.182 + mContentLength.AppendInt(cl); 1.183 + mContentLength.AppendLiteral("\r\n\r\n"); 1.184 + } 1.185 + else { 1.186 + mContentLength.AssignLiteral("\r\n"); 1.187 + } 1.188 + mCLStream->ShareData(mContentLength.get(), -1); 1.189 + mHeaderStream->ShareData(mHeaders.get(), -1); 1.190 +} 1.191 + 1.192 + 1.193 + 1.194 +#define INITSTREAMS \ 1.195 +if (!mStartedReading) { \ 1.196 + InitStreams(); \ 1.197 +} 1.198 + 1.199 +// Reset mStartedReading when Seek-ing to start 1.200 +NS_IMETHODIMP 1.201 +nsMIMEInputStream::Seek(int32_t whence, int64_t offset) 1.202 +{ 1.203 + nsresult rv; 1.204 + nsCOMPtr<nsISeekableStream> stream = do_QueryInterface(mStream); 1.205 + if (whence == NS_SEEK_SET && offset == 0) { 1.206 + rv = stream->Seek(whence, offset); 1.207 + if (NS_SUCCEEDED(rv)) 1.208 + mStartedReading = false; 1.209 + } 1.210 + else { 1.211 + INITSTREAMS; 1.212 + rv = stream->Seek(whence, offset); 1.213 + } 1.214 + 1.215 + return rv; 1.216 +} 1.217 + 1.218 +// Proxy ReadSegments since we need to be a good little nsIInputStream 1.219 +NS_IMETHODIMP nsMIMEInputStream::ReadSegments(nsWriteSegmentFun aWriter, 1.220 + void *aClosure, uint32_t aCount, 1.221 + uint32_t *_retval) 1.222 +{ 1.223 + INITSTREAMS; 1.224 + ReadSegmentsState state; 1.225 + state.mThisStream = this; 1.226 + state.mWriter = aWriter; 1.227 + state.mClosure = aClosure; 1.228 + return mStream->ReadSegments(ReadSegCb, &state, aCount, _retval); 1.229 +} 1.230 + 1.231 +NS_METHOD 1.232 +nsMIMEInputStream::ReadSegCb(nsIInputStream* aIn, void* aClosure, 1.233 + const char* aFromRawSegment, 1.234 + uint32_t aToOffset, uint32_t aCount, 1.235 + uint32_t *aWriteCount) 1.236 +{ 1.237 + ReadSegmentsState* state = (ReadSegmentsState*)aClosure; 1.238 + return (state->mWriter)(state->mThisStream, 1.239 + state->mClosure, 1.240 + aFromRawSegment, 1.241 + aToOffset, 1.242 + aCount, 1.243 + aWriteCount); 1.244 +} 1.245 + 1.246 +/** 1.247 + * Forward everything else to the mStream after calling InitStreams() 1.248 + */ 1.249 + 1.250 +// nsIInputStream 1.251 +NS_IMETHODIMP nsMIMEInputStream::Close(void) { INITSTREAMS; return mStream->Close(); } 1.252 +NS_IMETHODIMP nsMIMEInputStream::Available(uint64_t *_retval) { INITSTREAMS; return mStream->Available(_retval); } 1.253 +NS_IMETHODIMP nsMIMEInputStream::Read(char * buf, uint32_t count, uint32_t *_retval) { INITSTREAMS; return mStream->Read(buf, count, _retval); } 1.254 +NS_IMETHODIMP nsMIMEInputStream::IsNonBlocking(bool *aNonBlocking) { INITSTREAMS; return mStream->IsNonBlocking(aNonBlocking); } 1.255 + 1.256 +// nsISeekableStream 1.257 +NS_IMETHODIMP nsMIMEInputStream::Tell(int64_t *_retval) 1.258 +{ 1.259 + INITSTREAMS; 1.260 + nsCOMPtr<nsISeekableStream> stream = do_QueryInterface(mStream); 1.261 + return stream->Tell(_retval); 1.262 +} 1.263 +NS_IMETHODIMP nsMIMEInputStream::SetEOF(void) { 1.264 + INITSTREAMS; 1.265 + nsCOMPtr<nsISeekableStream> stream = do_QueryInterface(mStream); 1.266 + return stream->SetEOF(); 1.267 +} 1.268 + 1.269 + 1.270 +/** 1.271 + * Factory method used by do_CreateInstance 1.272 + */ 1.273 + 1.274 +nsresult 1.275 +nsMIMEInputStreamConstructor(nsISupports *outer, REFNSIID iid, void **result) 1.276 +{ 1.277 + *result = nullptr; 1.278 + 1.279 + if (outer) 1.280 + return NS_ERROR_NO_AGGREGATION; 1.281 + 1.282 + nsMIMEInputStream *inst = new nsMIMEInputStream(); 1.283 + if (!inst) 1.284 + return NS_ERROR_OUT_OF_MEMORY; 1.285 + 1.286 + NS_ADDREF(inst); 1.287 + 1.288 + nsresult rv = inst->Init(); 1.289 + if (NS_FAILED(rv)) { 1.290 + NS_RELEASE(inst); 1.291 + return rv; 1.292 + } 1.293 + 1.294 + rv = inst->QueryInterface(iid, result); 1.295 + NS_RELEASE(inst); 1.296 + 1.297 + return rv; 1.298 +} 1.299 + 1.300 +void 1.301 +nsMIMEInputStream::Serialize(InputStreamParams& aParams, 1.302 + FileDescriptorArray& aFileDescriptors) 1.303 +{ 1.304 + MIMEInputStreamParams params; 1.305 + 1.306 + if (mData) { 1.307 + nsCOMPtr<nsIInputStream> stream = do_QueryInterface(mData); 1.308 + MOZ_ASSERT(stream); 1.309 + 1.310 + InputStreamParams wrappedParams; 1.311 + SerializeInputStream(stream, wrappedParams, aFileDescriptors); 1.312 + 1.313 + NS_ASSERTION(wrappedParams.type() != InputStreamParams::T__None, 1.314 + "Wrapped stream failed to serialize!"); 1.315 + 1.316 + params.optionalStream() = wrappedParams; 1.317 + } 1.318 + else { 1.319 + params.optionalStream() = mozilla::void_t(); 1.320 + } 1.321 + 1.322 + params.headers() = mHeaders; 1.323 + params.contentLength() = mContentLength; 1.324 + params.startedReading() = mStartedReading; 1.325 + params.addContentLength() = mAddContentLength; 1.326 + 1.327 + aParams = params; 1.328 +} 1.329 + 1.330 +bool 1.331 +nsMIMEInputStream::Deserialize(const InputStreamParams& aParams, 1.332 + const FileDescriptorArray& aFileDescriptors) 1.333 +{ 1.334 + if (aParams.type() != InputStreamParams::TMIMEInputStreamParams) { 1.335 + NS_ERROR("Received unknown parameters from the other process!"); 1.336 + return false; 1.337 + } 1.338 + 1.339 + const MIMEInputStreamParams& params = 1.340 + aParams.get_MIMEInputStreamParams(); 1.341 + const OptionalInputStreamParams& wrappedParams = params.optionalStream(); 1.342 + 1.343 + mHeaders = params.headers(); 1.344 + mContentLength = params.contentLength(); 1.345 + mStartedReading = params.startedReading(); 1.346 + 1.347 + // nsMIMEInputStream::Init() already appended mHeaderStream & mCLStream 1.348 + mHeaderStream->ShareData(mHeaders.get(), 1.349 + mStartedReading ? mHeaders.Length() : 0); 1.350 + mCLStream->ShareData(mContentLength.get(), 1.351 + mStartedReading ? mContentLength.Length() : 0); 1.352 + 1.353 + nsCOMPtr<nsIInputStream> stream; 1.354 + if (wrappedParams.type() == OptionalInputStreamParams::TInputStreamParams) { 1.355 + stream = DeserializeInputStream(wrappedParams.get_InputStreamParams(), 1.356 + aFileDescriptors); 1.357 + if (!stream) { 1.358 + NS_WARNING("Failed to deserialize wrapped stream!"); 1.359 + return false; 1.360 + } 1.361 + 1.362 + mData = stream; 1.363 + 1.364 + if (NS_FAILED(mStream->AppendStream(mData))) { 1.365 + NS_WARNING("Failed to append stream!"); 1.366 + return false; 1.367 + } 1.368 + } 1.369 + else { 1.370 + NS_ASSERTION(wrappedParams.type() == OptionalInputStreamParams::Tvoid_t, 1.371 + "Unknown type for OptionalInputStreamParams!"); 1.372 + } 1.373 + 1.374 + mAddContentLength = params.addContentLength(); 1.375 + 1.376 + return true; 1.377 +}