Back to index

lightning-sunbird  0.9+nobinonly
nsMultiMixedConv.cpp
Go to the documentation of this file.
00001 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
00002 /* ***** BEGIN LICENSE BLOCK *****
00003  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
00004  *
00005  * The contents of this file are subject to the Mozilla Public License Version
00006  * 1.1 (the "License"); you may not use this file except in compliance with
00007  * the License. You may obtain a copy of the License at
00008  * http://www.mozilla.org/MPL/
00009  *
00010  * Software distributed under the License is distributed on an "AS IS" basis,
00011  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
00012  * for the specific language governing rights and limitations under the
00013  * License.
00014  *
00015  * The Original Code is mozilla.org code.
00016  *
00017  * The Initial Developer of the Original Code is
00018  * Netscape Communications Corporation.
00019  * Portions created by the Initial Developer are Copyright (C) 1998
00020  * the Initial Developer. All Rights Reserved.
00021  *
00022  * Contributor(s):
00023  *
00024  * Alternatively, the contents of this file may be used under the terms of
00025  * either the GNU General Public License Version 2 or later (the "GPL"), or
00026  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
00027  * in which case the provisions of the GPL or the LGPL are applicable instead
00028  * of those above. If you wish to allow use of your version of this file only
00029  * under the terms of either the GPL or the LGPL, and not to allow others to
00030  * use your version of this file under the terms of the MPL, indicate your
00031  * decision by deleting the provisions above and replace them with the notice
00032  * and other provisions required by the GPL or the LGPL. If you do not delete
00033  * the provisions above, a recipient may use your version of this file under
00034  * the terms of any one of the MPL, the GPL or the LGPL.
00035  *
00036  * ***** END LICENSE BLOCK ***** */
00037 
00038 #include "nsMultiMixedConv.h"
00039 #include "nsMemory.h"
00040 #include "nsInt64.h"
00041 #include "plstr.h"
00042 #include "nsIHttpChannel.h"
00043 #include "nsIServiceManager.h"
00044 #include "nsNetUtil.h"
00045 #include "nsMimeTypes.h"
00046 #include "nsIStringStream.h"
00047 #include "nsReadableUtils.h"
00048 #include "nsCRT.h"
00049 #include "nsIHttpChannelInternal.h"
00050 #include "nsURLHelper.h"
00051 
00052 //
00053 // Helper function for determining the length of data bytes up to
00054 // the next multipart token.  A token is usually preceded by a LF
00055 // or CRLF delimiter.
00056 // 
00057 static PRUint32
00058 LengthToToken(const char *cursor, const char *token)
00059 {
00060     PRUint32 len = token - cursor;
00061     // Trim off any LF or CRLF preceding the token
00062     if (len && *(token-1) == '\n') {
00063         --len;
00064         if (len && *(token-2) == '\r')
00065             --len;
00066     }
00067     return len;
00068 }
00069 
00070 nsPartChannel::nsPartChannel(nsIChannel *aMultipartChannel, PRUint32 aPartID) :
00071   mStatus(NS_OK),
00072   mContentLength(LL_MAXUINT),
00073   mIsByteRangeRequest(PR_FALSE),
00074   mByteRangeStart(0),
00075   mByteRangeEnd(0),
00076   mPartID(aPartID),
00077   mIsLastPart(PR_FALSE)
00078 {
00079     mMultipartChannel = aMultipartChannel;
00080 
00081     // Inherit the load flags from the original channel...
00082     mMultipartChannel->GetLoadFlags(&mLoadFlags);
00083 
00084     mMultipartChannel->GetLoadGroup(getter_AddRefs(mLoadGroup));
00085 }
00086 
00087 nsPartChannel::~nsPartChannel()
00088 {
00089 }
00090 
00091 void nsPartChannel::InitializeByteRange(PRInt64 aStart, PRInt64 aEnd)
00092 {
00093     mIsByteRangeRequest = PR_TRUE;
00094     
00095     mByteRangeStart = aStart;
00096     mByteRangeEnd   = aEnd;
00097 }
00098 
00099 
00100 //
00101 // nsISupports implementation...
00102 //
00103 
00104 NS_IMPL_ADDREF(nsPartChannel)
00105 NS_IMPL_RELEASE(nsPartChannel)
00106 
00107 NS_INTERFACE_MAP_BEGIN(nsPartChannel)
00108     NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIChannel)
00109     NS_INTERFACE_MAP_ENTRY(nsIRequest)
00110     NS_INTERFACE_MAP_ENTRY(nsIChannel)
00111     NS_INTERFACE_MAP_ENTRY(nsIByteRangeRequest)
00112     NS_INTERFACE_MAP_ENTRY(nsIMultiPartChannel)
00113 NS_INTERFACE_MAP_END
00114 
00115 //
00116 // nsIRequest implementation...
00117 //
00118 
00119 NS_IMETHODIMP
00120 nsPartChannel::GetName(nsACString &aResult)
00121 {
00122     return mMultipartChannel->GetName(aResult);
00123 }
00124 
00125 NS_IMETHODIMP
00126 nsPartChannel::IsPending(PRBool *aResult)
00127 {
00128     // For now, consider the active lifetime of each part the same as
00129     // the underlying multipart channel...  This is not exactly right,
00130     // but it is good enough :-)
00131     return mMultipartChannel->IsPending(aResult);
00132 }
00133 
00134 NS_IMETHODIMP
00135 nsPartChannel::GetStatus(nsresult *aResult)
00136 {
00137     nsresult rv = NS_OK;
00138 
00139     if (NS_FAILED(mStatus)) {
00140         *aResult = mStatus;
00141     } else {
00142         rv = mMultipartChannel->GetStatus(aResult);
00143     }
00144 
00145     return rv;
00146 }
00147 
00148 NS_IMETHODIMP
00149 nsPartChannel::Cancel(nsresult aStatus)
00150 {
00151     // Cancelling an individual part must not cancel the underlying
00152     // multipart channel...
00153     // XXX but we should stop sending data for _this_ part channel!
00154     mStatus = aStatus;
00155     return NS_OK;
00156 }
00157 
00158 NS_IMETHODIMP
00159 nsPartChannel::Suspend(void)
00160 {
00161     // Suspending an individual part must not suspend the underlying
00162     // multipart channel...
00163     // XXX why not?
00164     return NS_OK;
00165 }
00166 
00167 NS_IMETHODIMP
00168 nsPartChannel::Resume(void)
00169 {
00170     // Resuming an individual part must not resume the underlying
00171     // multipart channel...
00172     // XXX why not?
00173     return NS_OK;
00174 }
00175 
00176 //
00177 // nsIChannel implementation
00178 //
00179 
00180 NS_IMETHODIMP
00181 nsPartChannel::GetOriginalURI(nsIURI * *aURI)
00182 {
00183     return mMultipartChannel->GetOriginalURI(aURI);
00184 }
00185 
00186 NS_IMETHODIMP
00187 nsPartChannel::SetOriginalURI(nsIURI *aURI)
00188 {
00189     return mMultipartChannel->SetOriginalURI(aURI);
00190 }
00191 
00192 NS_IMETHODIMP
00193 nsPartChannel::GetURI(nsIURI * *aURI)
00194 {
00195     return mMultipartChannel->GetURI(aURI);
00196 }
00197 
00198 NS_IMETHODIMP
00199 nsPartChannel::Open(nsIInputStream **result)
00200 {
00201     // This channel cannot be opened!
00202     return NS_ERROR_FAILURE;
00203 }
00204 
00205 NS_IMETHODIMP
00206 nsPartChannel::AsyncOpen(nsIStreamListener *aListener, nsISupports *aContext)
00207 {
00208     // This channel cannot be opened!
00209     return NS_ERROR_FAILURE;
00210 }
00211 
00212 NS_IMETHODIMP
00213 nsPartChannel::GetLoadFlags(nsLoadFlags *aLoadFlags)
00214 {
00215     *aLoadFlags = mLoadFlags;
00216     return NS_OK;
00217 }
00218 
00219 NS_IMETHODIMP
00220 nsPartChannel::SetLoadFlags(nsLoadFlags aLoadFlags)
00221 {
00222     mLoadFlags = aLoadFlags;
00223     return NS_OK;
00224 }
00225 
00226 NS_IMETHODIMP
00227 nsPartChannel::GetLoadGroup(nsILoadGroup* *aLoadGroup)
00228 {
00229     *aLoadGroup = mLoadGroup;
00230     NS_IF_ADDREF(*aLoadGroup);
00231 
00232     return NS_OK;
00233 }
00234 
00235 NS_IMETHODIMP
00236 nsPartChannel::SetLoadGroup(nsILoadGroup* aLoadGroup)
00237 {
00238     mLoadGroup = aLoadGroup;
00239 
00240     return NS_OK;
00241 }
00242 
00243 NS_IMETHODIMP
00244 nsPartChannel::GetOwner(nsISupports* *aOwner)
00245 {
00246     return mMultipartChannel->GetOwner(aOwner);
00247 }
00248 
00249 NS_IMETHODIMP
00250 nsPartChannel::SetOwner(nsISupports* aOwner)
00251 {
00252     return mMultipartChannel->SetOwner(aOwner);
00253 }
00254 
00255 NS_IMETHODIMP
00256 nsPartChannel::GetNotificationCallbacks(nsIInterfaceRequestor* *aCallbacks)
00257 {
00258     return mMultipartChannel->GetNotificationCallbacks(aCallbacks);
00259 }
00260 
00261 NS_IMETHODIMP
00262 nsPartChannel::SetNotificationCallbacks(nsIInterfaceRequestor* aCallbacks)
00263 {
00264     return mMultipartChannel->SetNotificationCallbacks(aCallbacks);
00265 }
00266 
00267 NS_IMETHODIMP 
00268 nsPartChannel::GetSecurityInfo(nsISupports * *aSecurityInfo)
00269 {
00270     return mMultipartChannel->GetSecurityInfo(aSecurityInfo);
00271 }
00272 
00273 NS_IMETHODIMP
00274 nsPartChannel::GetContentType(nsACString &aContentType)
00275 {
00276     aContentType = mContentType;
00277     return NS_OK;
00278 }
00279 
00280 NS_IMETHODIMP
00281 nsPartChannel::SetContentType(const nsACString &aContentType)
00282 {
00283     PRBool dummy;
00284     net_ParseContentType(aContentType, mContentType, mContentCharset, &dummy);
00285     return NS_OK;
00286 }
00287 
00288 NS_IMETHODIMP
00289 nsPartChannel::GetContentCharset(nsACString &aContentCharset)
00290 {
00291     aContentCharset = mContentCharset;
00292     return NS_OK;
00293 }
00294 
00295 NS_IMETHODIMP
00296 nsPartChannel::SetContentCharset(const nsACString &aContentCharset)
00297 {
00298     mContentCharset = aContentCharset;
00299     return NS_OK;
00300 }
00301 
00302 NS_IMETHODIMP
00303 nsPartChannel::GetContentLength(PRInt32 *aContentLength)
00304 {
00305     *aContentLength = mContentLength; // XXX truncates 64-bit value
00306     return NS_OK;
00307 }
00308 
00309 NS_IMETHODIMP
00310 nsPartChannel::SetContentLength(PRInt32 aContentLength)
00311 {
00312     mContentLength = aContentLength;
00313     return NS_OK;
00314 }
00315 
00316 NS_IMETHODIMP
00317 nsPartChannel::GetContentDisposition(nsACString &aContentDisposition)
00318 {
00319     aContentDisposition = mContentDisposition;
00320     return NS_OK;
00321 }
00322 
00323 NS_IMETHODIMP
00324 nsPartChannel::SetContentDisposition(const nsACString &aContentDisposition)
00325 {
00326     mContentDisposition = aContentDisposition;
00327     return NS_OK;
00328 }
00329 
00330 NS_IMETHODIMP
00331 nsPartChannel::GetPartID(PRUint32 *aPartID)
00332 {
00333     *aPartID = mPartID;
00334     return NS_OK;
00335 }
00336 
00337 NS_IMETHODIMP
00338 nsPartChannel::GetIsLastPart(PRBool *aIsLastPart)
00339 {
00340     *aIsLastPart = mIsLastPart;
00341     return NS_OK;
00342 }
00343 
00344 //
00345 // nsIByteRangeRequest implementation...
00346 //
00347 
00348 NS_IMETHODIMP 
00349 nsPartChannel::GetIsByteRangeRequest(PRBool *aIsByteRangeRequest)
00350 {
00351     *aIsByteRangeRequest = mIsByteRangeRequest;
00352 
00353     return NS_OK;
00354 }
00355 
00356 
00357 NS_IMETHODIMP 
00358 nsPartChannel::GetStartRange(PRInt64 *aStartRange)
00359 {
00360     *aStartRange = mByteRangeStart;
00361 
00362     return NS_OK;
00363 }
00364 
00365 NS_IMETHODIMP 
00366 nsPartChannel::GetEndRange(PRInt64 *aEndRange)
00367 {
00368     *aEndRange = mByteRangeEnd;
00369     return NS_OK;
00370 }
00371 
00372 NS_IMETHODIMP
00373 nsPartChannel::GetBaseChannel(nsIChannel ** aReturn)
00374 {
00375     NS_ENSURE_ARG_POINTER(aReturn);
00376 
00377     *aReturn = mMultipartChannel;
00378     NS_IF_ADDREF(*aReturn);
00379     return NS_OK;
00380 }
00381 
00382 
00383 // nsISupports implementation
00384 NS_IMPL_THREADSAFE_ISUPPORTS3(nsMultiMixedConv,
00385                               nsIStreamConverter, 
00386                               nsIStreamListener,
00387                               nsIRequestObserver)
00388 
00389 
00390 // nsIStreamConverter implementation
00391 
00392 // No syncronous conversion at this time.
00393 NS_IMETHODIMP
00394 nsMultiMixedConv::Convert(nsIInputStream *aFromStream,
00395                           const char *aFromType,
00396                           const char *aToType,
00397                           nsISupports *aCtxt, nsIInputStream **_retval) {
00398     return NS_ERROR_NOT_IMPLEMENTED;
00399 }
00400 
00401 // Stream converter service calls this to initialize the actual stream converter (us).
00402 NS_IMETHODIMP
00403 nsMultiMixedConv::AsyncConvertData(const char *aFromType, const char *aToType,
00404                                    nsIStreamListener *aListener, nsISupports *aCtxt) {
00405     NS_ASSERTION(aListener && aFromType && aToType, "null pointer passed into multi mixed converter");
00406 
00407     // hook up our final listener. this guy gets the various On*() calls we want to throw
00408     // at him.
00409     //
00410     // WARNING: this listener must be able to handle multiple OnStartRequest, OnDataAvail()
00411     //  and OnStopRequest() call combinations. We call of series of these for each sub-part
00412     //  in the raw stream.
00413     mFinalListener = aListener;
00414     return NS_OK;
00415 }
00416 
00417 #define ERR_OUT { free(buffer); return rv; }
00418 
00419 // nsIStreamListener implementation
00420 NS_IMETHODIMP
00421 nsMultiMixedConv::OnDataAvailable(nsIRequest *request, nsISupports *context,
00422                                   nsIInputStream *inStr, PRUint32 sourceOffset, PRUint32 count) {
00423 
00424     if (mToken.IsEmpty()) // no token, no love.
00425         return NS_ERROR_FAILURE;
00426 
00427     nsresult rv = NS_OK;
00428     char *buffer = nsnull;
00429     PRUint32 bufLen = 0, read = 0;
00430 
00431     NS_ASSERTION(request, "multimixed converter needs a request");
00432 
00433     nsCOMPtr<nsIChannel> channel = do_QueryInterface(request, &rv);
00434     if (NS_FAILED(rv)) return rv;
00435 
00436     // fill buffer
00437     {
00438         bufLen = count + mBufLen;
00439         buffer = (char *) malloc(bufLen);
00440         if (!buffer)
00441             return NS_ERROR_OUT_OF_MEMORY;
00442 
00443         if (mBufLen) {
00444             // incorporate any buffered data into the parsing
00445             memcpy(buffer, mBuffer, mBufLen);
00446             free(mBuffer);
00447             mBuffer = 0;
00448             mBufLen = 0;
00449         }
00450         
00451         rv = inStr->Read(buffer + (bufLen - count), count, &read);
00452 
00453         if (NS_FAILED(rv) || read == 0) return rv;
00454         NS_ASSERTION(read == count, "poor data size assumption");
00455     }
00456 
00457     char *cursor = buffer;
00458 
00459     if (mFirstOnData) {
00460         // this is the first OnData() for this request. some servers
00461         // don't bother sending a token in the first "part." This is
00462         // illegal, but we'll handle the case anyway by shoving the
00463         // boundary token in for the server.
00464         mFirstOnData = PR_FALSE;
00465         NS_ASSERTION(!mBufLen, "this is our first time through, we can't have buffered data");
00466         const char * token = mToken.get();
00467            
00468         PushOverLine(cursor, bufLen);
00469 
00470         if (bufLen < mTokenLen+2) {
00471             // we don't have enough data yet to make this comparison.
00472             // skip this check, and try again the next time OnData()
00473             // is called.
00474             mFirstOnData = PR_TRUE;
00475         }
00476         else if (!PL_strnstr(cursor, token, mTokenLen+2)) {
00477             buffer = (char *) realloc(buffer, bufLen + mTokenLen + 1);
00478             if (!buffer)
00479                 return NS_ERROR_OUT_OF_MEMORY;
00480 
00481             memmove(buffer + mTokenLen + 1, buffer, bufLen);
00482             memcpy(buffer, token, mTokenLen);
00483             buffer[mTokenLen] = '\n';
00484 
00485             bufLen += (mTokenLen + 1);
00486 
00487             // need to reset cursor to the buffer again (bug 100595)
00488             cursor = buffer;
00489         }
00490     }
00491 
00492     char *token = nsnull;
00493 
00494     if (mProcessingHeaders) {
00495         // we were not able to process all the headers
00496         // for this "part" given the previous buffer given to 
00497         // us in the previous OnDataAvailable callback.
00498         PRBool done = PR_FALSE;
00499         rv = ParseHeaders(channel, cursor, bufLen, &done);
00500         if (NS_FAILED(rv)) ERR_OUT
00501 
00502         if (done) {
00503             mProcessingHeaders = PR_FALSE;
00504             rv = SendStart(channel);
00505             if (NS_FAILED(rv)) ERR_OUT
00506         }
00507     }
00508 
00509     PRInt32 tokenLinefeed = 1;
00510     while ( (token = FindToken(cursor, bufLen)) ) {
00511 
00512         if (*(token+mTokenLen+1) == '-') {
00513             // This was the last delimiter so we can stop processing
00514             rv = SendData(cursor, LengthToToken(cursor, token));
00515             free(buffer);
00516             if (NS_FAILED(rv)) return rv;
00517             return SendStop(NS_OK);
00518         }
00519 
00520         if (!mNewPart && token > cursor) {
00521             // headers are processed, we're pushing data now.
00522             NS_ASSERTION(!mProcessingHeaders, "we should be pushing raw data");
00523             rv = SendData(cursor, LengthToToken(cursor, token));
00524             bufLen -= token - cursor;
00525             if (NS_FAILED(rv)) ERR_OUT
00526         }
00527         // XXX else NS_ASSERTION(token == cursor, "?");
00528         token += mTokenLen;
00529         bufLen -= mTokenLen;
00530         tokenLinefeed = PushOverLine(token, bufLen);
00531 
00532         if (mNewPart) {
00533             // parse headers
00534             mNewPart = PR_FALSE;
00535             cursor = token;
00536             PRBool done = PR_FALSE; 
00537             rv = ParseHeaders(channel, cursor, bufLen, &done);
00538             if (NS_FAILED(rv)) ERR_OUT
00539             if (done) {
00540                 rv = SendStart(channel);
00541                 if (NS_FAILED(rv)) ERR_OUT
00542             }
00543             else {
00544                 // we haven't finished processing header info.
00545                 // we'll break out and try to process later.
00546                 mProcessingHeaders = PR_TRUE;
00547                 break;
00548             }
00549         }
00550         else {
00551             mNewPart = PR_TRUE;
00552             // Reset state so we don't carry it over from part to part
00553             mContentType.Truncate();
00554             mContentLength = LL_MAXUINT;
00555             mContentDisposition.Truncate();
00556             mIsByteRangeRequest = PR_FALSE;
00557             mByteRangeStart = 0;
00558             mByteRangeEnd = 0;
00559             
00560             rv = SendStop(NS_OK);
00561             if (NS_FAILED(rv)) ERR_OUT
00562             // reset the token to front. this allows us to treat
00563             // the token as a starting token.
00564             token -= mTokenLen + tokenLinefeed;
00565             bufLen += mTokenLen + tokenLinefeed;
00566             cursor = token;
00567         }
00568     }
00569 
00570     // at this point, we want to buffer up whatever amount (bufLen)
00571     // we have leftover. However, we *always* want to ensure that
00572     // we buffer enough data to handle a broken token.
00573 
00574     // carry over
00575     PRUint32 bufAmt = 0;
00576     if (mProcessingHeaders)
00577         bufAmt = bufLen;
00578     else if (bufLen) {
00579         // if the data ends in a linefeed, and we're in the middle
00580         // of a "part" (ie. mPartChannel exists) don't bother
00581         // buffering, go ahead and send the data we have. Otherwise
00582         // if we don't have a channel already, then we don't even
00583         // have enough info to start a part, go ahead and buffer
00584         // enough to collect a boundary token.
00585         if (!mPartChannel || !(cursor[bufLen-1] == nsCRT::LF) )
00586             bufAmt = PR_MIN(mTokenLen - 1, bufLen);
00587     }
00588 
00589     if (bufAmt) {
00590         rv = BufferData(cursor + (bufLen - bufAmt), bufAmt);
00591         if (NS_FAILED(rv)) ERR_OUT
00592         bufLen -= bufAmt;
00593     }
00594 
00595     if (bufLen) {
00596         rv = SendData(cursor, bufLen);
00597         if (NS_FAILED(rv)) ERR_OUT
00598     }
00599 
00600     free(buffer);
00601     return rv;
00602 }
00603 
00604 
00605 // nsIRequestObserver implementation
00606 NS_IMETHODIMP
00607 nsMultiMixedConv::OnStartRequest(nsIRequest *request, nsISupports *ctxt) {
00608     // we're assuming the content-type is available at this stage
00609     NS_ASSERTION(mToken.IsEmpty(), "a second on start???");
00610     const char *bndry = nsnull;
00611     nsCAutoString delimiter;
00612     nsresult rv = NS_OK;
00613     mContext = ctxt;
00614 
00615     mFirstOnData = PR_TRUE;
00616     mTotalSent   = 0;
00617 
00618     nsCOMPtr<nsIChannel> channel = do_QueryInterface(request, &rv);
00619     if (NS_FAILED(rv)) return rv;
00620     
00621     // ask the HTTP channel for the content-type and extract the boundary from it.
00622     nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface(channel, &rv);
00623     if (NS_SUCCEEDED(rv)) {
00624         rv = httpChannel->GetResponseHeader(NS_LITERAL_CSTRING("content-type"), delimiter);
00625         if (NS_FAILED(rv)) return rv;
00626     } else {
00627         // try asking the channel directly
00628         rv = channel->GetContentType(delimiter);
00629         if (NS_FAILED(rv)) return NS_ERROR_FAILURE;
00630     }
00631 
00632     bndry = strstr(delimiter.BeginWriting(), "boundary");
00633     if (!bndry) return NS_ERROR_FAILURE;
00634 
00635     bndry = strchr(bndry, '=');
00636     if (!bndry) return NS_ERROR_FAILURE;
00637 
00638     bndry++; // move past the equals sign
00639 
00640     char *attrib = (char *) strchr(bndry, ';');
00641     if (attrib) *attrib = '\0';
00642 
00643     nsCAutoString boundaryString(bndry);
00644     if (attrib) *attrib = ';';
00645 
00646     boundaryString.Trim(" \"");
00647 
00648     mToken = boundaryString;
00649     mTokenLen = boundaryString.Length();
00650     
00651     if (mTokenLen == 0)
00652         return NS_ERROR_FAILURE;
00653 
00654     return NS_OK;
00655 }
00656 
00657 NS_IMETHODIMP
00658 nsMultiMixedConv::OnStopRequest(nsIRequest *request, nsISupports *ctxt,
00659                                 nsresult aStatus) {
00660 
00661     if (mToken.IsEmpty())  // no token, no love.
00662         return NS_ERROR_FAILURE;
00663 
00664     if (mPartChannel) {
00665         mPartChannel->SetIsLastPart();
00666 
00667         // we've already called SendStart() (which sets up the mPartChannel,
00668         // and fires an OnStart()) send any data left over, and then fire the stop.
00669         if (mBufLen > 0 && mBuffer) {
00670             (void) SendData(mBuffer, mBufLen);
00671             // don't bother checking the return value here, if the send failed
00672             // we're done anyway as we're in the OnStop() callback.
00673             free(mBuffer);
00674             mBuffer = nsnull;
00675             mBufLen = 0;
00676         }
00677         (void) SendStop(aStatus);
00678     } else if (NS_FAILED(aStatus)) {
00679         // underlying data production problem. we should not be in
00680         // the middle of sending data. if we were, mPartChannel,
00681         // above, would have been true.
00682         
00683         // if we send the start, the URI Loader's m_targetStreamListener, may
00684         // be pointing at us causing a nice stack overflow.  So, don't call 
00685         // OnStartRequest!  -  This breaks necko's semantecs. 
00686         //(void) mFinalListener->OnStartRequest(request, ctxt);
00687         
00688         (void) mFinalListener->OnStopRequest(request, ctxt, aStatus);
00689     }
00690 
00691     return NS_OK;
00692 }
00693 
00694 
00695 // nsMultiMixedConv methods
00696 nsMultiMixedConv::nsMultiMixedConv() :
00697   mCurrentPartID(0)
00698 {
00699     mTokenLen           = 0;
00700     mNewPart            = PR_TRUE;
00701     mContentLength      = LL_MAXUINT;
00702     mBuffer             = nsnull;
00703     mBufLen             = 0;
00704     mProcessingHeaders  = PR_FALSE;
00705     mByteRangeStart     = 0;
00706     mByteRangeEnd       = 0;
00707     mTotalSent          = 0;
00708     mIsByteRangeRequest = PR_FALSE;
00709 }
00710 
00711 nsMultiMixedConv::~nsMultiMixedConv() {
00712     NS_ASSERTION(!mBuffer, "all buffered data should be gone");
00713     if (mBuffer) {
00714         free(mBuffer);
00715         mBuffer = nsnull;
00716     }
00717 }
00718 
00719 nsresult
00720 nsMultiMixedConv::BufferData(char *aData, PRUint32 aLen) {
00721     NS_ASSERTION(!mBuffer, "trying to over-write buffer");
00722 
00723     char *buffer = (char *) malloc(aLen);
00724     if (!buffer) return NS_ERROR_OUT_OF_MEMORY;
00725 
00726     memcpy(buffer, aData, aLen);
00727     mBuffer = buffer;
00728     mBufLen = aLen;
00729     return NS_OK;
00730 }
00731 
00732 
00733 nsresult
00734 nsMultiMixedConv::SendStart(nsIChannel *aChannel) {
00735     nsresult rv = NS_OK;
00736 
00737     if (mContentType.IsEmpty())
00738         mContentType.AssignLiteral(UNKNOWN_CONTENT_TYPE);
00739 
00740     // if we already have an mPartChannel, that means we never sent a Stop()
00741     // before starting up another "part." that would be bad.
00742     NS_ASSERTION(!mPartChannel, "tisk tisk, shouldn't be overwriting a channel");
00743 
00744     nsPartChannel *newChannel;
00745     newChannel = new nsPartChannel(aChannel, mCurrentPartID++);
00746     if (!newChannel)
00747         return NS_ERROR_OUT_OF_MEMORY;
00748 
00749     if (mIsByteRangeRequest) {
00750         newChannel->InitializeByteRange(mByteRangeStart, mByteRangeEnd);
00751     }
00752 
00753     mTotalSent = 0;
00754 
00755     // Set up the new part channel...
00756     mPartChannel = newChannel;
00757 
00758     rv = mPartChannel->SetContentType(mContentType);
00759     if (NS_FAILED(rv)) return rv;
00760 
00761     rv = mPartChannel->SetContentLength(mContentLength); // XXX Truncates 64-bit!
00762     if (NS_FAILED(rv)) return rv;
00763 
00764     rv = mPartChannel->SetContentDisposition(mContentDisposition);
00765     if (NS_FAILED(rv)) return rv;
00766 
00767     nsLoadFlags loadFlags = 0;
00768     mPartChannel->GetLoadFlags(&loadFlags);
00769     loadFlags |= nsIChannel::LOAD_REPLACE;
00770     mPartChannel->SetLoadFlags(loadFlags);
00771 
00772     nsCOMPtr<nsILoadGroup> loadGroup;
00773     (void)mPartChannel->GetLoadGroup(getter_AddRefs(loadGroup));
00774 
00775     // Add the new channel to the load group (if any)
00776     if (loadGroup) {
00777         rv = loadGroup->AddRequest(mPartChannel, nsnull);
00778         if (NS_FAILED(rv)) return rv;
00779     }
00780 
00781     // Let's start off the load. NOTE: we don't forward on the channel passed
00782     // into our OnDataAvailable() as it's the root channel for the raw stream.
00783     return mFinalListener->OnStartRequest(mPartChannel, mContext);
00784 }
00785 
00786 
00787 nsresult
00788 nsMultiMixedConv::SendStop(nsresult aStatus) {
00789     
00790     nsresult rv = NS_OK;
00791     if (mPartChannel) {
00792         rv = mFinalListener->OnStopRequest(mPartChannel, mContext, aStatus);
00793         // don't check for failure here, we need to remove the channel from 
00794         // the loadgroup.
00795 
00796         // Remove the channel from its load group (if any)
00797         nsCOMPtr<nsILoadGroup> loadGroup;
00798         (void) mPartChannel->GetLoadGroup(getter_AddRefs(loadGroup));
00799         if (loadGroup) 
00800             (void) loadGroup->RemoveRequest(mPartChannel, mContext, aStatus);
00801     }
00802 
00803     mPartChannel = 0;
00804     return rv;
00805 }
00806 
00807 nsresult
00808 nsMultiMixedConv::SendData(char *aBuffer, PRUint32 aLen) {
00809 
00810     nsresult rv = NS_OK;
00811     
00812     if (!mPartChannel) return NS_ERROR_FAILURE; // something went wrong w/ processing
00813 
00814     if (mContentLength != LL_MAXUINT) {
00815         // make sure that we don't send more than the mContentLength
00816         // XXX why? perhaps the Content-Length header was actually wrong!!
00817         if ((nsUint64(aLen) + mTotalSent) > mContentLength)
00818             aLen = mContentLength - mTotalSent;
00819 
00820         if (aLen == 0)
00821             return NS_OK;
00822     }
00823 
00824     PRUint32 offset = mTotalSent;
00825     mTotalSent += aLen;
00826 
00827     nsCOMPtr<nsIStringInputStream> ss(
00828             do_CreateInstance("@mozilla.org/io/string-input-stream;1", &rv));
00829     if (NS_FAILED(rv))
00830         return rv;
00831 
00832     rv = ss->ShareData(aBuffer, aLen);
00833     if (NS_FAILED(rv))
00834         return rv;
00835 
00836     nsCOMPtr<nsIInputStream> inStream(do_QueryInterface(ss, &rv));
00837     if (NS_FAILED(rv)) return rv;
00838 
00839     return mFinalListener->OnDataAvailable(mPartChannel, mContext, inStream, offset, aLen);
00840 }
00841 
00842 PRInt32
00843 nsMultiMixedConv::PushOverLine(char *&aPtr, PRUint32 &aLen) {
00844     PRInt32 chars = 0;
00845     if ((aLen > 0) && (*aPtr == nsCRT::CR || *aPtr == nsCRT::LF)) {
00846         if ((aLen > 1) && (aPtr[1] == nsCRT::LF))
00847             chars++;
00848         chars++;
00849         aPtr += chars;
00850         aLen -= chars;
00851     }
00852     return chars;
00853 }
00854 
00855 nsresult
00856 nsMultiMixedConv::ParseHeaders(nsIChannel *aChannel, char *&aPtr, 
00857                                PRUint32 &aLen, PRBool *_retval) {
00858     // NOTE: this data must be ascii.
00859     // NOTE: aPtr is NOT null terminated!
00860     nsresult rv = NS_OK;
00861     char *cursor = aPtr, *newLine = nsnull;
00862     PRUint32 cursorLen = aLen;
00863     PRBool done = PR_FALSE;
00864     PRUint32 lineFeedIncrement = 1;
00865     
00866     mContentLength = LL_MAXUINT; // XXX what if we were already called?
00867     while (cursorLen && (newLine = (char *) memchr(cursor, nsCRT::LF, cursorLen))) {
00868         // adjust for linefeeds
00869         if ((newLine > cursor) && (newLine[-1] == nsCRT::CR) ) { // CRLF
00870             lineFeedIncrement = 2;
00871             newLine--;
00872         }
00873         else
00874             lineFeedIncrement = 1; // reset
00875 
00876         if (newLine == cursor) {
00877             // move the newLine beyond the linefeed marker
00878             NS_ASSERTION(cursorLen >= lineFeedIncrement, "oops!");
00879 
00880             cursor += lineFeedIncrement;
00881             cursorLen -= lineFeedIncrement;
00882 
00883             done = PR_TRUE;
00884             break;
00885         }
00886 
00887         char tmpChar = *newLine;
00888         *newLine = '\0'; // cursor is now null terminated
00889         char *colon = (char *) strchr(cursor, ':');
00890         if (colon) {
00891             *colon = '\0';
00892             nsCAutoString headerStr(cursor);
00893             headerStr.CompressWhitespace();
00894             *colon = ':';
00895 
00896             nsCAutoString headerVal(colon + 1);
00897             headerVal.CompressWhitespace();
00898 
00899             // examine header
00900             if (headerStr.LowerCaseEqualsLiteral("content-type")) {
00901                 mContentType = headerVal;
00902             } else if (headerStr.LowerCaseEqualsLiteral("content-length")) {
00903                 mContentLength = atoi(headerVal.get()); // XXX 64-bit math?
00904             } else if (headerStr.LowerCaseEqualsLiteral("content-disposition")) {
00905                 mContentDisposition = headerVal;
00906             } else if (headerStr.LowerCaseEqualsLiteral("set-cookie")) {
00907                 nsCOMPtr<nsIHttpChannelInternal> httpInternal =
00908                     do_QueryInterface(aChannel);
00909                 if (httpInternal) {
00910                     httpInternal->SetCookie(headerVal.get());
00911                 }
00912             } else if (headerStr.LowerCaseEqualsLiteral("content-range") || 
00913                        headerStr.LowerCaseEqualsLiteral("range") ) {
00914                 // something like: Content-range: bytes 7000-7999/8000
00915                 char* tmpPtr;
00916 
00917                 tmpPtr = (char *) strchr(colon + 1, '/');
00918                 if (tmpPtr) 
00919                     *tmpPtr = '\0';
00920 
00921                 // pass the bytes-unit and the SP
00922                 char *range = (char *) strchr(colon + 2, ' ');
00923 
00924                 if (!range)
00925                     return NS_ERROR_FAILURE;
00926 
00927                 if (range[0] == '*'){
00928                     mByteRangeStart = mByteRangeEnd = 0;
00929                 }
00930                 else {
00931                     tmpPtr = (char *) strchr(range, '-');
00932                     if (!tmpPtr)
00933                         return NS_ERROR_FAILURE;
00934                     
00935                     tmpPtr[0] = '\0';
00936                     
00937                     mByteRangeStart = atoi(range); // XXX want 64-bit conv
00938                     tmpPtr++;
00939                     mByteRangeEnd = atoi(tmpPtr);
00940                 }
00941 
00942                 mIsByteRangeRequest = PR_TRUE;
00943                 if (mContentLength == LL_MAXUINT)
00944                     mContentLength = PRUint64(PRInt64(mByteRangeEnd - mByteRangeStart + nsInt64(1)));
00945             }
00946         }
00947         *newLine = tmpChar;
00948         newLine += lineFeedIncrement;
00949         cursorLen -= (newLine - cursor);
00950         cursor = newLine;
00951     }
00952 
00953     aPtr = cursor;
00954     aLen = cursorLen;
00955 
00956     *_retval = done;
00957     return rv;
00958 }
00959 
00960 char *
00961 nsMultiMixedConv::FindToken(char *aCursor, PRUint32 aLen) {
00962     // strnstr without looking for null termination
00963     const char *token = mToken.get();
00964     char *cur = aCursor;
00965 
00966     if (!(token && aCursor && *token)) {
00967         NS_WARNING("bad data");
00968         return nsnull;
00969     }
00970 
00971     for (; aLen >= mTokenLen; aCursor++, aLen--) {
00972         if (!memcmp(aCursor, token, mTokenLen) ) {
00973             if ((aCursor - cur) >= 2) {
00974                 // back the cursor up over a double dash for backwards compat.
00975                 if ((*(aCursor-1) == '-') && (*(aCursor-2) == '-')) {
00976                     aCursor -= 2;
00977                     aLen += 2;
00978 
00979                     // we're playing w/ double dash tokens, adjust.
00980                     mToken.Assign(aCursor, mTokenLen + 2);
00981                     mTokenLen = mToken.Length();
00982                 }
00983             }
00984             return aCursor;
00985         }
00986     }
00987 
00988     return nsnull;
00989 }
00990 
00991 nsresult
00992 NS_NewMultiMixedConv(nsMultiMixedConv** aMultiMixedConv)
00993 {
00994     NS_PRECONDITION(aMultiMixedConv != nsnull, "null ptr");
00995     if (! aMultiMixedConv)
00996         return NS_ERROR_NULL_POINTER;
00997 
00998     *aMultiMixedConv = new nsMultiMixedConv();
00999     if (! *aMultiMixedConv)
01000         return NS_ERROR_OUT_OF_MEMORY;
01001 
01002     NS_ADDREF(*aMultiMixedConv);
01003     return NS_OK;
01004 }
01005