Back to index

lightning-sunbird  0.9+nobinonly
nsParseMailbox.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) 1999
00020  * the Initial Developer. All Rights Reserved.
00021  *
00022  * Contributor(s):
00023  *   Pierre Phaneuf <pp@ludusdesign.com>
00024  *   David Bienvenu <bienvenu@mozilla.org>
00025  *
00026  * Alternatively, the contents of this file may be used under the terms of
00027  * either of the GNU General Public License Version 2 or later (the "GPL"),
00028  * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
00029  * in which case the provisions of the GPL or the LGPL are applicable instead
00030  * of those above. If you wish to allow use of your version of this file only
00031  * under the terms of either the GPL or the LGPL, and not to allow others to
00032  * use your version of this file under the terms of the MPL, indicate your
00033  * decision by deleting the provisions above and replace them with the notice
00034  * and other provisions required by the GPL or the LGPL. If you do not delete
00035  * the provisions above, a recipient may use your version of this file under
00036  * the terms of any one of the MPL, the GPL or the LGPL.
00037  *
00038  * ***** END LICENSE BLOCK ***** */
00039 
00040 #include "msgCore.h"
00041 #include "nsIURI.h"
00042 #include "nsParseMailbox.h"
00043 #include "nsIMsgHdr.h"
00044 #include "nsIMsgDatabase.h"
00045 #include "nsMsgMessageFlags.h"
00046 #include "nsIDBFolderInfo.h"
00047 #include "nsIInputStream.h"
00048 #include "nsILocalFile.h"
00049 #include "nsMsgLocalFolderHdrs.h"
00050 #include "nsMsgBaseCID.h"
00051 #include "nsMsgDBCID.h"
00052 #include "nsIMailboxUrl.h"
00053 #include "nsCRT.h"
00054 #include "nsFileStream.h"
00055 #include "nsMsgFolderFlags.h"
00056 #include "nsIMsgFolder.h"
00057 #include "nsXPIDLString.h"
00058 #include "nsIURL.h"
00059 #include "nsIMsgMailNewsUrl.h"
00060 #include "nsLocalStringBundle.h"
00061 #include "nsIMsgFilterList.h"
00062 #include "nsIMsgFilter.h"
00063 #include "nsIIOService.h"
00064 #include "nsNetCID.h"
00065 #include "nsRDFCID.h"
00066 #include "nsIRDFService.h"
00067 #include "nsMsgI18N.h"
00068 #include "nsAppDirectoryServiceDefs.h"
00069 #include "nsIMsgLocalMailFolder.h"
00070 #include "nsMsgUtils.h"
00071 #include "prprf.h"
00072 #include "nsEscape.h"
00073 #include "nsIMimeHeaders.h"
00074 #include "nsIMsgMdnGenerator.h"
00075 #include "nsMsgSearchCore.h"
00076 #include "nsMailHeaders.h"
00077 #include "nsIMsgMailSession.h"
00078 #include "nsIMsgComposeParams.h"
00079 #include "nsMsgCompCID.h"
00080 #include "nsIInterfaceRequestor.h"
00081 #include "nsIInterfaceRequestorUtils.h"
00082 #include "nsIDocShell.h"
00083 #include "nsIMsgCompose.h"
00084 #include "nsIDOMWindowInternal.h"
00085 #include "nsIPrefBranch.h"
00086 #include "nsIPrefService.h"
00087 #include "nsIMsgComposeService.h"
00088 #include "nsIMsgCopyService.h"
00089 #include "nsICryptoHash.h"
00090 
00091 static NS_DEFINE_CID(kCMailDB, NS_MAILDB_CID);
00092 static NS_DEFINE_CID(kIOServiceCID,              NS_IOSERVICE_CID);
00093 static NS_DEFINE_CID(kRDFServiceCID, NS_RDFSERVICE_CID);
00094 
00095 /* the following macros actually implement addref, release and query interface for our component. */
00096 NS_IMPL_ISUPPORTS_INHERITED2(nsMsgMailboxParser, nsParseMailMessageState, nsIStreamListener, nsIDBChangeListener)
00097 
00098 // Whenever data arrives from the connection, core netlib notifices the protocol by calling
00099 // OnDataAvailable. We then read and process the incoming data from the input stream. 
00100 NS_IMETHODIMP nsMsgMailboxParser::OnDataAvailable(nsIRequest *request, nsISupports *ctxt, nsIInputStream *aIStream, PRUint32 sourceOffset, PRUint32 aLength)
00101 {
00102     // right now, this really just means turn around and process the url
00103     nsresult rv = NS_OK;
00104     nsCOMPtr<nsIURI> url = do_QueryInterface(ctxt, &rv);
00105     if (NS_SUCCEEDED(rv))
00106         rv = ProcessMailboxInputStream(url, aIStream, aLength);
00107     return rv;
00108 }
00109 
00110 NS_IMETHODIMP nsMsgMailboxParser::OnStartRequest(nsIRequest *request, nsISupports *ctxt)
00111 {
00112     nsTime currentTime;
00113     m_startTime = currentTime;
00114 
00115 
00116     // extract the appropriate event sinks from the url and initialize them in our protocol data
00117     // the URL should be queried for a nsIMailboxURL. If it doesn't support a mailbox URL interface then
00118     // we have an error.
00119     nsresult rv = NS_OK;
00120 
00121     nsCOMPtr<nsIIOService> ioServ(do_GetService(kIOServiceCID, &rv));
00122 
00123     nsCOMPtr<nsIMailboxUrl> runningUrl = do_QueryInterface(ctxt, &rv);
00124 
00125     nsCOMPtr<nsIMsgMailNewsUrl> url = do_QueryInterface(ctxt);
00126     nsCOMPtr<nsIMsgFolder> folder = do_QueryReferent(m_folder);
00127 
00128     if (NS_SUCCEEDED(rv) && runningUrl && folder)
00129     {
00130         url->GetStatusFeedback(getter_AddRefs(m_statusFeedback));
00131 
00132         // okay, now fill in our event sinks...Note that each getter ref counts before
00133         // it returns the interface to us...we'll release when we are done
00134 
00135         folder->GetName(getter_Copies(m_folderName));
00136 
00137         nsCOMPtr<nsIFileSpec> path;
00138         folder->GetPath(getter_AddRefs(path));
00139        
00140         if (path)
00141         {
00142             nsFileSpec dbName;
00143             path->GetFileSpec(&dbName);
00144             // the size of the mailbox file is our total base line for measuring progress
00145             m_graph_progress_total = dbName.GetFileSize();
00146             UpdateStatusText(LOCAL_STATUS_SELECTING_MAILBOX);
00147 
00148             nsCOMPtr<nsIMsgDBService> msgDBService = do_GetService(NS_MSGDB_SERVICE_CONTRACTID, &rv);
00149             if (msgDBService)
00150             {
00151                 //Use OpenFolderDB to always open the db so that db's m_folder is set correctly.
00152                 rv = msgDBService->OpenFolderDB(folder, PR_TRUE, PR_TRUE, (nsIMsgDatabase **) getter_AddRefs(m_mailDB));
00153                 if (m_mailDB)
00154                     m_mailDB->AddListener(this);
00155             }
00156             NS_ASSERTION(m_mailDB, "failed to open mail db parsing folder");
00157         }
00158     }
00159 
00160     // need to get the mailbox name out of the url and call SetMailboxName with it.
00161     // then, we need to open the mail db for this parser.
00162     return rv;
00163 }
00164 
00165 // stop binding is a "notification" informing us that the stream associated with aURL is going away. 
00166 NS_IMETHODIMP nsMsgMailboxParser::OnStopRequest(nsIRequest *request, nsISupports *ctxt, nsresult aStatus)
00167 {
00168     DoneParsingFolder(aStatus);
00169     // what can we do? we can close the stream?
00170     m_urlInProgress = PR_FALSE;  // don't close the connection...we may be re-using it.
00171 
00172     if (m_mailDB)
00173         m_mailDB->RemoveListener(this);
00174     // and we want to mark ourselves for deletion or some how inform our protocol manager that we are 
00175     // available for another url if there is one....
00176 
00177     ReleaseFolderLock();
00178     // be sure to clear any status text and progress info..
00179     m_graph_progress_received = 0;
00180     UpdateProgressPercent();
00181     UpdateStatusText(LOCAL_STATUS_DOCUMENT_DONE);
00182 
00183     return NS_OK;
00184 }
00185 
00186 
00187 
00188 NS_IMETHODIMP nsMsgMailboxParser::OnHdrChange(nsIMsgDBHdr *aHdrChanged, PRUint32 aOldFlags, PRUint32 aNewFlags, nsIDBChangeListener *aInstigator)
00189 {
00190     return NS_OK;
00191 }
00192 
00193 NS_IMETHODIMP nsMsgMailboxParser::OnHdrDeleted(nsIMsgDBHdr *aHdrChanged, nsMsgKey aParentKey, PRInt32 aFlags, nsIDBChangeListener *aInstigator)
00194 {
00195     return NS_OK;
00196 }
00197 
00198 NS_IMETHODIMP nsMsgMailboxParser::OnHdrAdded(nsIMsgDBHdr *aHdrAdded, nsMsgKey aParentKey, PRInt32 aFlags, nsIDBChangeListener *aInstigator)
00199 {
00200     return NS_OK;
00201 }
00202 
00203 /* void OnParentChanged (in nsMsgKey aKeyChanged, in nsMsgKey oldParent, in nsMsgKey newParent, in nsIDBChangeListener aInstigator); */
00204 NS_IMETHODIMP nsMsgMailboxParser::OnParentChanged(nsMsgKey aKeyChanged, nsMsgKey oldParent, nsMsgKey newParent, nsIDBChangeListener *aInstigator)
00205 {
00206     return NS_OK;
00207 }
00208 
00209 /* void OnAnnouncerGoingAway (in nsIDBChangeAnnouncer instigator); */
00210 NS_IMETHODIMP nsMsgMailboxParser::OnAnnouncerGoingAway(nsIDBChangeAnnouncer *instigator)
00211 {
00212   if (m_mailDB)
00213     m_mailDB->RemoveListener(this);
00214 
00215   m_newMsgHdr = nsnull;
00216   m_mailDB = nsnull;
00217   return NS_OK;
00218 }
00219 
00220 /* void OnReadChanged (in nsIDBChangeListener instigator); */
00221 NS_IMETHODIMP nsMsgMailboxParser::OnReadChanged(nsIDBChangeListener *instigator)
00222 {
00223     return NS_OK;
00224 }
00225 
00226 /* void OnJunkScoreChanged (in nsIDBChangeListener instigator); */
00227 NS_IMETHODIMP nsMsgMailboxParser::OnJunkScoreChanged(nsIDBChangeListener *instigator)
00228 {
00229     return NS_OK;
00230 }
00231 
00232 nsMsgMailboxParser::nsMsgMailboxParser() : nsMsgLineBuffer(nsnull, PR_FALSE)
00233 {
00234   Init();
00235 }
00236 
00237 nsMsgMailboxParser::nsMsgMailboxParser(nsIMsgFolder *aFolder) : nsMsgLineBuffer(nsnull, PR_FALSE)
00238 {
00239   Init();
00240   m_folder = do_GetWeakReference(aFolder);
00241 }
00242 
00243 nsMsgMailboxParser::~nsMsgMailboxParser()
00244 {
00245   ReleaseFolderLock();
00246 }
00247 
00248 void nsMsgMailboxParser::Init()
00249 {
00250        m_obuffer = nsnull;
00251        m_obuffer_size = 0;
00252        m_graph_progress_total = 0;
00253        m_graph_progress_received = 0;
00254 }
00255 
00256 void nsMsgMailboxParser::UpdateStatusText (PRUint32 stringID)
00257 {
00258        if (m_statusFeedback)
00259        {
00260         nsresult rv;
00261         if (!mStringService) // if we haven't gotten the serivce yet...
00262             mStringService = do_GetService(NS_MSG_MAILBOXSTRINGSERVICE_CONTRACTID);
00263     
00264         nsXPIDLString finalString;
00265               if (stringID == LOCAL_STATUS_SELECTING_MAILBOX)
00266               {
00267             nsCOMPtr<nsIStringBundle> bundle;
00268             rv = mStringService->GetBundle(getter_AddRefs(bundle));
00269             NS_ASSERTION(NS_SUCCEEDED(rv), "GetBundle failed");
00270             if (NS_FAILED(rv)) return;
00271 
00272             const PRUnichar * stringArray[] = { m_folderName.get() };
00273             rv = bundle->FormatStringFromID(stringID, stringArray, 1,
00274                                                    getter_Copies(finalString));
00275               }
00276               else
00277             mStringService->GetStringByID(stringID,
00278                                           getter_Copies(finalString));
00279 
00280         m_statusFeedback->ShowStatusString(finalString);
00281 
00282     }
00283 }
00284 
00285 void nsMsgMailboxParser::UpdateProgressPercent ()
00286 {
00287   if (m_statusFeedback && m_graph_progress_total != 0)
00288   {
00289     // prevent overflow by dividing both by 100
00290     PRUint32 progressTotal = m_graph_progress_total / 100;
00291     PRUint32 progressReceived = m_graph_progress_received / 100;
00292     if (progressTotal > 0)
00293       m_statusFeedback->ShowProgress((100 *(progressReceived))  / progressTotal);   
00294   }
00295 }
00296 
00297 int nsMsgMailboxParser::ProcessMailboxInputStream(nsIURI* aURL, nsIInputStream *aIStream, PRUint32 aLength)
00298 {
00299        nsresult ret = NS_OK;
00300 
00301        PRUint32 bytesRead = 0;
00302 
00303        if (NS_SUCCEEDED(m_inputStream.GrowBuffer(aLength)))
00304        {
00305               // OK, this sucks, but we're going to have to copy into our
00306               // own byte buffer, and then pass that to the line buffering code,
00307               // which means a couple buffer copies.
00308               ret = aIStream->Read(m_inputStream.GetBuffer(), aLength, &bytesRead);
00309               if (NS_SUCCEEDED(ret))
00310                      ret = BufferInput(m_inputStream.GetBuffer(), bytesRead);
00311        }
00312        if (m_graph_progress_total > 0)
00313        {
00314               if (NS_SUCCEEDED(ret))
00315                 m_graph_progress_received += bytesRead;
00316        }
00317        return (ret);
00318 }
00319 
00320 void nsMsgMailboxParser::DoneParsingFolder(nsresult status)
00321 {
00322   /* End of file.  Flush out any partial line remaining in the buffer. */
00323   FlushLastLine();
00324   PublishMsgHeader(nsnull);
00325 
00326   // only mark the db valid if we've succeeded.
00327   if (NS_SUCCEEDED(status) && m_mailDB)   // finished parsing, so flush db folder info 
00328     UpdateDBFolderInfo();
00329   else if (m_mailDB)
00330     m_mailDB->SetSummaryValid(PR_FALSE);
00331 
00332   //   if (m_folder != nsnull)
00333   //          m_folder->SummaryChanged();
00334   FreeBuffers();
00335 }
00336 
00337 void nsMsgMailboxParser::FreeBuffers()
00338 {
00339        /* We're done reading the folder - we don't need these things
00340         any more. */
00341        PR_FREEIF (m_obuffer);
00342        m_obuffer_size = 0;
00343 }
00344 
00345 void nsMsgMailboxParser::UpdateDBFolderInfo()
00346 {
00347        UpdateDBFolderInfo(m_mailDB);
00348 }
00349 
00350 // update folder info in db so we know not to reparse.
00351 void nsMsgMailboxParser::UpdateDBFolderInfo(nsIMsgDatabase *mailDB)
00352 {
00353   mailDB->SetSummaryValid(PR_TRUE);
00354 }
00355 
00356 // Tell the world about the message header (add to db, and view, if any)
00357 PRInt32 nsMsgMailboxParser::PublishMsgHeader(nsIMsgWindow *msgWindow)
00358 {
00359   FinishHeader();
00360   if (m_newMsgHdr)
00361   {
00362     PRUint32 flags;
00363     (void)m_newMsgHdr->GetFlags(&flags);
00364     if (flags & MSG_FLAG_EXPUNGED)
00365     {
00366       nsCOMPtr<nsIDBFolderInfo> folderInfo;
00367       m_mailDB->GetDBFolderInfo(getter_AddRefs(folderInfo));
00368       PRUint32 size;
00369       (void)m_newMsgHdr->GetMessageSize(&size);
00370       folderInfo->ChangeExpungedBytes(size);
00371       m_newMsgHdr = nsnull;
00372     }
00373     else if (m_mailDB)
00374     {
00375       // add hdr but don't notify - shouldn't be requiring notifications 
00376       // during summary file rebuilding
00377       m_mailDB->AddNewHdrToDB(m_newMsgHdr, PR_FALSE);
00378       m_newMsgHdr = nsnull;
00379     }
00380     else
00381       NS_ASSERTION(PR_FALSE, "no database while parsing local folder");      // should have a DB, no?
00382   }
00383   else if (m_mailDB)
00384   {
00385     nsCOMPtr<nsIDBFolderInfo> folderInfo;
00386     m_mailDB->GetDBFolderInfo(getter_AddRefs(folderInfo));
00387     if (folderInfo)
00388       folderInfo->ChangeExpungedBytes(m_position - m_envelope_pos);
00389   }
00390   return 0;
00391 }
00392 
00393 void nsMsgMailboxParser::AbortNewHeader()
00394 {
00395   if (m_newMsgHdr && m_mailDB)
00396     m_newMsgHdr = nsnull;
00397 }
00398 
00399 PRInt32 nsMsgMailboxParser::HandleLine(char *line, PRUint32 lineLength)
00400 {
00401        int status = 0;
00402 
00403        /* If this is the very first line of a non-empty folder, make sure it's an envelope */
00404        if (m_graph_progress_received == 0)
00405        {
00406               /* This is the first block from the file.  Check to see if this
00407                  looks like a mail file. */
00408               const char *s = line;
00409               const char *end = s + lineLength;
00410               while (s < end && IS_SPACE(*s))
00411                      s++;
00412               if ((end - s) < 20 || !IsEnvelopeLine(s, end - s))
00413               {
00414 //                   char buf[500];
00415 //                   PR_snprintf (buf, sizeof(buf),
00416 //                                         XP_GetString(MK_MSG_NON_MAIL_FILE_READ_QUESTION),
00417 //                                         folder_name);
00418 //                   else if (!FE_Confirm (m_context, buf))
00419 //                          return NS_MSG_NOT_A_MAIL_FOLDER; /* #### NOT_A_MAIL_FILE */
00420               }
00421        }
00422 //     m_graph_progress_received += lineLength;
00423 
00424        // mailbox parser needs to do special stuff when it finds an envelope
00425        // after parsing a message body. So do that.
00426        if (line[0] == 'F' && IsEnvelopeLine(line, lineLength))
00427        {
00428               // **** This used to be
00429               // PR_ASSERT (m_parseMsgState->m_state == nsMailboxParseBodyState);
00430               // **** I am not sure this is a right thing to do. This happens when
00431               // going online, downloading a message while playing back append
00432               // draft/template offline operation. We are mixing
00433         // nsMailboxParseBodyState &&
00434               // nsMailboxParseHeadersState. David I need your help here too. **** jt
00435 
00436               NS_ASSERTION (m_state == nsIMsgParseMailMsgState::ParseBodyState ||
00437                                m_state == nsIMsgParseMailMsgState::ParseHeadersState, "invalid parse state"); /* else folder corrupted */
00438               PublishMsgHeader(nsnull);
00439               Clear();
00440               status = StartNewEnvelope(line, lineLength);
00441               NS_ASSERTION(status >= 0, " error starting envelope parsing mailbox");
00442               // at the start of each new message, update the progress bar
00443               UpdateProgressPercent();
00444               if (status < 0)
00445                      return status;
00446        }
00447        // otherwise, the message parser can handle it completely.
00448        else if (m_mailDB != nsnull)       // if no DB, do we need to parse at all?
00449               return ParseFolderLine(line, lineLength);
00450         else
00451           return NS_ERROR_NULL_POINTER; // need to error out if we don't have a db.
00452 
00453        return 0;
00454 
00455 }
00456 
00457 void
00458 nsMsgMailboxParser::ReleaseFolderLock()
00459 {
00460   nsresult result;
00461   nsCOMPtr<nsIMsgFolder> folder = do_QueryReferent(m_folder);
00462   if (!folder) 
00463     return;
00464   PRBool haveSemaphore;
00465   nsCOMPtr <nsISupports> supports = do_QueryInterface(NS_STATIC_CAST(nsIMsgParseMailMsgState*, this));
00466   result = folder->TestSemaphore(supports, &haveSemaphore);
00467   if(NS_SUCCEEDED(result) && haveSemaphore)
00468     result = folder->ReleaseSemaphore(supports);
00469   return;
00470 }
00471 
00472 NS_IMPL_ISUPPORTS1(nsParseMailMessageState, nsIMsgParseMailMsgState)
00473 
00474 nsParseMailMessageState::nsParseMailMessageState()
00475 {
00476   m_position = 0;
00477   m_IgnoreXMozillaStatus = PR_FALSE;
00478   m_useReceivedDate = PR_FALSE;
00479   m_state = nsIMsgParseMailMsgState::ParseBodyState;
00480 
00481   // setup handling of custom db headers, headers that are added to .msf files
00482   // as properties of the nsMsgHdr objects, controlled by the 
00483   // pref mailnews.customDBHeaders, a space-delimited list of headers.
00484   // E.g., if mailnews.customDBHeaders is "X-Spam-Score", and we're parsing
00485   // a mail message with the X-Spam-Score header, we'll set the 
00486   // "x-spam-score" property of nsMsgHdr to the value of the header.
00487   m_customDBHeaderValues = nsnull;
00488   nsXPIDLCString customDBHeaders;
00489   nsCOMPtr<nsIPrefBranch> pPrefBranch(do_GetService(NS_PREFSERVICE_CONTRACTID));
00490   if (pPrefBranch)
00491   {
00492      pPrefBranch->GetCharPref("mailnews.customDBHeaders",  getter_Copies(customDBHeaders));
00493      ToLowerCase(customDBHeaders);
00494      m_customDBHeaders.ParseString(customDBHeaders, " ");
00495      if (m_customDBHeaders.Count())
00496      {
00497        m_customDBHeaderValues = new struct message_header [m_customDBHeaders.Count()];
00498        if (!m_customDBHeaderValues)
00499          m_customDBHeaders.Clear();
00500      }
00501      pPrefBranch->GetBoolPref("mailnews.use_received_date", &m_useReceivedDate);
00502   }
00503   Clear();
00504 
00505   m_HeaderAddressParser = do_GetService(NS_MAILNEWS_MIME_HEADER_PARSER_CONTRACTID);
00506 }
00507 
00508 nsParseMailMessageState::~nsParseMailMessageState()
00509 {
00510   ClearAggregateHeader (m_toList);
00511   ClearAggregateHeader (m_ccList);
00512   delete [] m_customDBHeaderValues;
00513 }
00514 
00515 void nsParseMailMessageState::Init(PRUint32 fileposition)
00516 {
00517   m_state = nsIMsgParseMailMsgState::ParseBodyState;
00518   m_position = fileposition;
00519   m_newMsgHdr = nsnull;
00520 }
00521 
00522 NS_IMETHODIMP nsParseMailMessageState::Clear()
00523 {
00524   m_message_id.length = 0;
00525   m_references.length = 0;
00526   m_date.length = 0;
00527   m_from.length = 0;
00528   m_sender.length = 0;
00529   m_newsgroups.length = 0;
00530   m_subject.length = 0;
00531   m_status.length = 0;
00532   m_mozstatus.length = 0;
00533   m_mozstatus2.length = 0;
00534   m_envelope_from.length = 0;
00535   m_envelope_date.length = 0;
00536   m_priority.length = 0;
00537   m_keywords.length = 0;
00538   m_mdn_dnt.length = 0;
00539   m_return_path.length = 0;
00540   m_account_key.length = 0;
00541   m_in_reply_to.length = 0;
00542   m_replyTo.length = 0;
00543   m_content_type.length = 0;
00544   m_mdn_original_recipient.length = 0;
00545   m_body_lines = 0;
00546   m_newMsgHdr = nsnull;
00547   m_envelope_pos = 0;
00548   ClearAggregateHeader (m_toList);
00549   ClearAggregateHeader (m_ccList);
00550   m_headers.ResetWritePos();
00551   m_envelope.ResetWritePos();
00552   m_receivedTime = LL_ZERO;
00553   for (PRInt32 i = 0; i < m_customDBHeaders.Count(); i++)
00554     m_customDBHeaderValues[i].length = 0;
00555 
00556   return NS_OK;
00557 }
00558 
00559 NS_IMETHODIMP nsParseMailMessageState::SetState(nsMailboxParseState aState)
00560 {
00561   m_state = aState;
00562   return NS_OK;
00563 }
00564 
00565 NS_IMETHODIMP nsParseMailMessageState::GetState(nsMailboxParseState *aState)
00566 {
00567   if (!aState) 
00568     return NS_ERROR_NULL_POINTER;
00569   
00570   *aState = m_state;
00571   return NS_OK;
00572 }
00573 
00574 NS_IMETHODIMP
00575 nsParseMailMessageState::GetEnvelopePos(PRUint32 *aEnvelopePos)
00576 {
00577     if (!aEnvelopePos) 
00578         return NS_ERROR_NULL_POINTER;
00579     *aEnvelopePos = m_envelope_pos;
00580     return NS_OK;
00581 }
00582 
00583 NS_IMETHODIMP nsParseMailMessageState::SetEnvelopePos(PRUint32 aEnvelopePos)
00584 {
00585   m_envelope_pos = aEnvelopePos;
00586   m_position = m_envelope_pos;
00587   m_headerstartpos = m_position;
00588   return NS_OK;
00589 }
00590 
00591 NS_IMETHODIMP nsParseMailMessageState::GetNewMsgHdr(nsIMsgDBHdr ** aMsgHeader)
00592 {
00593   if (aMsgHeader)
00594     NS_IF_ADDREF(*aMsgHeader = m_newMsgHdr);
00595 
00596   return NS_OK;
00597 }
00598 
00599 NS_IMETHODIMP nsParseMailMessageState::ParseAFolderLine(const char *line, PRUint32 lineLength)
00600 {
00601   ParseFolderLine(line, lineLength);
00602   return NS_OK;
00603 }
00604 
00605 PRInt32 nsParseMailMessageState::ParseFolderLine(const char *line, PRUint32 lineLength)
00606 {
00607   int status = 0;
00608   
00609   if (m_state == nsIMsgParseMailMsgState::ParseHeadersState)
00610   {
00611     if (EMPTY_MESSAGE_LINE(line))
00612     {
00613       /* End of headers.  Now parse them. */
00614       status = ParseHeaders();
00615       NS_ASSERTION(status >= 0, "error parsing headers parsing mailbox");
00616       if (status < 0)
00617         return status;
00618       
00619       status = FinalizeHeaders();
00620       NS_ASSERTION(status >= 0, "error finalizing headers parsing mailbox");
00621       if (status < 0)
00622         return status;
00623       m_state = nsIMsgParseMailMsgState::ParseBodyState;
00624     }
00625     else
00626     {
00627       /* Otherwise, this line belongs to a header.  So append it to the
00628          header data, and stay in MBOX `MIME_PARSE_HEADERS' state.
00629       */
00630       m_headers.AppendBuffer(line, lineLength);
00631     }
00632   }
00633   else if ( m_state == nsIMsgParseMailMsgState::ParseBodyState)
00634   {
00635     m_body_lines++;
00636   }
00637   
00638   m_position += lineLength;
00639   
00640   return 0;
00641 }
00642 
00643 NS_IMETHODIMP nsParseMailMessageState::SetMailDB(nsIMsgDatabase *mailDB)
00644 {
00645   m_mailDB = mailDB;
00646   return NS_OK;
00647 }
00648 
00649 NS_IMETHODIMP nsParseMailMessageState::SetDBFolderStream(nsIOFileStream *fileStream)
00650 {
00651   NS_ASSERTION(m_mailDB, "m_mailDB is not set");
00652   if (m_mailDB)
00653     m_mailDB->SetFolderStream(fileStream);
00654   return NS_OK;
00655 }
00656 
00657 /* #define STRICT_ENVELOPE */
00658 
00659 PRBool
00660 nsParseMailMessageState::IsEnvelopeLine(const char *buf, PRInt32 buf_size)
00661 {
00662 #ifdef STRICT_ENVELOPE
00663   /* The required format is
00664           From jwz  Fri Jul  1 09:13:09 1994
00665         But we should also allow at least:
00666           From jwz  Fri, Jul 01 09:13:09 1994
00667           From jwz  Fri Jul  1 09:13:09 1994 PST
00668           From jwz  Fri Jul  1 09:13:09 1994 (+0700)
00669 
00670         We can't easily call XP_ParseTimeString() because the string is not
00671         null terminated (ok, we could copy it after a quick check...) but
00672         XP_ParseTimeString() may be too lenient for our purposes.
00673 
00674         DANGER!!  The released version of 2.0b1 was (on some systems,
00675         some Unix, some NT, possibly others) writing out envelope lines
00676         like "From - 10/13/95 11:22:33" which STRICT_ENVELOPE will reject!
00677    */
00678   const char *date, *end;
00679 
00680   if (buf_size < 29) return PR_FALSE;
00681   if (*buf != 'F') return PR_FALSE;
00682   if (strncmp(buf, "From ", 5)) return PR_FALSE;
00683 
00684   end = buf + buf_size;
00685   date = buf + 5;
00686 
00687   /* Skip horizontal whitespace between "From " and user name. */
00688   while ((*date == ' ' || *date == '\t') && date < end)
00689        date++;
00690 
00691   /* If at the end, it doesn't match. */
00692   if (IS_SPACE(*date) || date == end)
00693        return PR_FALSE;
00694 
00695   /* Skip over user name. */
00696   while (!IS_SPACE(*date) && date < end)
00697        date++;
00698 
00699   /* Skip horizontal whitespace between user name and date. */
00700   while ((*date == ' ' || *date == '\t') && date < end)
00701        date++;
00702 
00703   /* Don't want this to be localized. */
00704 # define TMP_ISALPHA(x) (((x) >= 'A' && (x) <= 'Z') || \
00705                                            ((x) >= 'a' && (x) <= 'z'))
00706 
00707   /* take off day-of-the-week. */
00708   if (date >= end - 3)
00709        return PR_FALSE;
00710   if (!TMP_ISALPHA(date[0]) || !TMP_ISALPHA(date[1]) || !TMP_ISALPHA(date[2]))
00711        return PR_FALSE;
00712   date += 3;
00713   /* Skip horizontal whitespace (and commas) between dotw and month. */
00714   if (*date != ' ' && *date != '\t' && *date != ',')
00715        return PR_FALSE;
00716   while ((*date == ' ' || *date == '\t' || *date == ',') && date < end)
00717        date++;
00718 
00719   /* take off month. */
00720   if (date >= end - 3)
00721        return PR_FALSE;
00722   if (!TMP_ISALPHA(date[0]) || !TMP_ISALPHA(date[1]) || !TMP_ISALPHA(date[2]))
00723        return PR_FALSE;
00724   date += 3;
00725   /* Skip horizontal whitespace between month and dotm. */
00726   if (date == end || (*date != ' ' && *date != '\t'))
00727        return PR_FALSE;
00728   while ((*date == ' ' || *date == '\t') && date < end)
00729        date++;
00730 
00731   /* Skip over digits and whitespace. */
00732   while (((*date >= '0' && *date <= '9') || *date == ' ' || *date == '\t') &&
00733                date < end)
00734        date++;
00735   /* Next character should be a colon. */
00736   if (date >= end || *date != ':')
00737        return PR_FALSE;
00738 
00739   /* Ok, that ought to be enough... */
00740 
00741 # undef TMP_ISALPHA
00742 
00743 #else  /* !STRICT_ENVELOPE */
00744 
00745   if (buf_size < 5) return PR_FALSE;
00746   if (*buf != 'F') return PR_FALSE;
00747   if (strncmp(buf, "From ", 5)) return PR_FALSE;
00748 
00749 #endif /* !STRICT_ENVELOPE */
00750 
00751   return PR_TRUE;
00752 }
00753 
00754 
00755 // We've found the start of the next message, so finish this one off.
00756 NS_IMETHODIMP nsParseMailMessageState::FinishHeader()
00757 {
00758   if (m_newMsgHdr)
00759   {
00760     m_newMsgHdr->SetMessageKey(m_envelope_pos);
00761     m_newMsgHdr->SetMessageSize(m_position - m_envelope_pos);  // dmb - no longer number of lines.
00762     m_newMsgHdr->SetLineCount(m_body_lines);
00763   }
00764   
00765   return NS_OK;
00766 }
00767 
00768 NS_IMETHODIMP nsParseMailMessageState::GetAllHeaders(char ** pHeaders, PRInt32 *pHeadersSize)
00769 {
00770   if (!pHeaders || !pHeadersSize)
00771     return NS_ERROR_NULL_POINTER;
00772   *pHeaders = m_headers.GetBuffer();
00773   *pHeadersSize = m_headers.GetBufferPos();
00774   return NS_OK;
00775 }
00776 
00777 // generate headers as a string, with CRLF between the headers
00778 NS_IMETHODIMP nsParseMailMessageState::GetHeaders(char ** pHeaders)
00779 {
00780   NS_ENSURE_ARG_POINTER(pHeaders);
00781   nsCString crlfHeaders;
00782   char *curHeader = m_headers.GetBuffer();
00783   for (PRUint32 headerPos = 0; headerPos < m_headers.GetBufferPos();)
00784   {
00785     crlfHeaders.Append(curHeader);
00786     crlfHeaders.Append(CRLF);
00787     PRInt32 headerLen = strlen(curHeader);
00788     curHeader += headerLen + 1;
00789     headerPos += headerLen + 1;
00790   }
00791   *pHeaders = nsCRT::strdup(crlfHeaders.get());
00792   return NS_OK;
00793 }
00794 
00795 struct message_header *nsParseMailMessageState::GetNextHeaderInAggregate (nsVoidArray &list)
00796 {
00797   // When parsing a message with multiple To or CC header lines, we're storing each line in a 
00798   // list, where the list represents the "aggregate" total of all the header. Here we get a new
00799   // line for the list
00800   
00801   struct message_header *header = (struct message_header*) PR_Calloc (1, sizeof(struct message_header));
00802   list.AppendElement (header);
00803   return header;
00804 }
00805 
00806 void nsParseMailMessageState::GetAggregateHeader (nsVoidArray &list, struct message_header *outHeader)
00807 {
00808   // When parsing a message with multiple To or CC header lines, we're storing each line in a 
00809   // list, where the list represents the "aggregate" total of all the header. Here we combine
00810   // all the lines together, as though they were really all found on the same line
00811   
00812   struct message_header *header = nsnull;
00813   int length = 0;
00814   int i;
00815   
00816   // Count up the bytes required to allocate the aggregated header
00817   for (i = 0; i < list.Count(); i++)
00818   {
00819     header = (struct message_header*) list.ElementAt(i);
00820     length += (header->length + 1); //+ for ","
00821     NS_ASSERTION(header->length == (PRInt32)strlen(header->value), "header corrupted");
00822   }
00823   
00824   if (length > 0)
00825   {
00826     char *value = (char*) PR_MALLOC (length + 1); //+1 for null term
00827     if (value)
00828     {
00829       // Catenate all the To lines together, separated by commas
00830       value[0] = '\0';
00831       int size = list.Count();
00832       for (i = 0; i < size; i++)
00833       {
00834         header = (struct message_header*) list.ElementAt(i);
00835         PL_strcat (value, header->value);
00836         if (i + 1 < size)
00837           PL_strcat (value, ",");
00838       }
00839       outHeader->length = length;
00840       outHeader->value = value;
00841     }
00842   }
00843   else
00844   {
00845     outHeader->length = 0;
00846     outHeader->value = nsnull;
00847   }
00848 }
00849 
00850 void nsParseMailMessageState::ClearAggregateHeader (nsVoidArray &list)
00851 {
00852   // Reset the aggregate headers. Free only the message_header struct since 
00853   // we don't own the value pointer
00854   
00855   for (int i = 0; i < list.Count(); i++)
00856     PR_Free ((struct message_header*) list.ElementAt(i));
00857   list.Clear();
00858 }
00859 
00860 // We've found a new envelope to parse.
00861 int nsParseMailMessageState::StartNewEnvelope(const char *line, PRUint32 lineLength)
00862 {
00863   m_envelope_pos = m_position;
00864   m_state = nsIMsgParseMailMsgState::ParseHeadersState;
00865   m_position += lineLength;
00866   m_headerstartpos = m_position;
00867   return ParseEnvelope (line, lineLength);
00868 }
00869 
00870 /* largely lifted from mimehtml.c, which does similar parsing, sigh...
00871 */
00872 int nsParseMailMessageState::ParseHeaders ()
00873 {
00874   char *buf = m_headers.GetBuffer();
00875   char *buf_end = buf + m_headers.GetBufferPos();
00876   while (buf < buf_end)
00877   {
00878     char *colon = PL_strchr (buf, ':');
00879     char *end;
00880     char *value = 0;
00881     struct message_header *header = 0;
00882     struct message_header receivedBy;
00883     
00884     if (! colon)
00885       break;
00886     
00887     end = colon;
00888     while (end > buf && (*end == ' ' || *end == '\t'))
00889       end--;
00890     
00891     switch (buf [0])
00892     {
00893     case 'C': case 'c':
00894       if (!nsCRT::strncasecmp ("CC", buf, end - buf))
00895         header = GetNextHeaderInAggregate(m_ccList);
00896       else if (!nsCRT::strncasecmp ("Content-Type", buf, end - buf))
00897         header = &m_content_type;
00898       break;
00899     case 'D': case 'd':
00900       if (!nsCRT::strncasecmp ("Date", buf, end - buf))
00901         header = &m_date;
00902       else if (!nsCRT::strncasecmp("Disposition-Notification-To", buf, end - buf))
00903         header = &m_mdn_dnt;
00904       break;
00905     case 'F': case 'f':
00906       if (!nsCRT::strncasecmp ("From", buf, end - buf))
00907         header = &m_from;
00908       break;
00909     case 'I' : case 'i':
00910       if (!nsCRT::strncasecmp ("In-Reply-To", buf, end - buf))
00911         header = &m_in_reply_to;
00912       break;
00913     case 'M': case 'm':
00914       if (!nsCRT::strncasecmp ("Message-ID", buf, end - buf))
00915         header = &m_message_id;
00916       break;
00917     case 'N': case 'n':
00918       if (!nsCRT::strncasecmp ("Newsgroups", buf, end - buf))
00919         header = &m_newsgroups;
00920       break;
00921     case 'O': case 'o':
00922       if (!nsCRT::strncasecmp ("Original-Recipient", buf, end - buf))
00923         header = &m_mdn_original_recipient;
00924       break;
00925     case 'R': case 'r':
00926       if (!nsCRT::strncasecmp ("References", buf, end - buf))
00927         header = &m_references;
00928       else if (!nsCRT::strncasecmp ("Return-Path", buf, end - buf))
00929         header = &m_return_path;
00930       // treat conventional Return-Receipt-To as MDN
00931       // Disposition-Notification-To
00932       else if (!nsCRT::strncasecmp ("Return-Receipt-To", buf, end - buf))
00933         header = &m_mdn_dnt;
00934       else if (!nsCRT::strncasecmp("Reply-To", buf, end - buf))
00935         header = &m_replyTo;
00936       else if (!PL_strncasecmp("Received", buf, end - buf))
00937       {
00938         header = &receivedBy;
00939         header->length = 0;
00940       }
00941       break;
00942     case 'S': case 's':
00943       if (!nsCRT::strncasecmp ("Subject", buf, end - buf))
00944         header = &m_subject;
00945       else if (!nsCRT::strncasecmp ("Sender", buf, end - buf))
00946         header = &m_sender;
00947       else if (!nsCRT::strncasecmp ("Status", buf, end - buf))
00948         header = &m_status;
00949       break;
00950     case 'T': case 't':
00951       if (!nsCRT::strncasecmp ("To", buf, end - buf))
00952         header = GetNextHeaderInAggregate(m_toList);
00953       break;
00954     case 'X':
00955       if (X_MOZILLA_STATUS2_LEN == end - buf &&
00956         !nsCRT::strncasecmp(X_MOZILLA_STATUS2, buf, end - buf) &&
00957         !m_IgnoreXMozillaStatus && !m_mozstatus2.length)
00958         header = &m_mozstatus2;
00959       else if ( X_MOZILLA_STATUS_LEN == end - buf &&
00960         !nsCRT::strncasecmp(X_MOZILLA_STATUS, buf, end - buf) && !m_IgnoreXMozillaStatus
00961         && !m_mozstatus.length)
00962         header = &m_mozstatus;
00963       else if (!nsCRT::strncasecmp(HEADER_X_MOZILLA_ACCOUNT_KEY, buf, end - buf)
00964         && !m_account_key.length)
00965         header = &m_account_key;
00966       // we could very well care what the priority header was when we 
00967       // remember its value. If so, need to remember it here. Also, 
00968       // different priority headers can appear in the same message, 
00969       // but we only rememeber the last one that we see.
00970       else if (!nsCRT::strncasecmp("X-Priority", buf, end - buf)
00971         || !nsCRT::strncasecmp("Priority", buf, end - buf))
00972         header = &m_priority;
00973       else if (!nsCRT::strncasecmp(HEADER_X_MOZILLA_KEYWORDS, buf, end - buf)
00974         && !m_keywords.length)
00975         header = &m_keywords;
00976       break;
00977     }
00978     if (!header && m_customDBHeaders.Count())
00979     {
00980       nsDependentCSubstring headerStr(buf, end);
00981 
00982       ToLowerCase(headerStr);
00983       PRInt32 customHeaderIndex = m_customDBHeaders.IndexOf(headerStr);
00984       if (customHeaderIndex != kNotFound)
00985         header = & m_customDBHeaderValues[customHeaderIndex];
00986     }
00987     
00988     buf = colon + 1;
00989     while (*buf == ' ' || *buf == '\t')
00990       buf++;
00991     
00992     value = buf;
00993     if (header)
00994       header->value = value;
00995     else
00996     {
00997     }
00998     
00999 SEARCH_NEWLINE:
01000     while (*buf != 0 && *buf != nsCRT::CR && *buf != nsCRT::LF)
01001       buf++;
01002     
01003     if (buf+1 >= buf_end)
01004       ;
01005     /* If "\r\n " or "\r\n\t" is next, that doesn't terminate the header. */
01006     else if (buf+2 < buf_end &&
01007                         (buf[0] == nsCRT::CR  && buf[1] == nsCRT::LF) &&
01008                            (buf[2] == ' ' || buf[2] == '\t'))
01009     {
01010       buf += 3;
01011       goto SEARCH_NEWLINE;
01012     }
01013     /* If "\r " or "\r\t" or "\n " or "\n\t" is next, that doesn't terminate
01014     the header either. */
01015     else if ((buf[0] == nsCRT::CR  || buf[0] == nsCRT::LF) &&
01016                         (buf[1] == ' ' || buf[1] == '\t'))
01017     {
01018       buf += 2;
01019       goto SEARCH_NEWLINE;
01020     }
01021     
01022     if (header)
01023       header->length = buf - header->value;
01024     
01025     if (*buf == nsCRT::CR || *buf == nsCRT::LF)
01026     {
01027       char *last = buf;
01028       if (*buf == nsCRT::CR && buf[1] == nsCRT::LF)
01029         buf++;
01030       buf++;
01031       *last = 0;     /* short-circuit const, and null-terminate header. */
01032     }
01033     
01034     if (header)
01035     {
01036       /* More const short-circuitry... */
01037       /* strip leading whitespace */
01038       while (IS_SPACE (*header->value))
01039         header->value++, header->length--;
01040       /* strip trailing whitespace */
01041       while (header->length > 0 &&
01042         IS_SPACE (header->value [header->length - 1]))
01043         ((char *) header->value) [--header->length] = 0;
01044       if (header == &receivedBy && LL_IS_ZERO(m_receivedTime))
01045       {
01046         // parse Received: header for date.
01047         // We trust the first header as that is closest to recipient,
01048         // and less likely to be spoofed.
01049         nsCAutoString receivedHdr(header->value, header->length);
01050         PRInt32 lastSemicolon = receivedHdr.RFindChar(';');
01051         if (lastSemicolon != kNotFound)
01052         {
01053           nsCAutoString receivedDate;
01054           receivedHdr.Right(receivedDate, receivedHdr.Length() - lastSemicolon - 1);
01055           receivedDate.Trim(" \t\b\r\n");
01056           PRTime resultTime;
01057           if (PR_ParseTimeString (receivedDate.get(), PR_FALSE, &resultTime) == PR_SUCCESS)
01058             m_receivedTime = resultTime;
01059         }
01060       }
01061     }
01062   }
01063   return 0;
01064 }
01065 
01066 int nsParseMailMessageState::ParseEnvelope (const char *line, PRUint32 line_size)
01067 {
01068   const char *end;
01069   char *s;
01070   
01071   m_envelope.AppendBuffer(line, line_size);
01072   end = m_envelope.GetBuffer() + line_size;
01073   s = m_envelope.GetBuffer() + 5;
01074   
01075   while (s < end && IS_SPACE (*s))
01076     s++;
01077   m_envelope_from.value = s;
01078   while (s < end && !IS_SPACE (*s))
01079     s++;
01080   m_envelope_from.length = s - m_envelope_from.value;
01081   
01082   while (s < end && IS_SPACE (*s))
01083     s++;
01084   m_envelope_date.value = s;
01085   m_envelope_date.length = (PRUint16) (line_size - (s - m_envelope.GetBuffer()));
01086   while (IS_SPACE (m_envelope_date.value [m_envelope_date.length - 1]))
01087     m_envelope_date.length--;
01088   
01089   /* #### short-circuit const */
01090   ((char *) m_envelope_from.value) [m_envelope_from.length] = 0;
01091   ((char *) m_envelope_date.value) [m_envelope_date.length] = 0;
01092   
01093   return 0;
01094 }
01095 
01096 #ifdef WE_CONDENSE_MIME_STRINGS
01097 static char *
01098 msg_condense_mime2_string(char *sourceStr)
01099 {
01100   char *returnVal = nsCRT::strdup(sourceStr);
01101   if (!returnVal) 
01102     return nsnull;
01103   
01104   MIME_StripContinuations(returnVal);
01105   
01106   return returnVal;
01107 }
01108 #endif // WE_CONDENSE_MIME_STRINGS
01109 
01110 int nsParseMailMessageState::InternSubject (struct message_header *header)
01111 {
01112   char *key;
01113   PRUint32 L;
01114   
01115   if (!header || header->length == 0)
01116   {
01117     m_newMsgHdr->SetSubject("");
01118     return 0;
01119   }
01120   
01121   NS_ASSERTION (header->length == (short) strlen(header->value), "subject corrupt while parsing message");
01122   
01123   key = (char *) header->value;  /* #### const evilness */
01124   
01125   L = header->length;
01126   
01127   
01128   PRUint32 flags;
01129   (void)m_newMsgHdr->GetFlags(&flags);
01130   /* strip "Re: " */
01131   /* We trust the X-Mozilla-Status line to be the smartest in almost
01132         all things.  One exception, however, is the HAS_RE flag.  Since
01133          we just parsed the subject header anyway, we expect that parsing
01134          to be smartest.  (After all, what if someone just went in and
01135         edited the subject line by hand?) */
01136   nsXPIDLCString modifiedSubject;
01137   if (NS_MsgStripRE((const char **) &key, &L, getter_Copies(modifiedSubject)))
01138     flags |= MSG_FLAG_HAS_RE;
01139   else
01140     flags &= ~MSG_FLAG_HAS_RE;
01141   m_newMsgHdr->SetFlags(flags); // this *does not* update the mozilla-status header in the local folder
01142   
01143   //  if (!*key) return 0; /* To catch a subject of "Re:" */
01144   
01145   // Condense the subject text into as few MIME-2 encoded words as possible.
01146 #ifdef WE_CONDENSE_MIME_STRINGS
01147   char *condensedKey = msg_condense_mime2_string(modifiedSubject.IsEmpty() ? key : modifiedSubject.get());
01148 #else
01149   char *condensedKey = nsnull;
01150 #endif
01151   m_newMsgHdr->SetSubject(condensedKey ? condensedKey : 
01152   (modifiedSubject.IsEmpty() ? key : modifiedSubject.get()));
01153   PR_FREEIF(condensedKey);
01154   
01155   return 0;
01156 }
01157 
01158 /* Like mbox_intern() but for headers which contain email addresses:
01159 we extract the "name" component of the first address, and discard
01160 the rest. */
01161 nsresult nsParseMailMessageState::InternRfc822 (struct message_header *header, 
01162                                                 char **ret_name)
01163 {
01164   char *s;
01165   nsresult ret=NS_OK;
01166   
01167   if (!header || header->length == 0)
01168     return NS_OK;
01169   
01170   NS_ASSERTION (header->length == (short) strlen (header->value), "invalid message_header");
01171   NS_ASSERTION (ret_name != nsnull, "null ret_name");
01172   
01173   if (m_HeaderAddressParser)
01174   {
01175     ret = m_HeaderAddressParser->ExtractHeaderAddressName (nsnull, header->value, &s);
01176     if (! s)
01177       return NS_ERROR_OUT_OF_MEMORY;
01178     
01179     *ret_name = s;
01180   }
01181   return ret;
01182 }
01183 
01184 // we've reached the end of the envelope, and need to turn all our accumulated message_headers
01185 // into a single nsIMsgDBHdr to store in a database.
01186 int nsParseMailMessageState::FinalizeHeaders()
01187 {
01188   int status = 0;
01189   struct message_header *sender;
01190   struct message_header *recipient;
01191   struct message_header *subject;
01192   struct message_header *id;
01193   struct message_header *inReplyTo;
01194   struct message_header *replyTo;
01195   struct message_header *references;
01196   struct message_header *date;
01197   struct message_header *statush;
01198   struct message_header *mozstatus;
01199   struct message_header *mozstatus2;
01200   struct message_header *priority;
01201   struct message_header *keywords;
01202   struct message_header *account_key;
01203   struct message_header *ccList;
01204   struct message_header *mdn_dnt;
01205   struct message_header md5_header;
01206   struct message_header *content_type;
01207   char md5_data [50];
01208   
01209   const char *s;
01210   PRUint32 flags = 0;
01211   PRUint32 delta = 0;
01212   nsMsgPriorityValue priorityFlags = nsMsgPriority::notSet;
01213   PRUint32 labelFlags = 0;
01214   
01215   if (!m_mailDB)            // if we don't have a valid db, skip the header.
01216     return 0;
01217   
01218   struct message_header to;
01219   GetAggregateHeader (m_toList, &to);
01220   struct message_header cc;
01221   GetAggregateHeader (m_ccList, &cc);
01222   
01223   sender     = (m_from.length          ? &m_from :
01224   m_sender.length        ? &m_sender :
01225   m_envelope_from.length ? &m_envelope_from :
01226   0);
01227   recipient  = (to.length         ? &to :
01228   cc.length         ? &cc :
01229   m_newsgroups.length ? &m_newsgroups :
01230   sender);
01231   ccList         = (cc.length ? &cc : 0);
01232   subject    = (m_subject.length    ? &m_subject    : 0);
01233   id         = (m_message_id.length ? &m_message_id : 0);
01234   references = (m_references.length ? &m_references : 0);
01235   statush    = (m_status.length     ? &m_status     : 0);
01236   mozstatus  = (m_mozstatus.length  ? &m_mozstatus  : 0);
01237   mozstatus2 = (m_mozstatus2.length  ? &m_mozstatus2  : 0);
01238   date       = (m_date.length       ? &m_date :
01239   m_envelope_date.length ? &m_envelope_date :
01240   0);
01241   priority   = (m_priority.length   ? &m_priority   : 0);
01242   keywords   =  (m_keywords.length   ? &m_keywords  : 0);
01243   mdn_dnt        = (m_mdn_dnt.length        ? &m_mdn_dnt         : 0);
01244   inReplyTo = (m_in_reply_to.length ? &m_in_reply_to : 0);
01245   replyTo = (m_replyTo.length ? &m_replyTo : 0);
01246   content_type = (m_content_type.length ? &m_content_type : 0);
01247   account_key = (m_account_key.length ? &m_account_key :0);
01248   
01249   if (mozstatus) 
01250   {
01251     if (strlen(mozstatus->value) == 4) 
01252     {
01253       int i;
01254       for (i=0,s=mozstatus->value ; i<4 ; i++,s++) 
01255       {
01256         flags = (flags << 4) | msg_UnHex(*s);
01257       }
01258       // strip off and remember priority bits.
01259       flags &= ~MSG_FLAG_RUNTIME_ONLY;
01260       priorityFlags = (nsMsgPriorityValue) ((flags & MSG_FLAG_PRIORITIES) >> 13);
01261       flags &= ~MSG_FLAG_PRIORITIES;
01262     }
01263     delta = (m_headerstartpos +
01264       (mozstatus->value - m_headers.GetBuffer()) -
01265       (2 + X_MOZILLA_STATUS_LEN)          /* 2 extra bytes for ": ". */
01266       ) - m_envelope_pos;
01267   }
01268   
01269   if (mozstatus2)
01270   {
01271     PRUint32 flags2 = 0;
01272     sscanf(mozstatus2->value, " %x ", &flags2);
01273     flags |= flags2;
01274   }
01275   
01276   if (!(flags & MSG_FLAG_EXPUNGED))       // message was deleted, don't bother creating a hdr.
01277   {
01278     nsresult ret = m_mailDB->CreateNewHdr(m_envelope_pos, getter_AddRefs(m_newMsgHdr));
01279     if (NS_SUCCEEDED(ret) && m_newMsgHdr)
01280     {
01281       PRUint32 origFlags;
01282       (void)m_newMsgHdr->GetFlags(&origFlags);
01283       if (origFlags & MSG_FLAG_HAS_RE)
01284         flags |= MSG_FLAG_HAS_RE;
01285       else
01286         flags &= ~MSG_FLAG_HAS_RE;
01287       
01288       flags &= ~MSG_FLAG_OFFLINE; // don't keep MSG_FLAG_OFFLINE for local msgs
01289       if (mdn_dnt && !(origFlags & MSG_FLAG_READ) &&
01290           !(origFlags & MSG_FLAG_MDN_REPORT_SENT) &&
01291           !(flags & MSG_FLAG_MDN_REPORT_SENT))
01292         flags |= MSG_FLAG_MDN_REPORT_NEEDED;
01293       
01294       m_newMsgHdr->SetFlags(flags);
01295       if (priorityFlags != nsMsgPriority::notSet)
01296         m_newMsgHdr->SetPriority(priorityFlags);
01297   
01298       // if we have a reply to header, and it's different from the from: header,
01299       // set the "replyTo" attribute on the msg hdr.
01300       if (replyTo && (!sender || replyTo->length != sender->length || strncmp(replyTo->value, sender->value, sender->length)))
01301         m_newMsgHdr->SetStringProperty("replyTo", replyTo->value);
01302       // convert the flag values (0xE000000) to label values (0-5)
01303       if (mozstatus2) // only do this if we have a mozstatus2 header
01304       {
01305         labelFlags = ((flags & MSG_FLAG_LABELS) >> 25);
01306         m_newMsgHdr->SetLabel(labelFlags);
01307       }
01308       if (delta < 0xffff) 
01309       {              /* Only use if fits in 16 bits. */
01310         m_newMsgHdr->SetStatusOffset((PRUint16) delta);
01311         if (!m_IgnoreXMozillaStatus) {    // imap doesn't care about X-MozillaStatus
01312           PRUint32 offset;
01313           (void)m_newMsgHdr->GetStatusOffset(&offset);
01314           NS_ASSERTION(offset < 10000, "invalid status offset"); /* ### Debugging hack */
01315         }
01316       }
01317       if (sender)
01318         m_newMsgHdr->SetAuthor(sender->value);
01319       if (recipient == &m_newsgroups)
01320       {
01321       /* In the case where the recipient is a newsgroup, truncate the string
01322       at the first comma.  This is used only for presenting the thread list,
01323       and newsgroup lines tend to be long and non-shared, and tend to bloat
01324       the string table.  So, by only showing the first newsgroup, we can
01325       reduce memory and file usage at the expense of only showing the one
01326       group in the summary list, and only being able to sort on the first
01327         group rather than the whole list.  It's worth it. */
01328         char * ch;
01329         NS_ASSERTION (recipient->length == (PRUint16) strlen(recipient->value), "invalid recipient");
01330         ch = PL_strchr(recipient->value, ',');
01331         if (ch)
01332         {
01333           /* generate a new string that terminates before the , */
01334           nsCAutoString firstGroup;
01335           firstGroup.Assign(recipient->value, ch - recipient->value);
01336           m_newMsgHdr->SetRecipients(firstGroup.get());
01337         }
01338         m_newMsgHdr->SetRecipients(recipient->value);
01339       }
01340       else if (recipient)
01341       {
01342         // note that we're now setting the whole recipient list,
01343         // not just the pretty name of the first recipient.
01344         PRUint32 numAddresses;
01345         char  *names;
01346         char  *addresses;
01347         
01348         ret = m_HeaderAddressParser->ParseHeaderAddresses (nsnull, recipient->value, &names, &addresses, &numAddresses);
01349         if (ret == NS_OK)
01350         {
01351           m_newMsgHdr->SetRecipientsArray(names, addresses, numAddresses);
01352           PR_Free(addresses);
01353           PR_Free(names);
01354         }
01355         else {       // hmm, should we just use the original string?
01356           m_newMsgHdr->SetRecipients(recipient->value);
01357         }
01358       }
01359       if (ccList)
01360       {
01361         PRUint32 numAddresses;
01362         char  *names;
01363         char  *addresses;
01364         
01365         ret = m_HeaderAddressParser->ParseHeaderAddresses (nsnull, ccList->value, &names, &addresses, &numAddresses);
01366         if (ret == NS_OK)
01367         {
01368           m_newMsgHdr->SetCCListArray(names, addresses, numAddresses);
01369           PR_Free(addresses);
01370           PR_Free(names);
01371         }
01372         else  // hmm, should we just use the original string?
01373           m_newMsgHdr->SetCcList(ccList->value);
01374       }
01375       status = InternSubject (subject);
01376       if (status >= 0)
01377       {
01378         if (! id)
01379         {
01380           // what to do about this? we used to do a hash of all the headers...
01381           nsCAutoString hash;
01382           const char *md5_bin = "dummy message id";
01383           nsresult rv;
01384           nsCOMPtr<nsICryptoHash> hasher = do_CreateInstance("@mozilla.org/security/hash;1", &rv);
01385           if (NS_SUCCEEDED(rv))
01386           {
01387             if (NS_SUCCEEDED(hasher->Init(nsICryptoHash::MD5)) && 
01388               NS_SUCCEEDED(hasher->Update((const PRUint8*) m_headers.GetBuffer(), m_headers.GetSize())) &&
01389               NS_SUCCEEDED(hasher->Finish(PR_FALSE, hash)))
01390                   md5_bin = hash.get();
01391           }
01392           PR_snprintf (md5_data, sizeof(md5_data),
01393             "<md5:"
01394             "%02X%02X%02X%02X%02X%02X%02X%02X"
01395             "%02X%02X%02X%02X%02X%02X%02X%02X"
01396             ">",
01397             md5_bin[0], md5_bin[1], md5_bin[2], md5_bin[3],
01398             md5_bin[4], md5_bin[5], md5_bin[6], md5_bin[7],
01399             md5_bin[8], md5_bin[9], md5_bin[10],md5_bin[11],
01400             md5_bin[12],md5_bin[13],md5_bin[14],md5_bin[15]);
01401           md5_header.value = md5_data;
01402           md5_header.length = strlen(md5_data);
01403           id = &md5_header;
01404         }
01405         
01406         /* Take off <> around message ID. */
01407         if (id->value[0] == '<')
01408           id->value++, id->length--;
01409 
01410         if (id->value[id->length-1] == '>') {
01411           /* generate a new null-terminated string without the final > */
01412           nsCAutoString rawMsgId;
01413           rawMsgId.Assign(id->value, id->length - 1);
01414           m_newMsgHdr->SetMessageId(rawMsgId.get());
01415         } else {
01416           m_newMsgHdr->SetMessageId(id->value);
01417         }
01418 
01419         if (!mozstatus && statush)
01420         {
01421           /* Parse a little bit of the Berkeley Mail status header. */
01422           for (s = statush->value; *s; s++) {
01423             PRUint32 msgFlags = 0;
01424             (void)m_newMsgHdr->GetFlags(&msgFlags);
01425             switch (*s)
01426             {
01427             case 'R': case 'r':
01428               m_newMsgHdr->SetFlags(msgFlags | MSG_FLAG_READ);
01429               break;
01430             case 'D': case 'd':
01431               /* msg->flags |= MSG_FLAG_EXPUNGED;  ### Is this reasonable? */
01432               break;
01433             case 'N': case 'n':
01434             case 'U': case 'u':
01435               m_newMsgHdr->SetFlags(msgFlags & ~MSG_FLAG_READ);
01436               break;
01437             }
01438           }
01439         }
01440   
01441         if (account_key != nsnull)
01442           m_newMsgHdr->SetAccountKey(account_key->value);
01443         // use in-reply-to header as references, if there's no references header
01444         if (references != nsnull)
01445           m_newMsgHdr->SetReferences(references->value);
01446         else if (inReplyTo != nsnull)
01447           m_newMsgHdr->SetReferences(inReplyTo->value);
01448         
01449         if (!LL_IS_ZERO(m_receivedTime) && (!date || m_useReceivedDate))
01450           m_newMsgHdr->SetDate(m_receivedTime);
01451         else
01452         {
01453           // if there's no date, or it's mal-formed, use now as the time.
01454           // PR_ParseTimeString won't touch resultTime unless it succeeds.
01455           // (this doesn't affect local messages, because we use the envelope
01456           // date if there's no Date: header, but it would affect IMAP msgs
01457           // w/o a Date: hdr or Received: headers)
01458           PRTime resultTime = PR_Now();
01459           if (date)
01460             PR_ParseTimeString (date->value, PR_FALSE, &resultTime);
01461           m_newMsgHdr->SetDate(resultTime);
01462         }
01463 
01464         if (priority)
01465           m_newMsgHdr->SetPriorityString(priority->value);
01466         else if (priorityFlags == nsMsgPriority::notSet)
01467           m_newMsgHdr->SetPriority(nsMsgPriority::none);
01468         if (keywords)
01469           m_newMsgHdr->SetStringProperty("keywords", keywords->value);
01470         for (PRInt32 i = 0; i < m_customDBHeaders.Count(); i++)
01471         {
01472           if (m_customDBHeaderValues[i].length)
01473             m_newMsgHdr->SetStringProperty((m_customDBHeaders[i])->get(), m_customDBHeaderValues[i].value);
01474         }
01475         if (content_type)
01476         {
01477           char *substring = PL_strstr(content_type->value, "charset");
01478           if (substring)
01479           {
01480             char *charset = PL_strchr (substring, '=');
01481             if (charset)
01482             {
01483               charset++;
01484               /* strip leading whitespace and double-quote */
01485               while (*charset && (IS_SPACE (*charset) || '\"' == *charset))
01486                 charset++;
01487               /* strip trailing whitespace and double-quote */
01488               char *end = charset;
01489               while (*end && !IS_SPACE (*end) && '\"' != *end && ';' != *end)
01490                 end++;
01491               if (*charset)
01492               {
01493                 if (*end != '\0') {
01494                   // if we're not at the very end of the line, we need
01495                   // to generate a new string without the trailing crud
01496                   nsCAutoString rawCharSet;
01497                   rawCharSet.Assign(charset, end - charset);
01498                   m_newMsgHdr->SetCharset(rawCharSet.get());
01499                 } else {
01500                   m_newMsgHdr->SetCharset(charset);
01501                 }
01502               }
01503             }
01504           }
01505           substring = PL_strcasestr(content_type->value, "multipart/mixed");
01506           if (substring)
01507           {
01508             PRUint32 newFlags;
01509             m_newMsgHdr->OrFlags(MSG_FLAG_ATTACHMENT, &newFlags);
01510           }
01511         }
01512       }
01513     } 
01514     else
01515     {
01516       NS_ASSERTION(PR_FALSE, "error creating message header");
01517       status = NS_ERROR_OUT_OF_MEMORY;    
01518     }
01519   }
01520   else
01521     status = 0;
01522 
01523   //### why is this stuff const?
01524   char *tmp = (char*) to.value;
01525   PR_Free(tmp);
01526   tmp = (char*) cc.value;
01527   PR_Free(tmp);
01528 
01529   return status;
01530 }
01531 
01532 nsParseNewMailState::nsParseNewMailState()
01533     : m_disableFilters(PR_FALSE)
01534 {
01535   m_inboxFileStream = nsnull;
01536   m_ibuffer = nsnull;
01537   m_ibuffer_size = 0;
01538   m_ibuffer_fp = 0;
01539   m_moveCoalescer = nsnull;
01540   m_numNotNewMessages = 0;
01541  }
01542 
01543 NS_IMPL_ISUPPORTS_INHERITED1(nsParseNewMailState, nsMsgMailboxParser, nsIMsgFilterHitNotify)
01544 
01545 nsresult
01546 nsParseNewMailState::Init(nsIMsgFolder *serverFolder, nsIMsgFolder *downloadFolder, nsFileSpec &folder, 
01547                           nsIOFileStream *inboxFileStream, nsIMsgWindow *aMsgWindow,
01548                           PRBool downloadingToTempFile)
01549 {
01550   nsresult rv;
01551   m_position = folder.GetFileSize();
01552   m_rootFolder = serverFolder;
01553   m_inboxFileSpec = folder;
01554   m_inboxFileStream = inboxFileStream;
01555   m_msgWindow = aMsgWindow;
01556   m_downloadFolder = downloadFolder;
01557   m_downloadingToTempFile = downloadingToTempFile;
01558 
01559   // the new mail parser isn't going to get the stream input, it seems, so we can't use
01560   // the OnStartRequest mechanism the mailbox parser uses. So, let's open the db right now.
01561   nsCOMPtr<nsIMsgDBService> msgDBService = do_GetService(NS_MSGDB_SERVICE_CONTRACTID, &rv);
01562   if (msgDBService)
01563   {
01564     nsCOMPtr <nsIFileSpec> dbFileSpec;
01565     NS_NewFileSpecWithSpec(folder, getter_AddRefs(dbFileSpec));
01566     rv = msgDBService->OpenFolderDB(downloadFolder, PR_TRUE, PR_FALSE, (nsIMsgDatabase **) getter_AddRefs(m_mailDB));
01567   }
01568   //   rv = nsMailDatabase::Open(folder, PR_TRUE, &m_mailDB, PR_FALSE);
01569   if (NS_FAILED(rv)) 
01570     return rv;
01571   
01572   nsCOMPtr <nsIMsgFolder> rootMsgFolder = do_QueryInterface(serverFolder, &rv);
01573   NS_ENSURE_SUCCESS(rv, rv);
01574   
01575   nsCOMPtr<nsIMsgIncomingServer> server;
01576   rv = rootMsgFolder->GetServer(getter_AddRefs(server));
01577   if (NS_SUCCEEDED(rv)) 
01578   {
01579     rv = server->GetFilterList(aMsgWindow, getter_AddRefs(m_filterList));
01580   
01581     if (m_filterList) 
01582     {
01583       rv = server->ConfigureTemporaryFilters(m_filterList);
01584     }
01585     // check if this server defers to another server, in which case
01586     // we'll use that server's filters as well.
01587     nsCOMPtr <nsIMsgFolder> deferredToRootFolder;
01588     server->GetRootMsgFolder(getter_AddRefs(deferredToRootFolder));
01589     if (rootMsgFolder != deferredToRootFolder)
01590     {
01591       nsCOMPtr <nsIMsgIncomingServer> deferredToServer;
01592       deferredToRootFolder->GetServer(getter_AddRefs(deferredToServer));
01593       if (deferredToServer)
01594         deferredToServer->GetFilterList(aMsgWindow, getter_AddRefs(m_deferredToServerFilterList));
01595     }
01596   }
01597   m_disableFilters = PR_FALSE;
01598   return NS_OK; 
01599 }
01600 
01601 nsParseNewMailState::~nsParseNewMailState()
01602 {
01603   if (m_mailDB)
01604     m_mailDB->Close(PR_TRUE);
01605 #ifdef DOING_JSFILTERS
01606   JSFilter_cleanup();
01607 #endif
01608 }
01609 
01610 // not an IMETHOD so we don't need to do error checking or return an error.
01611 // We only have one caller.
01612 void nsParseNewMailState::GetMsgWindow(nsIMsgWindow **aMsgWindow)
01613 {
01614   NS_IF_ADDREF(*aMsgWindow = m_msgWindow);
01615 }
01616 
01617 
01618 // This gets called for every message because libnet calls IncorporateBegin,
01619 // IncorporateWrite (once or more), and IncorporateComplete for every message.
01620 void nsParseNewMailState::DoneParsingFolder(nsresult status)
01621 {
01622   PRBool moved = PR_FALSE;
01623   /* End of file.  Flush out any partial line remaining in the buffer. */
01624   if (m_ibuffer_fp > 0) 
01625   {
01626     ParseFolderLine(m_ibuffer, m_ibuffer_fp);
01627     m_ibuffer_fp = 0;
01628   }
01629   PublishMsgHeader(nsnull);
01630   if (!moved && m_mailDB)   // finished parsing, so flush db folder info 
01631     UpdateDBFolderInfo();
01632   
01633     /* We're done reading the folder - we don't need these things
01634         any more. */
01635   PR_FREEIF (m_ibuffer);
01636   m_ibuffer_size = 0;
01637   PR_FREEIF (m_obuffer);
01638   m_obuffer_size = 0;
01639 }
01640 
01641 PRInt32 nsParseNewMailState::PublishMsgHeader(nsIMsgWindow *msgWindow)
01642 {
01643   PRBool moved = PR_FALSE;
01644   FinishHeader();
01645   
01646   if (m_newMsgHdr)
01647   {
01648     if (!m_disableFilters)
01649     {
01650       // flush the inbox because filters will read from disk
01651       m_inboxFileStream->flush();
01652       PRUint32 msgOffset;
01653       (void) m_newMsgHdr->GetMessageOffset(&msgOffset);
01654       m_curHdrOffset = msgOffset;
01655 
01656       nsCOMPtr<nsIMsgIncomingServer> server;
01657       nsresult rv = m_rootFolder->GetServer(getter_AddRefs(server));
01658       NS_ENSURE_SUCCESS(rv, 0);
01659       PRInt32 duplicateAction;
01660       server->GetIncomingDuplicateAction(&duplicateAction);
01661       if (duplicateAction != nsIMsgIncomingServer::keepDups)
01662       {
01663         PRBool isDup;
01664         server->IsNewHdrDuplicate(m_newMsgHdr, &isDup);
01665         if (isDup)
01666         {
01667           // we want to do something similar to applying filter hits.
01668           // if a dup is marked read, it shouldn't trigger biff.
01669           // Same for deleting it or moving it to trash.
01670           switch (duplicateAction)
01671           {
01672             case nsIMsgIncomingServer::deleteDups:
01673               {
01674                 m_inboxFileStream->close();
01675 
01676                 nsresult truncRet = m_inboxFileSpec.Truncate(msgOffset);
01677                 NS_ASSERTION(NS_SUCCEEDED(truncRet), "unable to truncate file");
01678                 if (NS_FAILED(truncRet))
01679                   m_rootFolder->ThrowAlertMsg("dupDeleteFolderTruncateFailed", msgWindow);
01680 
01681                 //  need to re-open the inbox file stream.
01682                 m_inboxFileStream->Open(m_inboxFileSpec, (PR_RDWR | PR_CREATE_FILE));
01683                 if (m_inboxFileStream)
01684                   m_inboxFileStream->seek(m_inboxFileSpec.GetFileSize());
01685   
01686                 m_mailDB->RemoveHeaderMdbRow(m_newMsgHdr);
01687               }
01688               break;
01689             case nsIMsgIncomingServer::moveDupsToTrash:
01690               {
01691                 nsCOMPtr <nsIMsgFolder> trash;
01692                 GetTrashFolder(getter_AddRefs(trash));
01693                 if (trash)
01694                   MoveIncorporatedMessage(m_newMsgHdr, m_mailDB, trash,
01695                                                           nsnull, msgWindow);
01696               }
01697               break;
01698             case nsIMsgIncomingServer::markDupsRead:
01699               MarkFilteredMessageRead(m_newMsgHdr);
01700               break;
01701           }
01702           PRInt32 numNewMessages;
01703           m_downloadFolder->GetNumNewMessages(PR_FALSE, &numNewMessages);
01704           m_downloadFolder->SetNumNewMessages(numNewMessages - 1);
01705 
01706           m_newMsgHdr = nsnull;
01707           return 0;
01708         }
01709       }
01710 
01711       ApplyFilters(&moved, msgWindow, msgOffset);
01712     }
01713     if (!moved)
01714     {
01715       if (m_mailDB)
01716       {
01717         PRUint32 newFlags, oldFlags;
01718         m_newMsgHdr->GetFlags(&oldFlags);
01719         if (!(oldFlags & MSG_FLAG_READ)) // don't mark read messages as new.
01720           m_newMsgHdr->OrFlags(MSG_FLAG_NEW, &newFlags);
01721         
01722         m_mailDB->AddNewHdrToDB(m_newMsgHdr, PR_TRUE);
01723         NotifyGlobalListeners(m_newMsgHdr);
01724       }
01725     } // if it was moved by imap filter, m_parseMsgState->m_newMsgHdr == nsnull
01726     m_newMsgHdr = nsnull;
01727   }
01728   return 0;
01729 }
01730 
01731 void nsParseNewMailState::NotifyGlobalListeners(nsIMsgDBHdr *newHdr)
01732 {
01733   if (!m_notificationService)
01734     m_notificationService = do_GetService("@mozilla.org/messenger/msgnotificationservice;1");
01735   if (m_notificationService)
01736     m_notificationService->NotifyItemAdded(newHdr);
01737 }
01738 
01739 nsresult nsParseNewMailState::GetTrashFolder(nsIMsgFolder **pTrashFolder)
01740 {
01741   nsresult rv=NS_ERROR_UNEXPECTED;
01742   if (!pTrashFolder)
01743     return NS_ERROR_NULL_POINTER;
01744   
01745   if(m_downloadFolder)
01746   {
01747     nsCOMPtr <nsIMsgIncomingServer> incomingServer;
01748     m_downloadFolder->GetServer(getter_AddRefs(incomingServer));
01749     nsCOMPtr <nsIMsgFolder> rootMsgFolder;
01750     incomingServer->GetRootMsgFolder(getter_AddRefs(rootMsgFolder));
01751     if (rootMsgFolder)
01752     {
01753       PRUint32 numFolders;
01754       rv = rootMsgFolder->GetFoldersWithFlag(MSG_FOLDER_FLAG_TRASH, 1, &numFolders, pTrashFolder);
01755       if (*pTrashFolder)
01756         NS_ADDREF(*pTrashFolder);
01757     }
01758   }
01759   return rv;
01760 }
01761 
01762 void nsParseNewMailState::ApplyFilters(PRBool *pMoved, nsIMsgWindow *msgWindow, PRUint32 msgOffset)
01763 {
01764   m_msgMovedByFilter = PR_FALSE;
01765   m_curHdrOffset = msgOffset;
01766 
01767   if (!m_disableFilters)
01768   {
01769     nsCOMPtr<nsIMsgDBHdr> msgHdr = m_newMsgHdr;
01770     nsCOMPtr<nsIMsgFolder> downloadFolder = m_downloadFolder;
01771     nsCOMPtr <nsIMsgFolder> rootMsgFolder = do_QueryInterface(m_rootFolder);
01772     if (rootMsgFolder)
01773     {
01774       PRUint32 numFolders;
01775       if (!downloadFolder)
01776         rootMsgFolder->GetFoldersWithFlag(MSG_FOLDER_FLAG_INBOX, 1, &numFolders, getter_AddRefs(downloadFolder));
01777       if (downloadFolder)
01778         downloadFolder->GetURI(getter_Copies(m_inboxUri));
01779       char * headers = m_headers.GetBuffer();
01780       PRUint32 headersSize = m_headers.GetBufferPos();
01781       nsresult matchTermStatus;
01782       // get nsILocalFile from m_inboxFileSpec
01783       nsCOMPtr <nsILocalFile> localFile;
01784       NS_FileSpecToIFile(&m_inboxFileSpec, getter_AddRefs(localFile));
01785       if (m_filterList)
01786         matchTermStatus = m_filterList->ApplyFiltersToHdr(nsMsgFilterType::InboxRule,
01787                     msgHdr, downloadFolder, m_mailDB, headers, headersSize, this, msgWindow, localFile);
01788       if (!m_msgMovedByFilter && m_deferredToServerFilterList)
01789       {
01790         matchTermStatus = m_deferredToServerFilterList->ApplyFiltersToHdr(nsMsgFilterType::InboxRule,
01791                     msgHdr, downloadFolder, m_mailDB, headers, headersSize, this, msgWindow, localFile);
01792       }
01793     }
01794   }
01795   if (pMoved)
01796     *pMoved = m_msgMovedByFilter;
01797 }
01798 
01799 NS_IMETHODIMP nsParseNewMailState::ApplyFilterHit(nsIMsgFilter *filter, nsIMsgWindow *msgWindow, PRBool *applyMore)
01800 {
01801   NS_ENSURE_ARG_POINTER(applyMore);
01802   
01803   nsMsgRuleActionType actionType;
01804   nsXPIDLCString actionTargetFolderUri;
01805   PRUint32    newFlags;
01806   nsresult rv = NS_OK;
01807   
01808   *applyMore = PR_TRUE;
01809   
01810   nsCOMPtr<nsIMsgDBHdr> msgHdr = m_newMsgHdr;
01811   
01812   nsCOMPtr<nsISupportsArray> filterActionList;
01813   rv = NS_NewISupportsArray(getter_AddRefs(filterActionList));
01814   NS_ENSURE_SUCCESS(rv, rv);
01815   rv = filter->GetSortedActionList(filterActionList);
01816   NS_ENSURE_SUCCESS(rv, rv);
01817   
01818   PRUint32 numActions;
01819   rv = filterActionList->Count(&numActions);
01820   NS_ENSURE_SUCCESS(rv, rv);
01821   
01822   PRBool loggingEnabled = PR_FALSE;
01823   if (m_filterList && numActions)
01824     m_filterList->GetLoggingEnabled(&loggingEnabled);
01825   
01826   PRBool msgIsNew = PR_TRUE;
01827   for (PRUint32 actionIndex =0; actionIndex < numActions && *applyMore; actionIndex++)
01828   {
01829     nsCOMPtr<nsIMsgRuleAction> filterAction;
01830     filterActionList->QueryElementAt(actionIndex, NS_GET_IID(nsIMsgRuleAction), getter_AddRefs(filterAction));
01831     if (!filterAction)
01832       continue;
01833     
01834     if (NS_SUCCEEDED(filterAction->GetType(&actionType)))
01835     {
01836       if (actionType == nsMsgFilterAction::MoveToFolder ||
01837           actionType == nsMsgFilterAction::CopyToFolder)
01838       {
01839         filterAction->GetTargetFolderUri(getter_Copies(actionTargetFolderUri));
01840         if (actionTargetFolderUri.IsEmpty())
01841         {
01842           NS_ASSERTION(PR_FALSE, "actionTargetFolderUri is empty");
01843           continue;
01844         }
01845       }
01846       switch (actionType)
01847       {
01848       case nsMsgFilterAction::Delete:
01849         {
01850           nsCOMPtr <nsIMsgFolder> trash;
01851           // set value to trash folder
01852           rv = GetTrashFolder(getter_AddRefs(trash));
01853           if (NS_SUCCEEDED(rv) && trash)
01854             rv = trash->GetURI(getter_Copies(actionTargetFolderUri));
01855           
01856           msgHdr->OrFlags(MSG_FLAG_READ, &newFlags);    // mark read in trash.
01857           msgIsNew = PR_FALSE;
01858         }
01859       case nsMsgFilterAction::MoveToFolder:
01860         // if moving to a different file, do it.
01861         if ((const char*)actionTargetFolderUri &&
01862           nsCRT::strcasecmp(m_inboxUri, actionTargetFolderUri))
01863         {
01864           nsresult err;
01865           nsCOMPtr<nsIRDFService> rdf(do_GetService(kRDFServiceCID, &err)); 
01866           NS_ENSURE_SUCCESS(err, err);
01867           nsCOMPtr<nsIRDFResource> res;
01868           err = rdf->GetResource(actionTargetFolderUri, getter_AddRefs(res));
01869           if (NS_FAILED(err))
01870             return err;
01871   
01872           nsCOMPtr<nsIMsgFolder> destIFolder(do_QueryInterface(res, &err));
01873           if (NS_FAILED(err))
01874             return err;        
01875   
01876 
01877           if (StringBeginsWith(actionTargetFolderUri, NS_LITERAL_CSTRING("imap:")))
01878           {
01879             if (!m_moveCoalescer)
01880               m_moveCoalescer = new nsImapMoveCoalescer(m_downloadFolder, m_msgWindow);
01881             NS_ENSURE_TRUE(m_moveCoalescer, NS_ERROR_OUT_OF_MEMORY);
01882             nsMsgKey msgKey;
01883             (void) msgHdr->GetMessageKey(&msgKey);
01884             m_moveCoalescer->AddMove(destIFolder , msgKey);
01885             if (loggingEnabled)
01886               (void)filter->LogRuleHit(filterAction, msgHdr); 
01887             err = NS_OK;
01888             msgIsNew = PR_FALSE;
01889           }
01890           else
01891           {
01892             err = MoveIncorporatedMessage(msgHdr, m_mailDB, destIFolder, filter, msgWindow);
01893             m_msgMovedByFilter = NS_SUCCEEDED(err);
01894             if (m_msgMovedByFilter)
01895             {
01896               if (loggingEnabled)
01897                 (void)filter->LogRuleHit(filterAction, msgHdr); 
01898               // if we're downloading to a temp file, our message key is wrong,
01899               // i.e., relative to the temp file and not the original mailbox,
01900               // and we need to let nsPop3Sink remove the message hdr after
01901               // it fixes the key.
01902               if (!m_downloadingToTempFile)
01903                 m_mailDB->RemoveHeaderMdbRow(msgHdr);
01904             }
01905           }
01906         }
01907         *applyMore = PR_FALSE; 
01908         break;
01909         case nsMsgFilterAction::CopyToFolder:
01910         {
01911           nsXPIDLCString uri;
01912           rv = m_rootFolder->GetURI(getter_Copies(uri));
01913 
01914           if (NS_STATIC_CAST(const char*, actionTargetFolderUri) &&
01915             strcmp(uri, actionTargetFolderUri))
01916           {
01917 
01918             nsCOMPtr<nsISupportsArray> messageArray;
01919             NS_NewISupportsArray(getter_AddRefs(messageArray));
01920             messageArray->AppendElement(msgHdr);
01921 
01922             nsCOMPtr<nsIMsgFolder> dstFolder;
01923             rv = GetExistingFolder(actionTargetFolderUri,
01924                                    getter_AddRefs(dstFolder));
01925             NS_ENSURE_SUCCESS(rv, rv);
01926 
01927             nsCOMPtr<nsIMsgCopyService> copyService =
01928               do_GetService(NS_MSGCOPYSERVICE_CONTRACTID, &rv);
01929             NS_ENSURE_SUCCESS(rv, rv);
01930             rv = copyService->CopyMessages(m_downloadFolder, messageArray, dstFolder,
01931                                            PR_FALSE, nsnull, msgWindow, PR_FALSE);
01932             NS_ENSURE_SUCCESS(rv, rv);
01933           }
01934         }
01935         break;
01936       case nsMsgFilterAction::MarkRead:
01937         msgIsNew = PR_FALSE;
01938         MarkFilteredMessageRead(msgHdr);
01939         break;
01940       case nsMsgFilterAction::KillThread:
01941         // The db will check for this flag when a hdr gets added to the db, and set the flag appropriately on the thread object
01942         msgHdr->OrFlags(MSG_FLAG_IGNORED, &newFlags);
01943         break;
01944       case nsMsgFilterAction::WatchThread:
01945         msgHdr->OrFlags(MSG_FLAG_WATCHED, &newFlags);
01946         break;
01947       case nsMsgFilterAction::MarkFlagged:
01948         msgHdr->MarkFlagged(PR_TRUE);
01949         break;
01950       case nsMsgFilterAction::ChangePriority:
01951         nsMsgPriorityValue filterPriority;
01952         filterAction->GetPriority(&filterPriority);
01953         msgHdr->SetPriority(filterPriority);
01954         break;
01955       case nsMsgFilterAction::AddTag:
01956       {
01957         nsXPIDLCString keyword;
01958         filterAction->GetStrValue(getter_Copies(keyword));
01959         nsCOMPtr<nsISupportsArray> messageArray;
01960         NS_NewISupportsArray(getter_AddRefs(messageArray));
01961         messageArray->AppendElement(msgHdr);
01962         m_downloadFolder->AddKeywordsToMessages(messageArray, keyword.get());
01963         break;
01964       }
01965       case nsMsgFilterAction::Label:
01966         nsMsgLabelValue filterLabel;
01967         filterAction->GetLabel(&filterLabel);
01968         nsMsgKey msgKey;
01969         msgHdr->GetMessageKey(&msgKey);
01970         m_mailDB->SetLabel(msgKey, filterLabel);
01971         break;
01972       case nsMsgFilterAction::JunkScore:
01973       {
01974         nsCAutoString junkScoreStr;
01975         PRInt32 junkScore;
01976         filterAction->GetJunkScore(&junkScore);
01977         junkScoreStr.AppendInt(junkScore);
01978         if (junkScore > 50)
01979           msgIsNew = PR_FALSE;
01980         nsMsgKey msgKey;
01981         msgHdr->GetMessageKey(&msgKey);
01982         msgHdr->SetStringProperty("junkscore", junkScoreStr.get());
01983         msgHdr->SetStringProperty("junkscoreorigin", "plugin");
01984         break;
01985       }
01986       case nsMsgFilterAction::Forward:
01987         {
01988           nsXPIDLCString forwardTo;
01989           filterAction->GetStrValue(getter_Copies(forwardTo));
01990           m_forwardTo.AppendCString(forwardTo);
01991           m_msgToForwardOrReply = msgHdr;
01992         }
01993         break;
01994       case nsMsgFilterAction::Reply:
01995         {
01996           nsXPIDLCString replyTemplateUri;
01997           filterAction->GetStrValue(getter_Copies(replyTemplateUri));
01998           m_replyTemplateUri.AppendCString(replyTemplateUri);
01999           m_msgToForwardOrReply = msgHdr;
02000         }
02001         break;
02002       case nsMsgFilterAction::DeleteFromPop3Server:
02003         {
02004           PRUint32 flags = 0;
02005           nsCOMPtr <nsIMsgFolder> downloadFolder;
02006           msgHdr->GetFolder(getter_AddRefs(downloadFolder));
02007           nsCOMPtr <nsIMsgLocalMailFolder> localFolder = do_QueryInterface(downloadFolder);
02008           msgHdr->GetFlags(&flags);
02009           if (localFolder)
02010           {
02011             nsCOMPtr<nsISupportsArray> messages;
02012             rv = NS_NewISupportsArray(getter_AddRefs(messages));
02013             NS_ENSURE_SUCCESS(rv, rv);
02014             nsCOMPtr<nsISupports> iSupports = do_QueryInterface(msgHdr);
02015             messages->AppendElement(iSupports);
02016             // This action ignores the deleteMailLeftOnServer preference
02017             localFolder->MarkMsgsOnPop3Server(messages, POP3_FORCE_DEL);
02018 
02019             // If this is just a header, throw it away. It's useless now
02020             // that the server copy is being deleted.
02021             if (flags & MSG_FLAG_PARTIAL)
02022             {
02023               m_msgMovedByFilter = PR_TRUE;
02024               msgIsNew = PR_FALSE;
02025             }
02026           }
02027         }
02028         break;
02029       case nsMsgFilterAction::FetchBodyFromPop3Server:
02030         {
02031          PRUint32 flags = 0;
02032           nsCOMPtr <nsIMsgFolder> downloadFolder;
02033           msgHdr->GetFolder(getter_AddRefs(downloadFolder));
02034           nsCOMPtr <nsIMsgLocalMailFolder> localFolder = do_QueryInterface(downloadFolder);
02035           msgHdr->GetFlags(&flags);
02036           if (localFolder && (flags & MSG_FLAG_PARTIAL))
02037           {
02038             nsCOMPtr<nsISupportsArray> messages;
02039             rv = NS_NewISupportsArray(getter_AddRefs(messages));
02040             NS_ENSURE_SUCCESS(rv, rv);
02041             nsCOMPtr<nsISupports> iSupports = do_QueryInterface(msgHdr);
02042             messages->AppendElement(iSupports);
02043             localFolder->MarkMsgsOnPop3Server(messages, POP3_FETCH_BODY);
02044             // Don't add this header to the DB, we're going to replace it
02045             // with the full message.
02046             m_msgMovedByFilter = PR_TRUE;
02047             msgIsNew = PR_FALSE;
02048             // Don't do anything else in this filter, wait until we
02049             // have the full message.
02050            *applyMore = PR_FALSE;
02051           }
02052         }
02053         break;
02054       default:
02055         break;
02056       }
02057       if (loggingEnabled && actionType != nsMsgFilterAction::MoveToFolder && actionType != nsMsgFilterAction::Delete)
02058         (void)filter->LogRuleHit(filterAction, msgHdr); 
02059     }
02060   }
02061   if (!msgIsNew)
02062   {
02063     PRInt32 numNewMessages;
02064     m_downloadFolder->GetNumNewMessages(PR_FALSE, &numNewMessages);
02065     if (numNewMessages > 0)
02066       m_downloadFolder->SetNumNewMessages(numNewMessages - 1);
02067     m_numNotNewMessages++;
02068   }
02069   return rv;
02070 }
02071 
02072 // this gets run in a second pass, after apply filters to a header.
02073 nsresult nsParseNewMailState::ApplyForwardAndReplyFilter(nsIMsgWindow *msgWindow)
02074 {
02075   nsresult rv = NS_OK;
02076   nsCOMPtr <nsIMsgIncomingServer> server;
02077 
02078   PRInt32 i;
02079   for (i = 0; i < m_forwardTo.Count(); i++)
02080   {
02081     if (!m_forwardTo[i]->IsEmpty())
02082     {
02083       nsAutoString forwardStr;
02084       forwardStr.AssignWithConversion(m_forwardTo[i]->get());
02085 
02086       rv = m_rootFolder->GetServer(getter_AddRefs(server));
02087       NS_ENSURE_SUCCESS(rv, rv);
02088       {
02089         nsCOMPtr <nsIMsgComposeService> compService = do_GetService (NS_MSGCOMPOSESERVICE_CONTRACTID) ;
02090         if (compService)
02091           rv = compService->ForwardMessage(forwardStr, m_msgToForwardOrReply, msgWindow, server);
02092       }
02093     }
02094   }
02095   m_forwardTo.Clear();
02096 
02097   for (i = 0; i < m_replyTemplateUri.Count(); i++)
02098   {
02099     if (!m_replyTemplateUri[i]->IsEmpty())
02100     {
02101       // copy this and truncate the original, so we don't accidentally re-use it on the next hdr.
02102       nsCAutoString replyTemplateUri(*m_replyTemplateUri[i]);
02103       rv = m_rootFolder->GetServer(getter_AddRefs(server));
02104       if (server && !replyTemplateUri.IsEmpty())
02105       {
02106         nsCOMPtr <nsIMsgComposeService> compService = do_GetService (NS_MSGCOMPOSESERVICE_CONTRACTID) ;
02107         if (compService)
02108           rv = compService->ReplyWithTemplate(m_msgToForwardOrReply, replyTemplateUri.get(), msgWindow, server);
02109       }
02110     }
02111   }
02112   m_replyTemplateUri.Clear();
02113   m_msgToForwardOrReply = nsnull;
02114   return rv;
02115 }
02116 
02117 
02118 int nsParseNewMailState::MarkFilteredMessageRead(nsIMsgDBHdr *msgHdr)
02119 {
02120   PRUint32 newFlags;
02121   if (m_mailDB)
02122     m_mailDB->MarkHdrRead(msgHdr, PR_TRUE, nsnull);
02123   else
02124     msgHdr->OrFlags(MSG_FLAG_READ, &newFlags);
02125   return 0;
02126 }
02127 
02128 nsresult nsParseNewMailState::EndMsgDownload()
02129 {
02130   if (m_moveCoalescer)
02131     m_moveCoalescer->PlaybackMoves();
02132 
02133   // need to do this for all folders that had messages filtered into them
02134   PRUint32 serverCount = m_filterTargetFolders.Count();
02135   nsresult rv;
02136   nsCOMPtr<nsIMsgMailSession> session = 
02137            do_GetService(NS_MSGMAILSESSION_CONTRACTID, &rv); 
02138   if (NS_SUCCEEDED(rv) && session) // don't use NS_ENSURE_SUCCESS here - we need to release semaphore below
02139   {
02140     for (PRUint32 index = 0; index < serverCount; index++)
02141     {
02142       PRBool folderOpen;
02143       session->IsFolderOpenInWindow(m_filterTargetFolders[index], &folderOpen);
02144       if (!folderOpen)
02145       {
02146         PRUint32 folderFlags;
02147         m_filterTargetFolders[index]->GetFlags(&folderFlags);
02148         if (! (folderFlags & (MSG_FOLDER_FLAG_TRASH | MSG_FOLDER_FLAG_INBOX)))
02149         {
02150           PRBool filtersRun;
02151           m_filterTargetFolders[index]->CallFilterPlugins(nsnull, &filtersRun);
02152           if (!filtersRun)
02153             m_filterTargetFolders[index]->SetMsgDatabase(nsnull);
02154         }
02155       }
02156     }
02157   }
02158   m_filterTargetFolders.Clear();
02159   return rv;
02160 }
02161 
02162 nsresult nsParseNewMailState::AppendMsgFromFile(nsIOFileStream *fileStream, 
02163                                                 PRUint32 offset, PRUint32 length,
02164                                                 nsFileSpec &destFileSpec)
02165 {
02166   fileStream->seek(PR_SEEK_SET, offset);
02167 
02168   nsIOFileStream *destFile = new nsIOFileStream(destFileSpec, PR_WRONLY | PR_CREATE_FILE);
02169   
02170   if (!destFile) 
02171   {
02172 #ifdef DEBUG_bienvenu
02173     NS_ASSERTION(PR_FALSE, "out of memory");
02174 #endif
02175     return  NS_MSG_ERROR_WRITING_MAIL_FOLDER;
02176   }
02177   
02178   destFile->seek(PR_SEEK_END, 0);
02179   PRInt32 newMsgPos = destFile->tell();
02180   
02181   if (!m_ibuffer)
02182     m_ibuffer_size = 10240;
02183   m_ibuffer_fp = 0;
02184   
02185   while (!m_ibuffer && (m_ibuffer_size >= 512))
02186   {
02187     m_ibuffer = (char *) PR_Malloc(m_ibuffer_size);
02188     if (m_ibuffer == nsnull)
02189       m_ibuffer_size /= 2;
02190   }
02191   NS_ASSERTION(m_ibuffer != nsnull, "couldn't get memory to move msg");
02192   while ((length > 0) && m_ibuffer)
02193   {
02194     PRUint32 nRead = m_inboxFileStream->read (m_ibuffer, length > m_ibuffer_size ? m_ibuffer_size  : length);
02195     if (nRead == 0)
02196       break;
02197     
02198     // we must monitor the number of bytes actually written to the file. (mscott)
02199     if (destFile->write(m_ibuffer, nRead) != (PRInt32) nRead) 
02200     {
02201       destFile->close();     
02202       
02203       // truncate  destination file in case message was partially written
02204       // ### how to do this with a stream?
02205       destFileSpec.Truncate(newMsgPos);
02206       delete destFile;
02207       return NS_MSG_ERROR_WRITING_MAIL_FOLDER;
02208     }
02209     
02210     length -= nRead;
02211   }
02212   
02213   NS_ASSERTION(length == 0, "didn't read all of original message in filter move");
02214   delete destFile;
02215   return NS_OK;
02216 }
02217 
02218 nsresult nsParseNewMailState::MoveIncorporatedMessage(nsIMsgDBHdr *mailHdr, 
02219                                                       nsIMsgDatabase *sourceDB, 
02220                                                       nsIMsgFolder *destIFolder,
02221                                                       nsIMsgFilter *filter,
02222                                                       nsIMsgWindow *msgWindow)
02223 {
02224   nsresult err = 0;
02225   
02226   // check if the destination is a real folder (by checking for null parent)
02227   // and if it can file messages (e.g., servers or news folders can't file messages).
02228   // Or read only imap folders...
02229   PRBool canFileMessages = PR_TRUE;
02230   nsCOMPtr<nsIMsgFolder> parentFolder;
02231   destIFolder->GetParent(getter_AddRefs(parentFolder));
02232   if (parentFolder)
02233     destIFolder->GetCanFileMessages(&canFileMessages);
02234   if (!parentFolder || !canFileMessages)
02235   {
02236     if (filter)
02237     {
02238       filter->SetEnabled(PR_FALSE);
02239       // we need to explicitly save the filter file.
02240       if (m_filterList)
02241         m_filterList->SaveToDefaultFile();
02242       destIFolder->ThrowAlertMsg("filterDisabled", msgWindow);
02243     }
02244     return NS_MSG_NOT_A_MAIL_FOLDER;
02245   }
02246   
02247   nsCOMPtr <nsIMsgLocalMailFolder> destLocalFolder = do_QueryInterface(destIFolder);
02248   if (destLocalFolder)
02249   {
02250     PRBool destFolderTooBig;
02251     destLocalFolder->WarnIfLocalFileTooBig(msgWindow, &destFolderTooBig);
02252     if (destFolderTooBig)
02253       return NS_MSG_ERROR_WRITING_MAIL_FOLDER;
02254   }
02255   nsCOMPtr <nsIFileSpec> destIFolderSpec;
02256 
02257   nsFileSpec destFolderSpec;
02258   destIFolder->GetPath(getter_AddRefs(destIFolderSpec));
02259   err = destIFolderSpec->GetFileSpec(&destFolderSpec);
02260   
02261   if (NS_FAILED(err))
02262     return err;
02263   
02264   nsCOMPtr <nsISupports> myISupports = do_QueryInterface(NS_STATIC_CAST(nsIMsgParseMailMsgState*, this));
02265   
02266   //   NS_RELEASE(myThis);
02267   // Make sure no one else is writing into this folder
02268   if (destIFolder && (err = destIFolder->AcquireSemaphore (myISupports)) != 0)
02269   {
02270     destIFolder->ThrowAlertMsg("filterFolderDeniedLocked", msgWindow);
02271     return err;
02272   }
02273   
02274   NS_ASSERTION(m_inboxFileStream != 0, "no input file stream");
02275   if (m_inboxFileStream == 0)
02276   {
02277 #ifdef DEBUG_bienvenu
02278     NS_ASSERTION(PR_FALSE, "couldn't get source file in move filter");
02279 #endif
02280     if (destIFolder)
02281       destIFolder->ReleaseSemaphore (myISupports);
02282     
02283     return NS_MSG_FOLDER_UNREADABLE;      // ### dmb
02284   }
02285   
02286   m_inboxFileStream->seek(PR_SEEK_SET, m_curHdrOffset);
02287   PRUint32 newMsgPos = destFolderSpec.GetFileSize();
02288   
02289   nsCOMPtr<nsIMsgLocalMailFolder> localFolder = do_QueryInterface(destIFolder);
02290   nsCOMPtr<nsIMsgDatabase> destMailDB;
02291   
02292   if (!localFolder)
02293     return NS_MSG_POP_FILTER_TARGET_ERROR;
02294   
02295   nsresult rv = localFolder->GetDatabaseWOReparse(getter_AddRefs(destMailDB));
02296   NS_ASSERTION(destMailDB, "failed to open mail db parsing folder");
02297   // don't force upgrade in place - open the db here before we start writing to the 
02298   // destination file because XP_Stat can return file size including bytes written...
02299 
02300   PRUint32 messageLength;
02301   mailHdr->GetMessageSize(&messageLength);
02302   rv = AppendMsgFromFile(m_inboxFileStream, m_curHdrOffset, messageLength, destFolderSpec);
02303 
02304   if (NS_FAILED(rv))
02305   {
02306     if (destMailDB)
02307       destMailDB->Close(PR_TRUE);
02308 
02309     if (destIFolder)
02310     {
02311       destIFolder->ReleaseSemaphore(myISupports);
02312       destIFolder->ThrowAlertMsg("filterFolderWriteFailed", msgWindow);
02313     }
02314     return NS_MSG_ERROR_WRITING_MAIL_FOLDER;
02315   }
02316     
02317   PRBool movedMsgIsNew = PR_FALSE;
02318   // if we have made it this far then the message has successfully been written to the new folder
02319   // now add the header to the destMailDB.
02320   if (NS_SUCCEEDED(rv) && destMailDB)
02321   {
02322     nsCOMPtr <nsIMsgDBHdr> newHdr;
02323     
02324     nsresult msgErr = destMailDB->CopyHdrFromExistingHdr(newMsgPos, mailHdr, PR_FALSE, getter_AddRefs(newHdr));
02325     if (NS_SUCCEEDED(msgErr) && newHdr)
02326     {
02327       PRUint32 newFlags;
02328       // set new byte offset, since the offset in the old file is certainly wrong
02329       newHdr->SetMessageKey (newMsgPos); 
02330       newHdr->GetFlags(&newFlags);
02331       if (! (newFlags & MSG_FLAG_READ))
02332       {
02333         nsXPIDLCString junkScoreStr;
02334         (void) newHdr->GetStringProperty("junkscore", getter_Copies(junkScoreStr));
02335         if (atoi(junkScoreStr.get()) < 50)
02336         {
02337           newHdr->OrFlags(MSG_FLAG_NEW, &newFlags);
02338           destMailDB->AddToNewList(newMsgPos);
02339           movedMsgIsNew = PR_TRUE;
02340         }
02341       }
02342       destMailDB->AddNewHdrToDB(newHdr, PR_TRUE);
02343       NotifyGlobalListeners(newHdr);
02344       m_msgToForwardOrReply = newHdr;
02345     }
02346   }
02347   else
02348   {
02349     if (destMailDB)
02350       destMailDB = nsnull;
02351   }
02352   if (movedMsgIsNew)
02353     destIFolder->SetHasNewMessages(PR_TRUE);
02354   if (m_filterTargetFolders.IndexOf(destIFolder) == kNotFound)
02355     m_filterTargetFolders.AppendObject(destIFolder);
02356   m_inboxFileStream->close();
02357 
02358   nsresult truncRet = m_inboxFileSpec.Truncate(m_curHdrOffset);
02359   NS_ASSERTION(NS_SUCCEEDED(truncRet), "unable to truncate file");
02360   if (NS_FAILED(truncRet))
02361    destIFolder->ThrowAlertMsg("filterFolderTruncateFailed", msgWindow);
02362   else
02363     // tell parser that we've truncated the Inbox
02364     nsParseMailMessageState::Init(m_curHdrOffset);
02365 
02366   //  need to re-open the inbox file stream.
02367   m_inboxFileStream->Open(m_inboxFileSpec, (PR_RDWR | PR_CREATE_FILE));
02368   if (m_inboxFileStream)
02369     m_inboxFileStream->seek(m_inboxFileSpec.GetFileSize());
02370   
02371   if (destIFolder)
02372     destIFolder->ReleaseSemaphore (myISupports);
02373   
02374   
02375   (void) localFolder->RefreshSizeOnDisk();
02376   if (destIFolder)
02377     destIFolder->SetFlag(MSG_FOLDER_FLAG_GOT_NEW);
02378   
02379   if (destMailDB != nsnull)
02380   {
02381     // update the folder size so we won't reparse.
02382     UpdateDBFolderInfo(destMailDB);
02383     if (destIFolder != nsnull)
02384       destIFolder->UpdateSummaryTotals(PR_TRUE);
02385     
02386     destMailDB->Commit(nsMsgDBCommitType::kLargeCommit);
02387   }  
02388   return err;
02389 }
02390