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