~ubuntu-branches/ubuntu/precise/kompozer/precise

« back to all changes in this revision

Viewing changes to mozilla/netwerk/streamconv/converters/nsMultiMixedConv.cpp

  • Committer: Bazaar Package Importer
  • Author(s): Anthony Yarusso
  • Date: 2007-08-27 01:11:03 UTC
  • Revision ID: james.westby@ubuntu.com-20070827011103-2jgf4s6532gqu2ka
Tags: upstream-0.7.10
ImportĀ upstreamĀ versionĀ 0.7.10

Show diffs side-by-side

added added

removed removed

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