summaryrefslogtreecommitdiffstats
path: root/mailnews/local/src/nsMailboxProtocol.cpp
blob: fad77aa9336dc67802699a25a1b7a410d081568a (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

#include "msgCore.h"

#include "nsMailboxProtocol.h"
#include "nscore.h"
#include "nsIOutputStream.h"
#include "nsIInputStreamPump.h"
#include "nsIMsgDatabase.h"
#include "nsIMsgHdr.h"
#include "nsMsgLineBuffer.h"
#include "nsMsgDBCID.h"
#include "nsIMsgMailNewsUrl.h"
#include "nsICopyMsgStreamListener.h"
#include "nsMsgMessageFlags.h"
#include "prtime.h"
#include "mozilla/Logging.h"
#include "prerror.h"
#include "prprf.h"
#include "nspr.h"

PRLogModuleInfo *MAILBOX;
#include "nsIFileStreams.h"
#include "nsIStreamTransportService.h"
#include "nsIStreamConverterService.h"
#include "nsIIOService.h"
#include "nsNetUtil.h"
#include "nsMsgUtils.h"
#include "nsIMsgWindow.h"
#include "nsIMimeHeaders.h"
#include "nsIMsgPluggableStore.h"
#include "nsISeekableStream.h"

#include "nsIMsgMdnGenerator.h"

/* the output_buffer_size must be larger than the largest possible line
 * 2000 seems good for news
 *
 * jwz: I increased this to 4k since it must be big enough to hold the
 * entire button-bar HTML, and with the new "mailto" format, that can
 * contain arbitrarily long header fields like "references".
 *
 * fortezza: proxy auth is huge, buffer increased to 8k (sigh).
 */
#define OUTPUT_BUFFER_SIZE (4096*2)

nsMailboxProtocol::nsMailboxProtocol(nsIURI * aURI)
    : nsMsgProtocol(aURI)
{
  m_lineStreamBuffer =nullptr;

  // initialize the pr log if it hasn't been initialiezed already
  if (!MAILBOX)
    MAILBOX = PR_NewLogModule("MAILBOX");
}

nsMailboxProtocol::~nsMailboxProtocol()
{
  // free our local state
  delete m_lineStreamBuffer;
}

nsresult nsMailboxProtocol::OpenMultipleMsgTransport(uint64_t offset, int32_t size)
{
  nsresult rv;

  nsCOMPtr<nsIStreamTransportService> serv =
      do_GetService(NS_STREAMTRANSPORTSERVICE_CONTRACTID, &rv);
  NS_ENSURE_SUCCESS(rv, rv);

  // XXX 64-bit
  rv = serv->CreateInputTransport(m_multipleMsgMoveCopyStream, int64_t(offset),
                                  int64_t(size), false,
                                  getter_AddRefs(m_transport));

  return rv;
}

nsresult nsMailboxProtocol::Initialize(nsIURI * aURL)
{
  NS_PRECONDITION(aURL, "invalid URL passed into MAILBOX Protocol");
  nsresult rv = NS_OK;
  if (aURL)
  {
    rv = aURL->QueryInterface(NS_GET_IID(nsIMailboxUrl), (void **) getter_AddRefs(m_runningUrl));
    if (NS_SUCCEEDED(rv) && m_runningUrl)
    {
      nsCOMPtr <nsIMsgWindow> window;
      rv = m_runningUrl->GetMailboxAction(&m_mailboxAction);
      // clear stopped flag on msg window, because we care.
      nsCOMPtr <nsIMsgMailNewsUrl> mailnewsUrl = do_QueryInterface(m_runningUrl);
      if (mailnewsUrl)
      {
        mailnewsUrl->GetMsgWindow(getter_AddRefs(window));
        if (window)
          window->SetStopped(false);
      }

      if (m_mailboxAction == nsIMailboxUrl::ActionParseMailbox)
      {
        // Set the length of the file equal to the max progress
        nsCOMPtr<nsIFile> file;
        GetFileFromURL(aURL, getter_AddRefs(file));
        if (file)
        {
          int64_t fileSize = 0;
          file->GetFileSize(&fileSize);
          mailnewsUrl->SetMaxProgress(fileSize);
        }

        rv = OpenFileSocket(aURL, 0, -1 /* read in all the bytes in the file */);
      }
      else
      {
        // we need to specify a byte range to read in so we read in JUST the message we want.
        rv = SetupMessageExtraction();
        if (NS_FAILED(rv)) return rv;
        uint32_t aMsgSize = 0;
        rv = m_runningUrl->GetMessageSize(&aMsgSize);
        NS_ASSERTION(NS_SUCCEEDED(rv), "oops....i messed something up");
        SetContentLength(aMsgSize);
        mailnewsUrl->SetMaxProgress(aMsgSize);

        if (RunningMultipleMsgUrl())
        {
          // if we're running multiple msg url, we clear the event sink because the multiple
          // msg urls will handle setting the progress.
          mProgressEventSink = nullptr;
        }

        nsCOMPtr<nsIMsgMessageUrl> msgUrl = do_QueryInterface(m_runningUrl, &rv);
        if (NS_SUCCEEDED(rv))
        {
          nsCOMPtr<nsIMsgFolder> folder;
          nsCOMPtr<nsIMsgDBHdr> msgHdr;
          rv = msgUrl->GetMessageHeader(getter_AddRefs(msgHdr));
          if (NS_SUCCEEDED(rv) && msgHdr)
          {
            rv = msgHdr->GetFolder(getter_AddRefs(folder));
            if (NS_SUCCEEDED(rv) && folder)
            {
              nsCOMPtr<nsIInputStream> stream;
              int64_t offset = 0;
              bool reusable = false;

              rv = folder->GetMsgInputStream(msgHdr, &reusable, getter_AddRefs(stream));
              NS_ENSURE_SUCCESS(rv, rv);
              nsCOMPtr<nsISeekableStream> seekableStream(do_QueryInterface(stream, &rv));
              NS_ENSURE_SUCCESS(rv, rv);
              seekableStream->Tell(&offset);
              // create input stream transport
              nsCOMPtr<nsIStreamTransportService> sts =
                  do_GetService(NS_STREAMTRANSPORTSERVICE_CONTRACTID, &rv);
              if (NS_FAILED(rv)) return rv;
              m_readCount = aMsgSize;
              // Save the stream for reuse, but only for multiple URLs.
              if (reusable && RunningMultipleMsgUrl())
                m_multipleMsgMoveCopyStream = stream;
              else
                reusable = false;
              rv = sts->CreateInputTransport(stream, offset,
                                             int64_t(aMsgSize), !reusable,
                                             getter_AddRefs(m_transport));

              m_socketIsOpen = false;
            }
          }
          if (!folder) // must be a .eml file
            rv = OpenFileSocket(aURL, 0, aMsgSize);
        }
        NS_ASSERTION(NS_SUCCEEDED(rv), "oops....i messed something up");
      }
    }
  }

  m_lineStreamBuffer = new nsMsgLineStreamBuffer(OUTPUT_BUFFER_SIZE, true);

  m_nextState = MAILBOX_READ_FOLDER;
  m_initialState = MAILBOX_READ_FOLDER;
  mCurrentProgress = 0;

  // do we really need both?
  m_tempMessageFile = m_tempMsgFile;
  return rv;
}

/////////////////////////////////////////////////////////////////////////////////////////////
// we suppport the nsIStreamListener interface
////////////////////////////////////////////////////////////////////////////////////////////

NS_IMETHODIMP nsMailboxProtocol::OnStartRequest(nsIRequest *request, nsISupports *ctxt)
{
  // extract the appropriate event sinks from the url and initialize them in our protocol data
  // the URL should be queried for a nsINewsURL. If it doesn't support a news URL interface then
  // we have an error.
  if (m_nextState == MAILBOX_READ_FOLDER && m_mailboxParser)
  {
    // we need to inform our mailbox parser that it's time to start...
    m_mailboxParser->OnStartRequest(request, ctxt);
  }
  return nsMsgProtocol::OnStartRequest(request, ctxt);
}

bool nsMailboxProtocol::RunningMultipleMsgUrl()
{
  if (m_mailboxAction == nsIMailboxUrl::ActionCopyMessage || m_mailboxAction == nsIMailboxUrl::ActionMoveMessage)
  {
    uint32_t numMoveCopyMsgs;
    nsresult rv = m_runningUrl->GetNumMoveCopyMsgs(&numMoveCopyMsgs);
    if (NS_SUCCEEDED(rv) && numMoveCopyMsgs > 1)
      return true;
  }
  return false;
}

// stop binding is a "notification" informing us that the stream associated with aURL is going away.
NS_IMETHODIMP nsMailboxProtocol::OnStopRequest(nsIRequest *request, nsISupports *ctxt, nsresult aStatus)
{
  nsresult rv;
  if (m_nextState == MAILBOX_READ_FOLDER && m_mailboxParser)
  {
    // we need to inform our mailbox parser that there is no more incoming data...
    m_mailboxParser->OnStopRequest(request, ctxt, aStatus);
  }
  else if (m_nextState == MAILBOX_READ_MESSAGE)
  {
    DoneReadingMessage();
  }
  // I'm not getting cancel status - maybe the load group still has the status.
  bool stopped = false;
  if (m_runningUrl)
  {
    nsCOMPtr <nsIMsgMailNewsUrl> mailnewsUrl = do_QueryInterface(m_runningUrl);
    if (mailnewsUrl)
    {
      nsCOMPtr <nsIMsgWindow> window;
      mailnewsUrl->GetMsgWindow(getter_AddRefs(window));
      if (window)
        window->GetStopped(&stopped);
    }

    if (!stopped && NS_SUCCEEDED(aStatus) && (m_mailboxAction == nsIMailboxUrl::ActionCopyMessage || m_mailboxAction == nsIMailboxUrl::ActionMoveMessage))
    {
      uint32_t numMoveCopyMsgs;
      uint32_t curMoveCopyMsgIndex;
      rv = m_runningUrl->GetNumMoveCopyMsgs(&numMoveCopyMsgs);
      if (NS_SUCCEEDED(rv) && numMoveCopyMsgs > 0)
      {
        m_runningUrl->GetCurMoveCopyMsgIndex(&curMoveCopyMsgIndex);
        if (++curMoveCopyMsgIndex < numMoveCopyMsgs)
        {
          if (!mSuppressListenerNotifications && m_channelListener)
          {
            nsCOMPtr<nsICopyMessageStreamListener> listener = do_QueryInterface(m_channelListener, &rv);
            if (listener)
            {
              listener->EndCopy(ctxt, aStatus);
              listener->StartMessage(); // start next message.
            }
          }
          m_runningUrl->SetCurMoveCopyMsgIndex(curMoveCopyMsgIndex);
          nsCOMPtr <nsIMsgDBHdr> nextMsg;
          rv = m_runningUrl->GetMoveCopyMsgHdrForIndex(curMoveCopyMsgIndex, getter_AddRefs(nextMsg));
          if (NS_SUCCEEDED(rv) && nextMsg)
          {
            uint32_t msgSize = 0;
            nsCOMPtr <nsIMsgFolder> msgFolder;
            nextMsg->GetFolder(getter_AddRefs(msgFolder));
            NS_ASSERTION(msgFolder, "couldn't get folder for next msg in multiple msg local copy");
            if (msgFolder)
            {
              nsCString uri;
              msgFolder->GetUriForMsg(nextMsg, uri);
              nsCOMPtr<nsIMsgMessageUrl> msgUrl = do_QueryInterface(m_runningUrl);
              if (msgUrl)
              {
                msgUrl->SetOriginalSpec(uri.get());
                msgUrl->SetUri(uri.get());

                uint64_t msgOffset;
                nextMsg->GetMessageOffset(&msgOffset);
                nextMsg->GetMessageSize(&msgSize);
                // now we have to seek to the right position in the file and
                // basically re-initialize the transport with the correct message size.
                // then, we have to make sure the url keeps running somehow.
                nsCOMPtr<nsISupports> urlSupports = do_QueryInterface(m_runningUrl);
                //
                // put us in a state where we are always notified of incoming data
                //

                m_transport = nullptr; // open new stream transport
                m_inputStream = nullptr;
                m_outputStream = nullptr;

                if (m_multipleMsgMoveCopyStream)
                {
                  rv = OpenMultipleMsgTransport(msgOffset, msgSize);
                }
                else
                {
                  nsCOMPtr<nsIInputStream> stream;
                  bool reusable = false;
                  rv = msgFolder->GetMsgInputStream(nextMsg, &reusable,
                                                    getter_AddRefs(stream));
                  NS_ASSERTION(!reusable, "We thought streams were not reusable!");

                  if (NS_SUCCEEDED(rv))
                  {
                    // create input stream transport
                    nsCOMPtr<nsIStreamTransportService> sts =
                        do_GetService(NS_STREAMTRANSPORTSERVICE_CONTRACTID, &rv);

                    if (NS_SUCCEEDED(rv))
                    {
                      m_readCount = msgSize;
                      rv = sts->CreateInputTransport(stream, msgOffset,
                                                     int64_t(msgSize), true,
                                                     getter_AddRefs(m_transport));
                    }
                  }
                }

                if (NS_SUCCEEDED(rv))
                {
                  if (!m_inputStream)
                    rv = m_transport->OpenInputStream(0, 0, 0, getter_AddRefs(m_inputStream));

                  if (NS_SUCCEEDED(rv))
                  {
                    nsCOMPtr<nsIInputStreamPump> pump;
                    rv = NS_NewInputStreamPump(getter_AddRefs(pump), m_inputStream);
                    if (NS_SUCCEEDED(rv)) {
                      rv = pump->AsyncRead(this, urlSupports);
                      if (NS_SUCCEEDED(rv))
                        m_request = pump;
                    }
                  }
                }

                NS_ASSERTION(NS_SUCCEEDED(rv), "AsyncRead failed");
                if (m_loadGroup)
                  m_loadGroup->RemoveRequest(static_cast<nsIRequest *>(this), nullptr, aStatus);
                m_socketIsOpen = true; // mark the channel as open
                return aStatus;
              }
            }
          }
        }
        else
        {
        }
      }
    }
  }
  // and we want to mark ourselves for deletion or some how inform our protocol manager that we are
  // available for another url if there is one.

  // mscott --> maybe we should set our state to done because we don't run multiple urls in a mailbox
  // protocol connection....
  m_nextState = MAILBOX_DONE;

  // the following is for smoke test purposes. QA is looking at this "Mailbox Done" string which
  // is printed out to the console and determining if the mail app loaded up correctly...obviously
  // this solution is not very good so we should look at something better, but don't remove this
  // line before talking to me (mscott) and mailnews QA....

  MOZ_LOG(MAILBOX, mozilla::LogLevel::Info, ("Mailbox Done\n"));

  // when on stop binding is called, we as the protocol are done...let's close down the connection
  // releasing all of our interfaces. It's important to remember that this on stop binding call
  // is coming from netlib so they are never going to ping us again with on data available. This means
  // we'll never be going through the Process loop...

  if (m_multipleMsgMoveCopyStream)
  {
    m_multipleMsgMoveCopyStream->Close();
    m_multipleMsgMoveCopyStream = nullptr;
  }
  nsMsgProtocol::OnStopRequest(request, ctxt, aStatus);
  return CloseSocket();
}

/////////////////////////////////////////////////////////////////////////////////////////////
// End of nsIStreamListenerSupport
//////////////////////////////////////////////////////////////////////////////////////////////

nsresult nsMailboxProtocol::DoneReadingMessage()
{
  nsresult rv = NS_OK;
  // and close the article file if it was open....

  if (m_mailboxAction == nsIMailboxUrl::ActionSaveMessageToDisk && m_msgFileOutputStream)
    rv = m_msgFileOutputStream->Close();

  return rv;
}

nsresult nsMailboxProtocol::SetupMessageExtraction()
{
  // Determine the number of bytes we are going to need to read out of the
  // mailbox url....
  nsCOMPtr<nsIMsgDBHdr> msgHdr;
  nsresult rv = NS_OK;

  NS_ASSERTION(m_runningUrl, "Not running a url");
  if (m_runningUrl)
  {
    uint32_t messageSize = 0;
    m_runningUrl->GetMessageSize(&messageSize);
    if (!messageSize)
    {
      nsCOMPtr<nsIMsgMessageUrl> msgUrl = do_QueryInterface(m_runningUrl, &rv);
      NS_ENSURE_SUCCESS(rv,rv);
      rv = msgUrl->GetMessageHeader(getter_AddRefs(msgHdr));
      if (NS_SUCCEEDED(rv) && msgHdr)
      {
        msgHdr->GetMessageSize(&messageSize);
        m_runningUrl->SetMessageSize(messageSize);
        msgHdr->GetMessageOffset(&m_msgOffset);
      }
      else
        NS_ASSERTION(false, "couldn't get message header");
    }
  }
  return rv;
}

/////////////////////////////////////////////////////////////////////////////////////////////
// Begin protocol state machine functions...
//////////////////////////////////////////////////////////////////////////////////////////////

nsresult nsMailboxProtocol::LoadUrl(nsIURI * aURL, nsISupports * aConsumer)
{
  nsresult rv = NS_OK;
  // if we were already initialized with a consumer, use it...
  nsCOMPtr<nsIStreamListener> consumer = do_QueryInterface(aConsumer);
  if (consumer)
    m_channelListener = consumer;

  if (aURL)
  {
    m_runningUrl = do_QueryInterface(aURL);
    if (m_runningUrl)
    {
      // find out from the url what action we are supposed to perform...
      rv = m_runningUrl->GetMailboxAction(&m_mailboxAction);

      bool convertData = false;

      // need to check if we're fetching an rfc822 part in order to
      // quote a message.
      if (m_mailboxAction == nsIMailboxUrl::ActionFetchMessage)
      {
        nsCOMPtr<nsIMsgMailNewsUrl> msgUrl = do_QueryInterface(m_runningUrl, &rv);
        NS_ENSURE_SUCCESS(rv,rv);

        nsAutoCString queryStr;
        rv = msgUrl->GetQuery(queryStr);
        NS_ENSURE_SUCCESS(rv,rv);

        // check if this is a filter plugin requesting the message.
        // in that case, set up a text converter
        convertData = (queryStr.Find("header=filter") != -1 ||
                       queryStr.Find("header=attach") != -1);
      }
      else if (m_mailboxAction == nsIMailboxUrl::ActionFetchPart)
      {
        // when fetching a part, we need to insert a converter into the listener chain order to
        // force just the part out of the message. Our channel listener is the consumer we'll
        // pass in to AsyncConvertData.
        convertData = true;
        consumer = m_channelListener;
      }
      if (convertData)
      {
          nsCOMPtr<nsIStreamConverterService> streamConverter = do_GetService("@mozilla.org/streamConverters;1", &rv);
          NS_ENSURE_SUCCESS(rv, rv);
          nsCOMPtr <nsIStreamListener> conversionListener;
          nsCOMPtr<nsIChannel> channel;
          QueryInterface(NS_GET_IID(nsIChannel), getter_AddRefs(channel));

          rv = streamConverter->AsyncConvertData("message/rfc822",
                                                 "*/*",
                                                 consumer, channel, getter_AddRefs(m_channelListener));
      }

      if (NS_SUCCEEDED(rv))
      {
        switch (m_mailboxAction)
        {
        case nsIMailboxUrl::ActionParseMailbox:
          // extract the mailbox parser..
          rv = m_runningUrl->GetMailboxParser(getter_AddRefs(m_mailboxParser));
          m_nextState = MAILBOX_READ_FOLDER;
          break;
        case nsIMailboxUrl::ActionSaveMessageToDisk:
          // ohhh, display message already writes a msg to disk (as part of a hack)
          // so we can piggy back off of that!! We just need to change m_tempMessageFile
          // to be the name of our save message to disk file. Since save message to disk
          // urls are run without a docshell to display the msg into, we won't be trying
          // to display the message after we write it to disk...
          {
            nsCOMPtr<nsIMsgMessageUrl> messageUrl = do_QueryInterface(m_runningUrl, &rv);
            if (NS_SUCCEEDED(rv))
            {
              messageUrl->GetMessageFile(getter_AddRefs(m_tempMessageFile));
              rv = MsgNewBufferedFileOutputStream(getter_AddRefs(m_msgFileOutputStream), m_tempMessageFile, -1, 00600);
              NS_ENSURE_SUCCESS(rv, rv);

              bool addDummyEnvelope = false;
              messageUrl->GetAddDummyEnvelope(&addDummyEnvelope);
              if (addDummyEnvelope)
                SetFlag(MAILBOX_MSG_PARSE_FIRST_LINE);
              else
                ClearFlag(MAILBOX_MSG_PARSE_FIRST_LINE);
            }
          }
          m_nextState = MAILBOX_READ_MESSAGE;
          break;
        case nsIMailboxUrl::ActionCopyMessage:
        case nsIMailboxUrl::ActionMoveMessage:
        case nsIMailboxUrl::ActionFetchMessage:
          ClearFlag(MAILBOX_MSG_PARSE_FIRST_LINE);
          m_nextState = MAILBOX_READ_MESSAGE;
          break;
        case nsIMailboxUrl::ActionFetchPart:
            m_nextState = MAILBOX_READ_MESSAGE;
            break;
        default:
          break;
        }
      }

      rv = nsMsgProtocol::LoadUrl(aURL, m_channelListener);

    } // if we received an MAILBOX url...
  } // if we received a url!

  return rv;
}

int32_t nsMailboxProtocol::ReadFolderResponse(nsIInputStream * inputStream, uint64_t sourceOffset, uint32_t length)
{
  // okay we are doing a folder read in 8K chunks of a mail folder....
  // this is almost too easy....we can just forward the data in this stream on to our
  // folder parser object!!!

  nsresult rv = NS_OK;
  mCurrentProgress += length;

  if (m_mailboxParser)
  {
    nsCOMPtr <nsIURI> url = do_QueryInterface(m_runningUrl);
    rv = m_mailboxParser->OnDataAvailable(nullptr, url, inputStream, sourceOffset, length); // let the parser deal with it...
  }
  if (NS_FAILED(rv))
  {
    m_nextState = MAILBOX_ERROR_DONE; // drop out of the loop....
    return -1;
  }

  // now wait for the next 8K chunk to come in.....
  SetFlag(MAILBOX_PAUSE_FOR_READ);

  // leave our state alone so when the next chunk of the mailbox comes in we jump to this state
  // and repeat....how does this process end? Well when the file is done being read in, core net lib
  // will issue an ::OnStopRequest to us...we'll use that as our sign to drop out of this state and to
  // close the protocol instance...

  return 0;
}

int32_t nsMailboxProtocol::ReadMessageResponse(nsIInputStream * inputStream, uint64_t sourceOffset, uint32_t length)
{
  char *line = nullptr;
  uint32_t status = 0;
  nsresult rv = NS_OK;
  mCurrentProgress += length;

  // if we are doing a move or a copy, forward the data onto the copy handler...
  // if we want to display the message then parse the incoming data...

  if (m_channelListener)
  {
    // just forward the data we read in to the listener...
    rv = m_channelListener->OnDataAvailable(this, m_channelContext, inputStream, sourceOffset, length);
  }
  else
  {
    bool pauseForMoreData = false;
    bool canonicalLineEnding = false;
    nsCOMPtr<nsIMsgMessageUrl> msgurl = do_QueryInterface(m_runningUrl);

    if (msgurl)
      msgurl->GetCanonicalLineEnding(&canonicalLineEnding);

    while ((line = m_lineStreamBuffer->ReadNextLine(inputStream, status, pauseForMoreData)) &&
           !pauseForMoreData)
    {
      /* When we're sending this line to a converter (ie,
      it's a message/rfc822) use the local line termination
      convention, not CRLF.  This makes text articles get
      saved with the local line terminators.  Since SMTP
      and NNTP mandate the use of CRLF, it is expected that
      the local system will convert that to the local line
      terminator as it is read.
      */
      // mscott - the firstline hack is aimed at making sure we don't write
      // out the dummy header when we are trying to display the message.
      // The dummy header is the From line with the date tag on it.
      if (m_msgFileOutputStream && TestFlag(MAILBOX_MSG_PARSE_FIRST_LINE))
      {
        uint32_t count = 0;
        rv = m_msgFileOutputStream->Write(line, PL_strlen(line), &count);
        if (NS_FAILED(rv))
          break;

        if (canonicalLineEnding)
          rv = m_msgFileOutputStream->Write(CRLF, 2, &count);
        else
          rv = m_msgFileOutputStream->Write(MSG_LINEBREAK,
                                            MSG_LINEBREAK_LEN, &count);

        if (NS_FAILED(rv))
          break;
      }
      else
        SetFlag(MAILBOX_MSG_PARSE_FIRST_LINE);
      PR_Free(line);
    }
    PR_Free(line);
  }

  SetFlag(MAILBOX_PAUSE_FOR_READ); // wait for more data to become available...
  if (mProgressEventSink && m_runningUrl)
  {
    int64_t maxProgress;
    nsCOMPtr<nsIMsgMailNewsUrl> mailnewsUrl(do_QueryInterface(m_runningUrl));
    mailnewsUrl->GetMaxProgress(&maxProgress);
    mProgressEventSink->OnProgress(this, m_channelContext,
                                   mCurrentProgress,
                                   maxProgress);
  }

  if (NS_FAILED(rv)) return -1;

  return 0;
}


/*
 * returns negative if the transfer is finished or error'd out
 *
 * returns zero or more if the transfer needs to be continued.
 */
nsresult nsMailboxProtocol::ProcessProtocolState(nsIURI * url, nsIInputStream * inputStream, uint64_t offset, uint32_t length)
{
  nsresult rv = NS_OK;
  int32_t status = 0;
  ClearFlag(MAILBOX_PAUSE_FOR_READ); /* already paused; reset */

  while(!TestFlag(MAILBOX_PAUSE_FOR_READ))
  {

    switch(m_nextState)
    {
    case MAILBOX_READ_MESSAGE:
      if (inputStream == nullptr)
        SetFlag(MAILBOX_PAUSE_FOR_READ);
      else
        status = ReadMessageResponse(inputStream, offset, length);
      break;
    case MAILBOX_READ_FOLDER:
      if (inputStream == nullptr)
        SetFlag(MAILBOX_PAUSE_FOR_READ);   // wait for file socket to read in the next chunk...
      else
        status = ReadFolderResponse(inputStream, offset, length);
      break;
    case MAILBOX_DONE:
    case MAILBOX_ERROR_DONE:
      {
        nsCOMPtr <nsIMsgMailNewsUrl> anotherUrl = do_QueryInterface(m_runningUrl);
        rv = m_nextState == MAILBOX_DONE ? NS_OK : NS_ERROR_FAILURE;
        anotherUrl->SetUrlState(false, rv);
        m_nextState = MAILBOX_FREE;
      }
      break;

    case MAILBOX_FREE:
      // MAILBOX is a one time use connection so kill it if we get here...
      CloseSocket();
      return rv; /* final end */

    default: /* should never happen !!! */
      m_nextState = MAILBOX_ERROR_DONE;
      break;
    }

    /* check for errors during load and call error
    * state if found
    */
    if(status < 0 && m_nextState != MAILBOX_FREE)
    {
      m_nextState = MAILBOX_ERROR_DONE;
      /* don't exit! loop around again and do the free case */
      ClearFlag(MAILBOX_PAUSE_FOR_READ);
    }
  } /* while(!MAILBOX_PAUSE_FOR_READ) */

  return rv;
}

nsresult nsMailboxProtocol::CloseSocket()
{
  // how do you force a release when closing the connection??
  nsMsgProtocol::CloseSocket();
  m_runningUrl = nullptr;
  m_mailboxParser = nullptr;
  return NS_OK;
}

// vim: ts=2 sw=2