1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/netwerk/streamconv/converters/nsMultiMixedConv.cpp Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,1074 @@ 1.4 +/* -*- Mode: C++; tab-width: 2; 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 +#include "nsMultiMixedConv.h" 1.10 +#include "plstr.h" 1.11 +#include "nsIHttpChannel.h" 1.12 +#include "nsNetUtil.h" 1.13 +#include "nsMimeTypes.h" 1.14 +#include "nsIStringStream.h" 1.15 +#include "nsCRT.h" 1.16 +#include "nsIHttpChannelInternal.h" 1.17 +#include "nsURLHelper.h" 1.18 +#include "nsIStreamConverterService.h" 1.19 +#include <algorithm> 1.20 + 1.21 +// 1.22 +// Helper function for determining the length of data bytes up to 1.23 +// the next multipart token. A token is usually preceded by a LF 1.24 +// or CRLF delimiter. 1.25 +// 1.26 +static uint32_t 1.27 +LengthToToken(const char *cursor, const char *token) 1.28 +{ 1.29 + uint32_t len = token - cursor; 1.30 + // Trim off any LF or CRLF preceding the token 1.31 + if (len && *(token-1) == '\n') { 1.32 + --len; 1.33 + if (len && *(token-2) == '\r') 1.34 + --len; 1.35 + } 1.36 + return len; 1.37 +} 1.38 + 1.39 +nsPartChannel::nsPartChannel(nsIChannel *aMultipartChannel, uint32_t aPartID, 1.40 + nsIStreamListener* aListener) : 1.41 + mMultipartChannel(aMultipartChannel), 1.42 + mListener(aListener), 1.43 + mStatus(NS_OK), 1.44 + mContentLength(UINT64_MAX), 1.45 + mIsByteRangeRequest(false), 1.46 + mByteRangeStart(0), 1.47 + mByteRangeEnd(0), 1.48 + mPartID(aPartID), 1.49 + mIsLastPart(false) 1.50 +{ 1.51 + mMultipartChannel = aMultipartChannel; 1.52 + 1.53 + // Inherit the load flags from the original channel... 1.54 + mMultipartChannel->GetLoadFlags(&mLoadFlags); 1.55 + 1.56 + mMultipartChannel->GetLoadGroup(getter_AddRefs(mLoadGroup)); 1.57 +} 1.58 + 1.59 +nsPartChannel::~nsPartChannel() 1.60 +{ 1.61 +} 1.62 + 1.63 +void nsPartChannel::InitializeByteRange(int64_t aStart, int64_t aEnd) 1.64 +{ 1.65 + mIsByteRangeRequest = true; 1.66 + 1.67 + mByteRangeStart = aStart; 1.68 + mByteRangeEnd = aEnd; 1.69 +} 1.70 + 1.71 +nsresult nsPartChannel::SendOnStartRequest(nsISupports* aContext) 1.72 +{ 1.73 + return mListener->OnStartRequest(this, aContext); 1.74 +} 1.75 + 1.76 +nsresult nsPartChannel::SendOnDataAvailable(nsISupports* aContext, 1.77 + nsIInputStream* aStream, 1.78 + uint64_t aOffset, uint32_t aLen) 1.79 +{ 1.80 + return mListener->OnDataAvailable(this, aContext, aStream, aOffset, aLen); 1.81 +} 1.82 + 1.83 +nsresult nsPartChannel::SendOnStopRequest(nsISupports* aContext, 1.84 + nsresult aStatus) 1.85 +{ 1.86 + // Drop the listener 1.87 + nsCOMPtr<nsIStreamListener> listener; 1.88 + listener.swap(mListener); 1.89 + return listener->OnStopRequest(this, aContext, aStatus); 1.90 +} 1.91 + 1.92 +void nsPartChannel::SetContentDisposition(const nsACString& aContentDispositionHeader) 1.93 +{ 1.94 + mContentDispositionHeader = aContentDispositionHeader; 1.95 + nsCOMPtr<nsIURI> uri; 1.96 + GetURI(getter_AddRefs(uri)); 1.97 + NS_GetFilenameFromDisposition(mContentDispositionFilename, 1.98 + mContentDispositionHeader, uri); 1.99 + mContentDisposition = NS_GetContentDispositionFromHeader(mContentDispositionHeader, this); 1.100 +} 1.101 + 1.102 +// 1.103 +// nsISupports implementation... 1.104 +// 1.105 + 1.106 +NS_IMPL_ADDREF(nsPartChannel) 1.107 +NS_IMPL_RELEASE(nsPartChannel) 1.108 + 1.109 +NS_INTERFACE_MAP_BEGIN(nsPartChannel) 1.110 + NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIChannel) 1.111 + NS_INTERFACE_MAP_ENTRY(nsIRequest) 1.112 + NS_INTERFACE_MAP_ENTRY(nsIChannel) 1.113 + NS_INTERFACE_MAP_ENTRY(nsIByteRangeRequest) 1.114 + NS_INTERFACE_MAP_ENTRY(nsIMultiPartChannel) 1.115 +NS_INTERFACE_MAP_END 1.116 + 1.117 +// 1.118 +// nsIRequest implementation... 1.119 +// 1.120 + 1.121 +NS_IMETHODIMP 1.122 +nsPartChannel::GetName(nsACString &aResult) 1.123 +{ 1.124 + return mMultipartChannel->GetName(aResult); 1.125 +} 1.126 + 1.127 +NS_IMETHODIMP 1.128 +nsPartChannel::IsPending(bool *aResult) 1.129 +{ 1.130 + // For now, consider the active lifetime of each part the same as 1.131 + // the underlying multipart channel... This is not exactly right, 1.132 + // but it is good enough :-) 1.133 + return mMultipartChannel->IsPending(aResult); 1.134 +} 1.135 + 1.136 +NS_IMETHODIMP 1.137 +nsPartChannel::GetStatus(nsresult *aResult) 1.138 +{ 1.139 + nsresult rv = NS_OK; 1.140 + 1.141 + if (NS_FAILED(mStatus)) { 1.142 + *aResult = mStatus; 1.143 + } else { 1.144 + rv = mMultipartChannel->GetStatus(aResult); 1.145 + } 1.146 + 1.147 + return rv; 1.148 +} 1.149 + 1.150 +NS_IMETHODIMP 1.151 +nsPartChannel::Cancel(nsresult aStatus) 1.152 +{ 1.153 + // Cancelling an individual part must not cancel the underlying 1.154 + // multipart channel... 1.155 + // XXX but we should stop sending data for _this_ part channel! 1.156 + mStatus = aStatus; 1.157 + return NS_OK; 1.158 +} 1.159 + 1.160 +NS_IMETHODIMP 1.161 +nsPartChannel::Suspend(void) 1.162 +{ 1.163 + // Suspending an individual part must not suspend the underlying 1.164 + // multipart channel... 1.165 + // XXX why not? 1.166 + return NS_OK; 1.167 +} 1.168 + 1.169 +NS_IMETHODIMP 1.170 +nsPartChannel::Resume(void) 1.171 +{ 1.172 + // Resuming an individual part must not resume the underlying 1.173 + // multipart channel... 1.174 + // XXX why not? 1.175 + return NS_OK; 1.176 +} 1.177 + 1.178 +// 1.179 +// nsIChannel implementation 1.180 +// 1.181 + 1.182 +NS_IMETHODIMP 1.183 +nsPartChannel::GetOriginalURI(nsIURI * *aURI) 1.184 +{ 1.185 + return mMultipartChannel->GetOriginalURI(aURI); 1.186 +} 1.187 + 1.188 +NS_IMETHODIMP 1.189 +nsPartChannel::SetOriginalURI(nsIURI *aURI) 1.190 +{ 1.191 + return mMultipartChannel->SetOriginalURI(aURI); 1.192 +} 1.193 + 1.194 +NS_IMETHODIMP 1.195 +nsPartChannel::GetURI(nsIURI * *aURI) 1.196 +{ 1.197 + return mMultipartChannel->GetURI(aURI); 1.198 +} 1.199 + 1.200 +NS_IMETHODIMP 1.201 +nsPartChannel::Open(nsIInputStream **result) 1.202 +{ 1.203 + // This channel cannot be opened! 1.204 + return NS_ERROR_FAILURE; 1.205 +} 1.206 + 1.207 +NS_IMETHODIMP 1.208 +nsPartChannel::AsyncOpen(nsIStreamListener *aListener, nsISupports *aContext) 1.209 +{ 1.210 + // This channel cannot be opened! 1.211 + return NS_ERROR_FAILURE; 1.212 +} 1.213 + 1.214 +NS_IMETHODIMP 1.215 +nsPartChannel::GetLoadFlags(nsLoadFlags *aLoadFlags) 1.216 +{ 1.217 + *aLoadFlags = mLoadFlags; 1.218 + return NS_OK; 1.219 +} 1.220 + 1.221 +NS_IMETHODIMP 1.222 +nsPartChannel::SetLoadFlags(nsLoadFlags aLoadFlags) 1.223 +{ 1.224 + mLoadFlags = aLoadFlags; 1.225 + return NS_OK; 1.226 +} 1.227 + 1.228 +NS_IMETHODIMP 1.229 +nsPartChannel::GetLoadGroup(nsILoadGroup* *aLoadGroup) 1.230 +{ 1.231 + *aLoadGroup = mLoadGroup; 1.232 + NS_IF_ADDREF(*aLoadGroup); 1.233 + 1.234 + return NS_OK; 1.235 +} 1.236 + 1.237 +NS_IMETHODIMP 1.238 +nsPartChannel::SetLoadGroup(nsILoadGroup* aLoadGroup) 1.239 +{ 1.240 + mLoadGroup = aLoadGroup; 1.241 + 1.242 + return NS_OK; 1.243 +} 1.244 + 1.245 +NS_IMETHODIMP 1.246 +nsPartChannel::GetOwner(nsISupports* *aOwner) 1.247 +{ 1.248 + return mMultipartChannel->GetOwner(aOwner); 1.249 +} 1.250 + 1.251 +NS_IMETHODIMP 1.252 +nsPartChannel::SetOwner(nsISupports* aOwner) 1.253 +{ 1.254 + return mMultipartChannel->SetOwner(aOwner); 1.255 +} 1.256 + 1.257 +NS_IMETHODIMP 1.258 +nsPartChannel::GetNotificationCallbacks(nsIInterfaceRequestor* *aCallbacks) 1.259 +{ 1.260 + return mMultipartChannel->GetNotificationCallbacks(aCallbacks); 1.261 +} 1.262 + 1.263 +NS_IMETHODIMP 1.264 +nsPartChannel::SetNotificationCallbacks(nsIInterfaceRequestor* aCallbacks) 1.265 +{ 1.266 + return mMultipartChannel->SetNotificationCallbacks(aCallbacks); 1.267 +} 1.268 + 1.269 +NS_IMETHODIMP 1.270 +nsPartChannel::GetSecurityInfo(nsISupports * *aSecurityInfo) 1.271 +{ 1.272 + return mMultipartChannel->GetSecurityInfo(aSecurityInfo); 1.273 +} 1.274 + 1.275 +NS_IMETHODIMP 1.276 +nsPartChannel::GetContentType(nsACString &aContentType) 1.277 +{ 1.278 + aContentType = mContentType; 1.279 + return NS_OK; 1.280 +} 1.281 + 1.282 +NS_IMETHODIMP 1.283 +nsPartChannel::SetContentType(const nsACString &aContentType) 1.284 +{ 1.285 + bool dummy; 1.286 + net_ParseContentType(aContentType, mContentType, mContentCharset, &dummy); 1.287 + return NS_OK; 1.288 +} 1.289 + 1.290 +NS_IMETHODIMP 1.291 +nsPartChannel::GetContentCharset(nsACString &aContentCharset) 1.292 +{ 1.293 + aContentCharset = mContentCharset; 1.294 + return NS_OK; 1.295 +} 1.296 + 1.297 +NS_IMETHODIMP 1.298 +nsPartChannel::SetContentCharset(const nsACString &aContentCharset) 1.299 +{ 1.300 + mContentCharset = aContentCharset; 1.301 + return NS_OK; 1.302 +} 1.303 + 1.304 +NS_IMETHODIMP 1.305 +nsPartChannel::GetContentLength(int64_t *aContentLength) 1.306 +{ 1.307 + *aContentLength = mContentLength; 1.308 + return NS_OK; 1.309 +} 1.310 + 1.311 +NS_IMETHODIMP 1.312 +nsPartChannel::SetContentLength(int64_t aContentLength) 1.313 +{ 1.314 + mContentLength = aContentLength; 1.315 + return NS_OK; 1.316 +} 1.317 + 1.318 +NS_IMETHODIMP 1.319 +nsPartChannel::GetContentDisposition(uint32_t *aContentDisposition) 1.320 +{ 1.321 + if (mContentDispositionHeader.IsEmpty()) 1.322 + return NS_ERROR_NOT_AVAILABLE; 1.323 + 1.324 + *aContentDisposition = mContentDisposition; 1.325 + return NS_OK; 1.326 +} 1.327 + 1.328 +NS_IMETHODIMP 1.329 +nsPartChannel::SetContentDisposition(uint32_t aContentDisposition) 1.330 +{ 1.331 + return NS_ERROR_NOT_AVAILABLE; 1.332 +} 1.333 + 1.334 +NS_IMETHODIMP 1.335 +nsPartChannel::GetContentDispositionFilename(nsAString &aContentDispositionFilename) 1.336 +{ 1.337 + if (mContentDispositionFilename.IsEmpty()) 1.338 + return NS_ERROR_NOT_AVAILABLE; 1.339 + 1.340 + aContentDispositionFilename = mContentDispositionFilename; 1.341 + return NS_OK; 1.342 +} 1.343 + 1.344 +NS_IMETHODIMP 1.345 +nsPartChannel::SetContentDispositionFilename(const nsAString &aContentDispositionFilename) 1.346 +{ 1.347 + return NS_ERROR_NOT_AVAILABLE; 1.348 +} 1.349 + 1.350 + 1.351 +NS_IMETHODIMP 1.352 +nsPartChannel::GetContentDispositionHeader(nsACString &aContentDispositionHeader) 1.353 +{ 1.354 + if (mContentDispositionHeader.IsEmpty()) 1.355 + return NS_ERROR_NOT_AVAILABLE; 1.356 + 1.357 + aContentDispositionHeader = mContentDispositionHeader; 1.358 + return NS_OK; 1.359 +} 1.360 + 1.361 +NS_IMETHODIMP 1.362 +nsPartChannel::GetPartID(uint32_t *aPartID) 1.363 +{ 1.364 + *aPartID = mPartID; 1.365 + return NS_OK; 1.366 +} 1.367 + 1.368 +NS_IMETHODIMP 1.369 +nsPartChannel::GetIsLastPart(bool *aIsLastPart) 1.370 +{ 1.371 + *aIsLastPart = mIsLastPart; 1.372 + return NS_OK; 1.373 +} 1.374 + 1.375 +// 1.376 +// nsIByteRangeRequest implementation... 1.377 +// 1.378 + 1.379 +NS_IMETHODIMP 1.380 +nsPartChannel::GetIsByteRangeRequest(bool *aIsByteRangeRequest) 1.381 +{ 1.382 + *aIsByteRangeRequest = mIsByteRangeRequest; 1.383 + 1.384 + return NS_OK; 1.385 +} 1.386 + 1.387 + 1.388 +NS_IMETHODIMP 1.389 +nsPartChannel::GetStartRange(int64_t *aStartRange) 1.390 +{ 1.391 + *aStartRange = mByteRangeStart; 1.392 + 1.393 + return NS_OK; 1.394 +} 1.395 + 1.396 +NS_IMETHODIMP 1.397 +nsPartChannel::GetEndRange(int64_t *aEndRange) 1.398 +{ 1.399 + *aEndRange = mByteRangeEnd; 1.400 + return NS_OK; 1.401 +} 1.402 + 1.403 +NS_IMETHODIMP 1.404 +nsPartChannel::GetBaseChannel(nsIChannel ** aReturn) 1.405 +{ 1.406 + NS_ENSURE_ARG_POINTER(aReturn); 1.407 + 1.408 + *aReturn = mMultipartChannel; 1.409 + NS_IF_ADDREF(*aReturn); 1.410 + return NS_OK; 1.411 +} 1.412 + 1.413 + 1.414 +// nsISupports implementation 1.415 +NS_IMPL_ISUPPORTS(nsMultiMixedConv, 1.416 + nsIStreamConverter, 1.417 + nsIStreamListener, 1.418 + nsIRequestObserver) 1.419 + 1.420 + 1.421 +// nsIStreamConverter implementation 1.422 + 1.423 +// No syncronous conversion at this time. 1.424 +NS_IMETHODIMP 1.425 +nsMultiMixedConv::Convert(nsIInputStream *aFromStream, 1.426 + const char *aFromType, 1.427 + const char *aToType, 1.428 + nsISupports *aCtxt, nsIInputStream **_retval) { 1.429 + return NS_ERROR_NOT_IMPLEMENTED; 1.430 +} 1.431 + 1.432 +// Stream converter service calls this to initialize the actual stream converter (us). 1.433 +NS_IMETHODIMP 1.434 +nsMultiMixedConv::AsyncConvertData(const char *aFromType, const char *aToType, 1.435 + nsIStreamListener *aListener, nsISupports *aCtxt) { 1.436 + NS_ASSERTION(aListener && aFromType && aToType, "null pointer passed into multi mixed converter"); 1.437 + 1.438 + // hook up our final listener. this guy gets the various On*() calls we want to throw 1.439 + // at him. 1.440 + // 1.441 + // WARNING: this listener must be able to handle multiple OnStartRequest, OnDataAvail() 1.442 + // and OnStopRequest() call combinations. We call of series of these for each sub-part 1.443 + // in the raw stream. 1.444 + mFinalListener = aListener; 1.445 + return NS_OK; 1.446 +} 1.447 + 1.448 +// AutoFree implementation to prevent memory leaks 1.449 +class AutoFree 1.450 +{ 1.451 +public: 1.452 + AutoFree() : mBuffer(nullptr) {} 1.453 + 1.454 + AutoFree(char *buffer) : mBuffer(buffer) {} 1.455 + 1.456 + ~AutoFree() { 1.457 + free(mBuffer); 1.458 + } 1.459 + 1.460 + AutoFree& operator=(char *buffer) { 1.461 + mBuffer = buffer; 1.462 + return *this; 1.463 + } 1.464 + 1.465 + operator char*() const { 1.466 + return mBuffer; 1.467 + } 1.468 +private: 1.469 + char *mBuffer; 1.470 +}; 1.471 + 1.472 +// nsIStreamListener implementation 1.473 +NS_IMETHODIMP 1.474 +nsMultiMixedConv::OnDataAvailable(nsIRequest *request, nsISupports *context, 1.475 + nsIInputStream *inStr, uint64_t sourceOffset, 1.476 + uint32_t count) { 1.477 + 1.478 + if (mToken.IsEmpty()) // no token, no love. 1.479 + return NS_ERROR_FAILURE; 1.480 + 1.481 + nsresult rv = NS_OK; 1.482 + AutoFree buffer = nullptr; 1.483 + uint32_t bufLen = 0, read = 0; 1.484 + 1.485 + NS_ASSERTION(request, "multimixed converter needs a request"); 1.486 + 1.487 + nsCOMPtr<nsIChannel> channel = do_QueryInterface(request, &rv); 1.488 + if (NS_FAILED(rv)) return rv; 1.489 + 1.490 + // fill buffer 1.491 + { 1.492 + bufLen = count + mBufLen; 1.493 + NS_ENSURE_TRUE((bufLen >= count) && (bufLen >= mBufLen), 1.494 + NS_ERROR_FAILURE); 1.495 + buffer = (char *) malloc(bufLen); 1.496 + if (!buffer) 1.497 + return NS_ERROR_OUT_OF_MEMORY; 1.498 + 1.499 + if (mBufLen) { 1.500 + // incorporate any buffered data into the parsing 1.501 + memcpy(buffer, mBuffer, mBufLen); 1.502 + free(mBuffer); 1.503 + mBuffer = 0; 1.504 + mBufLen = 0; 1.505 + } 1.506 + 1.507 + rv = inStr->Read(buffer + (bufLen - count), count, &read); 1.508 + 1.509 + if (NS_FAILED(rv) || read == 0) return rv; 1.510 + NS_ASSERTION(read == count, "poor data size assumption"); 1.511 + } 1.512 + 1.513 + char *cursor = buffer; 1.514 + 1.515 + if (mFirstOnData) { 1.516 + // this is the first OnData() for this request. some servers 1.517 + // don't bother sending a token in the first "part." This is 1.518 + // illegal, but we'll handle the case anyway by shoving the 1.519 + // boundary token in for the server. 1.520 + mFirstOnData = false; 1.521 + NS_ASSERTION(!mBufLen, "this is our first time through, we can't have buffered data"); 1.522 + const char * token = mToken.get(); 1.523 + 1.524 + PushOverLine(cursor, bufLen); 1.525 + 1.526 + if (bufLen < mTokenLen+2) { 1.527 + // we don't have enough data yet to make this comparison. 1.528 + // skip this check, and try again the next time OnData() 1.529 + // is called. 1.530 + mFirstOnData = true; 1.531 + } 1.532 + else if (!PL_strnstr(cursor, token, mTokenLen+2)) { 1.533 + buffer = (char *) realloc(buffer, bufLen + mTokenLen + 1); 1.534 + if (!buffer) 1.535 + return NS_ERROR_OUT_OF_MEMORY; 1.536 + 1.537 + memmove(buffer + mTokenLen + 1, buffer, bufLen); 1.538 + memcpy(buffer, token, mTokenLen); 1.539 + buffer[mTokenLen] = '\n'; 1.540 + 1.541 + bufLen += (mTokenLen + 1); 1.542 + 1.543 + // need to reset cursor to the buffer again (bug 100595) 1.544 + cursor = buffer; 1.545 + } 1.546 + } 1.547 + 1.548 + char *token = nullptr; 1.549 + 1.550 + if (mProcessingHeaders) { 1.551 + // we were not able to process all the headers 1.552 + // for this "part" given the previous buffer given to 1.553 + // us in the previous OnDataAvailable callback. 1.554 + bool done = false; 1.555 + rv = ParseHeaders(channel, cursor, bufLen, &done); 1.556 + if (NS_FAILED(rv)) return rv; 1.557 + 1.558 + if (done) { 1.559 + mProcessingHeaders = false; 1.560 + rv = SendStart(channel); 1.561 + if (NS_FAILED(rv)) return rv; 1.562 + } 1.563 + } 1.564 + 1.565 + int32_t tokenLinefeed = 1; 1.566 + while ( (token = FindToken(cursor, bufLen)) ) { 1.567 + 1.568 + if (((token + mTokenLen) < (cursor + bufLen)) && 1.569 + (*(token + mTokenLen + 1) == '-')) { 1.570 + // This was the last delimiter so we can stop processing 1.571 + rv = SendData(cursor, LengthToToken(cursor, token)); 1.572 + if (NS_FAILED(rv)) return rv; 1.573 + return SendStop(NS_OK); 1.574 + } 1.575 + 1.576 + if (!mNewPart && token > cursor) { 1.577 + // headers are processed, we're pushing data now. 1.578 + NS_ASSERTION(!mProcessingHeaders, "we should be pushing raw data"); 1.579 + rv = SendData(cursor, LengthToToken(cursor, token)); 1.580 + bufLen -= token - cursor; 1.581 + if (NS_FAILED(rv)) return rv; 1.582 + } 1.583 + // XXX else NS_ASSERTION(token == cursor, "?"); 1.584 + token += mTokenLen; 1.585 + bufLen -= mTokenLen; 1.586 + tokenLinefeed = PushOverLine(token, bufLen); 1.587 + 1.588 + if (mNewPart) { 1.589 + // parse headers 1.590 + mNewPart = false; 1.591 + cursor = token; 1.592 + bool done = false; 1.593 + rv = ParseHeaders(channel, cursor, bufLen, &done); 1.594 + if (NS_FAILED(rv)) return rv; 1.595 + if (done) { 1.596 + rv = SendStart(channel); 1.597 + if (NS_FAILED(rv)) return rv; 1.598 + } 1.599 + else { 1.600 + // we haven't finished processing header info. 1.601 + // we'll break out and try to process later. 1.602 + mProcessingHeaders = true; 1.603 + break; 1.604 + } 1.605 + } 1.606 + else { 1.607 + mNewPart = true; 1.608 + // Reset state so we don't carry it over from part to part 1.609 + mContentType.Truncate(); 1.610 + mContentLength = UINT64_MAX; 1.611 + mContentDisposition.Truncate(); 1.612 + mIsByteRangeRequest = false; 1.613 + mByteRangeStart = 0; 1.614 + mByteRangeEnd = 0; 1.615 + 1.616 + rv = SendStop(NS_OK); 1.617 + if (NS_FAILED(rv)) return rv; 1.618 + // reset the token to front. this allows us to treat 1.619 + // the token as a starting token. 1.620 + token -= mTokenLen + tokenLinefeed; 1.621 + bufLen += mTokenLen + tokenLinefeed; 1.622 + cursor = token; 1.623 + } 1.624 + } 1.625 + 1.626 + // at this point, we want to buffer up whatever amount (bufLen) 1.627 + // we have leftover. However, we *always* want to ensure that 1.628 + // we buffer enough data to handle a broken token. 1.629 + 1.630 + // carry over 1.631 + uint32_t bufAmt = 0; 1.632 + if (mProcessingHeaders) 1.633 + bufAmt = bufLen; 1.634 + else if (bufLen) { 1.635 + // if the data ends in a linefeed, and we're in the middle 1.636 + // of a "part" (ie. mPartChannel exists) don't bother 1.637 + // buffering, go ahead and send the data we have. Otherwise 1.638 + // if we don't have a channel already, then we don't even 1.639 + // have enough info to start a part, go ahead and buffer 1.640 + // enough to collect a boundary token. 1.641 + if (!mPartChannel || !(cursor[bufLen-1] == nsCRT::LF) ) 1.642 + bufAmt = std::min(mTokenLen - 1, bufLen); 1.643 + } 1.644 + 1.645 + if (bufAmt) { 1.646 + rv = BufferData(cursor + (bufLen - bufAmt), bufAmt); 1.647 + if (NS_FAILED(rv)) return rv; 1.648 + bufLen -= bufAmt; 1.649 + } 1.650 + 1.651 + if (bufLen) { 1.652 + rv = SendData(cursor, bufLen); 1.653 + if (NS_FAILED(rv)) return rv; 1.654 + } 1.655 + 1.656 + return rv; 1.657 +} 1.658 + 1.659 + 1.660 +// nsIRequestObserver implementation 1.661 +NS_IMETHODIMP 1.662 +nsMultiMixedConv::OnStartRequest(nsIRequest *request, nsISupports *ctxt) { 1.663 + // we're assuming the content-type is available at this stage 1.664 + NS_ASSERTION(mToken.IsEmpty(), "a second on start???"); 1.665 + const char *bndry = nullptr; 1.666 + nsAutoCString delimiter; 1.667 + nsresult rv = NS_OK; 1.668 + mContext = ctxt; 1.669 + 1.670 + mFirstOnData = true; 1.671 + mTotalSent = 0; 1.672 + 1.673 + nsCOMPtr<nsIChannel> channel = do_QueryInterface(request, &rv); 1.674 + if (NS_FAILED(rv)) return rv; 1.675 + 1.676 + // ask the HTTP channel for the content-type and extract the boundary from it. 1.677 + nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface(channel, &rv); 1.678 + if (NS_SUCCEEDED(rv)) { 1.679 + rv = httpChannel->GetResponseHeader(NS_LITERAL_CSTRING("content-type"), delimiter); 1.680 + if (NS_FAILED(rv)) return rv; 1.681 + } else { 1.682 + // try asking the channel directly 1.683 + rv = channel->GetContentType(delimiter); 1.684 + if (NS_FAILED(rv)) return NS_ERROR_FAILURE; 1.685 + } 1.686 + 1.687 + bndry = strstr(delimiter.BeginWriting(), "boundary"); 1.688 + if (!bndry) return NS_ERROR_FAILURE; 1.689 + 1.690 + bndry = strchr(bndry, '='); 1.691 + if (!bndry) return NS_ERROR_FAILURE; 1.692 + 1.693 + bndry++; // move past the equals sign 1.694 + 1.695 + char *attrib = (char *) strchr(bndry, ';'); 1.696 + if (attrib) *attrib = '\0'; 1.697 + 1.698 + nsAutoCString boundaryString(bndry); 1.699 + if (attrib) *attrib = ';'; 1.700 + 1.701 + boundaryString.Trim(" \""); 1.702 + 1.703 + mToken = boundaryString; 1.704 + mTokenLen = boundaryString.Length(); 1.705 + 1.706 + if (mTokenLen == 0) 1.707 + return NS_ERROR_FAILURE; 1.708 + 1.709 + return NS_OK; 1.710 +} 1.711 + 1.712 +NS_IMETHODIMP 1.713 +nsMultiMixedConv::OnStopRequest(nsIRequest *request, nsISupports *ctxt, 1.714 + nsresult aStatus) { 1.715 + 1.716 + if (mToken.IsEmpty()) // no token, no love. 1.717 + return NS_ERROR_FAILURE; 1.718 + 1.719 + if (mPartChannel) { 1.720 + mPartChannel->SetIsLastPart(); 1.721 + 1.722 + // we've already called SendStart() (which sets up the mPartChannel, 1.723 + // and fires an OnStart()) send any data left over, and then fire the stop. 1.724 + if (mBufLen > 0 && mBuffer) { 1.725 + (void) SendData(mBuffer, mBufLen); 1.726 + // don't bother checking the return value here, if the send failed 1.727 + // we're done anyway as we're in the OnStop() callback. 1.728 + free(mBuffer); 1.729 + mBuffer = nullptr; 1.730 + mBufLen = 0; 1.731 + } 1.732 + (void) SendStop(aStatus); 1.733 + } else if (NS_FAILED(aStatus)) { 1.734 + // underlying data production problem. we should not be in 1.735 + // the middle of sending data. if we were, mPartChannel, 1.736 + // above, would have been true. 1.737 + 1.738 + // if we send the start, the URI Loader's m_targetStreamListener, may 1.739 + // be pointing at us causing a nice stack overflow. So, don't call 1.740 + // OnStartRequest! - This breaks necko's semantecs. 1.741 + //(void) mFinalListener->OnStartRequest(request, ctxt); 1.742 + 1.743 + (void) mFinalListener->OnStopRequest(request, ctxt, aStatus); 1.744 + } 1.745 + 1.746 + return NS_OK; 1.747 +} 1.748 + 1.749 + 1.750 +// nsMultiMixedConv methods 1.751 +nsMultiMixedConv::nsMultiMixedConv() : 1.752 + mCurrentPartID(0) 1.753 +{ 1.754 + mTokenLen = 0; 1.755 + mNewPart = true; 1.756 + mContentLength = UINT64_MAX; 1.757 + mBuffer = nullptr; 1.758 + mBufLen = 0; 1.759 + mProcessingHeaders = false; 1.760 + mByteRangeStart = 0; 1.761 + mByteRangeEnd = 0; 1.762 + mTotalSent = 0; 1.763 + mIsByteRangeRequest = false; 1.764 +} 1.765 + 1.766 +nsMultiMixedConv::~nsMultiMixedConv() { 1.767 + NS_ASSERTION(!mBuffer, "all buffered data should be gone"); 1.768 + if (mBuffer) { 1.769 + free(mBuffer); 1.770 + mBuffer = nullptr; 1.771 + } 1.772 +} 1.773 + 1.774 +nsresult 1.775 +nsMultiMixedConv::BufferData(char *aData, uint32_t aLen) { 1.776 + NS_ASSERTION(!mBuffer, "trying to over-write buffer"); 1.777 + 1.778 + char *buffer = (char *) malloc(aLen); 1.779 + if (!buffer) return NS_ERROR_OUT_OF_MEMORY; 1.780 + 1.781 + memcpy(buffer, aData, aLen); 1.782 + mBuffer = buffer; 1.783 + mBufLen = aLen; 1.784 + return NS_OK; 1.785 +} 1.786 + 1.787 + 1.788 +nsresult 1.789 +nsMultiMixedConv::SendStart(nsIChannel *aChannel) { 1.790 + nsresult rv = NS_OK; 1.791 + 1.792 + nsCOMPtr<nsIStreamListener> partListener(mFinalListener); 1.793 + if (mContentType.IsEmpty()) { 1.794 + mContentType.AssignLiteral(UNKNOWN_CONTENT_TYPE); 1.795 + nsCOMPtr<nsIStreamConverterService> serv = 1.796 + do_GetService(NS_STREAMCONVERTERSERVICE_CONTRACTID, &rv); 1.797 + if (NS_SUCCEEDED(rv)) { 1.798 + nsCOMPtr<nsIStreamListener> converter; 1.799 + rv = serv->AsyncConvertData(UNKNOWN_CONTENT_TYPE, 1.800 + "*/*", 1.801 + mFinalListener, 1.802 + mContext, 1.803 + getter_AddRefs(converter)); 1.804 + if (NS_SUCCEEDED(rv)) { 1.805 + partListener = converter; 1.806 + } 1.807 + } 1.808 + } 1.809 + 1.810 + // if we already have an mPartChannel, that means we never sent a Stop() 1.811 + // before starting up another "part." that would be bad. 1.812 + NS_ASSERTION(!mPartChannel, "tisk tisk, shouldn't be overwriting a channel"); 1.813 + 1.814 + nsPartChannel *newChannel; 1.815 + newChannel = new nsPartChannel(aChannel, mCurrentPartID++, partListener); 1.816 + if (!newChannel) 1.817 + return NS_ERROR_OUT_OF_MEMORY; 1.818 + 1.819 + if (mIsByteRangeRequest) { 1.820 + newChannel->InitializeByteRange(mByteRangeStart, mByteRangeEnd); 1.821 + } 1.822 + 1.823 + mTotalSent = 0; 1.824 + 1.825 + // Set up the new part channel... 1.826 + mPartChannel = newChannel; 1.827 + 1.828 + rv = mPartChannel->SetContentType(mContentType); 1.829 + if (NS_FAILED(rv)) return rv; 1.830 + 1.831 + rv = mPartChannel->SetContentLength(mContentLength); 1.832 + if (NS_FAILED(rv)) return rv; 1.833 + 1.834 + mPartChannel->SetContentDisposition(mContentDisposition); 1.835 + 1.836 + nsLoadFlags loadFlags = 0; 1.837 + mPartChannel->GetLoadFlags(&loadFlags); 1.838 + loadFlags |= nsIChannel::LOAD_REPLACE; 1.839 + mPartChannel->SetLoadFlags(loadFlags); 1.840 + 1.841 + nsCOMPtr<nsILoadGroup> loadGroup; 1.842 + (void)mPartChannel->GetLoadGroup(getter_AddRefs(loadGroup)); 1.843 + 1.844 + // Add the new channel to the load group (if any) 1.845 + if (loadGroup) { 1.846 + rv = loadGroup->AddRequest(mPartChannel, nullptr); 1.847 + if (NS_FAILED(rv)) return rv; 1.848 + } 1.849 + 1.850 + // Let's start off the load. NOTE: we don't forward on the channel passed 1.851 + // into our OnDataAvailable() as it's the root channel for the raw stream. 1.852 + return mPartChannel->SendOnStartRequest(mContext); 1.853 +} 1.854 + 1.855 + 1.856 +nsresult 1.857 +nsMultiMixedConv::SendStop(nsresult aStatus) { 1.858 + 1.859 + nsresult rv = NS_OK; 1.860 + if (mPartChannel) { 1.861 + rv = mPartChannel->SendOnStopRequest(mContext, aStatus); 1.862 + // don't check for failure here, we need to remove the channel from 1.863 + // the loadgroup. 1.864 + 1.865 + // Remove the channel from its load group (if any) 1.866 + nsCOMPtr<nsILoadGroup> loadGroup; 1.867 + (void) mPartChannel->GetLoadGroup(getter_AddRefs(loadGroup)); 1.868 + if (loadGroup) 1.869 + (void) loadGroup->RemoveRequest(mPartChannel, mContext, aStatus); 1.870 + } 1.871 + 1.872 + mPartChannel = 0; 1.873 + return rv; 1.874 +} 1.875 + 1.876 +nsresult 1.877 +nsMultiMixedConv::SendData(char *aBuffer, uint32_t aLen) { 1.878 + 1.879 + nsresult rv = NS_OK; 1.880 + 1.881 + if (!mPartChannel) return NS_ERROR_FAILURE; // something went wrong w/ processing 1.882 + 1.883 + if (mContentLength != UINT64_MAX) { 1.884 + // make sure that we don't send more than the mContentLength 1.885 + // XXX why? perhaps the Content-Length header was actually wrong!! 1.886 + if ((uint64_t(aLen) + mTotalSent) > mContentLength) 1.887 + aLen = static_cast<uint32_t>(mContentLength - mTotalSent); 1.888 + 1.889 + if (aLen == 0) 1.890 + return NS_OK; 1.891 + } 1.892 + 1.893 + uint64_t offset = mTotalSent; 1.894 + mTotalSent += aLen; 1.895 + 1.896 + nsCOMPtr<nsIStringInputStream> ss( 1.897 + do_CreateInstance("@mozilla.org/io/string-input-stream;1", &rv)); 1.898 + if (NS_FAILED(rv)) 1.899 + return rv; 1.900 + 1.901 + rv = ss->ShareData(aBuffer, aLen); 1.902 + if (NS_FAILED(rv)) 1.903 + return rv; 1.904 + 1.905 + nsCOMPtr<nsIInputStream> inStream(do_QueryInterface(ss, &rv)); 1.906 + if (NS_FAILED(rv)) return rv; 1.907 + 1.908 + return mPartChannel->SendOnDataAvailable(mContext, inStream, offset, aLen); 1.909 +} 1.910 + 1.911 +int32_t 1.912 +nsMultiMixedConv::PushOverLine(char *&aPtr, uint32_t &aLen) { 1.913 + int32_t chars = 0; 1.914 + if ((aLen > 0) && (*aPtr == nsCRT::CR || *aPtr == nsCRT::LF)) { 1.915 + if ((aLen > 1) && (aPtr[1] == nsCRT::LF)) 1.916 + chars++; 1.917 + chars++; 1.918 + aPtr += chars; 1.919 + aLen -= chars; 1.920 + } 1.921 + return chars; 1.922 +} 1.923 + 1.924 +nsresult 1.925 +nsMultiMixedConv::ParseHeaders(nsIChannel *aChannel, char *&aPtr, 1.926 + uint32_t &aLen, bool *_retval) { 1.927 + // NOTE: this data must be ascii. 1.928 + // NOTE: aPtr is NOT null terminated! 1.929 + nsresult rv = NS_OK; 1.930 + char *cursor = aPtr, *newLine = nullptr; 1.931 + uint32_t cursorLen = aLen; 1.932 + bool done = false; 1.933 + uint32_t lineFeedIncrement = 1; 1.934 + 1.935 + mContentLength = UINT64_MAX; // XXX what if we were already called? 1.936 + while (cursorLen && (newLine = (char *) memchr(cursor, nsCRT::LF, cursorLen))) { 1.937 + // adjust for linefeeds 1.938 + if ((newLine > cursor) && (newLine[-1] == nsCRT::CR) ) { // CRLF 1.939 + lineFeedIncrement = 2; 1.940 + newLine--; 1.941 + } 1.942 + else 1.943 + lineFeedIncrement = 1; // reset 1.944 + 1.945 + if (newLine == cursor) { 1.946 + // move the newLine beyond the linefeed marker 1.947 + NS_ASSERTION(cursorLen >= lineFeedIncrement, "oops!"); 1.948 + 1.949 + cursor += lineFeedIncrement; 1.950 + cursorLen -= lineFeedIncrement; 1.951 + 1.952 + done = true; 1.953 + break; 1.954 + } 1.955 + 1.956 + char tmpChar = *newLine; 1.957 + *newLine = '\0'; // cursor is now null terminated 1.958 + char *colon = (char *) strchr(cursor, ':'); 1.959 + if (colon) { 1.960 + *colon = '\0'; 1.961 + nsAutoCString headerStr(cursor); 1.962 + headerStr.CompressWhitespace(); 1.963 + *colon = ':'; 1.964 + 1.965 + nsAutoCString headerVal(colon + 1); 1.966 + headerVal.CompressWhitespace(); 1.967 + 1.968 + // examine header 1.969 + if (headerStr.LowerCaseEqualsLiteral("content-type")) { 1.970 + mContentType = headerVal; 1.971 + } else if (headerStr.LowerCaseEqualsLiteral("content-length")) { 1.972 + mContentLength = nsCRT::atoll(headerVal.get()); 1.973 + } else if (headerStr.LowerCaseEqualsLiteral("content-disposition")) { 1.974 + mContentDisposition = headerVal; 1.975 + } else if (headerStr.LowerCaseEqualsLiteral("set-cookie")) { 1.976 + nsCOMPtr<nsIHttpChannelInternal> httpInternal = 1.977 + do_QueryInterface(aChannel); 1.978 + if (httpInternal) { 1.979 + httpInternal->SetCookie(headerVal.get()); 1.980 + } 1.981 + } else if (headerStr.LowerCaseEqualsLiteral("content-range") || 1.982 + headerStr.LowerCaseEqualsLiteral("range") ) { 1.983 + // something like: Content-range: bytes 7000-7999/8000 1.984 + char* tmpPtr; 1.985 + 1.986 + tmpPtr = (char *) strchr(colon + 1, '/'); 1.987 + if (tmpPtr) 1.988 + *tmpPtr = '\0'; 1.989 + 1.990 + // pass the bytes-unit and the SP 1.991 + char *range = (char *) strchr(colon + 2, ' '); 1.992 + if (!range) 1.993 + return NS_ERROR_FAILURE; 1.994 + 1.995 + do { 1.996 + range++; 1.997 + } while (*range == ' '); 1.998 + 1.999 + if (range[0] == '*'){ 1.1000 + mByteRangeStart = mByteRangeEnd = 0; 1.1001 + } 1.1002 + else { 1.1003 + tmpPtr = (char *) strchr(range, '-'); 1.1004 + if (!tmpPtr) 1.1005 + return NS_ERROR_FAILURE; 1.1006 + 1.1007 + tmpPtr[0] = '\0'; 1.1008 + 1.1009 + mByteRangeStart = nsCRT::atoll(range); 1.1010 + tmpPtr++; 1.1011 + mByteRangeEnd = nsCRT::atoll(tmpPtr); 1.1012 + } 1.1013 + 1.1014 + mIsByteRangeRequest = true; 1.1015 + if (mContentLength == UINT64_MAX) 1.1016 + mContentLength = uint64_t(mByteRangeEnd - mByteRangeStart + 1); 1.1017 + } 1.1018 + } 1.1019 + *newLine = tmpChar; 1.1020 + newLine += lineFeedIncrement; 1.1021 + cursorLen -= (newLine - cursor); 1.1022 + cursor = newLine; 1.1023 + } 1.1024 + 1.1025 + aPtr = cursor; 1.1026 + aLen = cursorLen; 1.1027 + 1.1028 + *_retval = done; 1.1029 + return rv; 1.1030 +} 1.1031 + 1.1032 +char * 1.1033 +nsMultiMixedConv::FindToken(char *aCursor, uint32_t aLen) { 1.1034 + // strnstr without looking for null termination 1.1035 + const char *token = mToken.get(); 1.1036 + char *cur = aCursor; 1.1037 + 1.1038 + if (!(token && aCursor && *token)) { 1.1039 + NS_WARNING("bad data"); 1.1040 + return nullptr; 1.1041 + } 1.1042 + 1.1043 + for (; aLen >= mTokenLen; aCursor++, aLen--) { 1.1044 + if (!memcmp(aCursor, token, mTokenLen) ) { 1.1045 + if ((aCursor - cur) >= 2) { 1.1046 + // back the cursor up over a double dash for backwards compat. 1.1047 + if ((*(aCursor-1) == '-') && (*(aCursor-2) == '-')) { 1.1048 + aCursor -= 2; 1.1049 + aLen += 2; 1.1050 + 1.1051 + // we're playing w/ double dash tokens, adjust. 1.1052 + mToken.Assign(aCursor, mTokenLen + 2); 1.1053 + mTokenLen = mToken.Length(); 1.1054 + } 1.1055 + } 1.1056 + return aCursor; 1.1057 + } 1.1058 + } 1.1059 + 1.1060 + return nullptr; 1.1061 +} 1.1062 + 1.1063 +nsresult 1.1064 +NS_NewMultiMixedConv(nsMultiMixedConv** aMultiMixedConv) 1.1065 +{ 1.1066 + NS_PRECONDITION(aMultiMixedConv != nullptr, "null ptr"); 1.1067 + if (! aMultiMixedConv) 1.1068 + return NS_ERROR_NULL_POINTER; 1.1069 + 1.1070 + *aMultiMixedConv = new nsMultiMixedConv(); 1.1071 + if (! *aMultiMixedConv) 1.1072 + return NS_ERROR_OUT_OF_MEMORY; 1.1073 + 1.1074 + NS_ADDREF(*aMultiMixedConv); 1.1075 + return NS_OK; 1.1076 +} 1.1077 +