123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122 |
- /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
- /* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
- #include "nsMultiMixedConv.h"
- #include "plstr.h"
- #include "nsIHttpChannel.h"
- #include "nsNetCID.h"
- #include "nsMimeTypes.h"
- #include "nsIStringStream.h"
- #include "nsCRT.h"
- #include "nsIHttpChannelInternal.h"
- #include "nsURLHelper.h"
- #include "nsIStreamConverterService.h"
- #include "nsICacheInfoChannel.h"
- #include <algorithm>
- #include "nsContentSecurityManager.h"
- #include "nsHttp.h"
- #include "nsNetUtil.h"
- #include "nsIURI.h"
- #include "nsHttpHeaderArray.h"
- //
- // Helper function for determining the length of data bytes up to
- // the next multipart token. A token is usually preceded by a LF
- // or CRLF delimiter.
- //
- static uint32_t
- LengthToToken(const char *cursor, const char *token)
- {
- uint32_t len = token - cursor;
- // Trim off any LF or CRLF preceding the token
- if (len && *(token-1) == '\n') {
- --len;
- if (len && *(token-2) == '\r')
- --len;
- }
- return len;
- }
- nsPartChannel::nsPartChannel(nsIChannel *aMultipartChannel, uint32_t aPartID,
- nsIStreamListener* aListener) :
- mMultipartChannel(aMultipartChannel),
- mListener(aListener),
- mStatus(NS_OK),
- mContentLength(UINT64_MAX),
- mIsByteRangeRequest(false),
- mByteRangeStart(0),
- mByteRangeEnd(0),
- mPartID(aPartID),
- mIsLastPart(false)
- {
- // Inherit the load flags from the original channel...
- mMultipartChannel->GetLoadFlags(&mLoadFlags);
- mMultipartChannel->GetLoadGroup(getter_AddRefs(mLoadGroup));
- }
- nsPartChannel::~nsPartChannel()
- {
- }
- void nsPartChannel::InitializeByteRange(int64_t aStart, int64_t aEnd)
- {
- mIsByteRangeRequest = true;
-
- mByteRangeStart = aStart;
- mByteRangeEnd = aEnd;
- }
- nsresult nsPartChannel::SendOnStartRequest(nsISupports* aContext)
- {
- return mListener->OnStartRequest(this, aContext);
- }
- nsresult nsPartChannel::SendOnDataAvailable(nsISupports* aContext,
- nsIInputStream* aStream,
- uint64_t aOffset, uint32_t aLen)
- {
- return mListener->OnDataAvailable(this, aContext, aStream, aOffset, aLen);
- }
- nsresult nsPartChannel::SendOnStopRequest(nsISupports* aContext,
- nsresult aStatus)
- {
- // Drop the listener
- nsCOMPtr<nsIStreamListener> listener;
- listener.swap(mListener);
- return listener->OnStopRequest(this, aContext, aStatus);
- }
- void nsPartChannel::SetContentDisposition(const nsACString& aContentDispositionHeader)
- {
- mContentDispositionHeader = aContentDispositionHeader;
- nsCOMPtr<nsIURI> uri;
- GetURI(getter_AddRefs(uri));
- NS_GetFilenameFromDisposition(mContentDispositionFilename,
- mContentDispositionHeader, uri);
- mContentDisposition = NS_GetContentDispositionFromHeader(mContentDispositionHeader, this);
- }
- //
- // nsISupports implementation...
- //
- NS_IMPL_ADDREF(nsPartChannel)
- NS_IMPL_RELEASE(nsPartChannel)
- NS_INTERFACE_MAP_BEGIN(nsPartChannel)
- NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIChannel)
- NS_INTERFACE_MAP_ENTRY(nsIRequest)
- NS_INTERFACE_MAP_ENTRY(nsIChannel)
- NS_INTERFACE_MAP_ENTRY(nsIByteRangeRequest)
- NS_INTERFACE_MAP_ENTRY(nsIMultiPartChannel)
- NS_INTERFACE_MAP_END
- //
- // nsIRequest implementation...
- //
- NS_IMETHODIMP
- nsPartChannel::GetName(nsACString &aResult)
- {
- return mMultipartChannel->GetName(aResult);
- }
- NS_IMETHODIMP
- nsPartChannel::IsPending(bool *aResult)
- {
- // For now, consider the active lifetime of each part the same as
- // the underlying multipart channel... This is not exactly right,
- // but it is good enough :-)
- return mMultipartChannel->IsPending(aResult);
- }
- NS_IMETHODIMP
- nsPartChannel::GetStatus(nsresult *aResult)
- {
- nsresult rv = NS_OK;
- if (NS_FAILED(mStatus)) {
- *aResult = mStatus;
- } else {
- rv = mMultipartChannel->GetStatus(aResult);
- }
- return rv;
- }
- NS_IMETHODIMP
- nsPartChannel::Cancel(nsresult aStatus)
- {
- // Cancelling an individual part must not cancel the underlying
- // multipart channel...
- // XXX but we should stop sending data for _this_ part channel!
- mStatus = aStatus;
- return NS_OK;
- }
- NS_IMETHODIMP
- nsPartChannel::Suspend(void)
- {
- // Suspending an individual part must not suspend the underlying
- // multipart channel...
- // XXX why not?
- return NS_OK;
- }
- NS_IMETHODIMP
- nsPartChannel::Resume(void)
- {
- // Resuming an individual part must not resume the underlying
- // multipart channel...
- // XXX why not?
- return NS_OK;
- }
- //
- // nsIChannel implementation
- //
- NS_IMETHODIMP
- nsPartChannel::GetOriginalURI(nsIURI * *aURI)
- {
- return mMultipartChannel->GetOriginalURI(aURI);
- }
- NS_IMETHODIMP
- nsPartChannel::SetOriginalURI(nsIURI *aURI)
- {
- return mMultipartChannel->SetOriginalURI(aURI);
- }
- NS_IMETHODIMP
- nsPartChannel::GetURI(nsIURI * *aURI)
- {
- return mMultipartChannel->GetURI(aURI);
- }
- NS_IMETHODIMP
- nsPartChannel::Open(nsIInputStream **result)
- {
- // This channel cannot be opened!
- return NS_ERROR_FAILURE;
- }
- NS_IMETHODIMP
- nsPartChannel::Open2(nsIInputStream** aStream)
- {
- nsCOMPtr<nsIStreamListener> listener;
- nsresult rv = nsContentSecurityManager::doContentSecurityCheck(this, listener);
- NS_ENSURE_SUCCESS(rv, rv);
- return Open(aStream);
- }
- NS_IMETHODIMP
- nsPartChannel::AsyncOpen(nsIStreamListener *aListener, nsISupports *aContext)
- {
- // This channel cannot be opened!
- return NS_ERROR_FAILURE;
- }
- NS_IMETHODIMP
- nsPartChannel::AsyncOpen2(nsIStreamListener *aListener)
- {
- nsCOMPtr<nsIStreamListener> listener = aListener;
- nsresult rv = nsContentSecurityManager::doContentSecurityCheck(this, listener);
- NS_ENSURE_SUCCESS(rv, rv);
- return AsyncOpen(listener, nullptr);
- }
- NS_IMETHODIMP
- nsPartChannel::GetLoadFlags(nsLoadFlags *aLoadFlags)
- {
- *aLoadFlags = mLoadFlags;
- return NS_OK;
- }
- NS_IMETHODIMP
- nsPartChannel::SetLoadFlags(nsLoadFlags aLoadFlags)
- {
- mLoadFlags = aLoadFlags;
- return NS_OK;
- }
- NS_IMETHODIMP
- nsPartChannel::GetLoadGroup(nsILoadGroup* *aLoadGroup)
- {
- *aLoadGroup = mLoadGroup;
- NS_IF_ADDREF(*aLoadGroup);
- return NS_OK;
- }
- NS_IMETHODIMP
- nsPartChannel::SetLoadGroup(nsILoadGroup* aLoadGroup)
- {
- mLoadGroup = aLoadGroup;
- return NS_OK;
- }
- NS_IMETHODIMP
- nsPartChannel::GetOwner(nsISupports* *aOwner)
- {
- return mMultipartChannel->GetOwner(aOwner);
- }
- NS_IMETHODIMP
- nsPartChannel::SetOwner(nsISupports* aOwner)
- {
- return mMultipartChannel->SetOwner(aOwner);
- }
- NS_IMETHODIMP
- nsPartChannel::GetLoadInfo(nsILoadInfo* *aLoadInfo)
- {
- return mMultipartChannel->GetLoadInfo(aLoadInfo);
- }
- NS_IMETHODIMP
- nsPartChannel::SetLoadInfo(nsILoadInfo* aLoadInfo)
- {
- return mMultipartChannel->SetLoadInfo(aLoadInfo);
- }
- NS_IMETHODIMP
- nsPartChannel::GetNotificationCallbacks(nsIInterfaceRequestor* *aCallbacks)
- {
- return mMultipartChannel->GetNotificationCallbacks(aCallbacks);
- }
- NS_IMETHODIMP
- nsPartChannel::SetNotificationCallbacks(nsIInterfaceRequestor* aCallbacks)
- {
- return mMultipartChannel->SetNotificationCallbacks(aCallbacks);
- }
- NS_IMETHODIMP
- nsPartChannel::GetSecurityInfo(nsISupports * *aSecurityInfo)
- {
- return mMultipartChannel->GetSecurityInfo(aSecurityInfo);
- }
- NS_IMETHODIMP
- nsPartChannel::GetContentType(nsACString &aContentType)
- {
- aContentType = mContentType;
- return NS_OK;
- }
- NS_IMETHODIMP
- nsPartChannel::SetContentType(const nsACString &aContentType)
- {
- bool dummy;
- net_ParseContentType(aContentType, mContentType, mContentCharset, &dummy);
- return NS_OK;
- }
- NS_IMETHODIMP
- nsPartChannel::GetContentCharset(nsACString &aContentCharset)
- {
- aContentCharset = mContentCharset;
- return NS_OK;
- }
- NS_IMETHODIMP
- nsPartChannel::SetContentCharset(const nsACString &aContentCharset)
- {
- mContentCharset = aContentCharset;
- return NS_OK;
- }
- NS_IMETHODIMP
- nsPartChannel::GetContentLength(int64_t *aContentLength)
- {
- *aContentLength = mContentLength;
- return NS_OK;
- }
- NS_IMETHODIMP
- nsPartChannel::SetContentLength(int64_t aContentLength)
- {
- mContentLength = aContentLength;
- return NS_OK;
- }
- NS_IMETHODIMP
- nsPartChannel::GetContentDisposition(uint32_t *aContentDisposition)
- {
- if (mContentDispositionHeader.IsEmpty())
- return NS_ERROR_NOT_AVAILABLE;
- *aContentDisposition = mContentDisposition;
- return NS_OK;
- }
- NS_IMETHODIMP
- nsPartChannel::SetContentDisposition(uint32_t aContentDisposition)
- {
- return NS_ERROR_NOT_AVAILABLE;
- }
- NS_IMETHODIMP
- nsPartChannel::GetContentDispositionFilename(nsAString &aContentDispositionFilename)
- {
- if (mContentDispositionFilename.IsEmpty())
- return NS_ERROR_NOT_AVAILABLE;
- aContentDispositionFilename = mContentDispositionFilename;
- return NS_OK;
- }
- NS_IMETHODIMP
- nsPartChannel::SetContentDispositionFilename(const nsAString &aContentDispositionFilename)
- {
- return NS_ERROR_NOT_AVAILABLE;
- }
- NS_IMETHODIMP
- nsPartChannel::GetContentDispositionHeader(nsACString &aContentDispositionHeader)
- {
- if (mContentDispositionHeader.IsEmpty())
- return NS_ERROR_NOT_AVAILABLE;
- aContentDispositionHeader = mContentDispositionHeader;
- return NS_OK;
- }
- NS_IMETHODIMP
- nsPartChannel::GetPartID(uint32_t *aPartID)
- {
- *aPartID = mPartID;
- return NS_OK;
- }
- NS_IMETHODIMP
- nsPartChannel::GetIsLastPart(bool *aIsLastPart)
- {
- *aIsLastPart = mIsLastPart;
- return NS_OK;
- }
- //
- // nsIByteRangeRequest implementation...
- //
- NS_IMETHODIMP
- nsPartChannel::GetIsByteRangeRequest(bool *aIsByteRangeRequest)
- {
- *aIsByteRangeRequest = mIsByteRangeRequest;
- return NS_OK;
- }
- NS_IMETHODIMP
- nsPartChannel::GetStartRange(int64_t *aStartRange)
- {
- *aStartRange = mByteRangeStart;
- return NS_OK;
- }
- NS_IMETHODIMP
- nsPartChannel::GetEndRange(int64_t *aEndRange)
- {
- *aEndRange = mByteRangeEnd;
- return NS_OK;
- }
- NS_IMETHODIMP
- nsPartChannel::GetBaseChannel(nsIChannel ** aReturn)
- {
- NS_ENSURE_ARG_POINTER(aReturn);
- *aReturn = mMultipartChannel;
- NS_IF_ADDREF(*aReturn);
- return NS_OK;
- }
- // nsISupports implementation
- NS_IMPL_ISUPPORTS(nsMultiMixedConv,
- nsIStreamConverter,
- nsIStreamListener,
- nsIRequestObserver)
- // nsIStreamConverter implementation
- // No syncronous conversion at this time.
- NS_IMETHODIMP
- nsMultiMixedConv::Convert(nsIInputStream *aFromStream,
- const char *aFromType,
- const char *aToType,
- nsISupports *aCtxt, nsIInputStream **_retval) {
- return NS_ERROR_NOT_IMPLEMENTED;
- }
- // Stream converter service calls this to initialize the actual stream converter (us).
- NS_IMETHODIMP
- nsMultiMixedConv::AsyncConvertData(const char *aFromType, const char *aToType,
- nsIStreamListener *aListener, nsISupports *aCtxt) {
- NS_ASSERTION(aListener && aFromType && aToType, "null pointer passed into multi mixed converter");
- // hook up our final listener. this guy gets the various On*() calls we want to throw
- // at him.
- //
- // WARNING: this listener must be able to handle multiple OnStartRequest, OnDataAvail()
- // and OnStopRequest() call combinations. We call of series of these for each sub-part
- // in the raw stream.
- mFinalListener = aListener;
- return NS_OK;
- }
- // AutoFree implementation to prevent memory leaks
- class AutoFree
- {
- public:
- AutoFree() : mBuffer(nullptr) {}
- explicit AutoFree(char *buffer) : mBuffer(buffer) {}
- ~AutoFree() {
- free(mBuffer);
- }
- AutoFree& operator=(char *buffer) {
- mBuffer = buffer;
- return *this;
- }
- operator char*() const {
- return mBuffer;
- }
- private:
- char *mBuffer;
- };
- // nsIStreamListener implementation
- NS_IMETHODIMP
- nsMultiMixedConv::OnDataAvailable(nsIRequest *request, nsISupports *context,
- nsIInputStream *inStr, uint64_t sourceOffset,
- uint32_t count) {
- nsresult rv = NS_OK;
- AutoFree buffer(nullptr);
- uint32_t bufLen = 0, read = 0;
- NS_ASSERTION(request, "multimixed converter needs a request");
- nsCOMPtr<nsIChannel> channel = do_QueryInterface(request, &rv);
- if (NS_FAILED(rv)) return rv;
- // fill buffer
- {
- bufLen = count + mBufLen;
- NS_ENSURE_TRUE((bufLen >= count) && (bufLen >= mBufLen),
- NS_ERROR_FAILURE);
- buffer = (char *) malloc(bufLen);
- if (!buffer)
- return NS_ERROR_OUT_OF_MEMORY;
- if (mBufLen) {
- // incorporate any buffered data into the parsing
- memcpy(buffer, mBuffer, mBufLen);
- free(mBuffer);
- mBuffer = 0;
- mBufLen = 0;
- }
-
- rv = inStr->Read(buffer + (bufLen - count), count, &read);
- if (NS_FAILED(rv) || read == 0) return rv;
- NS_ASSERTION(read == count, "poor data size assumption");
- }
- char *cursor = buffer;
- if (mFirstOnData) {
- // this is the first OnData() for this request. some servers
- // don't bother sending a token in the first "part." This is
- // illegal, but we'll handle the case anyway by shoving the
- // boundary token in for the server.
- mFirstOnData = false;
- NS_ASSERTION(!mBufLen, "this is our first time through, we can't have buffered data");
- const char * token = mToken.get();
- PushOverLine(cursor, bufLen);
- bool needMoreChars = bufLen < mTokenLen + 2;
- nsAutoCString firstBuffer(buffer, bufLen);
- int32_t posCR = firstBuffer.Find("\r");
- if (needMoreChars || (posCR == kNotFound)) {
- // we don't have enough data yet to make this comparison.
- // skip this check, and try again the next time OnData()
- // is called.
- mFirstOnData = true;
- } else if (!PL_strnstr(cursor, token, mTokenLen + 2)) {
- char *newBuffer = (char *) realloc(buffer, bufLen + mTokenLen + 1);
- if (!newBuffer)
- return NS_ERROR_OUT_OF_MEMORY;
- buffer = newBuffer;
- memmove(buffer + mTokenLen + 1, buffer, bufLen);
- memcpy(buffer, token, mTokenLen);
- buffer[mTokenLen] = '\n';
- bufLen += (mTokenLen + 1);
- // need to reset cursor to the buffer again (bug 100595)
- cursor = buffer;
- }
- }
- char *token = nullptr;
- if (mProcessingHeaders) {
- // we were not able to process all the headers
- // for this "part" given the previous buffer given to
- // us in the previous OnDataAvailable callback.
- bool done = false;
- rv = ParseHeaders(channel, cursor, bufLen, &done);
- if (NS_FAILED(rv)) return rv;
- if (done) {
- mProcessingHeaders = false;
- rv = SendStart(channel);
- if (NS_FAILED(rv)) return rv;
- }
- }
- int32_t tokenLinefeed = 1;
- while ( (token = FindToken(cursor, bufLen)) ) {
- if (((token + mTokenLen + 1) < (cursor + bufLen)) &&
- (*(token + mTokenLen + 1) == '-')) {
- // This was the last delimiter so we can stop processing
- rv = SendData(cursor, LengthToToken(cursor, token));
- if (NS_FAILED(rv)) return rv;
- if (mPartChannel) {
- mPartChannel->SetIsLastPart();
- }
- return SendStop(NS_OK);
- }
- if (!mNewPart && token > cursor) {
- // headers are processed, we're pushing data now.
- NS_ASSERTION(!mProcessingHeaders, "we should be pushing raw data");
- rv = SendData(cursor, LengthToToken(cursor, token));
- bufLen -= token - cursor;
- if (NS_FAILED(rv)) return rv;
- }
- // XXX else NS_ASSERTION(token == cursor, "?");
- token += mTokenLen;
- bufLen -= mTokenLen;
- tokenLinefeed = PushOverLine(token, bufLen);
- if (mNewPart) {
- // parse headers
- mNewPart = false;
- cursor = token;
- bool done = false;
- rv = ParseHeaders(channel, cursor, bufLen, &done);
- if (NS_FAILED(rv)) return rv;
- if (done) {
- rv = SendStart(channel);
- if (NS_FAILED(rv)) return rv;
- }
- else {
- // we haven't finished processing header info.
- // we'll break out and try to process later.
- mProcessingHeaders = true;
- break;
- }
- }
- else {
- mNewPart = true;
- // Reset state so we don't carry it over from part to part
- mContentType.Truncate();
- mContentLength = UINT64_MAX;
- mContentDisposition.Truncate();
- mIsByteRangeRequest = false;
- mByteRangeStart = 0;
- mByteRangeEnd = 0;
-
- rv = SendStop(NS_OK);
- if (NS_FAILED(rv)) return rv;
- // reset the token to front. this allows us to treat
- // the token as a starting token.
- token -= mTokenLen + tokenLinefeed;
- bufLen += mTokenLen + tokenLinefeed;
- cursor = token;
- }
- }
- // at this point, we want to buffer up whatever amount (bufLen)
- // we have leftover. However, we *always* want to ensure that
- // we buffer enough data to handle a broken token.
- // carry over
- uint32_t bufAmt = 0;
- if (mProcessingHeaders)
- bufAmt = bufLen;
- else if (bufLen) {
- // if the data ends in a linefeed, and we're in the middle
- // of a "part" (ie. mPartChannel exists) don't bother
- // buffering, go ahead and send the data we have. Otherwise
- // if we don't have a channel already, then we don't even
- // have enough info to start a part, go ahead and buffer
- // enough to collect a boundary token.
- if (!mPartChannel || !(cursor[bufLen-1] == nsCRT::LF) )
- bufAmt = std::min(mTokenLen - 1, bufLen);
- }
- if (bufAmt) {
- rv = BufferData(cursor + (bufLen - bufAmt), bufAmt);
- if (NS_FAILED(rv)) return rv;
- bufLen -= bufAmt;
- }
- if (bufLen) {
- rv = SendData(cursor, bufLen);
- if (NS_FAILED(rv)) return rv;
- }
- return rv;
- }
- // nsIRequestObserver implementation
- NS_IMETHODIMP
- nsMultiMixedConv::OnStartRequest(nsIRequest *request, nsISupports *ctxt) {
- // we're assuming the content-type is available at this stage
- NS_ASSERTION(mToken.IsEmpty(), "a second on start???");
- const char *bndry = nullptr;
- nsAutoCString delimiter;
- nsresult rv = NS_OK;
- mContext = ctxt;
- mFirstOnData = true;
- mTotalSent = 0;
- nsCOMPtr<nsIChannel> channel = do_QueryInterface(request, &rv);
- if (NS_FAILED(rv)) return rv;
- // ask the HTTP channel for the content-type and extract the boundary from it.
- nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface(channel, &rv);
- if (NS_SUCCEEDED(rv)) {
- rv = httpChannel->GetResponseHeader(NS_LITERAL_CSTRING("content-type"), delimiter);
- if (NS_FAILED(rv)) {
- return rv;
- }
- } else {
- // try asking the channel directly
- rv = channel->GetContentType(delimiter);
- if (NS_FAILED(rv)) {
- return NS_ERROR_FAILURE;
- }
- }
- bndry = strstr(delimiter.BeginWriting(), "boundary");
- if (!bndry) {
- return NS_ERROR_FAILURE;
- }
- bndry = strchr(bndry, '=');
- if (!bndry) return NS_ERROR_FAILURE;
- bndry++; // move past the equals sign
- char *attrib = (char *) strchr(bndry, ';');
- if (attrib) *attrib = '\0';
- nsAutoCString boundaryString(bndry);
- if (attrib) *attrib = ';';
- boundaryString.Trim(" \"");
- mToken = boundaryString;
- mTokenLen = boundaryString.Length();
- if (mTokenLen == 0) {
- return NS_ERROR_FAILURE;
- }
- return NS_OK;
- }
- NS_IMETHODIMP
- nsMultiMixedConv::OnStopRequest(nsIRequest *request, nsISupports *ctxt,
- nsresult aStatus)
- {
- if (mToken.IsEmpty()) { // no token, no love.
- return NS_ERROR_FAILURE;
- }
- if (mPartChannel) {
- mPartChannel->SetIsLastPart();
- // we've already called SendStart() (which sets up the mPartChannel,
- // and fires an OnStart()) send any data left over, and then fire the stop.
- if (mBufLen > 0 && mBuffer) {
- (void) SendData(mBuffer, mBufLen);
- // don't bother checking the return value here, if the send failed
- // we're done anyway as we're in the OnStop() callback.
- free(mBuffer);
- mBuffer = nullptr;
- mBufLen = 0;
- }
- (void) SendStop(aStatus);
- } else if (NS_FAILED(aStatus)) {
- // underlying data production problem. we should not be in
- // the middle of sending data. if we were, mPartChannel,
- // above, would have been true.
- // if we send the start, the URI Loader's m_targetStreamListener, may
- // be pointing at us causing a nice stack overflow. So, don't call
- // OnStartRequest! - This breaks necko's semantecs.
- //(void) mFinalListener->OnStartRequest(request, ctxt);
- (void) mFinalListener->OnStopRequest(request, ctxt, aStatus);
- }
- return NS_OK;
- }
- // nsMultiMixedConv methods
- nsMultiMixedConv::nsMultiMixedConv() :
- mCurrentPartID(0)
- {
- mTokenLen = 0;
- mNewPart = true;
- mContentLength = UINT64_MAX;
- mBuffer = nullptr;
- mBufLen = 0;
- mProcessingHeaders = false;
- mByteRangeStart = 0;
- mByteRangeEnd = 0;
- mTotalSent = 0;
- mIsByteRangeRequest = false;
- }
- nsMultiMixedConv::~nsMultiMixedConv() {
- NS_ASSERTION(!mBuffer, "all buffered data should be gone");
- if (mBuffer) {
- free(mBuffer);
- mBuffer = nullptr;
- }
- }
- nsresult
- nsMultiMixedConv::BufferData(char *aData, uint32_t aLen) {
- NS_ASSERTION(!mBuffer, "trying to over-write buffer");
- char *buffer = (char *) malloc(aLen);
- if (!buffer) return NS_ERROR_OUT_OF_MEMORY;
- memcpy(buffer, aData, aLen);
- mBuffer = buffer;
- mBufLen = aLen;
- return NS_OK;
- }
- nsresult
- nsMultiMixedConv::SendStart(nsIChannel *aChannel) {
- nsresult rv = NS_OK;
- nsCOMPtr<nsIStreamListener> partListener(mFinalListener);
- if (mContentType.IsEmpty()) {
- mContentType.AssignLiteral(UNKNOWN_CONTENT_TYPE);
- nsCOMPtr<nsIStreamConverterService> serv =
- do_GetService(NS_STREAMCONVERTERSERVICE_CONTRACTID, &rv);
- if (NS_SUCCEEDED(rv)) {
- nsCOMPtr<nsIStreamListener> converter;
- rv = serv->AsyncConvertData(UNKNOWN_CONTENT_TYPE,
- "*/*",
- mFinalListener,
- mContext,
- getter_AddRefs(converter));
- if (NS_SUCCEEDED(rv)) {
- partListener = converter;
- }
- }
- }
- // if we already have an mPartChannel, that means we never sent a Stop()
- // before starting up another "part." that would be bad.
- NS_ASSERTION(!mPartChannel, "tisk tisk, shouldn't be overwriting a channel");
- nsPartChannel *newChannel;
- newChannel = new nsPartChannel(aChannel, mCurrentPartID++, partListener);
- if (!newChannel)
- return NS_ERROR_OUT_OF_MEMORY;
- if (mIsByteRangeRequest) {
- newChannel->InitializeByteRange(mByteRangeStart, mByteRangeEnd);
- }
- mTotalSent = 0;
- // Set up the new part channel...
- mPartChannel = newChannel;
- rv = mPartChannel->SetContentType(mContentType);
- if (NS_FAILED(rv)) return rv;
- rv = mPartChannel->SetContentLength(mContentLength);
- if (NS_FAILED(rv)) return rv;
- mPartChannel->SetContentDisposition(mContentDisposition);
- nsLoadFlags loadFlags = 0;
- mPartChannel->GetLoadFlags(&loadFlags);
- loadFlags |= nsIChannel::LOAD_REPLACE;
- mPartChannel->SetLoadFlags(loadFlags);
- nsCOMPtr<nsILoadGroup> loadGroup;
- (void)mPartChannel->GetLoadGroup(getter_AddRefs(loadGroup));
- // Add the new channel to the load group (if any)
- if (loadGroup) {
- rv = loadGroup->AddRequest(mPartChannel, nullptr);
- if (NS_FAILED(rv)) return rv;
- }
- // Let's start off the load. NOTE: we don't forward on the channel passed
- // into our OnDataAvailable() as it's the root channel for the raw stream.
- return mPartChannel->SendOnStartRequest(mContext);
- }
- nsresult
- nsMultiMixedConv::SendStop(nsresult aStatus) {
-
- nsresult rv = NS_OK;
- if (mPartChannel) {
- rv = mPartChannel->SendOnStopRequest(mContext, aStatus);
- // don't check for failure here, we need to remove the channel from
- // the loadgroup.
- // Remove the channel from its load group (if any)
- nsCOMPtr<nsILoadGroup> loadGroup;
- (void) mPartChannel->GetLoadGroup(getter_AddRefs(loadGroup));
- if (loadGroup)
- (void) loadGroup->RemoveRequest(mPartChannel, mContext, aStatus);
- }
- mPartChannel = nullptr;
- return rv;
- }
- nsresult
- nsMultiMixedConv::SendData(char *aBuffer, uint32_t aLen) {
- nsresult rv = NS_OK;
-
- if (!mPartChannel) return NS_ERROR_FAILURE; // something went wrong w/ processing
- if (mContentLength != UINT64_MAX) {
- // make sure that we don't send more than the mContentLength
- // XXX why? perhaps the Content-Length header was actually wrong!!
- if ((uint64_t(aLen) + mTotalSent) > mContentLength)
- aLen = static_cast<uint32_t>(mContentLength - mTotalSent);
- if (aLen == 0)
- return NS_OK;
- }
- uint64_t offset = mTotalSent;
- mTotalSent += aLen;
- nsCOMPtr<nsIStringInputStream> ss(
- do_CreateInstance("@mozilla.org/io/string-input-stream;1", &rv));
- if (NS_FAILED(rv))
- return rv;
- rv = ss->ShareData(aBuffer, aLen);
- if (NS_FAILED(rv))
- return rv;
- nsCOMPtr<nsIInputStream> inStream(do_QueryInterface(ss, &rv));
- if (NS_FAILED(rv)) return rv;
- return mPartChannel->SendOnDataAvailable(mContext, inStream, offset, aLen);
- }
- int32_t
- nsMultiMixedConv::PushOverLine(char *&aPtr, uint32_t &aLen) {
- int32_t chars = 0;
- if ((aLen > 0) && (*aPtr == nsCRT::CR || *aPtr == nsCRT::LF)) {
- if ((aLen > 1) && (aPtr[1] == nsCRT::LF))
- chars++;
- chars++;
- aPtr += chars;
- aLen -= chars;
- }
- return chars;
- }
- nsresult
- nsMultiMixedConv::ParseHeaders(nsIChannel *aChannel, char *&aPtr,
- uint32_t &aLen, bool *_retval) {
- // NOTE: this data must be ascii.
- // NOTE: aPtr is NOT null terminated!
- nsresult rv = NS_OK;
- char *cursor = aPtr, *newLine = nullptr;
- uint32_t cursorLen = aLen;
- bool done = false;
- uint32_t lineFeedIncrement = 1;
- mContentLength = UINT64_MAX; // XXX what if we were already called?
- while (cursorLen && (newLine = (char *) memchr(cursor, nsCRT::LF, cursorLen))) {
- // adjust for linefeeds
- if ((newLine > cursor) && (newLine[-1] == nsCRT::CR) ) { // CRLF
- lineFeedIncrement = 2;
- newLine--;
- }
- else
- lineFeedIncrement = 1; // reset
- if (newLine == cursor) {
- // move the newLine beyond the linefeed marker
- NS_ASSERTION(cursorLen >= lineFeedIncrement, "oops!");
- cursor += lineFeedIncrement;
- cursorLen -= lineFeedIncrement;
- done = true;
- break;
- }
- char tmpChar = *newLine;
- *newLine = '\0'; // cursor is now null terminated
- char *colon = (char *) strchr(cursor, ':');
- if (colon) {
- *colon = '\0';
- nsAutoCString headerStr(cursor);
- headerStr.CompressWhitespace();
- *colon = ':';
- nsAutoCString headerVal(colon + 1);
- headerVal.CompressWhitespace();
- // examine header
- if (headerStr.LowerCaseEqualsLiteral("content-type")) {
- mContentType = headerVal;
- } else if (headerStr.LowerCaseEqualsLiteral("content-length")) {
- mContentLength = nsCRT::atoll(headerVal.get());
- } else if (headerStr.LowerCaseEqualsLiteral("content-disposition")) {
- mContentDisposition = headerVal;
- } else if (headerStr.LowerCaseEqualsLiteral("set-cookie")) {
- nsCOMPtr<nsIHttpChannelInternal> httpInternal =
- do_QueryInterface(aChannel);
- if (httpInternal) {
- httpInternal->SetCookie(headerVal.get());
- }
- } else if (headerStr.LowerCaseEqualsLiteral("content-range") ||
- headerStr.LowerCaseEqualsLiteral("range") ) {
- // something like: Content-range: bytes 7000-7999/8000
- char* tmpPtr;
- tmpPtr = (char *) strchr(colon + 1, '/');
- if (tmpPtr)
- *tmpPtr = '\0';
- // pass the bytes-unit and the SP
- char *range = (char *) strchr(colon + 2, ' ');
- if (!range)
- return NS_ERROR_FAILURE;
- do {
- range++;
- } while (*range == ' ');
- if (range[0] == '*'){
- mByteRangeStart = mByteRangeEnd = 0;
- }
- else {
- tmpPtr = (char *) strchr(range, '-');
- if (!tmpPtr)
- return NS_ERROR_FAILURE;
-
- tmpPtr[0] = '\0';
-
- mByteRangeStart = nsCRT::atoll(range);
- tmpPtr++;
- mByteRangeEnd = nsCRT::atoll(tmpPtr);
- }
- mIsByteRangeRequest = true;
- if (mContentLength == UINT64_MAX)
- mContentLength = uint64_t(mByteRangeEnd - mByteRangeStart + 1);
- }
- }
- *newLine = tmpChar;
- newLine += lineFeedIncrement;
- cursorLen -= (newLine - cursor);
- cursor = newLine;
- }
- aPtr = cursor;
- aLen = cursorLen;
- *_retval = done;
- return rv;
- }
- char *
- nsMultiMixedConv::FindToken(char *aCursor, uint32_t aLen) {
- // strnstr without looking for null termination
- const char *token = mToken.get();
- char *cur = aCursor;
- if (!(token && aCursor && *token)) {
- NS_WARNING("bad data");
- return nullptr;
- }
- for (; aLen >= mTokenLen; aCursor++, aLen--) {
- if (!memcmp(aCursor, token, mTokenLen) ) {
- if ((aCursor - cur) >= 2) {
- // back the cursor up over a double dash for backwards compat.
- if ((*(aCursor-1) == '-') && (*(aCursor-2) == '-')) {
- aCursor -= 2;
- aLen += 2;
- // we're playing w/ double dash tokens, adjust.
- mToken.Assign(aCursor, mTokenLen + 2);
- mTokenLen = mToken.Length();
- }
- }
- return aCursor;
- }
- }
- return nullptr;
- }
- nsresult
- NS_NewMultiMixedConv(nsMultiMixedConv** aMultiMixedConv)
- {
- NS_PRECONDITION(aMultiMixedConv != nullptr, "null ptr");
- if (! aMultiMixedConv)
- return NS_ERROR_NULL_POINTER;
- *aMultiMixedConv = new nsMultiMixedConv();
- if (! *aMultiMixedConv)
- return NS_ERROR_OUT_OF_MEMORY;
- NS_ADDREF(*aMultiMixedConv);
- return NS_OK;
- }
|