Bug 470455 - test_database_sync_embed_visits.js leaks, r=sdwilsh
[wine-gecko.git] / netwerk / streamconv / converters / nsMultiMixedConv.cpp
blob9765ffffa89f1bd9d0acd1c2bd4ef1751bf275c7
1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /* ***** BEGIN LICENSE BLOCK *****
3 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
5 * The contents of this file are subject to the Mozilla Public License Version
6 * 1.1 (the "License"); you may not use this file except in compliance with
7 * the License. You may obtain a copy of the License at
8 * http://www.mozilla.org/MPL/
10 * Software distributed under the License is distributed on an "AS IS" basis,
11 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
12 * for the specific language governing rights and limitations under the
13 * License.
15 * The Original Code is mozilla.org code.
17 * The Initial Developer of the Original Code is
18 * Netscape Communications Corporation.
19 * Portions created by the Initial Developer are Copyright (C) 1998
20 * the Initial Developer. All Rights Reserved.
22 * Contributor(s):
24 * Alternatively, the contents of this file may be used under the terms of
25 * either the GNU General Public License Version 2 or later (the "GPL"), or
26 * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
27 * in which case the provisions of the GPL or the LGPL are applicable instead
28 * of those above. If you wish to allow use of your version of this file only
29 * under the terms of either the GPL or the LGPL, and not to allow others to
30 * use your version of this file under the terms of the MPL, indicate your
31 * decision by deleting the provisions above and replace them with the notice
32 * and other provisions required by the GPL or the LGPL. If you do not delete
33 * the provisions above, a recipient may use your version of this file under
34 * the terms of any one of the MPL, the GPL or the LGPL.
36 * ***** END LICENSE BLOCK ***** */
38 #include "nsMultiMixedConv.h"
39 #include "nsMemory.h"
40 #include "nsInt64.h"
41 #include "plstr.h"
42 #include "nsIHttpChannel.h"
43 #include "nsIServiceManager.h"
44 #include "nsNetUtil.h"
45 #include "nsMimeTypes.h"
46 #include "nsIStringStream.h"
47 #include "nsReadableUtils.h"
48 #include "nsCRT.h"
49 #include "nsIHttpChannelInternal.h"
50 #include "nsURLHelper.h"
53 // Helper function for determining the length of data bytes up to
54 // the next multipart token. A token is usually preceded by a LF
55 // or CRLF delimiter.
56 //
57 static PRUint32
58 LengthToToken(const char *cursor, const char *token)
60 PRUint32 len = token - cursor;
61 // Trim off any LF or CRLF preceding the token
62 if (len && *(token-1) == '\n') {
63 --len;
64 if (len && *(token-2) == '\r')
65 --len;
67 return len;
70 nsPartChannel::nsPartChannel(nsIChannel *aMultipartChannel, PRUint32 aPartID) :
71 mStatus(NS_OK),
72 mContentLength(LL_MAXUINT),
73 mIsByteRangeRequest(PR_FALSE),
74 mByteRangeStart(0),
75 mByteRangeEnd(0),
76 mPartID(aPartID),
77 mIsLastPart(PR_FALSE)
79 mMultipartChannel = aMultipartChannel;
81 // Inherit the load flags from the original channel...
82 mMultipartChannel->GetLoadFlags(&mLoadFlags);
84 mMultipartChannel->GetLoadGroup(getter_AddRefs(mLoadGroup));
87 nsPartChannel::~nsPartChannel()
91 void nsPartChannel::InitializeByteRange(PRInt64 aStart, PRInt64 aEnd)
93 mIsByteRangeRequest = PR_TRUE;
95 mByteRangeStart = aStart;
96 mByteRangeEnd = aEnd;
101 // nsISupports implementation...
104 NS_IMPL_ADDREF(nsPartChannel)
105 NS_IMPL_RELEASE(nsPartChannel)
107 NS_INTERFACE_MAP_BEGIN(nsPartChannel)
108 NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIChannel)
109 NS_INTERFACE_MAP_ENTRY(nsIRequest)
110 NS_INTERFACE_MAP_ENTRY(nsIChannel)
111 NS_INTERFACE_MAP_ENTRY(nsIByteRangeRequest)
112 NS_INTERFACE_MAP_ENTRY(nsIMultiPartChannel)
113 NS_INTERFACE_MAP_END
116 // nsIRequest implementation...
119 NS_IMETHODIMP
120 nsPartChannel::GetName(nsACString &aResult)
122 return mMultipartChannel->GetName(aResult);
125 NS_IMETHODIMP
126 nsPartChannel::IsPending(PRBool *aResult)
128 // For now, consider the active lifetime of each part the same as
129 // the underlying multipart channel... This is not exactly right,
130 // but it is good enough :-)
131 return mMultipartChannel->IsPending(aResult);
134 NS_IMETHODIMP
135 nsPartChannel::GetStatus(nsresult *aResult)
137 nsresult rv = NS_OK;
139 if (NS_FAILED(mStatus)) {
140 *aResult = mStatus;
141 } else {
142 rv = mMultipartChannel->GetStatus(aResult);
145 return rv;
148 NS_IMETHODIMP
149 nsPartChannel::Cancel(nsresult aStatus)
151 // Cancelling an individual part must not cancel the underlying
152 // multipart channel...
153 // XXX but we should stop sending data for _this_ part channel!
154 mStatus = aStatus;
155 return NS_OK;
158 NS_IMETHODIMP
159 nsPartChannel::Suspend(void)
161 // Suspending an individual part must not suspend the underlying
162 // multipart channel...
163 // XXX why not?
164 return NS_OK;
167 NS_IMETHODIMP
168 nsPartChannel::Resume(void)
170 // Resuming an individual part must not resume the underlying
171 // multipart channel...
172 // XXX why not?
173 return NS_OK;
177 // nsIChannel implementation
180 NS_IMETHODIMP
181 nsPartChannel::GetOriginalURI(nsIURI * *aURI)
183 return mMultipartChannel->GetOriginalURI(aURI);
186 NS_IMETHODIMP
187 nsPartChannel::SetOriginalURI(nsIURI *aURI)
189 return mMultipartChannel->SetOriginalURI(aURI);
192 NS_IMETHODIMP
193 nsPartChannel::GetURI(nsIURI * *aURI)
195 return mMultipartChannel->GetURI(aURI);
198 NS_IMETHODIMP
199 nsPartChannel::Open(nsIInputStream **result)
201 // This channel cannot be opened!
202 return NS_ERROR_FAILURE;
205 NS_IMETHODIMP
206 nsPartChannel::AsyncOpen(nsIStreamListener *aListener, nsISupports *aContext)
208 // This channel cannot be opened!
209 return NS_ERROR_FAILURE;
212 NS_IMETHODIMP
213 nsPartChannel::GetLoadFlags(nsLoadFlags *aLoadFlags)
215 *aLoadFlags = mLoadFlags;
216 return NS_OK;
219 NS_IMETHODIMP
220 nsPartChannel::SetLoadFlags(nsLoadFlags aLoadFlags)
222 mLoadFlags = aLoadFlags;
223 return NS_OK;
226 NS_IMETHODIMP
227 nsPartChannel::GetLoadGroup(nsILoadGroup* *aLoadGroup)
229 *aLoadGroup = mLoadGroup;
230 NS_IF_ADDREF(*aLoadGroup);
232 return NS_OK;
235 NS_IMETHODIMP
236 nsPartChannel::SetLoadGroup(nsILoadGroup* aLoadGroup)
238 mLoadGroup = aLoadGroup;
240 return NS_OK;
243 NS_IMETHODIMP
244 nsPartChannel::GetOwner(nsISupports* *aOwner)
246 return mMultipartChannel->GetOwner(aOwner);
249 NS_IMETHODIMP
250 nsPartChannel::SetOwner(nsISupports* aOwner)
252 return mMultipartChannel->SetOwner(aOwner);
255 NS_IMETHODIMP
256 nsPartChannel::GetNotificationCallbacks(nsIInterfaceRequestor* *aCallbacks)
258 return mMultipartChannel->GetNotificationCallbacks(aCallbacks);
261 NS_IMETHODIMP
262 nsPartChannel::SetNotificationCallbacks(nsIInterfaceRequestor* aCallbacks)
264 return mMultipartChannel->SetNotificationCallbacks(aCallbacks);
267 NS_IMETHODIMP
268 nsPartChannel::GetSecurityInfo(nsISupports * *aSecurityInfo)
270 return mMultipartChannel->GetSecurityInfo(aSecurityInfo);
273 NS_IMETHODIMP
274 nsPartChannel::GetContentType(nsACString &aContentType)
276 aContentType = mContentType;
277 return NS_OK;
280 NS_IMETHODIMP
281 nsPartChannel::SetContentType(const nsACString &aContentType)
283 PRBool dummy;
284 net_ParseContentType(aContentType, mContentType, mContentCharset, &dummy);
285 return NS_OK;
288 NS_IMETHODIMP
289 nsPartChannel::GetContentCharset(nsACString &aContentCharset)
291 aContentCharset = mContentCharset;
292 return NS_OK;
295 NS_IMETHODIMP
296 nsPartChannel::SetContentCharset(const nsACString &aContentCharset)
298 mContentCharset = aContentCharset;
299 return NS_OK;
302 NS_IMETHODIMP
303 nsPartChannel::GetContentLength(PRInt32 *aContentLength)
305 *aContentLength = mContentLength; // XXX truncates 64-bit value
306 return NS_OK;
309 NS_IMETHODIMP
310 nsPartChannel::SetContentLength(PRInt32 aContentLength)
312 mContentLength = aContentLength;
313 return NS_OK;
316 NS_IMETHODIMP
317 nsPartChannel::GetContentDisposition(nsACString &aContentDisposition)
319 aContentDisposition = mContentDisposition;
320 return NS_OK;
323 NS_IMETHODIMP
324 nsPartChannel::SetContentDisposition(const nsACString &aContentDisposition)
326 mContentDisposition = aContentDisposition;
327 return NS_OK;
330 NS_IMETHODIMP
331 nsPartChannel::GetPartID(PRUint32 *aPartID)
333 *aPartID = mPartID;
334 return NS_OK;
337 NS_IMETHODIMP
338 nsPartChannel::GetIsLastPart(PRBool *aIsLastPart)
340 *aIsLastPart = mIsLastPart;
341 return NS_OK;
345 // nsIByteRangeRequest implementation...
348 NS_IMETHODIMP
349 nsPartChannel::GetIsByteRangeRequest(PRBool *aIsByteRangeRequest)
351 *aIsByteRangeRequest = mIsByteRangeRequest;
353 return NS_OK;
357 NS_IMETHODIMP
358 nsPartChannel::GetStartRange(PRInt64 *aStartRange)
360 *aStartRange = mByteRangeStart;
362 return NS_OK;
365 NS_IMETHODIMP
366 nsPartChannel::GetEndRange(PRInt64 *aEndRange)
368 *aEndRange = mByteRangeEnd;
369 return NS_OK;
372 NS_IMETHODIMP
373 nsPartChannel::GetBaseChannel(nsIChannel ** aReturn)
375 NS_ENSURE_ARG_POINTER(aReturn);
377 *aReturn = mMultipartChannel;
378 NS_IF_ADDREF(*aReturn);
379 return NS_OK;
383 // nsISupports implementation
384 NS_IMPL_ISUPPORTS3(nsMultiMixedConv,
385 nsIStreamConverter,
386 nsIStreamListener,
387 nsIRequestObserver)
390 // nsIStreamConverter implementation
392 // No syncronous conversion at this time.
393 NS_IMETHODIMP
394 nsMultiMixedConv::Convert(nsIInputStream *aFromStream,
395 const char *aFromType,
396 const char *aToType,
397 nsISupports *aCtxt, nsIInputStream **_retval) {
398 return NS_ERROR_NOT_IMPLEMENTED;
401 // Stream converter service calls this to initialize the actual stream converter (us).
402 NS_IMETHODIMP
403 nsMultiMixedConv::AsyncConvertData(const char *aFromType, const char *aToType,
404 nsIStreamListener *aListener, nsISupports *aCtxt) {
405 NS_ASSERTION(aListener && aFromType && aToType, "null pointer passed into multi mixed converter");
407 // hook up our final listener. this guy gets the various On*() calls we want to throw
408 // at him.
410 // WARNING: this listener must be able to handle multiple OnStartRequest, OnDataAvail()
411 // and OnStopRequest() call combinations. We call of series of these for each sub-part
412 // in the raw stream.
413 mFinalListener = aListener;
414 return NS_OK;
417 #define ERR_OUT { free(buffer); return rv; }
419 // nsIStreamListener implementation
420 NS_IMETHODIMP
421 nsMultiMixedConv::OnDataAvailable(nsIRequest *request, nsISupports *context,
422 nsIInputStream *inStr, PRUint32 sourceOffset, PRUint32 count) {
424 if (mToken.IsEmpty()) // no token, no love.
425 return NS_ERROR_FAILURE;
427 nsresult rv = NS_OK;
428 char *buffer = nsnull;
429 PRUint32 bufLen = 0, read = 0;
431 NS_ASSERTION(request, "multimixed converter needs a request");
433 nsCOMPtr<nsIChannel> channel = do_QueryInterface(request, &rv);
434 if (NS_FAILED(rv)) return rv;
436 // fill buffer
438 bufLen = count + mBufLen;
439 buffer = (char *) malloc(bufLen);
440 if (!buffer)
441 return NS_ERROR_OUT_OF_MEMORY;
443 if (mBufLen) {
444 // incorporate any buffered data into the parsing
445 memcpy(buffer, mBuffer, mBufLen);
446 free(mBuffer);
447 mBuffer = 0;
448 mBufLen = 0;
451 rv = inStr->Read(buffer + (bufLen - count), count, &read);
453 if (NS_FAILED(rv) || read == 0) return rv;
454 NS_ASSERTION(read == count, "poor data size assumption");
457 char *cursor = buffer;
459 if (mFirstOnData) {
460 // this is the first OnData() for this request. some servers
461 // don't bother sending a token in the first "part." This is
462 // illegal, but we'll handle the case anyway by shoving the
463 // boundary token in for the server.
464 mFirstOnData = PR_FALSE;
465 NS_ASSERTION(!mBufLen, "this is our first time through, we can't have buffered data");
466 const char * token = mToken.get();
468 PushOverLine(cursor, bufLen);
470 if (bufLen < mTokenLen+2) {
471 // we don't have enough data yet to make this comparison.
472 // skip this check, and try again the next time OnData()
473 // is called.
474 mFirstOnData = PR_TRUE;
476 else if (!PL_strnstr(cursor, token, mTokenLen+2)) {
477 buffer = (char *) realloc(buffer, bufLen + mTokenLen + 1);
478 if (!buffer)
479 return NS_ERROR_OUT_OF_MEMORY;
481 memmove(buffer + mTokenLen + 1, buffer, bufLen);
482 memcpy(buffer, token, mTokenLen);
483 buffer[mTokenLen] = '\n';
485 bufLen += (mTokenLen + 1);
487 // need to reset cursor to the buffer again (bug 100595)
488 cursor = buffer;
492 char *token = nsnull;
494 if (mProcessingHeaders) {
495 // we were not able to process all the headers
496 // for this "part" given the previous buffer given to
497 // us in the previous OnDataAvailable callback.
498 PRBool done = PR_FALSE;
499 rv = ParseHeaders(channel, cursor, bufLen, &done);
500 if (NS_FAILED(rv)) ERR_OUT
502 if (done) {
503 mProcessingHeaders = PR_FALSE;
504 rv = SendStart(channel);
505 if (NS_FAILED(rv)) ERR_OUT
509 PRInt32 tokenLinefeed = 1;
510 while ( (token = FindToken(cursor, bufLen)) ) {
512 if (*(token+mTokenLen+1) == '-') {
513 // This was the last delimiter so we can stop processing
514 rv = SendData(cursor, LengthToToken(cursor, token));
515 free(buffer);
516 if (NS_FAILED(rv)) return rv;
517 return SendStop(NS_OK);
520 if (!mNewPart && token > cursor) {
521 // headers are processed, we're pushing data now.
522 NS_ASSERTION(!mProcessingHeaders, "we should be pushing raw data");
523 rv = SendData(cursor, LengthToToken(cursor, token));
524 bufLen -= token - cursor;
525 if (NS_FAILED(rv)) ERR_OUT
527 // XXX else NS_ASSERTION(token == cursor, "?");
528 token += mTokenLen;
529 bufLen -= mTokenLen;
530 tokenLinefeed = PushOverLine(token, bufLen);
532 if (mNewPart) {
533 // parse headers
534 mNewPart = PR_FALSE;
535 cursor = token;
536 PRBool done = PR_FALSE;
537 rv = ParseHeaders(channel, cursor, bufLen, &done);
538 if (NS_FAILED(rv)) ERR_OUT
539 if (done) {
540 rv = SendStart(channel);
541 if (NS_FAILED(rv)) ERR_OUT
543 else {
544 // we haven't finished processing header info.
545 // we'll break out and try to process later.
546 mProcessingHeaders = PR_TRUE;
547 break;
550 else {
551 mNewPart = PR_TRUE;
552 // Reset state so we don't carry it over from part to part
553 mContentType.Truncate();
554 mContentLength = LL_MAXUINT;
555 mContentDisposition.Truncate();
556 mIsByteRangeRequest = PR_FALSE;
557 mByteRangeStart = 0;
558 mByteRangeEnd = 0;
560 rv = SendStop(NS_OK);
561 if (NS_FAILED(rv)) ERR_OUT
562 // reset the token to front. this allows us to treat
563 // the token as a starting token.
564 token -= mTokenLen + tokenLinefeed;
565 bufLen += mTokenLen + tokenLinefeed;
566 cursor = token;
570 // at this point, we want to buffer up whatever amount (bufLen)
571 // we have leftover. However, we *always* want to ensure that
572 // we buffer enough data to handle a broken token.
574 // carry over
575 PRUint32 bufAmt = 0;
576 if (mProcessingHeaders)
577 bufAmt = bufLen;
578 else if (bufLen) {
579 // if the data ends in a linefeed, and we're in the middle
580 // of a "part" (ie. mPartChannel exists) don't bother
581 // buffering, go ahead and send the data we have. Otherwise
582 // if we don't have a channel already, then we don't even
583 // have enough info to start a part, go ahead and buffer
584 // enough to collect a boundary token.
585 if (!mPartChannel || !(cursor[bufLen-1] == nsCRT::LF) )
586 bufAmt = PR_MIN(mTokenLen - 1, bufLen);
589 if (bufAmt) {
590 rv = BufferData(cursor + (bufLen - bufAmt), bufAmt);
591 if (NS_FAILED(rv)) ERR_OUT
592 bufLen -= bufAmt;
595 if (bufLen) {
596 rv = SendData(cursor, bufLen);
597 if (NS_FAILED(rv)) ERR_OUT
600 free(buffer);
601 return rv;
605 // nsIRequestObserver implementation
606 NS_IMETHODIMP
607 nsMultiMixedConv::OnStartRequest(nsIRequest *request, nsISupports *ctxt) {
608 // we're assuming the content-type is available at this stage
609 NS_ASSERTION(mToken.IsEmpty(), "a second on start???");
610 const char *bndry = nsnull;
611 nsCAutoString delimiter;
612 nsresult rv = NS_OK;
613 mContext = ctxt;
615 mFirstOnData = PR_TRUE;
616 mTotalSent = 0;
618 nsCOMPtr<nsIChannel> channel = do_QueryInterface(request, &rv);
619 if (NS_FAILED(rv)) return rv;
621 // ask the HTTP channel for the content-type and extract the boundary from it.
622 nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface(channel, &rv);
623 if (NS_SUCCEEDED(rv)) {
624 rv = httpChannel->GetResponseHeader(NS_LITERAL_CSTRING("content-type"), delimiter);
625 if (NS_FAILED(rv)) return rv;
626 } else {
627 // try asking the channel directly
628 rv = channel->GetContentType(delimiter);
629 if (NS_FAILED(rv)) return NS_ERROR_FAILURE;
632 bndry = strstr(delimiter.BeginWriting(), "boundary");
633 if (!bndry) return NS_ERROR_FAILURE;
635 bndry = strchr(bndry, '=');
636 if (!bndry) return NS_ERROR_FAILURE;
638 bndry++; // move past the equals sign
640 char *attrib = (char *) strchr(bndry, ';');
641 if (attrib) *attrib = '\0';
643 nsCAutoString boundaryString(bndry);
644 if (attrib) *attrib = ';';
646 boundaryString.Trim(" \"");
648 mToken = boundaryString;
649 mTokenLen = boundaryString.Length();
651 if (mTokenLen == 0)
652 return NS_ERROR_FAILURE;
654 return NS_OK;
657 NS_IMETHODIMP
658 nsMultiMixedConv::OnStopRequest(nsIRequest *request, nsISupports *ctxt,
659 nsresult aStatus) {
661 if (mToken.IsEmpty()) // no token, no love.
662 return NS_ERROR_FAILURE;
664 if (mPartChannel) {
665 mPartChannel->SetIsLastPart();
667 // we've already called SendStart() (which sets up the mPartChannel,
668 // and fires an OnStart()) send any data left over, and then fire the stop.
669 if (mBufLen > 0 && mBuffer) {
670 (void) SendData(mBuffer, mBufLen);
671 // don't bother checking the return value here, if the send failed
672 // we're done anyway as we're in the OnStop() callback.
673 free(mBuffer);
674 mBuffer = nsnull;
675 mBufLen = 0;
677 (void) SendStop(aStatus);
678 } else if (NS_FAILED(aStatus)) {
679 // underlying data production problem. we should not be in
680 // the middle of sending data. if we were, mPartChannel,
681 // above, would have been true.
683 // if we send the start, the URI Loader's m_targetStreamListener, may
684 // be pointing at us causing a nice stack overflow. So, don't call
685 // OnStartRequest! - This breaks necko's semantecs.
686 //(void) mFinalListener->OnStartRequest(request, ctxt);
688 (void) mFinalListener->OnStopRequest(request, ctxt, aStatus);
691 return NS_OK;
695 // nsMultiMixedConv methods
696 nsMultiMixedConv::nsMultiMixedConv() :
697 mCurrentPartID(0)
699 mTokenLen = 0;
700 mNewPart = PR_TRUE;
701 mContentLength = LL_MAXUINT;
702 mBuffer = nsnull;
703 mBufLen = 0;
704 mProcessingHeaders = PR_FALSE;
705 mByteRangeStart = 0;
706 mByteRangeEnd = 0;
707 mTotalSent = 0;
708 mIsByteRangeRequest = PR_FALSE;
711 nsMultiMixedConv::~nsMultiMixedConv() {
712 NS_ASSERTION(!mBuffer, "all buffered data should be gone");
713 if (mBuffer) {
714 free(mBuffer);
715 mBuffer = nsnull;
719 nsresult
720 nsMultiMixedConv::BufferData(char *aData, PRUint32 aLen) {
721 NS_ASSERTION(!mBuffer, "trying to over-write buffer");
723 char *buffer = (char *) malloc(aLen);
724 if (!buffer) return NS_ERROR_OUT_OF_MEMORY;
726 memcpy(buffer, aData, aLen);
727 mBuffer = buffer;
728 mBufLen = aLen;
729 return NS_OK;
733 nsresult
734 nsMultiMixedConv::SendStart(nsIChannel *aChannel) {
735 nsresult rv = NS_OK;
737 if (mContentType.IsEmpty())
738 mContentType.AssignLiteral(UNKNOWN_CONTENT_TYPE);
740 // if we already have an mPartChannel, that means we never sent a Stop()
741 // before starting up another "part." that would be bad.
742 NS_ASSERTION(!mPartChannel, "tisk tisk, shouldn't be overwriting a channel");
744 nsPartChannel *newChannel;
745 newChannel = new nsPartChannel(aChannel, mCurrentPartID++);
746 if (!newChannel)
747 return NS_ERROR_OUT_OF_MEMORY;
749 if (mIsByteRangeRequest) {
750 newChannel->InitializeByteRange(mByteRangeStart, mByteRangeEnd);
753 mTotalSent = 0;
755 // Set up the new part channel...
756 mPartChannel = newChannel;
758 rv = mPartChannel->SetContentType(mContentType);
759 if (NS_FAILED(rv)) return rv;
761 rv = mPartChannel->SetContentLength(mContentLength); // XXX Truncates 64-bit!
762 if (NS_FAILED(rv)) return rv;
764 rv = mPartChannel->SetContentDisposition(mContentDisposition);
765 if (NS_FAILED(rv)) return rv;
767 nsLoadFlags loadFlags = 0;
768 mPartChannel->GetLoadFlags(&loadFlags);
769 loadFlags |= nsIChannel::LOAD_REPLACE;
770 mPartChannel->SetLoadFlags(loadFlags);
772 nsCOMPtr<nsILoadGroup> loadGroup;
773 (void)mPartChannel->GetLoadGroup(getter_AddRefs(loadGroup));
775 // Add the new channel to the load group (if any)
776 if (loadGroup) {
777 rv = loadGroup->AddRequest(mPartChannel, nsnull);
778 if (NS_FAILED(rv)) return rv;
781 // Let's start off the load. NOTE: we don't forward on the channel passed
782 // into our OnDataAvailable() as it's the root channel for the raw stream.
783 return mFinalListener->OnStartRequest(mPartChannel, mContext);
787 nsresult
788 nsMultiMixedConv::SendStop(nsresult aStatus) {
790 nsresult rv = NS_OK;
791 if (mPartChannel) {
792 rv = mFinalListener->OnStopRequest(mPartChannel, mContext, aStatus);
793 // don't check for failure here, we need to remove the channel from
794 // the loadgroup.
796 // Remove the channel from its load group (if any)
797 nsCOMPtr<nsILoadGroup> loadGroup;
798 (void) mPartChannel->GetLoadGroup(getter_AddRefs(loadGroup));
799 if (loadGroup)
800 (void) loadGroup->RemoveRequest(mPartChannel, mContext, aStatus);
803 mPartChannel = 0;
804 return rv;
807 nsresult
808 nsMultiMixedConv::SendData(char *aBuffer, PRUint32 aLen) {
810 nsresult rv = NS_OK;
812 if (!mPartChannel) return NS_ERROR_FAILURE; // something went wrong w/ processing
814 if (mContentLength != LL_MAXUINT) {
815 // make sure that we don't send more than the mContentLength
816 // XXX why? perhaps the Content-Length header was actually wrong!!
817 if ((nsUint64(aLen) + mTotalSent) > mContentLength)
818 aLen = mContentLength - mTotalSent;
820 if (aLen == 0)
821 return NS_OK;
824 PRUint32 offset = mTotalSent;
825 mTotalSent += aLen;
827 nsCOMPtr<nsIStringInputStream> ss(
828 do_CreateInstance("@mozilla.org/io/string-input-stream;1", &rv));
829 if (NS_FAILED(rv))
830 return rv;
832 rv = ss->ShareData(aBuffer, aLen);
833 if (NS_FAILED(rv))
834 return rv;
836 nsCOMPtr<nsIInputStream> inStream(do_QueryInterface(ss, &rv));
837 if (NS_FAILED(rv)) return rv;
839 return mFinalListener->OnDataAvailable(mPartChannel, mContext, inStream, offset, aLen);
842 PRInt32
843 nsMultiMixedConv::PushOverLine(char *&aPtr, PRUint32 &aLen) {
844 PRInt32 chars = 0;
845 if ((aLen > 0) && (*aPtr == nsCRT::CR || *aPtr == nsCRT::LF)) {
846 if ((aLen > 1) && (aPtr[1] == nsCRT::LF))
847 chars++;
848 chars++;
849 aPtr += chars;
850 aLen -= chars;
852 return chars;
855 nsresult
856 nsMultiMixedConv::ParseHeaders(nsIChannel *aChannel, char *&aPtr,
857 PRUint32 &aLen, PRBool *_retval) {
858 // NOTE: this data must be ascii.
859 // NOTE: aPtr is NOT null terminated!
860 nsresult rv = NS_OK;
861 char *cursor = aPtr, *newLine = nsnull;
862 PRUint32 cursorLen = aLen;
863 PRBool done = PR_FALSE;
864 PRUint32 lineFeedIncrement = 1;
866 mContentLength = LL_MAXUINT; // XXX what if we were already called?
867 while (cursorLen && (newLine = (char *) memchr(cursor, nsCRT::LF, cursorLen))) {
868 // adjust for linefeeds
869 if ((newLine > cursor) && (newLine[-1] == nsCRT::CR) ) { // CRLF
870 lineFeedIncrement = 2;
871 newLine--;
873 else
874 lineFeedIncrement = 1; // reset
876 if (newLine == cursor) {
877 // move the newLine beyond the linefeed marker
878 NS_ASSERTION(cursorLen >= lineFeedIncrement, "oops!");
880 cursor += lineFeedIncrement;
881 cursorLen -= lineFeedIncrement;
883 done = PR_TRUE;
884 break;
887 char tmpChar = *newLine;
888 *newLine = '\0'; // cursor is now null terminated
889 char *colon = (char *) strchr(cursor, ':');
890 if (colon) {
891 *colon = '\0';
892 nsCAutoString headerStr(cursor);
893 headerStr.CompressWhitespace();
894 *colon = ':';
896 nsCAutoString headerVal(colon + 1);
897 headerVal.CompressWhitespace();
899 // examine header
900 if (headerStr.LowerCaseEqualsLiteral("content-type")) {
901 mContentType = headerVal;
902 } else if (headerStr.LowerCaseEqualsLiteral("content-length")) {
903 mContentLength = atoi(headerVal.get()); // XXX 64-bit math?
904 } else if (headerStr.LowerCaseEqualsLiteral("content-disposition")) {
905 mContentDisposition = headerVal;
906 } else if (headerStr.LowerCaseEqualsLiteral("set-cookie")) {
907 nsCOMPtr<nsIHttpChannelInternal> httpInternal =
908 do_QueryInterface(aChannel);
909 if (httpInternal) {
910 httpInternal->SetCookie(headerVal.get());
912 } else if (headerStr.LowerCaseEqualsLiteral("content-range") ||
913 headerStr.LowerCaseEqualsLiteral("range") ) {
914 // something like: Content-range: bytes 7000-7999/8000
915 char* tmpPtr;
917 tmpPtr = (char *) strchr(colon + 1, '/');
918 if (tmpPtr)
919 *tmpPtr = '\0';
921 // pass the bytes-unit and the SP
922 char *range = (char *) strchr(colon + 2, ' ');
924 if (!range)
925 return NS_ERROR_FAILURE;
927 if (range[0] == '*'){
928 mByteRangeStart = mByteRangeEnd = 0;
930 else {
931 tmpPtr = (char *) strchr(range, '-');
932 if (!tmpPtr)
933 return NS_ERROR_FAILURE;
935 tmpPtr[0] = '\0';
937 mByteRangeStart = atoi(range); // XXX want 64-bit conv
938 tmpPtr++;
939 mByteRangeEnd = atoi(tmpPtr);
942 mIsByteRangeRequest = PR_TRUE;
943 if (mContentLength == LL_MAXUINT)
944 mContentLength = PRUint64(PRInt64(mByteRangeEnd - mByteRangeStart + nsInt64(1)));
947 *newLine = tmpChar;
948 newLine += lineFeedIncrement;
949 cursorLen -= (newLine - cursor);
950 cursor = newLine;
953 aPtr = cursor;
954 aLen = cursorLen;
956 *_retval = done;
957 return rv;
960 char *
961 nsMultiMixedConv::FindToken(char *aCursor, PRUint32 aLen) {
962 // strnstr without looking for null termination
963 const char *token = mToken.get();
964 char *cur = aCursor;
966 if (!(token && aCursor && *token)) {
967 NS_WARNING("bad data");
968 return nsnull;
971 for (; aLen >= mTokenLen; aCursor++, aLen--) {
972 if (!memcmp(aCursor, token, mTokenLen) ) {
973 if ((aCursor - cur) >= 2) {
974 // back the cursor up over a double dash for backwards compat.
975 if ((*(aCursor-1) == '-') && (*(aCursor-2) == '-')) {
976 aCursor -= 2;
977 aLen += 2;
979 // we're playing w/ double dash tokens, adjust.
980 mToken.Assign(aCursor, mTokenLen + 2);
981 mTokenLen = mToken.Length();
984 return aCursor;
988 return nsnull;
991 nsresult
992 NS_NewMultiMixedConv(nsMultiMixedConv** aMultiMixedConv)
994 NS_PRECONDITION(aMultiMixedConv != nsnull, "null ptr");
995 if (! aMultiMixedConv)
996 return NS_ERROR_NULL_POINTER;
998 *aMultiMixedConv = new nsMultiMixedConv();
999 if (! *aMultiMixedConv)
1000 return NS_ERROR_OUT_OF_MEMORY;
1002 NS_ADDREF(*aMultiMixedConv);
1003 return NS_OK;