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);