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