Back to index

lightning-sunbird  0.9+nobinonly
nsMailboxProtocol.cpp
Go to the documentation of this file.
00001 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
00002 /* ***** BEGIN LICENSE BLOCK *****
00003  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
00004  *
00005  * The contents of this file are subject to the Mozilla Public License Version
00006  * 1.1 (the "License"); you may not use this file except in compliance with
00007  * the License. You may obtain a copy of the License at
00008  * http://www.mozilla.org/MPL/
00009  *
00010  * Software distributed under the License is distributed on an "AS IS" basis,
00011  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
00012  * for the specific language governing rights and limitations under the
00013  * License.
00014  *
00015  * The Original Code is mozilla.org code.
00016  *
00017  * The Initial Developer of the Original Code is
00018  * Netscape Communications Corporation.
00019  * Portions created by the Initial Developer are Copyright (C) 1998
00020  * the Initial Developer. All Rights Reserved.
00021  *
00022  * Contributor(s):
00023  *   Pierre Phaneuf <pp@ludusdesign.com>
00024  *
00025  * Alternatively, the contents of this file may be used under the terms of
00026  * either of the GNU General Public License Version 2 or later (the "GPL"),
00027  * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
00028  * in which case the provisions of the GPL or the LGPL are applicable instead
00029  * of those above. If you wish to allow use of your version of this file only
00030  * under the terms of either the GPL or the LGPL, and not to allow others to
00031  * use your version of this file under the terms of the MPL, indicate your
00032  * decision by deleting the provisions above and replace them with the notice
00033  * and other provisions required by the GPL or the LGPL. If you do not delete
00034  * the provisions above, a recipient may use your version of this file under
00035  * the terms of any one of the MPL, the GPL or the LGPL.
00036  *
00037  * ***** END LICENSE BLOCK ***** */
00038 
00039 #ifdef MOZ_LOGGING
00040 // sorry, this has to be before the pre-compiled header
00041 #define FORCE_PR_LOG /* Allow logging in the release build */
00042 #endif
00043 
00044 #include "msgCore.h"
00045 
00046 #include "nsMailboxProtocol.h"
00047 #include "nscore.h"
00048 #include "nsIOutputStream.h"
00049 #include "nsIMsgDatabase.h"
00050 #include "nsIMsgHdr.h"
00051 #include "nsMsgLineBuffer.h"
00052 #include "nsMsgDBCID.h"
00053 #include "nsIMsgMailNewsUrl.h"
00054 #include "nsICopyMsgStreamListener.h"
00055 #include "nsMsgMessageFlags.h"
00056 #include "prtime.h"
00057 #include "prlog.h"
00058 #include "prerror.h"
00059 #include "prprf.h"
00060 #include "nspr.h"
00061 
00062 #include "nsFileStream.h"
00063 PRLogModuleInfo *MAILBOX;
00064 #include "nsIFileStreams.h"
00065 #include "nsIStreamTransportService.h"
00066 #include "nsIStreamConverterService.h"
00067 #include "nsIIOService.h"
00068 #include "nsXPIDLString.h"
00069 #include "nsNetUtil.h"
00070 #include "nsIMsgWindow.h"
00071 #include "nsIMimeHeaders.h"
00072 
00073 #include "nsIMsgMdnGenerator.h"
00074 
00075 /* the output_buffer_size must be larger than the largest possible line
00076  * 2000 seems good for news
00077  *
00078  * jwz: I increased this to 4k since it must be big enough to hold the
00079  * entire button-bar HTML, and with the new "mailto" format, that can
00080  * contain arbitrarily long header fields like "references".
00081  *
00082  * fortezza: proxy auth is huge, buffer increased to 8k (sigh).
00083  */
00084 #define OUTPUT_BUFFER_SIZE (4096*2)
00085 
00086 nsMailboxProtocol::nsMailboxProtocol(nsIURI * aURI)
00087     : nsMsgProtocol(aURI)
00088 {
00089   m_lineStreamBuffer =nsnull;
00090 
00091   // initialize the pr log if it hasn't been initialiezed already
00092   if (!MAILBOX)
00093     MAILBOX = PR_NewLogModule("MAILBOX");
00094 }
00095 
00096 nsMailboxProtocol::~nsMailboxProtocol()
00097 {
00098   // free our local state 
00099   delete m_lineStreamBuffer;
00100 }
00101 
00102 NS_IMETHODIMP nsMailboxProtocol::GetContentLength(PRInt32 * aContentLength)
00103 {
00104   *aContentLength = -1;
00105   if (m_mailboxAction == nsIMailboxUrl::ActionParseMailbox)
00106   {
00107     // our file transport knows the entire length of the berkley mail folder
00108     // so get it from there.
00109     if (!m_request)
00110       return NS_OK;
00111 
00112     nsCOMPtr<nsIChannel> info = do_QueryInterface(m_request);
00113     if (info) info->GetContentLength(aContentLength);
00114     return NS_OK;
00115 
00116   }
00117   else if (m_runningUrl)
00118   {
00119     PRUint32 msgSize = 0;
00120     m_runningUrl->GetMessageSize(&msgSize);
00121     *aContentLength = (PRInt32) msgSize;
00122   }
00123 
00124   return NS_OK;
00125 }
00126 
00127 nsresult nsMailboxProtocol::OpenMultipleMsgTransport(PRUint32 offset, PRInt32 size)
00128 {
00129   nsresult rv;
00130 
00131   NS_DEFINE_CID(kStreamTransportServiceCID, NS_STREAMTRANSPORTSERVICE_CID);
00132   nsCOMPtr<nsIStreamTransportService> serv =
00133       do_GetService(kStreamTransportServiceCID, &rv);
00134   NS_ENSURE_SUCCESS(rv, rv);
00135 
00136   // XXX 64-bit
00137   rv = serv->CreateInputTransport(m_multipleMsgMoveCopyStream, nsInt64(offset),
00138                                   nsInt64(size), PR_FALSE,
00139                                   getter_AddRefs(m_transport));
00140 
00141   return rv;
00142 }
00143 
00144 nsresult nsMailboxProtocol::OpenFileSocketForReuse(nsIURI * aURL, PRUint32 aStartPosition, PRInt32 aReadCount)
00145 {
00146   NS_ENSURE_ARG_POINTER(aURL);
00147 
00148   nsresult rv = NS_OK;
00149   m_readCount = aReadCount;
00150 
00151   nsCOMPtr <nsIFile> file;
00152 
00153   rv = GetFileFromURL(aURL, getter_AddRefs(file));
00154   NS_ENSURE_SUCCESS(rv, rv);
00155     
00156   nsCOMPtr<nsIFileInputStream> fileStream = do_CreateInstance(NS_LOCALFILEINPUTSTREAM_CONTRACTID, &rv);
00157   NS_ENSURE_SUCCESS(rv, rv);
00158 
00159   m_multipleMsgMoveCopyStream = do_QueryInterface(fileStream, &rv);
00160   NS_ENSURE_SUCCESS(rv, rv);
00161   fileStream->Init(file,  PR_RDONLY, 0664, PR_FALSE);  //just have to read the messages
00162 
00163   rv = OpenMultipleMsgTransport(aStartPosition, aReadCount);
00164 
00165   m_socketIsOpen = PR_FALSE;
00166   return rv;
00167 }
00168 
00169 
00170 nsresult nsMailboxProtocol::Initialize(nsIURI * aURL)
00171 {
00172   NS_PRECONDITION(aURL, "invalid URL passed into MAILBOX Protocol");
00173   nsresult rv = NS_OK;
00174   if (aURL)
00175   {
00176     rv = aURL->QueryInterface(NS_GET_IID(nsIMailboxUrl), (void **) getter_AddRefs(m_runningUrl));
00177     if (NS_SUCCEEDED(rv) && m_runningUrl)
00178     {
00179       nsCOMPtr <nsIMsgWindow> window;
00180       rv = m_runningUrl->GetMailboxAction(&m_mailboxAction); 
00181       // clear stopped flag on msg window, because we care.
00182       nsCOMPtr <nsIMsgMailNewsUrl> mailnewsUrl = do_QueryInterface(m_runningUrl);
00183       if (mailnewsUrl)
00184       {
00185         mailnewsUrl->GetMsgWindow(getter_AddRefs(window));
00186         if (window)
00187           window->SetStopped(PR_FALSE);
00188       }
00189       if (m_mailboxAction == nsIMailboxUrl::ActionParseMailbox)
00190         rv = OpenFileSocket(aURL, 0, -1 /* read in all the bytes in the file */);
00191       else
00192       {
00193         // we need to specify a byte range to read in so we read in JUST the message we want.
00194         rv = SetupMessageExtraction();
00195         if (NS_FAILED(rv)) return rv;
00196         nsMsgKey aMsgKey;
00197         PRUint32 aMsgSize = 0;
00198         rv = m_runningUrl->GetMessageKey(&aMsgKey);
00199         NS_ASSERTION(NS_SUCCEEDED(rv), "oops....i messed something up");
00200         rv = m_runningUrl->GetMessageSize(&aMsgSize);
00201         NS_ASSERTION(NS_SUCCEEDED(rv), "oops....i messed something up");
00202         if (RunningMultipleMsgUrl())
00203         {
00204           rv = OpenFileSocketForReuse(aURL, (PRUint32) aMsgKey, aMsgSize);
00205           // if we're running multiple msg url, we clear the event sink because the multiple
00206           // msg urls will handle setting the progress.
00207           mProgressEventSink = nsnull;
00208         }
00209         else
00210           rv = OpenFileSocket(aURL, (PRUint32) aMsgKey, aMsgSize);
00211         NS_ASSERTION(NS_SUCCEEDED(rv), "oops....i messed something up");
00212       }
00213     }
00214   }
00215   
00216 #if defined(XP_MAC)
00217   m_lineStreamBuffer = new nsMsgLineStreamBuffer(OUTPUT_BUFFER_SIZE, PR_TRUE, PR_TRUE, '\r');
00218 #else
00219   m_lineStreamBuffer = new nsMsgLineStreamBuffer(OUTPUT_BUFFER_SIZE, PR_TRUE);
00220 #endif
00221   
00222   m_nextState = MAILBOX_READ_FOLDER;
00223   m_initialState = MAILBOX_READ_FOLDER;
00224   mCurrentProgress = 0;
00225   
00226   NS_NewFileSpecWithSpec(m_tempMsgFileSpec, getter_AddRefs(m_tempMessageFile));
00227   return rv;
00228 }
00229 
00231 // we suppport the nsIStreamListener interface 
00233 
00234 NS_IMETHODIMP nsMailboxProtocol::OnStartRequest(nsIRequest *request, nsISupports *ctxt)
00235 {
00236   // extract the appropriate event sinks from the url and initialize them in our protocol data
00237   // the URL should be queried for a nsINewsURL. If it doesn't support a news URL interface then
00238   // we have an error.
00239   if (m_nextState == MAILBOX_READ_FOLDER && m_mailboxParser)
00240   {
00241     // we need to inform our mailbox parser that it's time to start...
00242     m_mailboxParser->OnStartRequest(request, ctxt);
00243   }
00244   return nsMsgProtocol::OnStartRequest(request, ctxt);
00245 }
00246 
00247 PRBool nsMailboxProtocol::RunningMultipleMsgUrl()
00248 {
00249   if (m_mailboxAction == nsIMailboxUrl::ActionCopyMessage || m_mailboxAction == nsIMailboxUrl::ActionMoveMessage)
00250   {
00251     PRUint32 numMoveCopyMsgs;
00252     nsresult rv = m_runningUrl->GetNumMoveCopyMsgs(&numMoveCopyMsgs);
00253     if (NS_SUCCEEDED(rv) && numMoveCopyMsgs > 1)
00254       return PR_TRUE;
00255   }
00256   return PR_FALSE;
00257 }
00258 
00259 // stop binding is a "notification" informing us that the stream associated with aURL is going away. 
00260 NS_IMETHODIMP nsMailboxProtocol::OnStopRequest(nsIRequest *request, nsISupports *ctxt, nsresult aStatus)
00261 {
00262   nsresult rv;
00263   if (m_nextState == MAILBOX_READ_FOLDER && m_mailboxParser)
00264   {
00265     // we need to inform our mailbox parser that there is no more incoming data...
00266     m_mailboxParser->OnStopRequest(request, ctxt, aStatus);
00267   }
00268   else if (m_nextState == MAILBOX_READ_MESSAGE) 
00269   {
00270     DoneReadingMessage();
00271   }
00272   // I'm not getting cancel status - maybe the load group still has the status.
00273   PRBool stopped = PR_FALSE;
00274   if (m_runningUrl)
00275   {
00276     nsCOMPtr <nsIMsgMailNewsUrl> mailnewsUrl = do_QueryInterface(m_runningUrl);
00277     if (mailnewsUrl)
00278     {
00279       nsCOMPtr <nsIMsgWindow> window;
00280       mailnewsUrl->GetMsgWindow(getter_AddRefs(window));
00281       if (window)
00282         window->GetStopped(&stopped);
00283     }
00284     
00285     if (!stopped && NS_SUCCEEDED(aStatus) && (m_mailboxAction == nsIMailboxUrl::ActionCopyMessage || m_mailboxAction == nsIMailboxUrl::ActionMoveMessage))
00286     {
00287       PRUint32 numMoveCopyMsgs;
00288       PRUint32 curMoveCopyMsgIndex;
00289       rv = m_runningUrl->GetNumMoveCopyMsgs(&numMoveCopyMsgs);
00290       if (NS_SUCCEEDED(rv) && numMoveCopyMsgs > 0)
00291       {
00292         m_runningUrl->GetCurMoveCopyMsgIndex(&curMoveCopyMsgIndex);
00293         if (++curMoveCopyMsgIndex < numMoveCopyMsgs)
00294         {
00295           if (!mSuppressListenerNotifications && m_channelListener)
00296           {
00297                   nsCOMPtr<nsICopyMessageStreamListener> listener = do_QueryInterface(m_channelListener, &rv);
00298             if (listener)
00299             {
00300               listener->EndCopy(ctxt, aStatus);
00301               listener->StartMessage(); // start next message.
00302             }
00303           }
00304           m_runningUrl->SetCurMoveCopyMsgIndex(curMoveCopyMsgIndex);
00305           nsCOMPtr <nsIMsgDBHdr> nextMsg;
00306           rv = m_runningUrl->GetMoveCopyMsgHdrForIndex(curMoveCopyMsgIndex, getter_AddRefs(nextMsg));
00307           if (NS_SUCCEEDED(rv) && nextMsg)
00308           {
00309             PRUint32 msgSize = 0;
00310             nsMsgKey msgKey;
00311             nsCOMPtr <nsIMsgFolder> msgFolder;
00312             nextMsg->GetFolder(getter_AddRefs(msgFolder));
00313             if (msgFolder)
00314             {
00315               nsXPIDLCString uri;
00316               msgFolder->GetUriForMsg(nextMsg, getter_Copies(uri));
00317               nsCOMPtr<nsIMsgMessageUrl> msgUrl = do_QueryInterface(m_runningUrl);
00318               if (msgUrl)
00319               {
00320                 msgUrl->SetOriginalSpec(uri);
00321                 msgUrl->SetUri(uri);
00322                 
00323                 nextMsg->GetMessageKey(&msgKey);
00324                 nextMsg->GetMessageSize(&msgSize);
00325                 // now we have to seek to the right position in the file and
00326                 // basically re-initialize the transport with the correct message size.
00327                 // then, we have to make sure the url keeps running somehow.
00328                 nsCOMPtr<nsISupports> urlSupports = do_QueryInterface(m_runningUrl);
00329                 //
00330                 // put us in a state where we are always notified of incoming data
00331                 //
00332                 
00333                 m_transport = 0; // open new stream transport
00334                 m_inputStream = 0;
00335                 m_outputStream = 0;
00336                 
00337                 rv = OpenMultipleMsgTransport(msgKey, msgSize);
00338                 if (NS_SUCCEEDED(rv))
00339                 {
00340                   if (!m_inputStream)
00341                     rv = m_transport->OpenInputStream(0, 0, 0, getter_AddRefs(m_inputStream));
00342                   
00343                   if (NS_SUCCEEDED(rv))
00344                   {
00345                     nsCOMPtr<nsIInputStreamPump> pump;
00346                     rv = NS_NewInputStreamPump(getter_AddRefs(pump), m_inputStream);
00347                     if (NS_SUCCEEDED(rv)) {
00348                       rv = pump->AsyncRead(this, urlSupports);
00349                       if (NS_SUCCEEDED(rv))
00350                         m_request = pump;
00351                     }
00352                   }
00353                 }
00354                 
00355                 NS_ASSERTION(NS_SUCCEEDED(rv), "AsyncRead failed");
00356                 if (m_loadGroup)
00357                   m_loadGroup->RemoveRequest(NS_STATIC_CAST(nsIRequest *, this), nsnull, aStatus);
00358                 m_socketIsOpen = PR_TRUE; // mark the channel as open
00359                 return aStatus;
00360               }
00361             }
00362           }
00363         }
00364         else
00365         {
00366         }
00367       }
00368     }
00369   }
00370   // and we want to mark ourselves for deletion or some how inform our protocol manager that we are 
00371   // available for another url if there is one.
00372   
00373   // mscott --> maybe we should set our state to done because we don't run multiple urls in a mailbox
00374   // protocol connection....
00375   m_nextState = MAILBOX_DONE;
00376   
00377   // the following is for smoke test purposes. QA is looking at this "Mailbox Done" string which
00378   // is printed out to the console and determining if the mail app loaded up correctly...obviously
00379   // this solution is not very good so we should look at something better, but don't remove this
00380   // line before talking to me (mscott) and mailnews QA....
00381   
00382   PR_LOG(MAILBOX, PR_LOG_ALWAYS, ("Mailbox Done\n"));
00383   
00384   // when on stop binding is called, we as the protocol are done...let's close down the connection
00385   // releasing all of our interfaces. It's important to remember that this on stop binding call
00386   // is coming from netlib so they are never going to ping us again with on data available. This means
00387   // we'll never be going through the Process loop...
00388   
00389   if (m_multipleMsgMoveCopyStream)
00390   {
00391     m_multipleMsgMoveCopyStream->Close();
00392     m_multipleMsgMoveCopyStream = nsnull;
00393   }
00394   nsMsgProtocol::OnStopRequest(request, ctxt, aStatus);
00395   return CloseSocket(); 
00396 }
00397 
00399 // End of nsIStreamListenerSupport
00401 
00402 PRInt32 nsMailboxProtocol::DoneReadingMessage()
00403 {
00404   nsresult rv = NS_OK;
00405   // and close the article file if it was open....
00406   
00407   if (m_mailboxAction == nsIMailboxUrl::ActionSaveMessageToDisk && m_tempMessageFile)
00408     rv = m_tempMessageFile->CloseStream();
00409   
00410   return rv;
00411 }
00412 
00413 PRInt32 nsMailboxProtocol::SetupMessageExtraction()
00414 {
00415   // Determine the number of bytes we are going to need to read out of the 
00416   // mailbox url....
00417   nsCOMPtr<nsIMsgDBHdr> msgHdr;
00418   nsresult rv = NS_OK;
00419   
00420   NS_ASSERTION(m_runningUrl, "Not running a url");
00421   if (m_runningUrl)
00422   {
00423     PRUint32 messageSize = 0;
00424     m_runningUrl->GetMessageSize(&messageSize);
00425     if (!messageSize)
00426     {
00427       nsCOMPtr<nsIMsgMessageUrl> msgUrl = do_QueryInterface(m_runningUrl, &rv);
00428       NS_ENSURE_SUCCESS(rv,rv);
00429       rv = msgUrl->GetMessageHeader(getter_AddRefs(msgHdr));
00430       if (NS_SUCCEEDED(rv) && msgHdr)
00431       {
00432         msgHdr->GetMessageSize(&messageSize);
00433         m_runningUrl->SetMessageSize(messageSize);
00434       }
00435       else
00436         NS_ASSERTION(PR_FALSE, "couldn't get message header");
00437     }
00438   }
00439   return rv;
00440 }
00441 
00443 // Begin protocol state machine functions...
00445 
00446 nsresult nsMailboxProtocol::LoadUrl(nsIURI * aURL, nsISupports * aConsumer)
00447 {
00448   nsresult rv = NS_OK;
00449   // if we were already initialized with a consumer, use it...
00450   nsCOMPtr<nsIStreamListener> consumer = do_QueryInterface(aConsumer);
00451   if (consumer)
00452     m_channelListener = consumer;
00453   
00454   if (aURL)
00455   {
00456     m_runningUrl = do_QueryInterface(aURL);
00457     if (m_runningUrl)
00458     {
00459       // find out from the url what action we are supposed to perform...
00460       rv = m_runningUrl->GetMailboxAction(&m_mailboxAction);
00461       
00462       PRBool convertData = PR_FALSE;
00463 
00464       // need to check if we're fetching an rfc822 part in order to
00465       // quote a message.
00466       if (m_mailboxAction == nsIMailboxUrl::ActionFetchMessage)
00467       {
00468         nsCOMPtr<nsIMsgMailNewsUrl> msgUrl = do_QueryInterface(m_runningUrl, &rv);
00469         NS_ENSURE_SUCCESS(rv,rv);
00470 
00471         nsCAutoString queryStr;
00472         rv = msgUrl->GetQuery(queryStr);
00473         NS_ENSURE_SUCCESS(rv,rv);
00474 
00475         // check if this is a filter plugin requesting the message.
00476         // in that case, set up a text converter
00477         convertData = (queryStr.Find("header=filter") != kNotFound || queryStr.Find("header=attach") != kNotFound);
00478       }
00479       else if (m_mailboxAction == nsIMailboxUrl::ActionFetchPart)
00480       {
00481         // when fetching a part, we need to insert a converter into the listener chain order to
00482         // force just the part out of the message. Our channel listener is the consumer we'll
00483         // pass in to AsyncConvertData.
00484         convertData = PR_TRUE;
00485         consumer = m_channelListener;
00486       }
00487       if (convertData)
00488       {
00489           nsCOMPtr<nsIStreamConverterService> streamConverter = do_GetService("@mozilla.org/streamConverters;1", &rv);
00490           NS_ENSURE_SUCCESS(rv, rv);
00491           nsCOMPtr <nsIStreamListener> conversionListener;
00492           nsCOMPtr<nsIChannel> channel;
00493           QueryInterface(NS_GET_IID(nsIChannel), getter_AddRefs(channel));
00494 
00495           rv = streamConverter->AsyncConvertData("message/rfc822",
00496                                                  "*/*",
00497                                                  consumer, channel, getter_AddRefs(m_channelListener));
00498       }
00499       
00500       if (NS_SUCCEEDED(rv))
00501       {
00502         switch (m_mailboxAction)
00503         {
00504         case nsIMailboxUrl::ActionParseMailbox:
00505           // extract the mailbox parser..
00506           rv = m_runningUrl->GetMailboxParser(getter_AddRefs(m_mailboxParser));
00507           m_nextState = MAILBOX_READ_FOLDER;
00508           break;
00509         case nsIMailboxUrl::ActionSaveMessageToDisk:
00510           // ohhh, display message already writes a msg to disk (as part of a hack)
00511           // so we can piggy back off of that!! We just need to change m_tempMessageFile
00512           // to be the name of our save message to disk file. Since save message to disk
00513           // urls are run without a docshell to display the msg into, we won't be trying
00514           // to display the message after we write it to disk...
00515           {
00516             nsCOMPtr<nsIMsgMessageUrl> msgUri = do_QueryInterface(m_runningUrl);
00517             msgUri->GetMessageFile(getter_AddRefs(m_tempMessageFile));
00518             m_tempMessageFile->OpenStreamForWriting();
00519           }
00520         case nsIMailboxUrl::ActionCopyMessage:
00521         case nsIMailboxUrl::ActionMoveMessage:
00522         case nsIMailboxUrl::ActionFetchMessage:
00523           if (m_mailboxAction == nsIMailboxUrl::ActionSaveMessageToDisk) 
00524           {
00525             nsCOMPtr<nsIMsgMessageUrl> messageUrl = do_QueryInterface(aURL, &rv);
00526             if (NS_SUCCEEDED(rv))
00527             {
00528               PRBool addDummyEnvelope = PR_FALSE;
00529               messageUrl->GetAddDummyEnvelope(&addDummyEnvelope);
00530               if (addDummyEnvelope)
00531                 SetFlag(MAILBOX_MSG_PARSE_FIRST_LINE);
00532               else
00533                 ClearFlag(MAILBOX_MSG_PARSE_FIRST_LINE);
00534             }
00535           }
00536           else
00537           {
00538             ClearFlag(MAILBOX_MSG_PARSE_FIRST_LINE);
00539           }
00540           
00541           m_nextState = MAILBOX_READ_MESSAGE;
00542           break;
00543         case nsIMailboxUrl::ActionFetchPart:
00544             m_nextState = MAILBOX_READ_MESSAGE;
00545             break;
00546         default:
00547           break;
00548         }
00549       }
00550       
00551       rv = nsMsgProtocol::LoadUrl(aURL, m_channelListener);
00552       
00553     } // if we received an MAILBOX url...
00554   } // if we received a url!
00555   
00556   return rv;
00557 }
00558 
00559 PRInt32 nsMailboxProtocol::ReadFolderResponse(nsIInputStream * inputStream, PRUint32 sourceOffset, PRUint32 length)
00560 {
00561   // okay we are doing a folder read in 8K chunks of a mail folder....
00562   // this is almost too easy....we can just forward the data in this stream on to our
00563   // folder parser object!!!
00564   
00565   nsresult rv = NS_OK;
00566   mCurrentProgress += length;
00567   
00568   if (m_mailboxParser)
00569   {
00570     nsCOMPtr <nsIURI> url = do_QueryInterface(m_runningUrl);
00571     rv = m_mailboxParser->OnDataAvailable(nsnull, url, inputStream, sourceOffset, length); // let the parser deal with it...
00572   }
00573   if (NS_FAILED(rv))
00574   {
00575     m_nextState = MAILBOX_ERROR_DONE; // drop out of the loop....
00576     return -1;
00577   }
00578   
00579   // now wait for the next 8K chunk to come in.....
00580   SetFlag(MAILBOX_PAUSE_FOR_READ);
00581   
00582   // leave our state alone so when the next chunk of the mailbox comes in we jump to this state
00583   // and repeat....how does this process end? Well when the file is done being read in, core net lib
00584   // will issue an ::OnStopRequest to us...we'll use that as our sign to drop out of this state and to
00585   // close the protocol instance...
00586   
00587   return 0; 
00588 }
00589 
00590 PRInt32 nsMailboxProtocol::ReadMessageResponse(nsIInputStream * inputStream, PRUint32 sourceOffset, PRUint32 length)
00591 {
00592   char *line = nsnull;
00593   PRUint32 status = 0;
00594   nsresult rv = NS_OK;
00595   mCurrentProgress += length;
00596   
00597   // if we are doing a move or a copy, forward the data onto the copy handler...
00598   // if we want to display the message then parse the incoming data...
00599   
00600   if (m_channelListener)
00601   {
00602     // just forward the data we read in to the listener...
00603     rv = m_channelListener->OnDataAvailable(this, m_channelContext, inputStream, sourceOffset, length);
00604   }
00605   else
00606   {
00607     PRBool pauseForMoreData = PR_FALSE;
00608     PRBool canonicalLineEnding = PR_FALSE;
00609     nsCOMPtr<nsIMsgMessageUrl> msgurl = do_QueryInterface(m_runningUrl);
00610     
00611     if (msgurl)
00612       msgurl->GetCanonicalLineEnding(&canonicalLineEnding);
00613     do
00614     {
00615       char *saveLine;
00616       saveLine = line = m_lineStreamBuffer->ReadNextLine(inputStream, status, pauseForMoreData);
00617       
00618       if (!line || (line[0] == '.' && line[1] == 0))
00619       {
00620         // we reached the end of the message!
00621         ClearFlag(MAILBOX_PAUSE_FOR_READ);
00622       } // otherwise process the line
00623       else
00624       {
00625         if (line[0] == '.')
00626           line++; // skip over the '.'
00627         
00628         /* When we're sending this line to a converter (ie,
00629         it's a message/rfc822) use the local line termination
00630         convention, not CRLF.  This makes text articles get
00631         saved with the local line terminators.  Since SMTP
00632         and NNTP mandate the use of CRLF, it is expected that
00633         the local system will convert that to the local line
00634         terminator as it is read.
00635         */
00636         // mscott - the firstline hack is aimed at making sure we don't write
00637         // out the dummy header when we are trying to display the message.
00638         // The dummy header is the From line with the date tag on it.
00639         if (m_tempMessageFile && TestFlag(MAILBOX_MSG_PARSE_FIRST_LINE))
00640         {
00641           PRInt32 count = 0;
00642           if (line)
00643             rv = m_tempMessageFile->Write(line, PL_strlen(line),
00644             &count);
00645           if (NS_FAILED(rv)) break;
00646           
00647           if (canonicalLineEnding)
00648             rv = m_tempMessageFile->Write(CRLF, 2, &count);
00649           else
00650             rv = m_tempMessageFile->Write(MSG_LINEBREAK,
00651             MSG_LINEBREAK_LEN, &count);
00652           
00653           if (NS_FAILED(rv)) break;
00654         }
00655         else
00656           SetFlag(MAILBOX_MSG_PARSE_FIRST_LINE);
00657       } 
00658       PR_Free(saveLine);
00659     }
00660     while (line && !pauseForMoreData);
00661   }
00662   
00663   SetFlag(MAILBOX_PAUSE_FOR_READ); // wait for more data to become available...
00664   if (mProgressEventSink)
00665   {
00666     PRInt32 contentLength = 0;
00667     GetContentLength(&contentLength);
00668     // XXX 64-bit
00669     mProgressEventSink->OnProgress(this, m_channelContext,
00670                                    nsUint64(mCurrentProgress),
00671                                    nsUint64(contentLength));
00672   }
00673   
00674   if (NS_FAILED(rv)) return -1;
00675   
00676   return 0;
00677 }
00678 
00679 
00680 /*
00681  * returns negative if the transfer is finished or error'd out
00682  *
00683  * returns zero or more if the transfer needs to be continued.
00684  */
00685 nsresult nsMailboxProtocol::ProcessProtocolState(nsIURI * url, nsIInputStream * inputStream, PRUint32 offset, PRUint32 length)
00686 {
00687   nsresult rv = NS_OK;
00688   PRInt32 status = 0;
00689   ClearFlag(MAILBOX_PAUSE_FOR_READ); /* already paused; reset */
00690   
00691   while(!TestFlag(MAILBOX_PAUSE_FOR_READ))
00692   {
00693     
00694     switch(m_nextState) 
00695     {
00696     case MAILBOX_READ_MESSAGE:
00697       if (inputStream == nsnull)
00698         SetFlag(MAILBOX_PAUSE_FOR_READ);
00699       else
00700         status = ReadMessageResponse(inputStream, offset, length);
00701       break;
00702     case MAILBOX_READ_FOLDER:
00703       if (inputStream == nsnull)
00704         SetFlag(MAILBOX_PAUSE_FOR_READ);   // wait for file socket to read in the next chunk...
00705       else
00706         status = ReadFolderResponse(inputStream, offset, length);
00707       break;
00708     case MAILBOX_DONE:
00709     case MAILBOX_ERROR_DONE:
00710       {
00711         nsCOMPtr <nsIMsgMailNewsUrl> anotherUrl = do_QueryInterface(m_runningUrl);
00712         rv = m_nextState == MAILBOX_DONE ? NS_OK : NS_ERROR_FAILURE;
00713         anotherUrl->SetUrlState(PR_FALSE, rv);
00714         m_nextState = MAILBOX_FREE;
00715       }
00716       break;
00717       
00718     case MAILBOX_FREE:
00719       // MAILBOX is a one time use connection so kill it if we get here...
00720       CloseSocket(); 
00721       return rv; /* final end */
00722       
00723     default: /* should never happen !!! */
00724       m_nextState = MAILBOX_ERROR_DONE;
00725       break;
00726     }
00727     
00728     /* check for errors during load and call error 
00729     * state if found
00730     */
00731     if(status < 0 && m_nextState != MAILBOX_FREE)
00732     {
00733       m_nextState = MAILBOX_ERROR_DONE;
00734       /* don't exit! loop around again and do the free case */
00735       ClearFlag(MAILBOX_PAUSE_FOR_READ);
00736     }
00737   } /* while(!MAILBOX_PAUSE_FOR_READ) */
00738   
00739   return rv;
00740 }
00741 
00742 nsresult nsMailboxProtocol::CloseSocket()
00743 {
00744   // how do you force a release when closing the connection??
00745   nsMsgProtocol::CloseSocket(); 
00746   m_runningUrl = nsnull;
00747   m_mailboxParser = nsnull;
00748   return 0;
00749 }
00750 
00751 // vim: ts=2 sw=2