netwerk/streamconv/converters/nsMultiMixedConv.cpp

Thu, 15 Jan 2015 15:55:04 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Thu, 15 Jan 2015 15:55:04 +0100
branch
TOR_BUG_9701
changeset 9
a63d609f5ebe
permissions
-rw-r--r--

Back out 97036ab72558 which inappropriately compared turds to third parties.

     1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
     2 /* This Source Code Form is subject to the terms of the Mozilla Public
     3  * License, v. 2.0. If a copy of the MPL was not distributed with this
     4  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
     6 #include "nsMultiMixedConv.h"
     7 #include "plstr.h"
     8 #include "nsIHttpChannel.h"
     9 #include "nsNetUtil.h"
    10 #include "nsMimeTypes.h"
    11 #include "nsIStringStream.h"
    12 #include "nsCRT.h"
    13 #include "nsIHttpChannelInternal.h"
    14 #include "nsURLHelper.h"
    15 #include "nsIStreamConverterService.h"
    16 #include <algorithm>
    18 //
    19 // Helper function for determining the length of data bytes up to
    20 // the next multipart token.  A token is usually preceded by a LF
    21 // or CRLF delimiter.
    22 // 
    23 static uint32_t
    24 LengthToToken(const char *cursor, const char *token)
    25 {
    26     uint32_t len = token - cursor;
    27     // Trim off any LF or CRLF preceding the token
    28     if (len && *(token-1) == '\n') {
    29         --len;
    30         if (len && *(token-2) == '\r')
    31             --len;
    32     }
    33     return len;
    34 }
    36 nsPartChannel::nsPartChannel(nsIChannel *aMultipartChannel, uint32_t aPartID,
    37                              nsIStreamListener* aListener) :
    38   mMultipartChannel(aMultipartChannel),
    39   mListener(aListener),
    40   mStatus(NS_OK),
    41   mContentLength(UINT64_MAX),
    42   mIsByteRangeRequest(false),
    43   mByteRangeStart(0),
    44   mByteRangeEnd(0),
    45   mPartID(aPartID),
    46   mIsLastPart(false)
    47 {
    48     mMultipartChannel = aMultipartChannel;
    50     // Inherit the load flags from the original channel...
    51     mMultipartChannel->GetLoadFlags(&mLoadFlags);
    53     mMultipartChannel->GetLoadGroup(getter_AddRefs(mLoadGroup));
    54 }
    56 nsPartChannel::~nsPartChannel()
    57 {
    58 }
    60 void nsPartChannel::InitializeByteRange(int64_t aStart, int64_t aEnd)
    61 {
    62     mIsByteRangeRequest = true;
    64     mByteRangeStart = aStart;
    65     mByteRangeEnd   = aEnd;
    66 }
    68 nsresult nsPartChannel::SendOnStartRequest(nsISupports* aContext)
    69 {
    70     return mListener->OnStartRequest(this, aContext);
    71 }
    73 nsresult nsPartChannel::SendOnDataAvailable(nsISupports* aContext,
    74                                             nsIInputStream* aStream,
    75                                             uint64_t aOffset, uint32_t aLen)
    76 {
    77     return mListener->OnDataAvailable(this, aContext, aStream, aOffset, aLen);
    78 }
    80 nsresult nsPartChannel::SendOnStopRequest(nsISupports* aContext,
    81                                           nsresult aStatus)
    82 {
    83     // Drop the listener
    84     nsCOMPtr<nsIStreamListener> listener;
    85     listener.swap(mListener);
    86     return listener->OnStopRequest(this, aContext, aStatus);
    87 }
    89 void nsPartChannel::SetContentDisposition(const nsACString& aContentDispositionHeader)
    90 {
    91     mContentDispositionHeader = aContentDispositionHeader;
    92     nsCOMPtr<nsIURI> uri;
    93     GetURI(getter_AddRefs(uri));
    94     NS_GetFilenameFromDisposition(mContentDispositionFilename,
    95                                   mContentDispositionHeader, uri);
    96     mContentDisposition = NS_GetContentDispositionFromHeader(mContentDispositionHeader, this);
    97 }
    99 //
   100 // nsISupports implementation...
   101 //
   103 NS_IMPL_ADDREF(nsPartChannel)
   104 NS_IMPL_RELEASE(nsPartChannel)
   106 NS_INTERFACE_MAP_BEGIN(nsPartChannel)
   107     NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIChannel)
   108     NS_INTERFACE_MAP_ENTRY(nsIRequest)
   109     NS_INTERFACE_MAP_ENTRY(nsIChannel)
   110     NS_INTERFACE_MAP_ENTRY(nsIByteRangeRequest)
   111     NS_INTERFACE_MAP_ENTRY(nsIMultiPartChannel)
   112 NS_INTERFACE_MAP_END
   114 //
   115 // nsIRequest implementation...
   116 //
   118 NS_IMETHODIMP
   119 nsPartChannel::GetName(nsACString &aResult)
   120 {
   121     return mMultipartChannel->GetName(aResult);
   122 }
   124 NS_IMETHODIMP
   125 nsPartChannel::IsPending(bool *aResult)
   126 {
   127     // For now, consider the active lifetime of each part the same as
   128     // the underlying multipart channel...  This is not exactly right,
   129     // but it is good enough :-)
   130     return mMultipartChannel->IsPending(aResult);
   131 }
   133 NS_IMETHODIMP
   134 nsPartChannel::GetStatus(nsresult *aResult)
   135 {
   136     nsresult rv = NS_OK;
   138     if (NS_FAILED(mStatus)) {
   139         *aResult = mStatus;
   140     } else {
   141         rv = mMultipartChannel->GetStatus(aResult);
   142     }
   144     return rv;
   145 }
   147 NS_IMETHODIMP
   148 nsPartChannel::Cancel(nsresult aStatus)
   149 {
   150     // Cancelling an individual part must not cancel the underlying
   151     // multipart channel...
   152     // XXX but we should stop sending data for _this_ part channel!
   153     mStatus = aStatus;
   154     return NS_OK;
   155 }
   157 NS_IMETHODIMP
   158 nsPartChannel::Suspend(void)
   159 {
   160     // Suspending an individual part must not suspend the underlying
   161     // multipart channel...
   162     // XXX why not?
   163     return NS_OK;
   164 }
   166 NS_IMETHODIMP
   167 nsPartChannel::Resume(void)
   168 {
   169     // Resuming an individual part must not resume the underlying
   170     // multipart channel...
   171     // XXX why not?
   172     return NS_OK;
   173 }
   175 //
   176 // nsIChannel implementation
   177 //
   179 NS_IMETHODIMP
   180 nsPartChannel::GetOriginalURI(nsIURI * *aURI)
   181 {
   182     return mMultipartChannel->GetOriginalURI(aURI);
   183 }
   185 NS_IMETHODIMP
   186 nsPartChannel::SetOriginalURI(nsIURI *aURI)
   187 {
   188     return mMultipartChannel->SetOriginalURI(aURI);
   189 }
   191 NS_IMETHODIMP
   192 nsPartChannel::GetURI(nsIURI * *aURI)
   193 {
   194     return mMultipartChannel->GetURI(aURI);
   195 }
   197 NS_IMETHODIMP
   198 nsPartChannel::Open(nsIInputStream **result)
   199 {
   200     // This channel cannot be opened!
   201     return NS_ERROR_FAILURE;
   202 }
   204 NS_IMETHODIMP
   205 nsPartChannel::AsyncOpen(nsIStreamListener *aListener, nsISupports *aContext)
   206 {
   207     // This channel cannot be opened!
   208     return NS_ERROR_FAILURE;
   209 }
   211 NS_IMETHODIMP
   212 nsPartChannel::GetLoadFlags(nsLoadFlags *aLoadFlags)
   213 {
   214     *aLoadFlags = mLoadFlags;
   215     return NS_OK;
   216 }
   218 NS_IMETHODIMP
   219 nsPartChannel::SetLoadFlags(nsLoadFlags aLoadFlags)
   220 {
   221     mLoadFlags = aLoadFlags;
   222     return NS_OK;
   223 }
   225 NS_IMETHODIMP
   226 nsPartChannel::GetLoadGroup(nsILoadGroup* *aLoadGroup)
   227 {
   228     *aLoadGroup = mLoadGroup;
   229     NS_IF_ADDREF(*aLoadGroup);
   231     return NS_OK;
   232 }
   234 NS_IMETHODIMP
   235 nsPartChannel::SetLoadGroup(nsILoadGroup* aLoadGroup)
   236 {
   237     mLoadGroup = aLoadGroup;
   239     return NS_OK;
   240 }
   242 NS_IMETHODIMP
   243 nsPartChannel::GetOwner(nsISupports* *aOwner)
   244 {
   245     return mMultipartChannel->GetOwner(aOwner);
   246 }
   248 NS_IMETHODIMP
   249 nsPartChannel::SetOwner(nsISupports* aOwner)
   250 {
   251     return mMultipartChannel->SetOwner(aOwner);
   252 }
   254 NS_IMETHODIMP
   255 nsPartChannel::GetNotificationCallbacks(nsIInterfaceRequestor* *aCallbacks)
   256 {
   257     return mMultipartChannel->GetNotificationCallbacks(aCallbacks);
   258 }
   260 NS_IMETHODIMP
   261 nsPartChannel::SetNotificationCallbacks(nsIInterfaceRequestor* aCallbacks)
   262 {
   263     return mMultipartChannel->SetNotificationCallbacks(aCallbacks);
   264 }
   266 NS_IMETHODIMP 
   267 nsPartChannel::GetSecurityInfo(nsISupports * *aSecurityInfo)
   268 {
   269     return mMultipartChannel->GetSecurityInfo(aSecurityInfo);
   270 }
   272 NS_IMETHODIMP
   273 nsPartChannel::GetContentType(nsACString &aContentType)
   274 {
   275     aContentType = mContentType;
   276     return NS_OK;
   277 }
   279 NS_IMETHODIMP
   280 nsPartChannel::SetContentType(const nsACString &aContentType)
   281 {
   282     bool dummy;
   283     net_ParseContentType(aContentType, mContentType, mContentCharset, &dummy);
   284     return NS_OK;
   285 }
   287 NS_IMETHODIMP
   288 nsPartChannel::GetContentCharset(nsACString &aContentCharset)
   289 {
   290     aContentCharset = mContentCharset;
   291     return NS_OK;
   292 }
   294 NS_IMETHODIMP
   295 nsPartChannel::SetContentCharset(const nsACString &aContentCharset)
   296 {
   297     mContentCharset = aContentCharset;
   298     return NS_OK;
   299 }
   301 NS_IMETHODIMP
   302 nsPartChannel::GetContentLength(int64_t *aContentLength)
   303 {
   304     *aContentLength = mContentLength;
   305     return NS_OK;
   306 }
   308 NS_IMETHODIMP
   309 nsPartChannel::SetContentLength(int64_t aContentLength)
   310 {
   311     mContentLength = aContentLength;
   312     return NS_OK;
   313 }
   315 NS_IMETHODIMP
   316 nsPartChannel::GetContentDisposition(uint32_t *aContentDisposition)
   317 {
   318     if (mContentDispositionHeader.IsEmpty())
   319         return NS_ERROR_NOT_AVAILABLE;
   321     *aContentDisposition = mContentDisposition;
   322     return NS_OK;
   323 }
   325 NS_IMETHODIMP
   326 nsPartChannel::SetContentDisposition(uint32_t aContentDisposition)
   327 {
   328     return NS_ERROR_NOT_AVAILABLE;
   329 }
   331 NS_IMETHODIMP
   332 nsPartChannel::GetContentDispositionFilename(nsAString &aContentDispositionFilename)
   333 {
   334     if (mContentDispositionFilename.IsEmpty())
   335         return NS_ERROR_NOT_AVAILABLE;
   337     aContentDispositionFilename = mContentDispositionFilename;
   338     return NS_OK;
   339 }
   341 NS_IMETHODIMP
   342 nsPartChannel::SetContentDispositionFilename(const nsAString &aContentDispositionFilename)
   343 {
   344     return NS_ERROR_NOT_AVAILABLE;
   345 }
   348 NS_IMETHODIMP
   349 nsPartChannel::GetContentDispositionHeader(nsACString &aContentDispositionHeader)
   350 {
   351     if (mContentDispositionHeader.IsEmpty())
   352         return NS_ERROR_NOT_AVAILABLE;
   354     aContentDispositionHeader = mContentDispositionHeader;
   355     return NS_OK;
   356 }
   358 NS_IMETHODIMP
   359 nsPartChannel::GetPartID(uint32_t *aPartID)
   360 {
   361     *aPartID = mPartID;
   362     return NS_OK;
   363 }
   365 NS_IMETHODIMP
   366 nsPartChannel::GetIsLastPart(bool *aIsLastPart)
   367 {
   368     *aIsLastPart = mIsLastPart;
   369     return NS_OK;
   370 }
   372 //
   373 // nsIByteRangeRequest implementation...
   374 //
   376 NS_IMETHODIMP 
   377 nsPartChannel::GetIsByteRangeRequest(bool *aIsByteRangeRequest)
   378 {
   379     *aIsByteRangeRequest = mIsByteRangeRequest;
   381     return NS_OK;
   382 }
   385 NS_IMETHODIMP 
   386 nsPartChannel::GetStartRange(int64_t *aStartRange)
   387 {
   388     *aStartRange = mByteRangeStart;
   390     return NS_OK;
   391 }
   393 NS_IMETHODIMP 
   394 nsPartChannel::GetEndRange(int64_t *aEndRange)
   395 {
   396     *aEndRange = mByteRangeEnd;
   397     return NS_OK;
   398 }
   400 NS_IMETHODIMP
   401 nsPartChannel::GetBaseChannel(nsIChannel ** aReturn)
   402 {
   403     NS_ENSURE_ARG_POINTER(aReturn);
   405     *aReturn = mMultipartChannel;
   406     NS_IF_ADDREF(*aReturn);
   407     return NS_OK;
   408 }
   411 // nsISupports implementation
   412 NS_IMPL_ISUPPORTS(nsMultiMixedConv,
   413                   nsIStreamConverter,
   414                   nsIStreamListener,
   415                   nsIRequestObserver)
   418 // nsIStreamConverter implementation
   420 // No syncronous conversion at this time.
   421 NS_IMETHODIMP
   422 nsMultiMixedConv::Convert(nsIInputStream *aFromStream,
   423                           const char *aFromType,
   424                           const char *aToType,
   425                           nsISupports *aCtxt, nsIInputStream **_retval) {
   426     return NS_ERROR_NOT_IMPLEMENTED;
   427 }
   429 // Stream converter service calls this to initialize the actual stream converter (us).
   430 NS_IMETHODIMP
   431 nsMultiMixedConv::AsyncConvertData(const char *aFromType, const char *aToType,
   432                                    nsIStreamListener *aListener, nsISupports *aCtxt) {
   433     NS_ASSERTION(aListener && aFromType && aToType, "null pointer passed into multi mixed converter");
   435     // hook up our final listener. this guy gets the various On*() calls we want to throw
   436     // at him.
   437     //
   438     // WARNING: this listener must be able to handle multiple OnStartRequest, OnDataAvail()
   439     //  and OnStopRequest() call combinations. We call of series of these for each sub-part
   440     //  in the raw stream.
   441     mFinalListener = aListener;
   442     return NS_OK;
   443 }
   445 // AutoFree implementation to prevent memory leaks
   446 class AutoFree
   447 {
   448 public:
   449   AutoFree() : mBuffer(nullptr) {}
   451   AutoFree(char *buffer) : mBuffer(buffer) {}
   453   ~AutoFree() {
   454     free(mBuffer);
   455   }
   457   AutoFree& operator=(char *buffer) {
   458     mBuffer = buffer;
   459     return *this;
   460   }
   462   operator char*() const {
   463     return mBuffer;
   464   }
   465 private:
   466   char *mBuffer;
   467 };
   469 // nsIStreamListener implementation
   470 NS_IMETHODIMP
   471 nsMultiMixedConv::OnDataAvailable(nsIRequest *request, nsISupports *context,
   472                                   nsIInputStream *inStr, uint64_t sourceOffset,
   473                                   uint32_t count) {
   475     if (mToken.IsEmpty()) // no token, no love.
   476         return NS_ERROR_FAILURE;
   478     nsresult rv = NS_OK;
   479     AutoFree buffer = nullptr;
   480     uint32_t bufLen = 0, read = 0;
   482     NS_ASSERTION(request, "multimixed converter needs a request");
   484     nsCOMPtr<nsIChannel> channel = do_QueryInterface(request, &rv);
   485     if (NS_FAILED(rv)) return rv;
   487     // fill buffer
   488     {
   489         bufLen = count + mBufLen;
   490         NS_ENSURE_TRUE((bufLen >= count) && (bufLen >= mBufLen),
   491                        NS_ERROR_FAILURE);
   492         buffer = (char *) malloc(bufLen);
   493         if (!buffer)
   494             return NS_ERROR_OUT_OF_MEMORY;
   496         if (mBufLen) {
   497             // incorporate any buffered data into the parsing
   498             memcpy(buffer, mBuffer, mBufLen);
   499             free(mBuffer);
   500             mBuffer = 0;
   501             mBufLen = 0;
   502         }
   504         rv = inStr->Read(buffer + (bufLen - count), count, &read);
   506         if (NS_FAILED(rv) || read == 0) return rv;
   507         NS_ASSERTION(read == count, "poor data size assumption");
   508     }
   510     char *cursor = buffer;
   512     if (mFirstOnData) {
   513         // this is the first OnData() for this request. some servers
   514         // don't bother sending a token in the first "part." This is
   515         // illegal, but we'll handle the case anyway by shoving the
   516         // boundary token in for the server.
   517         mFirstOnData = false;
   518         NS_ASSERTION(!mBufLen, "this is our first time through, we can't have buffered data");
   519         const char * token = mToken.get();
   521         PushOverLine(cursor, bufLen);
   523         if (bufLen < mTokenLen+2) {
   524             // we don't have enough data yet to make this comparison.
   525             // skip this check, and try again the next time OnData()
   526             // is called.
   527             mFirstOnData = true;
   528         }
   529         else if (!PL_strnstr(cursor, token, mTokenLen+2)) {
   530             buffer = (char *) realloc(buffer, bufLen + mTokenLen + 1);
   531             if (!buffer)
   532                 return NS_ERROR_OUT_OF_MEMORY;
   534             memmove(buffer + mTokenLen + 1, buffer, bufLen);
   535             memcpy(buffer, token, mTokenLen);
   536             buffer[mTokenLen] = '\n';
   538             bufLen += (mTokenLen + 1);
   540             // need to reset cursor to the buffer again (bug 100595)
   541             cursor = buffer;
   542         }
   543     }
   545     char *token = nullptr;
   547     if (mProcessingHeaders) {
   548         // we were not able to process all the headers
   549         // for this "part" given the previous buffer given to 
   550         // us in the previous OnDataAvailable callback.
   551         bool done = false;
   552         rv = ParseHeaders(channel, cursor, bufLen, &done);
   553         if (NS_FAILED(rv)) return rv;
   555         if (done) {
   556             mProcessingHeaders = false;
   557             rv = SendStart(channel);
   558             if (NS_FAILED(rv)) return rv;
   559         }
   560     }
   562     int32_t tokenLinefeed = 1;
   563     while ( (token = FindToken(cursor, bufLen)) ) {
   565         if (((token + mTokenLen) < (cursor + bufLen)) &&
   566             (*(token + mTokenLen + 1) == '-')) {
   567             // This was the last delimiter so we can stop processing
   568             rv = SendData(cursor, LengthToToken(cursor, token));
   569             if (NS_FAILED(rv)) return rv;
   570             return SendStop(NS_OK);
   571         }
   573         if (!mNewPart && token > cursor) {
   574             // headers are processed, we're pushing data now.
   575             NS_ASSERTION(!mProcessingHeaders, "we should be pushing raw data");
   576             rv = SendData(cursor, LengthToToken(cursor, token));
   577             bufLen -= token - cursor;
   578             if (NS_FAILED(rv)) return rv;
   579         }
   580         // XXX else NS_ASSERTION(token == cursor, "?");
   581         token += mTokenLen;
   582         bufLen -= mTokenLen;
   583         tokenLinefeed = PushOverLine(token, bufLen);
   585         if (mNewPart) {
   586             // parse headers
   587             mNewPart = false;
   588             cursor = token;
   589             bool done = false; 
   590             rv = ParseHeaders(channel, cursor, bufLen, &done);
   591             if (NS_FAILED(rv)) return rv;
   592             if (done) {
   593                 rv = SendStart(channel);
   594                 if (NS_FAILED(rv)) return rv;
   595             }
   596             else {
   597                 // we haven't finished processing header info.
   598                 // we'll break out and try to process later.
   599                 mProcessingHeaders = true;
   600                 break;
   601             }
   602         }
   603         else {
   604             mNewPart = true;
   605             // Reset state so we don't carry it over from part to part
   606             mContentType.Truncate();
   607             mContentLength = UINT64_MAX;
   608             mContentDisposition.Truncate();
   609             mIsByteRangeRequest = false;
   610             mByteRangeStart = 0;
   611             mByteRangeEnd = 0;
   613             rv = SendStop(NS_OK);
   614             if (NS_FAILED(rv)) return rv;
   615             // reset the token to front. this allows us to treat
   616             // the token as a starting token.
   617             token -= mTokenLen + tokenLinefeed;
   618             bufLen += mTokenLen + tokenLinefeed;
   619             cursor = token;
   620         }
   621     }
   623     // at this point, we want to buffer up whatever amount (bufLen)
   624     // we have leftover. However, we *always* want to ensure that
   625     // we buffer enough data to handle a broken token.
   627     // carry over
   628     uint32_t bufAmt = 0;
   629     if (mProcessingHeaders)
   630         bufAmt = bufLen;
   631     else if (bufLen) {
   632         // if the data ends in a linefeed, and we're in the middle
   633         // of a "part" (ie. mPartChannel exists) don't bother
   634         // buffering, go ahead and send the data we have. Otherwise
   635         // if we don't have a channel already, then we don't even
   636         // have enough info to start a part, go ahead and buffer
   637         // enough to collect a boundary token.
   638         if (!mPartChannel || !(cursor[bufLen-1] == nsCRT::LF) )
   639             bufAmt = std::min(mTokenLen - 1, bufLen);
   640     }
   642     if (bufAmt) {
   643         rv = BufferData(cursor + (bufLen - bufAmt), bufAmt);
   644         if (NS_FAILED(rv)) return rv;
   645         bufLen -= bufAmt;
   646     }
   648     if (bufLen) {
   649         rv = SendData(cursor, bufLen);
   650         if (NS_FAILED(rv)) return rv;
   651     }
   653     return rv;
   654 }
   657 // nsIRequestObserver implementation
   658 NS_IMETHODIMP
   659 nsMultiMixedConv::OnStartRequest(nsIRequest *request, nsISupports *ctxt) {
   660     // we're assuming the content-type is available at this stage
   661     NS_ASSERTION(mToken.IsEmpty(), "a second on start???");
   662     const char *bndry = nullptr;
   663     nsAutoCString delimiter;
   664     nsresult rv = NS_OK;
   665     mContext = ctxt;
   667     mFirstOnData = true;
   668     mTotalSent   = 0;
   670     nsCOMPtr<nsIChannel> channel = do_QueryInterface(request, &rv);
   671     if (NS_FAILED(rv)) return rv;
   673     // ask the HTTP channel for the content-type and extract the boundary from it.
   674     nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface(channel, &rv);
   675     if (NS_SUCCEEDED(rv)) {
   676         rv = httpChannel->GetResponseHeader(NS_LITERAL_CSTRING("content-type"), delimiter);
   677         if (NS_FAILED(rv)) return rv;
   678     } else {
   679         // try asking the channel directly
   680         rv = channel->GetContentType(delimiter);
   681         if (NS_FAILED(rv)) return NS_ERROR_FAILURE;
   682     }
   684     bndry = strstr(delimiter.BeginWriting(), "boundary");
   685     if (!bndry) return NS_ERROR_FAILURE;
   687     bndry = strchr(bndry, '=');
   688     if (!bndry) return NS_ERROR_FAILURE;
   690     bndry++; // move past the equals sign
   692     char *attrib = (char *) strchr(bndry, ';');
   693     if (attrib) *attrib = '\0';
   695     nsAutoCString boundaryString(bndry);
   696     if (attrib) *attrib = ';';
   698     boundaryString.Trim(" \"");
   700     mToken = boundaryString;
   701     mTokenLen = boundaryString.Length();
   703     if (mTokenLen == 0)
   704         return NS_ERROR_FAILURE;
   706     return NS_OK;
   707 }
   709 NS_IMETHODIMP
   710 nsMultiMixedConv::OnStopRequest(nsIRequest *request, nsISupports *ctxt,
   711                                 nsresult aStatus) {
   713     if (mToken.IsEmpty())  // no token, no love.
   714         return NS_ERROR_FAILURE;
   716     if (mPartChannel) {
   717         mPartChannel->SetIsLastPart();
   719         // we've already called SendStart() (which sets up the mPartChannel,
   720         // and fires an OnStart()) send any data left over, and then fire the stop.
   721         if (mBufLen > 0 && mBuffer) {
   722             (void) SendData(mBuffer, mBufLen);
   723             // don't bother checking the return value here, if the send failed
   724             // we're done anyway as we're in the OnStop() callback.
   725             free(mBuffer);
   726             mBuffer = nullptr;
   727             mBufLen = 0;
   728         }
   729         (void) SendStop(aStatus);
   730     } else if (NS_FAILED(aStatus)) {
   731         // underlying data production problem. we should not be in
   732         // the middle of sending data. if we were, mPartChannel,
   733         // above, would have been true.
   735         // if we send the start, the URI Loader's m_targetStreamListener, may
   736         // be pointing at us causing a nice stack overflow.  So, don't call 
   737         // OnStartRequest!  -  This breaks necko's semantecs. 
   738         //(void) mFinalListener->OnStartRequest(request, ctxt);
   740         (void) mFinalListener->OnStopRequest(request, ctxt, aStatus);
   741     }
   743     return NS_OK;
   744 }
   747 // nsMultiMixedConv methods
   748 nsMultiMixedConv::nsMultiMixedConv() :
   749   mCurrentPartID(0)
   750 {
   751     mTokenLen           = 0;
   752     mNewPart            = true;
   753     mContentLength      = UINT64_MAX;
   754     mBuffer             = nullptr;
   755     mBufLen             = 0;
   756     mProcessingHeaders  = false;
   757     mByteRangeStart     = 0;
   758     mByteRangeEnd       = 0;
   759     mTotalSent          = 0;
   760     mIsByteRangeRequest = false;
   761 }
   763 nsMultiMixedConv::~nsMultiMixedConv() {
   764     NS_ASSERTION(!mBuffer, "all buffered data should be gone");
   765     if (mBuffer) {
   766         free(mBuffer);
   767         mBuffer = nullptr;
   768     }
   769 }
   771 nsresult
   772 nsMultiMixedConv::BufferData(char *aData, uint32_t aLen) {
   773     NS_ASSERTION(!mBuffer, "trying to over-write buffer");
   775     char *buffer = (char *) malloc(aLen);
   776     if (!buffer) return NS_ERROR_OUT_OF_MEMORY;
   778     memcpy(buffer, aData, aLen);
   779     mBuffer = buffer;
   780     mBufLen = aLen;
   781     return NS_OK;
   782 }
   785 nsresult
   786 nsMultiMixedConv::SendStart(nsIChannel *aChannel) {
   787     nsresult rv = NS_OK;
   789     nsCOMPtr<nsIStreamListener> partListener(mFinalListener);
   790     if (mContentType.IsEmpty()) {
   791         mContentType.AssignLiteral(UNKNOWN_CONTENT_TYPE);
   792         nsCOMPtr<nsIStreamConverterService> serv =
   793             do_GetService(NS_STREAMCONVERTERSERVICE_CONTRACTID, &rv);
   794         if (NS_SUCCEEDED(rv)) {
   795             nsCOMPtr<nsIStreamListener> converter;
   796             rv = serv->AsyncConvertData(UNKNOWN_CONTENT_TYPE,
   797                                         "*/*",
   798                                         mFinalListener,
   799                                         mContext,
   800                                         getter_AddRefs(converter));
   801             if (NS_SUCCEEDED(rv)) {
   802                 partListener = converter;
   803             }
   804         }
   805     }
   807     // if we already have an mPartChannel, that means we never sent a Stop()
   808     // before starting up another "part." that would be bad.
   809     NS_ASSERTION(!mPartChannel, "tisk tisk, shouldn't be overwriting a channel");
   811     nsPartChannel *newChannel;
   812     newChannel = new nsPartChannel(aChannel, mCurrentPartID++, partListener);
   813     if (!newChannel)
   814         return NS_ERROR_OUT_OF_MEMORY;
   816     if (mIsByteRangeRequest) {
   817         newChannel->InitializeByteRange(mByteRangeStart, mByteRangeEnd);
   818     }
   820     mTotalSent = 0;
   822     // Set up the new part channel...
   823     mPartChannel = newChannel;
   825     rv = mPartChannel->SetContentType(mContentType);
   826     if (NS_FAILED(rv)) return rv;
   828     rv = mPartChannel->SetContentLength(mContentLength);
   829     if (NS_FAILED(rv)) return rv;
   831     mPartChannel->SetContentDisposition(mContentDisposition);
   833     nsLoadFlags loadFlags = 0;
   834     mPartChannel->GetLoadFlags(&loadFlags);
   835     loadFlags |= nsIChannel::LOAD_REPLACE;
   836     mPartChannel->SetLoadFlags(loadFlags);
   838     nsCOMPtr<nsILoadGroup> loadGroup;
   839     (void)mPartChannel->GetLoadGroup(getter_AddRefs(loadGroup));
   841     // Add the new channel to the load group (if any)
   842     if (loadGroup) {
   843         rv = loadGroup->AddRequest(mPartChannel, nullptr);
   844         if (NS_FAILED(rv)) return rv;
   845     }
   847     // Let's start off the load. NOTE: we don't forward on the channel passed
   848     // into our OnDataAvailable() as it's the root channel for the raw stream.
   849     return mPartChannel->SendOnStartRequest(mContext);
   850 }
   853 nsresult
   854 nsMultiMixedConv::SendStop(nsresult aStatus) {
   856     nsresult rv = NS_OK;
   857     if (mPartChannel) {
   858         rv = mPartChannel->SendOnStopRequest(mContext, aStatus);
   859         // don't check for failure here, we need to remove the channel from 
   860         // the loadgroup.
   862         // Remove the channel from its load group (if any)
   863         nsCOMPtr<nsILoadGroup> loadGroup;
   864         (void) mPartChannel->GetLoadGroup(getter_AddRefs(loadGroup));
   865         if (loadGroup) 
   866             (void) loadGroup->RemoveRequest(mPartChannel, mContext, aStatus);
   867     }
   869     mPartChannel = 0;
   870     return rv;
   871 }
   873 nsresult
   874 nsMultiMixedConv::SendData(char *aBuffer, uint32_t aLen) {
   876     nsresult rv = NS_OK;
   878     if (!mPartChannel) return NS_ERROR_FAILURE; // something went wrong w/ processing
   880     if (mContentLength != UINT64_MAX) {
   881         // make sure that we don't send more than the mContentLength
   882         // XXX why? perhaps the Content-Length header was actually wrong!!
   883         if ((uint64_t(aLen) + mTotalSent) > mContentLength)
   884             aLen = static_cast<uint32_t>(mContentLength - mTotalSent);
   886         if (aLen == 0)
   887             return NS_OK;
   888     }
   890     uint64_t offset = mTotalSent;
   891     mTotalSent += aLen;
   893     nsCOMPtr<nsIStringInputStream> ss(
   894             do_CreateInstance("@mozilla.org/io/string-input-stream;1", &rv));
   895     if (NS_FAILED(rv))
   896         return rv;
   898     rv = ss->ShareData(aBuffer, aLen);
   899     if (NS_FAILED(rv))
   900         return rv;
   902     nsCOMPtr<nsIInputStream> inStream(do_QueryInterface(ss, &rv));
   903     if (NS_FAILED(rv)) return rv;
   905     return mPartChannel->SendOnDataAvailable(mContext, inStream, offset, aLen);
   906 }
   908 int32_t
   909 nsMultiMixedConv::PushOverLine(char *&aPtr, uint32_t &aLen) {
   910     int32_t chars = 0;
   911     if ((aLen > 0) && (*aPtr == nsCRT::CR || *aPtr == nsCRT::LF)) {
   912         if ((aLen > 1) && (aPtr[1] == nsCRT::LF))
   913             chars++;
   914         chars++;
   915         aPtr += chars;
   916         aLen -= chars;
   917     }
   918     return chars;
   919 }
   921 nsresult
   922 nsMultiMixedConv::ParseHeaders(nsIChannel *aChannel, char *&aPtr, 
   923                                uint32_t &aLen, bool *_retval) {
   924     // NOTE: this data must be ascii.
   925     // NOTE: aPtr is NOT null terminated!
   926     nsresult rv = NS_OK;
   927     char *cursor = aPtr, *newLine = nullptr;
   928     uint32_t cursorLen = aLen;
   929     bool done = false;
   930     uint32_t lineFeedIncrement = 1;
   932     mContentLength = UINT64_MAX; // XXX what if we were already called?
   933     while (cursorLen && (newLine = (char *) memchr(cursor, nsCRT::LF, cursorLen))) {
   934         // adjust for linefeeds
   935         if ((newLine > cursor) && (newLine[-1] == nsCRT::CR) ) { // CRLF
   936             lineFeedIncrement = 2;
   937             newLine--;
   938         }
   939         else
   940             lineFeedIncrement = 1; // reset
   942         if (newLine == cursor) {
   943             // move the newLine beyond the linefeed marker
   944             NS_ASSERTION(cursorLen >= lineFeedIncrement, "oops!");
   946             cursor += lineFeedIncrement;
   947             cursorLen -= lineFeedIncrement;
   949             done = true;
   950             break;
   951         }
   953         char tmpChar = *newLine;
   954         *newLine = '\0'; // cursor is now null terminated
   955         char *colon = (char *) strchr(cursor, ':');
   956         if (colon) {
   957             *colon = '\0';
   958             nsAutoCString headerStr(cursor);
   959             headerStr.CompressWhitespace();
   960             *colon = ':';
   962             nsAutoCString headerVal(colon + 1);
   963             headerVal.CompressWhitespace();
   965             // examine header
   966             if (headerStr.LowerCaseEqualsLiteral("content-type")) {
   967                 mContentType = headerVal;
   968             } else if (headerStr.LowerCaseEqualsLiteral("content-length")) {
   969                 mContentLength = nsCRT::atoll(headerVal.get());
   970             } else if (headerStr.LowerCaseEqualsLiteral("content-disposition")) {
   971                 mContentDisposition = headerVal;
   972             } else if (headerStr.LowerCaseEqualsLiteral("set-cookie")) {
   973                 nsCOMPtr<nsIHttpChannelInternal> httpInternal =
   974                     do_QueryInterface(aChannel);
   975                 if (httpInternal) {
   976                     httpInternal->SetCookie(headerVal.get());
   977                 }
   978             } else if (headerStr.LowerCaseEqualsLiteral("content-range") || 
   979                        headerStr.LowerCaseEqualsLiteral("range") ) {
   980                 // something like: Content-range: bytes 7000-7999/8000
   981                 char* tmpPtr;
   983                 tmpPtr = (char *) strchr(colon + 1, '/');
   984                 if (tmpPtr) 
   985                     *tmpPtr = '\0';
   987                 // pass the bytes-unit and the SP
   988                 char *range = (char *) strchr(colon + 2, ' ');
   989                 if (!range)
   990                     return NS_ERROR_FAILURE;
   992                 do {
   993                     range++;
   994                 } while (*range == ' ');
   996                 if (range[0] == '*'){
   997                     mByteRangeStart = mByteRangeEnd = 0;
   998                 }
   999                 else {
  1000                     tmpPtr = (char *) strchr(range, '-');
  1001                     if (!tmpPtr)
  1002                         return NS_ERROR_FAILURE;
  1004                     tmpPtr[0] = '\0';
  1006                     mByteRangeStart = nsCRT::atoll(range);
  1007                     tmpPtr++;
  1008                     mByteRangeEnd = nsCRT::atoll(tmpPtr);
  1011                 mIsByteRangeRequest = true;
  1012                 if (mContentLength == UINT64_MAX)
  1013                     mContentLength = uint64_t(mByteRangeEnd - mByteRangeStart + 1);
  1016         *newLine = tmpChar;
  1017         newLine += lineFeedIncrement;
  1018         cursorLen -= (newLine - cursor);
  1019         cursor = newLine;
  1022     aPtr = cursor;
  1023     aLen = cursorLen;
  1025     *_retval = done;
  1026     return rv;
  1029 char *
  1030 nsMultiMixedConv::FindToken(char *aCursor, uint32_t aLen) {
  1031     // strnstr without looking for null termination
  1032     const char *token = mToken.get();
  1033     char *cur = aCursor;
  1035     if (!(token && aCursor && *token)) {
  1036         NS_WARNING("bad data");
  1037         return nullptr;
  1040     for (; aLen >= mTokenLen; aCursor++, aLen--) {
  1041         if (!memcmp(aCursor, token, mTokenLen) ) {
  1042             if ((aCursor - cur) >= 2) {
  1043                 // back the cursor up over a double dash for backwards compat.
  1044                 if ((*(aCursor-1) == '-') && (*(aCursor-2) == '-')) {
  1045                     aCursor -= 2;
  1046                     aLen += 2;
  1048                     // we're playing w/ double dash tokens, adjust.
  1049                     mToken.Assign(aCursor, mTokenLen + 2);
  1050                     mTokenLen = mToken.Length();
  1053             return aCursor;
  1057     return nullptr;
  1060 nsresult
  1061 NS_NewMultiMixedConv(nsMultiMixedConv** aMultiMixedConv)
  1063     NS_PRECONDITION(aMultiMixedConv != nullptr, "null ptr");
  1064     if (! aMultiMixedConv)
  1065         return NS_ERROR_NULL_POINTER;
  1067     *aMultiMixedConv = new nsMultiMixedConv();
  1068     if (! *aMultiMixedConv)
  1069         return NS_ERROR_OUT_OF_MEMORY;
  1071     NS_ADDREF(*aMultiMixedConv);
  1072     return NS_OK;

mercurial