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
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.
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"
42 #include "nsIHttpChannel.h"
43 #include "nsIServiceManager.h"
44 #include "nsNetUtil.h"
45 #include "nsMimeTypes.h"
46 #include "nsIStringStream.h"
47 #include "nsReadableUtils.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
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') {
64 if (len
&& *(token
-2) == '\r')
70 nsPartChannel::nsPartChannel(nsIChannel
*aMultipartChannel
, PRUint32 aPartID
) :
72 mContentLength(LL_MAXUINT
),
73 mIsByteRangeRequest(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
;
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
)
116 // nsIRequest implementation...
120 nsPartChannel::GetName(nsACString
&aResult
)
122 return mMultipartChannel
->GetName(aResult
);
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
);
135 nsPartChannel::GetStatus(nsresult
*aResult
)
139 if (NS_FAILED(mStatus
)) {
142 rv
= mMultipartChannel
->GetStatus(aResult
);
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!
159 nsPartChannel::Suspend(void)
161 // Suspending an individual part must not suspend the underlying
162 // multipart channel...
168 nsPartChannel::Resume(void)
170 // Resuming an individual part must not resume the underlying
171 // multipart channel...
177 // nsIChannel implementation
181 nsPartChannel::GetOriginalURI(nsIURI
* *aURI
)
183 return mMultipartChannel
->GetOriginalURI(aURI
);
187 nsPartChannel::SetOriginalURI(nsIURI
*aURI
)
189 return mMultipartChannel
->SetOriginalURI(aURI
);
193 nsPartChannel::GetURI(nsIURI
* *aURI
)
195 return mMultipartChannel
->GetURI(aURI
);
199 nsPartChannel::Open(nsIInputStream
**result
)
201 // This channel cannot be opened!
202 return NS_ERROR_FAILURE
;
206 nsPartChannel::AsyncOpen(nsIStreamListener
*aListener
, nsISupports
*aContext
)
208 // This channel cannot be opened!
209 return NS_ERROR_FAILURE
;
213 nsPartChannel::GetLoadFlags(nsLoadFlags
*aLoadFlags
)
215 *aLoadFlags
= mLoadFlags
;
220 nsPartChannel::SetLoadFlags(nsLoadFlags aLoadFlags
)
222 mLoadFlags
= aLoadFlags
;
227 nsPartChannel::GetLoadGroup(nsILoadGroup
* *aLoadGroup
)
229 *aLoadGroup
= mLoadGroup
;
230 NS_IF_ADDREF(*aLoadGroup
);
236 nsPartChannel::SetLoadGroup(nsILoadGroup
* aLoadGroup
)
238 mLoadGroup
= aLoadGroup
;
244 nsPartChannel::GetOwner(nsISupports
* *aOwner
)
246 return mMultipartChannel
->GetOwner(aOwner
);
250 nsPartChannel::SetOwner(nsISupports
* aOwner
)
252 return mMultipartChannel
->SetOwner(aOwner
);
256 nsPartChannel::GetNotificationCallbacks(nsIInterfaceRequestor
* *aCallbacks
)
258 return mMultipartChannel
->GetNotificationCallbacks(aCallbacks
);
262 nsPartChannel::SetNotificationCallbacks(nsIInterfaceRequestor
* aCallbacks
)
264 return mMultipartChannel
->SetNotificationCallbacks(aCallbacks
);
268 nsPartChannel::GetSecurityInfo(nsISupports
* *aSecurityInfo
)
270 return mMultipartChannel
->GetSecurityInfo(aSecurityInfo
);
274 nsPartChannel::GetContentType(nsACString
&aContentType
)
276 aContentType
= mContentType
;
281 nsPartChannel::SetContentType(const nsACString
&aContentType
)
284 net_ParseContentType(aContentType
, mContentType
, mContentCharset
, &dummy
);
289 nsPartChannel::GetContentCharset(nsACString
&aContentCharset
)
291 aContentCharset
= mContentCharset
;
296 nsPartChannel::SetContentCharset(const nsACString
&aContentCharset
)
298 mContentCharset
= aContentCharset
;
303 nsPartChannel::GetContentLength(PRInt32
*aContentLength
)
305 *aContentLength
= mContentLength
; // XXX truncates 64-bit value
310 nsPartChannel::SetContentLength(PRInt32 aContentLength
)
312 mContentLength
= aContentLength
;
317 nsPartChannel::GetContentDisposition(nsACString
&aContentDisposition
)
319 aContentDisposition
= mContentDisposition
;
324 nsPartChannel::SetContentDisposition(const nsACString
&aContentDisposition
)
326 mContentDisposition
= aContentDisposition
;
331 nsPartChannel::GetPartID(PRUint32
*aPartID
)
338 nsPartChannel::GetIsLastPart(PRBool
*aIsLastPart
)
340 *aIsLastPart
= mIsLastPart
;
345 // nsIByteRangeRequest implementation...
349 nsPartChannel::GetIsByteRangeRequest(PRBool
*aIsByteRangeRequest
)
351 *aIsByteRangeRequest
= mIsByteRangeRequest
;
358 nsPartChannel::GetStartRange(PRInt64
*aStartRange
)
360 *aStartRange
= mByteRangeStart
;
366 nsPartChannel::GetEndRange(PRInt64
*aEndRange
)
368 *aEndRange
= mByteRangeEnd
;
373 nsPartChannel::GetBaseChannel(nsIChannel
** aReturn
)
375 NS_ENSURE_ARG_POINTER(aReturn
);
377 *aReturn
= mMultipartChannel
;
378 NS_IF_ADDREF(*aReturn
);
383 // nsISupports implementation
384 NS_IMPL_ISUPPORTS3(nsMultiMixedConv
,
390 // nsIStreamConverter implementation
392 // No syncronous conversion at this time.
394 nsMultiMixedConv::Convert(nsIInputStream
*aFromStream
,
395 const char *aFromType
,
397 nsISupports
*aCtxt
, nsIInputStream
**_retval
) {
398 return NS_ERROR_NOT_IMPLEMENTED
;
401 // Stream converter service calls this to initialize the actual stream converter (us).
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
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
;
417 #define ERR_OUT { free(buffer); return rv; }
419 // nsIStreamListener implementation
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
;
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
;
438 bufLen
= count
+ mBufLen
;
439 buffer
= (char *) malloc(bufLen
);
441 return NS_ERROR_OUT_OF_MEMORY
;
444 // incorporate any buffered data into the parsing
445 memcpy(buffer
, mBuffer
, mBufLen
);
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
;
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()
474 mFirstOnData
= PR_TRUE
;
476 else if (!PL_strnstr(cursor
, token
, mTokenLen
+2)) {
477 buffer
= (char *) realloc(buffer
, bufLen
+ mTokenLen
+ 1);
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)
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
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
));
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, "?");
530 tokenLinefeed
= PushOverLine(token
, bufLen
);
536 PRBool done
= PR_FALSE
;
537 rv
= ParseHeaders(channel
, cursor
, bufLen
, &done
);
538 if (NS_FAILED(rv
)) ERR_OUT
540 rv
= SendStart(channel
);
541 if (NS_FAILED(rv
)) ERR_OUT
544 // we haven't finished processing header info.
545 // we'll break out and try to process later.
546 mProcessingHeaders
= 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
;
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
;
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.
576 if (mProcessingHeaders
)
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
);
590 rv
= BufferData(cursor
+ (bufLen
- bufAmt
), bufAmt
);
591 if (NS_FAILED(rv
)) ERR_OUT
596 rv
= SendData(cursor
, bufLen
);
597 if (NS_FAILED(rv
)) ERR_OUT
605 // nsIRequestObserver implementation
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
;
615 mFirstOnData
= PR_TRUE
;
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
;
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();
652 return NS_ERROR_FAILURE
;
658 nsMultiMixedConv::OnStopRequest(nsIRequest
*request
, nsISupports
*ctxt
,
661 if (mToken
.IsEmpty()) // no token, no love.
662 return NS_ERROR_FAILURE
;
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.
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
);
695 // nsMultiMixedConv methods
696 nsMultiMixedConv::nsMultiMixedConv() :
701 mContentLength
= LL_MAXUINT
;
704 mProcessingHeaders
= PR_FALSE
;
708 mIsByteRangeRequest
= PR_FALSE
;
711 nsMultiMixedConv::~nsMultiMixedConv() {
712 NS_ASSERTION(!mBuffer
, "all buffered data should be gone");
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
);
734 nsMultiMixedConv::SendStart(nsIChannel
*aChannel
) {
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
++);
747 return NS_ERROR_OUT_OF_MEMORY
;
749 if (mIsByteRangeRequest
) {
750 newChannel
->InitializeByteRange(mByteRangeStart
, mByteRangeEnd
);
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)
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
);
788 nsMultiMixedConv::SendStop(nsresult aStatus
) {
792 rv
= mFinalListener
->OnStopRequest(mPartChannel
, mContext
, aStatus
);
793 // don't check for failure here, we need to remove the channel from
796 // Remove the channel from its load group (if any)
797 nsCOMPtr
<nsILoadGroup
> loadGroup
;
798 (void) mPartChannel
->GetLoadGroup(getter_AddRefs(loadGroup
));
800 (void) loadGroup
->RemoveRequest(mPartChannel
, mContext
, aStatus
);
808 nsMultiMixedConv::SendData(char *aBuffer
, PRUint32 aLen
) {
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
;
824 PRUint32 offset
= mTotalSent
;
827 nsCOMPtr
<nsIStringInputStream
> ss(
828 do_CreateInstance("@mozilla.org/io/string-input-stream;1", &rv
));
832 rv
= ss
->ShareData(aBuffer
, aLen
);
836 nsCOMPtr
<nsIInputStream
> inStream(do_QueryInterface(ss
, &rv
));
837 if (NS_FAILED(rv
)) return rv
;
839 return mFinalListener
->OnDataAvailable(mPartChannel
, mContext
, inStream
, offset
, aLen
);
843 nsMultiMixedConv::PushOverLine(char *&aPtr
, PRUint32
&aLen
) {
845 if ((aLen
> 0) && (*aPtr
== nsCRT::CR
|| *aPtr
== nsCRT::LF
)) {
846 if ((aLen
> 1) && (aPtr
[1] == nsCRT::LF
))
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!
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;
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
;
887 char tmpChar
= *newLine
;
888 *newLine
= '\0'; // cursor is now null terminated
889 char *colon
= (char *) strchr(cursor
, ':');
892 nsCAutoString
headerStr(cursor
);
893 headerStr
.CompressWhitespace();
896 nsCAutoString
headerVal(colon
+ 1);
897 headerVal
.CompressWhitespace();
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
);
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
917 tmpPtr
= (char *) strchr(colon
+ 1, '/');
921 // pass the bytes-unit and the SP
922 char *range
= (char *) strchr(colon
+ 2, ' ');
925 return NS_ERROR_FAILURE
;
927 if (range
[0] == '*'){
928 mByteRangeStart
= mByteRangeEnd
= 0;
931 tmpPtr
= (char *) strchr(range
, '-');
933 return NS_ERROR_FAILURE
;
937 mByteRangeStart
= atoi(range
); // XXX want 64-bit conv
939 mByteRangeEnd
= atoi(tmpPtr
);
942 mIsByteRangeRequest
= PR_TRUE
;
943 if (mContentLength
== LL_MAXUINT
)
944 mContentLength
= PRUint64(PRInt64(mByteRangeEnd
- mByteRangeStart
+ nsInt64(1)));
948 newLine
+= lineFeedIncrement
;
949 cursorLen
-= (newLine
- cursor
);
961 nsMultiMixedConv::FindToken(char *aCursor
, PRUint32 aLen
) {
962 // strnstr without looking for null termination
963 const char *token
= mToken
.get();
966 if (!(token
&& aCursor
&& *token
)) {
967 NS_WARNING("bad data");
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) == '-')) {
979 // we're playing w/ double dash tokens, adjust.
980 mToken
.Assign(aCursor
, mTokenLen
+ 2);
981 mTokenLen
= mToken
.Length();
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
);