Back to index

lightning-sunbird  0.9+nobinonly
nsLocalMailFolder.cpp
Go to the documentation of this file.
00001 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
00002 /* ***** BEGIN LICENSE BLOCK *****
00003  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
00004  *
00005  * The contents of this file are subject to the Mozilla Public License Version
00006  * 1.1 (the "License"); you may not use this file except in compliance with
00007  * the License. You may obtain a copy of the License at
00008  * http://www.mozilla.org/MPL/
00009  *
00010  * Software distributed under the License is distributed on an "AS IS" basis,
00011  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
00012  * for the specific language governing rights and limitations under the
00013  * License.
00014  *
00015  * The Original Code is mozilla.org code.
00016  *
00017  * The Initial Developer of the Original Code is
00018  * Netscape Communications Corporation.
00019  * Portions created by the Initial Developer are Copyright (C) 1998
00020  * the Initial Developer. All Rights Reserved.
00021  *
00022  * Contributor(s):
00023  *   jefft@netscape.com
00024  *   putterman@netscape.com
00025  *   bienvenu@nventure.com
00026  *   warren@netscape.com
00027  *   alecf@netscape.com
00028  *   sspitzer@netscape.com
00029  *   Pierre Phaneuf <pp@ludusdesign.com>
00030  *   Howard Chu <hyc@highlandsun.com>
00031  *   William Bonnet <wbonnet@on-x.com>
00032  *
00033  * Alternatively, the contents of this file may be used under the terms of
00034  * either of the GNU General Public License Version 2 or later (the "GPL"),
00035  * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
00036  * in which case the provisions of the GPL or the LGPL are applicable instead
00037  * of those above. If you wish to allow use of your version of this file only
00038  * under the terms of either the GPL or the LGPL, and not to allow others to
00039  * use your version of this file under the terms of the MPL, indicate your
00040  * decision by deleting the provisions above and replace them with the notice
00041  * and other provisions required by the GPL or the LGPL. If you do not delete
00042  * the provisions above, a recipient may use your version of this file under
00043  * the terms of any one of the MPL, the GPL or the LGPL.
00044  *
00045  * ***** END LICENSE BLOCK ***** */
00046 
00047 #include "nsIPrefService.h"
00048 #include "nsIPrefBranch.h"
00049 #include "prlog.h"
00050 
00051 #include "msgCore.h"    // precompiled header...
00052 
00053 #include "nsLocalMailFolder.h"   
00054 #include "nsMsgLocalFolderHdrs.h"
00055 #include "nsMsgFolderFlags.h"
00056 #include "nsMsgMessageFlags.h"
00057 #include "prprf.h"
00058 #include "nsISupportsArray.h"
00059 #include "nsIServiceManager.h"
00060 #include "nsIEnumerator.h"
00061 #include "nsIMailboxService.h"
00062 #include "nsParseMailbox.h"
00063 #include "nsIMsgAccountManager.h"
00064 #include "nsIMsgWindow.h"
00065 #include "nsCOMPtr.h"
00066 #include "nsIRDFService.h"
00067 #include "nsIRDFDataSource.h"
00068 #include "nsRDFCID.h"
00069 #include "nsFileStream.h"
00070 #include "nsMsgDBCID.h"
00071 #include "nsMsgUtils.h"
00072 #include "nsLocalUtils.h"
00073 #include "nsIPop3IncomingServer.h"
00074 #include "nsILocalMailIncomingServer.h"
00075 #include "nsIMsgIncomingServer.h"
00076 #include "nsMsgBaseCID.h"
00077 #include "nsMsgLocalCID.h"
00078 #include "nsString.h"
00079 #include "nsIMsgFolderCacheElement.h"
00080 #include "nsReadableUtils.h"
00081 #include "nsUnicharUtils.h"
00082 #include "nsLocalFolderSummarySpec.h"
00083 #include "nsMsgUtils.h"
00084 #include "nsICopyMsgStreamListener.h"
00085 #include "nsIMsgCopyService.h"
00086 #include "nsLocalUndoTxn.h"
00087 #include "nsMsgTxn.h"
00088 #include "nsIFileSpec.h"
00089 #include "nsIMessenger.h"
00090 #include "nsMsgBaseCID.h"
00091 #include "nsNativeCharsetUtils.h"
00092 #include "nsIDocShell.h"
00093 #include "nsIPrompt.h"
00094 #include "nsIInterfaceRequestor.h"
00095 #include "nsIInterfaceRequestorUtils.h"
00096 #include "nsIPop3URL.h"
00097 #include "nsIMsgMailSession.h"
00098 #include "nsIMsgFolderCompactor.h"
00099 #include "nsNetCID.h"
00100 #include "nsEscape.h"
00101 #include "nsLocalStringBundle.h"
00102 #include "nsIMsgMailNewsUrl.h"
00103 #include "nsISpamSettings.h"
00104 #include "nsINoIncomingServer.h"
00105 #include "nsNativeCharsetUtils.h"
00106 #include "nsMailHeaders.h"
00107 #include "nsCOMArray.h"
00108 #include "nsILineInputStream.h"
00109 #include "nsIFileStreams.h"
00110 #include "nsAutoPtr.h"
00111 #include "nsIRssIncomingServer.h"
00112 #include "nsNetUtil.h"
00113 #include "nsIMsgFolderNotificationService.h"
00114 
00115 static NS_DEFINE_CID(kMailboxServiceCID,          NS_MAILBOXSERVICE_CID);
00116 static NS_DEFINE_CID(kCMailDB, NS_MAILDB_CID);
00117 
00119 // nsLocal
00121 
00122 nsLocalMailCopyState::nsLocalMailCopyState() :
00123   m_fileStream(nsnull), m_curDstKey(0xffffffff), m_curCopyIndex(0),
00124   m_totalMsgCount(0), m_dataBufferSize(0), m_leftOver(0),
00125   m_isMove(PR_FALSE), m_dummyEnvelopeNeeded(PR_FALSE), m_fromLineSeen(PR_FALSE), m_writeFailed(PR_FALSE),
00126   m_notifyFolderLoaded(PR_FALSE)
00127 {
00128   LL_I2L(m_lastProgressTime, PR_IntervalToMilliseconds(PR_IntervalNow()));
00129 }
00130 
00131 nsLocalMailCopyState::~nsLocalMailCopyState()
00132 {
00133   PR_Free(m_dataBuffer);
00134   if (m_fileStream)
00135   {
00136     if (m_fileStream->is_open())
00137     m_fileStream->close();
00138     delete m_fileStream;
00139   }
00140   if (m_messageService)
00141   {
00142     nsCOMPtr <nsIMsgFolder> srcFolder = do_QueryInterface(m_srcSupport);
00143     if (srcFolder && m_message)
00144     {
00145       nsXPIDLCString uri;
00146       srcFolder->GetUriForMsg(m_message, getter_Copies(uri));
00147     }
00148   }
00149 }
00150   
00151 nsLocalFolderScanState::nsLocalFolderScanState() :
00152   m_fileSpec(nsnull), m_uidl(nsnull)
00153 {
00154 }
00155 
00156 nsLocalFolderScanState::~nsLocalFolderScanState()
00157 {
00158 }
00159 
00161 // nsMsgLocalMailFolder interface
00163 
00164 nsMsgLocalMailFolder::nsMsgLocalMailFolder(void)
00165   : mHaveReadNameFromDB(PR_FALSE),
00166     mInitialized(PR_FALSE), mCopyState(nsnull), mType(nsnull),
00167     mCheckForNewMessagesAfterParsing(PR_FALSE), mNumFilterClassifyRequests(0), 
00168     m_parsingFolder(PR_FALSE), mDownloadState(DOWNLOAD_STATE_NONE)
00169 {
00170 }
00171 
00172 nsMsgLocalMailFolder::~nsMsgLocalMailFolder(void)
00173 {
00174 }
00175 
00176 NS_IMPL_ISUPPORTS_INHERITED3(nsMsgLocalMailFolder,
00177                              nsMsgDBFolder,
00178                              nsICopyMessageListener,
00179                              nsIMsgLocalMailFolder,
00180                              nsIJunkMailClassificationListener)
00181 
00182 
00183 
00184 static PRBool
00185 nsStringEndsWith(nsString& name, const char *ending)
00186 {
00187   PRInt32 len = name.Length();
00188   if (len == 0) return PR_FALSE;
00189 
00190   PRInt32 endingLen = strlen(ending);
00191   return (len > endingLen && name.RFind(ending, PR_TRUE) == len - endingLen);
00192 }
00193   
00194 static PRBool
00195 nsShouldIgnoreFile(nsString& name)
00196 {
00197   PRUnichar firstChar=name.CharAt(0);
00198   if (firstChar == '.' || firstChar == '#' || name.CharAt(name.Length() - 1) == '~')
00199     return PR_TRUE;
00200 
00201   if (name.LowerCaseEqualsLiteral("msgfilterrules.dat") ||
00202       name.LowerCaseEqualsLiteral("rules.dat") || 
00203       name.LowerCaseEqualsLiteral("filterlog.html") || 
00204       name.LowerCaseEqualsLiteral("junklog.html") || 
00205       name.LowerCaseEqualsLiteral("rulesbackup.dat"))
00206     return PR_TRUE;
00207 
00208 
00209   // don't add summary files to the list of folders;
00210   // don't add popstate files to the list either, or rules (sort.dat). 
00211   if (nsStringEndsWith(name, ".snm") ||
00212       name.LowerCaseEqualsLiteral("popstate.dat") ||
00213       name.LowerCaseEqualsLiteral("sort.dat") ||
00214       name.LowerCaseEqualsLiteral("mailfilt.log") ||
00215       name.LowerCaseEqualsLiteral("filters.js") ||
00216       nsStringEndsWith(name, ".toc"))
00217     return PR_TRUE;
00218 
00219   // ignore RSS data source files
00220   if (name.LowerCaseEqualsLiteral("feeds.rdf") ||
00221       name.LowerCaseEqualsLiteral("feeditems.rdf"))
00222     return PR_TRUE;
00223 
00224   // The .mozmsgs dir is for spotlight support
00225     return (nsStringEndsWith(name, ".mozmsgs") || nsStringEndsWith(name,".sbd") ||
00226             nsStringEndsWith(name,".msf"));
00227 }
00228 
00229 NS_IMETHODIMP
00230 nsMsgLocalMailFolder::Init(const char* aURI)
00231 {
00232   nsresult rv;
00233   rv = nsMsgDBFolder::Init(aURI);
00234   if (NS_FAILED(rv)) return rv;
00235 
00236   // XXX - DEADCODE - we don't need to override this, do we?
00237   return rv;
00238 
00239 }
00240 
00241 nsresult
00242 nsMsgLocalMailFolder::CreateSubFolders(nsFileSpec &path)
00243 {
00244   nsresult rv = NS_OK;
00245   nsAutoString currentFolderNameStr;
00246   nsCOMPtr<nsIMsgFolder> child;
00247 
00248   for (nsDirectoryIterator dir(path, PR_FALSE); dir.Exists(); dir++) 
00249   {
00250     nsFileSpec currentFolderPath = dir.Spec();
00251 
00252     char *leafName = currentFolderPath.GetLeafName();
00253     NS_CopyNativeToUnicode(nsDependentCString(leafName), currentFolderNameStr);
00254     PR_Free(leafName);
00255 
00256     // here we should handle the case where the current file is a .sbd directory w/o
00257     // a matching folder file, or a directory w/o the name .sbd
00258     if (nsShouldIgnoreFile(currentFolderNameStr))
00259       continue;
00260 
00261     rv = AddSubfolder(currentFolderNameStr, getter_AddRefs(child));  
00262     if (child)
00263     { 
00264       nsXPIDLString folderName;
00265       child->GetName(getter_Copies(folderName));  // try to get it from cache/db
00266       if (folderName.IsEmpty())
00267         child->SetPrettyName(currentFolderNameStr.get());
00268     }
00269   }
00270   return rv;
00271 }
00272 
00273 NS_IMETHODIMP nsMsgLocalMailFolder::AddSubfolder(const nsAString &name,
00274                                                  nsIMsgFolder **child)
00275 {
00276   nsresult rv = nsMsgDBFolder::AddSubfolder(name, child);
00277   NS_ENSURE_SUCCESS(rv, rv);
00278   nsCOMPtr <nsIFileSpec> path;
00279   // need to make sure folder exists...
00280   (*child)->GetPath(getter_AddRefs(path));
00281   if (path)
00282   {
00283     PRBool exists;
00284     rv = path->Exists(&exists);
00285     if (!exists) 
00286         rv = path->Touch();
00287   }
00288   return rv;
00289 }
00290 
00291 NS_IMETHODIMP nsMsgLocalMailFolder::GetManyHeadersToDownload(PRBool *retval)
00292 {
00293   PRBool isLocked;
00294   // if the folder is locked, we're probably reparsing - let's build the
00295   // view when we've finished reparsing.
00296   GetLocked(&isLocked);
00297   if (isLocked)
00298   {
00299     *retval = PR_TRUE;
00300     return NS_OK;
00301   }
00302   else
00303   {
00304     return nsMsgDBFolder::GetManyHeadersToDownload(retval);
00305   }
00306 
00307 }
00308 //run the url to parse the mailbox
00309 NS_IMETHODIMP nsMsgLocalMailFolder::ParseFolder(nsIMsgWindow *aMsgWindow, nsIUrlListener *listener)
00310 {
00311   nsresult rv = NS_OK;
00312   nsCOMPtr<nsIFileSpec> pathSpec;
00313   rv = GetPath(getter_AddRefs(pathSpec));
00314   if (NS_FAILED(rv)) return rv;
00315   
00316   nsFileSpec path;
00317   rv = pathSpec->GetFileSpec(&path);
00318   if (NS_FAILED(rv)) return rv;
00319   
00320   
00321   nsCOMPtr<nsIMailboxService> mailboxService = 
00322     do_GetService(kMailboxServiceCID, &rv);
00323   
00324   if (NS_FAILED(rv)) return rv; 
00325   nsMsgMailboxParser *parser = new nsMsgMailboxParser(this);
00326   if(!parser)
00327     return NS_ERROR_OUT_OF_MEMORY;
00328   
00329   PRBool isLocked;
00330   nsCOMPtr <nsISupports> supports = do_QueryInterface(NS_STATIC_CAST(nsIMsgParseMailMsgState*, parser));
00331   GetLocked(&isLocked);
00332   if(!isLocked)
00333     AcquireSemaphore(supports);
00334   else
00335   {
00336     NS_ASSERTION(PR_FALSE, "Could not get folder lock");
00337     return NS_MSG_FOLDER_BUSY;
00338   }
00339 
00340   if (listener != this)
00341     mReparseListener = listener;
00342 
00343   rv = mailboxService->ParseMailbox(aMsgWindow, path, parser, this, nsnull);
00344   if (NS_SUCCEEDED(rv))
00345     m_parsingFolder = PR_TRUE;
00346   return rv;
00347 }
00348 
00349 // this won't force a reparse of the folder if the db is invalid.
00350 NS_IMETHODIMP
00351 nsMsgLocalMailFolder::GetMsgDatabase(nsIMsgWindow *aMsgWindow,
00352                               nsIMsgDatabase** aMsgDatabase)
00353 {
00354   return GetDatabaseWOReparse(aMsgDatabase);
00355 }
00356 
00357 
00358 NS_IMETHODIMP
00359 nsMsgLocalMailFolder::Enumerate(nsIEnumerator* *result)
00360 {
00361 #if 0
00362   nsresult rv; 
00363 
00364   // local mail folders contain both messages and folders:
00365   nsCOMPtr<nsIEnumerator> folders;
00366   nsCOMPtr<nsIEnumerator> messages;
00367   rv = GetSubFolders(getter_AddRefs(folders));
00368   if (NS_FAILED(rv)) return rv;
00369   rv = GetMessages(nsnull, getter_AddRefs(messages));
00370   if (NS_FAILED(rv)) return rv;
00371   return NS_NewConjoiningEnumerator(folders, messages, 
00372                                     (nsIBidirectionalEnumerator**)result);
00373 #endif
00374   NS_ASSERTION(PR_FALSE, "isn't this obsolete?");
00375   return NS_ERROR_FAILURE;
00376 }
00377 
00378 NS_IMETHODIMP
00379 nsMsgLocalMailFolder::GetSubFolders(nsIEnumerator* *result)
00380 {
00381   PRBool isServer;
00382   nsresult rv = GetIsServer(&isServer);
00383 
00384   if (!mInitialized) {
00385     nsCOMPtr<nsIFileSpec> pathSpec;
00386     rv = GetPath(getter_AddRefs(pathSpec));
00387     if (NS_FAILED(rv)) return rv;
00388     
00389     nsFileSpec path;
00390     rv = pathSpec->GetFileSpec(&path);
00391     if (NS_FAILED(rv)) return rv;
00392     
00393     if (!path.Exists())
00394       path.CreateDirectory();
00395     if (!path.IsDirectory())
00396       AddDirectorySeparator(path);
00397     
00398     mInitialized = PR_TRUE;      // need to set this flag here to avoid infinite recursion
00399     // we have to treat the root folder specially, because it's name
00400     // doesn't end with .sbd
00401     PRInt32 newFlags = MSG_FOLDER_FLAG_MAIL;
00402     if (path.IsDirectory()) 
00403     {
00404       newFlags |= (MSG_FOLDER_FLAG_DIRECTORY | MSG_FOLDER_FLAG_ELIDED);
00405       SetFlag(newFlags);
00406 
00407       PRBool createdDefaultMailboxes = PR_FALSE;
00408       nsCOMPtr<nsILocalMailIncomingServer> localMailServer;
00409 
00410       if (isServer) 
00411       {
00412           nsCOMPtr<nsIMsgIncomingServer> server;
00413           rv = GetServer(getter_AddRefs(server));
00414           if (NS_FAILED(rv)) return rv;
00415           if (!server) return NS_MSG_INVALID_OR_MISSING_SERVER;
00416 
00417           localMailServer = do_QueryInterface(server, &rv);
00418           if (NS_FAILED(rv)) return rv;
00419           if (!localMailServer) return NS_MSG_INVALID_OR_MISSING_SERVER;
00420           
00421           nsCOMPtr<nsIFileSpec> spec;
00422           rv = NS_NewFileSpecWithSpec(path, getter_AddRefs(spec));
00423           if (NS_FAILED(rv)) return rv;
00424           
00425           // first create the folders on disk (as empty files)
00426           rv = localMailServer->CreateDefaultMailboxes(spec);
00427           NS_ENSURE_SUCCESS(rv, rv);
00428           if (NS_FAILED(rv)) return rv;
00429           createdDefaultMailboxes = PR_TRUE;
00430       }
00431 
00432       // now, discover those folders
00433       rv = CreateSubFolders(path);
00434       if (NS_FAILED(rv)) return rv;
00435 
00436       // must happen after CreateSubFolders, or the folders won't exist.
00437       if (createdDefaultMailboxes && isServer) 
00438       {
00439         rv = localMailServer->SetFlagsOnDefaultMailboxes();
00440         if (NS_FAILED(rv)) return rv;
00441       }
00442       /*we need to create all the folders at start-up because if a folder having subfolders is
00443       closed then the datasource will not ask for subfolders. For IMAP logging onto the 
00444       server will create imap folders and for news we don't have any 2nd level newsgroup */
00445 
00446       PRUint32 cnt;
00447       rv = mSubFolders->Count(&cnt);
00448       if (NS_SUCCEEDED(rv))
00449       {
00450         nsCOMPtr<nsIEnumerator> enumerator;
00451         for (PRUint32 i=0; i< cnt;i++)
00452         {
00453           nsCOMPtr<nsIMsgFolder> folder = do_QueryElementAt(mSubFolders, i, &rv);
00454           if (folder && NS_SUCCEEDED(rv))
00455           {
00456             rv = folder->GetSubFolders(getter_AddRefs(enumerator));
00457             NS_ASSERTION(NS_SUCCEEDED(rv),"GetSubFolders failed");
00458           }
00459         }
00460       }
00461     }
00462     UpdateSummaryTotals(PR_FALSE);
00463   }
00464   rv = mSubFolders->Enumerate(result);
00465   return rv;
00466 }
00467 
00468 nsresult nsMsgLocalMailFolder::GetDatabase(nsIMsgWindow *aMsgWindow)
00469 {
00470   nsCOMPtr <nsIMsgDatabase> msgDB;
00471   return GetDatabaseWOReparse(getter_AddRefs(msgDB));
00472 }
00473 
00474 //we treat failure as null db returned
00475 NS_IMETHODIMP nsMsgLocalMailFolder::GetDatabaseWOReparse(nsIMsgDatabase **aDatabase)
00476 {
00477   nsresult rv=NS_OK;
00478   if (m_parsingFolder)
00479     return NS_MSG_ERROR_FOLDER_SUMMARY_OUT_OF_DATE;
00480 
00481   NS_ENSURE_ARG(aDatabase);
00482   if (!mDatabase)
00483   {
00484     nsCOMPtr <nsIFileSpec> destIFolderSpec;
00485     rv = GetPath(getter_AddRefs(destIFolderSpec));
00486     
00487     NS_ENSURE_SUCCESS(rv, rv);
00488     
00489     nsCOMPtr<nsIMsgDBService> msgDBService = do_GetService(NS_MSGDB_SERVICE_CONTRACTID, &rv);
00490     if (NS_SUCCEEDED(rv) && msgDBService)
00491     {
00492       rv = msgDBService->OpenFolderDB(this, PR_FALSE, PR_TRUE, (nsIMsgDatabase **) getter_AddRefs(mDatabase));
00493       if (mDatabase && NS_SUCCEEDED(rv))
00494       {
00495         mDatabase->AddListener(this);
00496         UpdateNewMessages();
00497       }
00498     }
00499   }
00500   *aDatabase = mDatabase;
00501   NS_IF_ADDREF(*aDatabase);
00502   return rv;
00503 }
00504 
00505 
00506 // Makes sure the database is open and exists.  If the database is out of date,
00507 // then this call will run an async url to reparse the folder. The passed in 
00508 // url listener will get called when the url is done.
00509 NS_IMETHODIMP nsMsgLocalMailFolder::GetDatabaseWithReparse(nsIUrlListener *aReparseUrlListener, nsIMsgWindow *aMsgWindow, 
00510                                                 nsIMsgDatabase **aMsgDatabase)
00511 {
00512   nsresult rv = NS_OK;
00513   // if we're already reparsing, just remember the listener so we can notify it
00514   // when we've finished.
00515   if (m_parsingFolder)
00516   {
00517     NS_ASSERTION(!mReparseListener, "can't have an existing listener");
00518     mReparseListener = aReparseUrlListener;
00519     return NS_MSG_ERROR_FOLDER_SUMMARY_OUT_OF_DATE;
00520   }
00521   if (!mDatabase)
00522   {
00523     nsCOMPtr<nsIFileSpec> pathSpec;
00524     rv = GetPath(getter_AddRefs(pathSpec));
00525     if (NS_FAILED(rv)) return rv;
00526     PRBool exists;
00527     rv = pathSpec->Exists(&exists);
00528     NS_ENSURE_SUCCESS(rv,rv);
00529     if (!exists) return NS_ERROR_NULL_POINTER;  //mDatabase will be null at this point.
00530     
00531     nsresult folderOpen = NS_OK;
00532     nsCOMPtr<nsIMsgDBService> msgDBService = do_GetService(NS_MSGDB_SERVICE_CONTRACTID, &rv);
00533     if (msgDBService)
00534     {
00535       folderOpen = msgDBService->OpenFolderDB(this, PR_TRUE, PR_TRUE, getter_AddRefs(mDatabase));
00536       if(NS_FAILED(folderOpen) &&
00537         folderOpen == NS_MSG_ERROR_FOLDER_SUMMARY_OUT_OF_DATE)
00538       {
00539         nsCOMPtr <nsIDBFolderInfo> dbFolderInfo;
00540         nsCOMPtr <nsIDBFolderInfo> transferInfo;
00541         if (mDatabase)
00542        {
00543           mDatabase->GetDBFolderInfo(getter_AddRefs(dbFolderInfo));
00544           if (dbFolderInfo)
00545           {
00546             if (folderOpen == NS_MSG_ERROR_FOLDER_SUMMARY_MISSING)
00547               dbFolderInfo->SetFlags(mFlags);
00548             dbFolderInfo->SetNumMessages(0);
00549             dbFolderInfo->SetNumUnreadMessages(0);
00550             dbFolderInfo->GetTransferInfo(getter_AddRefs(transferInfo));
00551           }
00552           dbFolderInfo = nsnull;
00553         }
00554         if (mDatabase)
00555         {
00556           dbFolderInfo = nsnull;
00557           mDatabase->ForceClosed();
00558         }
00559         mDatabase = nsnull;
00560       
00561         nsFileSpec dbName;
00562         rv = pathSpec->GetFileSpec(&dbName);
00563         NS_ENSURE_SUCCESS(rv, rv);
00564         nsLocalFolderSummarySpec  summarySpec(dbName);
00565         // Remove summary file.
00566         summarySpec.Delete(PR_FALSE);
00567       
00568         // if it's out of date then reopen with upgrade.
00569         if (NS_FAILED(rv = msgDBService->OpenFolderDB(this, PR_TRUE, PR_TRUE, getter_AddRefs(mDatabase)))
00570           && rv != NS_MSG_ERROR_FOLDER_SUMMARY_MISSING && rv != NS_MSG_ERROR_FOLDER_SUMMARY_OUT_OF_DATE)
00571           return rv;
00572         else if (transferInfo && mDatabase)
00573         {
00574           SetDBTransferInfo(transferInfo);
00575         }
00576       }
00577       
00578     }
00579     
00580     if(mDatabase)
00581     {
00582       
00583       if(mAddListener)
00584         mDatabase->AddListener(this);
00585       
00586       // if we have to regenerate the folder, run the parser url.
00587       if(folderOpen == NS_MSG_ERROR_FOLDER_SUMMARY_MISSING ||
00588         folderOpen == NS_MSG_ERROR_FOLDER_SUMMARY_OUT_OF_DATE)
00589       {
00590         if(NS_FAILED(rv = ParseFolder(aMsgWindow, aReparseUrlListener)))
00591         {
00592           if (rv == NS_MSG_FOLDER_BUSY)
00593           {
00594             mDatabase->RemoveListener(this);  //we need to null out the db so that parsing gets kicked off again.
00595             mDatabase = nsnull;
00596             ThrowAlertMsg("parsingFolderFailed", aMsgWindow);
00597           }
00598           return rv;
00599         }
00600         else
00601           return NS_ERROR_NOT_INITIALIZED;
00602       }
00603       else
00604       {
00605         // We have a valid database so lets extract necessary info.
00606         UpdateSummaryTotals(PR_TRUE);
00607       }
00608     }
00609   }
00610   NS_IF_ADDREF(*aMsgDatabase = mDatabase);
00611   return rv;
00612 }
00613 
00614 NS_IMETHODIMP
00615 nsMsgLocalMailFolder::UpdateFolder(nsIMsgWindow *aWindow)
00616 {
00617   (void) RefreshSizeOnDisk();
00618   nsresult rv;
00619 
00620   nsCOMPtr<nsIMsgAccountManager> accountManager = 
00621            do_GetService(NS_MSGACCOUNTMANAGER_CONTRACTID, &rv);
00622   NS_ENSURE_SUCCESS(rv, rv);
00623   PRBool userNeedsToAuthenticate = PR_FALSE;
00624   // if we're PasswordProtectLocalCache, then we need to find out if the server is authenticated.
00625   (void) accountManager->GetUserNeedsToAuthenticate(&userNeedsToAuthenticate);
00626   if (userNeedsToAuthenticate)
00627   {
00628     nsCOMPtr<nsIMsgIncomingServer> server;
00629     rv = GetServer(getter_AddRefs(server)); 
00630     if (NS_FAILED(rv)) return rv;
00631     if (!server) 
00632       return NS_MSG_INVALID_OR_MISSING_SERVER;
00633     // need to check if this is a pop3 or no mail server to determine which password
00634     // we should challenge the user with.
00635     nsCOMPtr<nsIMsgIncomingServer> serverToAuthenticateAgainst;
00636     nsCOMPtr<nsINoIncomingServer> noIncomingServer = do_QueryInterface(server, &rv);
00637     if (noIncomingServer)
00638     {
00639       nsCOMPtr<nsIMsgAccount> defaultAccount;
00640       accountManager->GetDefaultAccount(getter_AddRefs(defaultAccount));
00641       if (defaultAccount)
00642         defaultAccount->GetIncomingServer(getter_AddRefs(serverToAuthenticateAgainst));
00643     }
00644     else
00645     {
00646       GetServer(getter_AddRefs(serverToAuthenticateAgainst));
00647     }
00648     if (serverToAuthenticateAgainst)
00649     {
00650       PRBool passwordMatches = PR_FALSE;
00651       rv = PromptForCachePassword(serverToAuthenticateAgainst, aWindow, passwordMatches);
00652       if (!passwordMatches)
00653         return NS_ERROR_FAILURE;
00654     }
00655   }
00656   //If we don't currently have a database, get it.  Otherwise, the folder has been updated (presumably this
00657   //changes when we download headers when opening inbox).  If it's updated, send NotifyFolderLoaded.
00658   if(!mDatabase)
00659     rv = GetDatabaseWithReparse(this, aWindow, getter_AddRefs(mDatabase)); 
00660   else
00661   {
00662     PRBool valid;
00663     rv = mDatabase->GetSummaryValid(&valid);
00664     // don't notify folder loaded or try compaction if db isn't valid
00665     // (we're probably reparsing or copying msgs to it)
00666     if (NS_SUCCEEDED(rv) && valid)
00667     {
00668       NotifyFolderEvent(mFolderLoadedAtom);
00669       NS_ENSURE_SUCCESS(rv,rv);
00670     }
00671     else if (mCopyState)
00672       mCopyState->m_notifyFolderLoaded = PR_TRUE; //defer folder loaded notification
00673     else if (!m_parsingFolder)// if the db was already open, it's probably OK to load it if not parsing
00674       NotifyFolderEvent(mFolderLoadedAtom);
00675   }
00676   PRBool filtersRun;
00677   PRBool hasNewMessages;
00678   GetHasNewMessages(&hasNewMessages);
00679   if (mDatabase)
00680     ApplyRetentionSettings();
00681   // if we have new messages, try the filter plugins.
00682   if (NS_SUCCEEDED(rv) && hasNewMessages)
00683     (void) CallFilterPlugins(aWindow, &filtersRun);
00684   return rv;
00685 }
00686 
00687 NS_IMETHODIMP
00688 nsMsgLocalMailFolder::GetMessages(nsIMsgWindow *aMsgWindow, nsISimpleEnumerator* *result)
00689 {
00690   nsCOMPtr <nsIMsgDatabase> msgDB;
00691   nsresult rv = GetDatabaseWOReparse(getter_AddRefs(msgDB));
00692 
00693   if(NS_SUCCEEDED(rv))
00694     return msgDB->EnumerateMessages(result);
00695   return rv;
00696 }
00697 
00698 
00699 NS_IMETHODIMP nsMsgLocalMailFolder::GetFolderURL(char **url)
00700 {
00701   const char *urlScheme = "mailbox:";
00702   
00703   if(!url)
00704     return NS_ERROR_NULL_POINTER;
00705   
00706   nsresult rv;
00707   
00708   nsCOMPtr<nsIFileSpec> pathSpec;
00709   rv = GetPath(getter_AddRefs(pathSpec));
00710   if (NS_FAILED(rv)) return rv;
00711   
00712   nsFileSpec path;
00713   rv = pathSpec->GetFileSpec(&path);
00714   if (NS_FAILED(rv)) return rv;
00715   
00716   nsCAutoString tmpPath((nsFilePath)path);
00717   
00718   nsCAutoString urlStr(urlScheme);
00719   urlStr.Append(tmpPath);
00720   
00721   *url = ToNewCString(urlStr);
00722   return NS_OK;
00723   
00724 }
00725 
00726 NS_IMETHODIMP nsMsgLocalMailFolder::CreateStorageIfMissing(nsIUrlListener* aUrlListener)
00727 {
00728   nsresult rv = NS_OK;
00729   nsCOMPtr <nsIMsgFolder> msgParent;
00730   GetParentMsgFolder(getter_AddRefs(msgParent));
00731   
00732   // parent is probably not set because *this* was probably created by rdf
00733   // and not by folder discovery. So, we have to compute the parent.
00734   if (!msgParent)
00735   {
00736     nsCAutoString folderName(mURI);
00737     
00738     nsCAutoString uri;
00739     
00740     PRInt32 leafPos = folderName.RFindChar('/');
00741     
00742     nsCAutoString parentName(folderName);
00743     
00744     if (leafPos > 0)
00745     {
00746       // If there is a hierarchy, there is a parent.
00747       // Don't strip off slash if it's the first character
00748       parentName.Truncate(leafPos);
00749       // get the corresponding RDF resource
00750       // RDF will create the folder resource if it doesn't already exist
00751       nsCOMPtr<nsIRDFService> rdf = do_GetService("@mozilla.org/rdf/rdf-service;1", &rv);
00752       NS_ENSURE_SUCCESS(rv,rv);
00753       
00754       nsCOMPtr<nsIRDFResource> resource;
00755       rv = rdf->GetResource(parentName, getter_AddRefs(resource));
00756       NS_ENSURE_SUCCESS(rv,rv);
00757       
00758       msgParent = do_QueryInterface(resource, &rv);
00759       NS_ENSURE_SUCCESS(rv,rv);
00760     }
00761   }
00762   
00763   if (msgParent)
00764   {
00765     nsXPIDLString folderName;
00766     GetName(getter_Copies(folderName));
00767     rv = msgParent->CreateSubfolder(folderName, nsnull);
00768   }
00769   return rv;
00770 }
00771 
00772 
00773 nsresult
00774 nsMsgLocalMailFolder::CreateSubfolder(const PRUnichar *folderName, nsIMsgWindow *msgWindow )
00775 {
00776   nsresult rv = CheckIfFolderExists(folderName, this, msgWindow);
00777   if(NS_FAILED(rv))  //we already throw an alert - no need for an assertion
00778     return rv;
00779   
00780   nsFileSpec path;
00781   nsCOMPtr<nsIMsgFolder> child;
00782   //Get a directory based on our current path.
00783   rv = CreateDirectoryForFolder(path);
00784   if(NS_FAILED(rv))
00785     return rv;
00786   
00787   //Now we have a valid directory or we have returned.
00788   //Make sure the new folder name is valid
00789   nsAutoString safeFolderName(folderName);
00790   NS_MsgHashIfNecessary(safeFolderName);
00791   nsCAutoString nativeFolderName;
00792   rv = NS_CopyUnicodeToNative(safeFolderName, nativeFolderName);
00793   if (NS_FAILED(rv) || nativeFolderName.IsEmpty()) {
00794     ThrowAlertMsg("folderCreationFailed", msgWindow);
00795     // I'm returning this value so the dialog stays up
00796     return NS_MSG_FOLDER_EXISTS;
00797   }
00798   
00799   path += nativeFolderName.get();   
00800   if (path.Exists()) //check this because localized names are different from disk names
00801   {
00802     ThrowAlertMsg("folderExists", msgWindow);
00803     return NS_MSG_FOLDER_EXISTS;
00804   }
00805   
00806   nsOutputFileStream outputStream(path, PR_WRONLY | PR_CREATE_FILE, 00600); 
00807   if (outputStream.is_open())
00808   {
00809     outputStream.flush();
00810     outputStream.close();
00811   }
00812   
00813   //GetFlags and SetFlags in AddSubfolder will fail because we have no db at this point but mFlags is set.
00814   rv = AddSubfolder(safeFolderName, getter_AddRefs(child));
00815   if (!child || NS_FAILED(rv))
00816   {
00817     path.Delete(PR_FALSE);
00818     return rv;
00819   }
00820     
00821   // Create an empty database for this mail folder, set its name from the user  
00822   nsCOMPtr<nsIMsgDBService> msgDBService = do_GetService(NS_MSGDB_SERVICE_CONTRACTID, &rv);
00823   if (msgDBService)
00824   {
00825     nsCOMPtr<nsIMsgDatabase> unusedDB;
00826     rv = msgDBService->OpenFolderDB(child, PR_TRUE, PR_TRUE, getter_AddRefs(unusedDB));
00827     
00828     if ((NS_SUCCEEDED(rv) || rv == NS_MSG_ERROR_FOLDER_SUMMARY_MISSING 
00829       || rv == NS_MSG_ERROR_FOLDER_SUMMARY_OUT_OF_DATE) && unusedDB)
00830     {
00831       //need to set the folder name
00832       nsCOMPtr<nsIDBFolderInfo> folderInfo;
00833       rv = unusedDB->GetDBFolderInfo(getter_AddRefs(folderInfo));
00834       if(NS_SUCCEEDED(rv))
00835       {
00836         folderInfo->SetMailboxName(safeFolderName);
00837       }
00838       unusedDB->SetSummaryValid(PR_TRUE);
00839       unusedDB->Close(PR_TRUE);
00840     }
00841     else
00842     {
00843       path.Delete(PR_FALSE);
00844       rv = NS_MSG_CANT_CREATE_FOLDER;
00845     }
00846   }
00847   if(NS_SUCCEEDED(rv))
00848   {
00849     //we need to notify explicitly the flag change because it failed when we did AddSubfolder
00850     child->OnFlagChange(mFlags);
00851     child->SetPrettyName(folderName);  //because empty trash will create a new trash folder
00852     NotifyItemAdded(child);
00853   }
00854   return rv;
00855 }
00856 
00857 NS_IMETHODIMP nsMsgLocalMailFolder::CompactAll(nsIUrlListener *aListener, nsIMsgWindow *aMsgWindow, nsISupportsArray *aFolderArray, PRBool aCompactOfflineAlso, nsISupportsArray *aOfflineFolderArray)
00858 {
00859   nsresult rv = NS_OK;
00860   nsCOMPtr<nsISupportsArray> folderArray;
00861   if (!aFolderArray)
00862   {
00863     nsCOMPtr<nsIMsgFolder> rootFolder;
00864     nsCOMPtr<nsISupportsArray> allDescendents;
00865     rv = GetRootFolder(getter_AddRefs(rootFolder));  
00866     if (NS_SUCCEEDED(rv) && rootFolder)
00867     {
00868       NS_NewISupportsArray(getter_AddRefs(allDescendents));
00869       rootFolder->ListDescendents(allDescendents);
00870       PRUint32 cnt =0;
00871       rv = allDescendents->Count(&cnt);
00872       NS_ENSURE_SUCCESS(rv,rv);
00873       NS_NewISupportsArray(getter_AddRefs(folderArray));
00874       PRUint32 expungedBytes=0;
00875       for (PRUint32 i=0; i< cnt;i++)
00876       {
00877         nsCOMPtr<nsISupports> supports = getter_AddRefs(allDescendents->ElementAt(i));
00878         nsCOMPtr<nsIMsgFolder> folder = do_QueryInterface(supports, &rv);
00879         NS_ENSURE_SUCCESS(rv,rv);
00880     
00881         expungedBytes=0;
00882         if (folder)
00883           rv = folder->GetExpungedBytes(&expungedBytes);
00884     
00885         NS_ENSURE_SUCCESS(rv,rv);
00886     
00887         if (expungedBytes > 0)
00888           rv = folderArray->AppendElement(supports);
00889       }
00890       rv = folderArray->Count(&cnt);
00891       NS_ENSURE_SUCCESS(rv,rv);
00892       if (cnt == 0 )
00893         return NotifyCompactCompleted();
00894     }
00895   }
00896   nsCOMPtr <nsIMsgFolderCompactor> folderCompactor =  do_CreateInstance(NS_MSGLOCALFOLDERCOMPACTOR_CONTRACTID, &rv);
00897   if (NS_SUCCEEDED(rv) && folderCompactor)
00898     if (aFolderArray)
00899        rv = folderCompactor->CompactAll(aFolderArray, aMsgWindow, aCompactOfflineAlso, aOfflineFolderArray);  
00900     else if (folderArray)
00901        rv = folderCompactor->CompactAll(folderArray, aMsgWindow, aCompactOfflineAlso, aOfflineFolderArray);  
00902   return rv;
00903 }
00904 
00905 NS_IMETHODIMP nsMsgLocalMailFolder::Compact(nsIUrlListener *aListener, nsIMsgWindow *aMsgWindow)
00906 {
00907   nsresult rv;
00908   nsCOMPtr <nsIMsgFolderCompactor> folderCompactor =  do_CreateInstance(NS_MSGLOCALFOLDERCOMPACTOR_CONTRACTID, &rv);
00909   if (NS_SUCCEEDED(rv) && folderCompactor)
00910   {
00911     PRUint32 expungedBytes = 0;
00912 
00913     GetExpungedBytes(&expungedBytes);
00914     // check if we need to compact the folder
00915 
00916     if (expungedBytes > 0)
00917       rv = folderCompactor->Compact(this, PR_FALSE, aMsgWindow);
00918     else
00919       rv = NotifyCompactCompleted();
00920   }
00921   return rv;
00922 }
00923 
00924 NS_IMETHODIMP nsMsgLocalMailFolder::EmptyTrash(nsIMsgWindow *msgWindow,
00925                                                nsIUrlListener *aListener)
00926 {
00927     nsresult rv;
00928     nsCOMPtr<nsIMsgFolder> trashFolder;
00929     rv = GetTrashFolder(getter_AddRefs(trashFolder));
00930     if (NS_SUCCEEDED(rv))
00931     {
00932         PRUint32 flags;
00933         nsXPIDLCString trashUri;
00934         trashFolder->GetURI(getter_Copies(trashUri));
00935         trashFolder->GetFlags(&flags);
00936         PRInt32 totalMessages = 0;
00937         rv = trashFolder->GetTotalMessages(PR_TRUE, &totalMessages);
00938                   
00939         if (totalMessages <= 0) 
00940         {
00941           nsCOMPtr<nsIEnumerator> aEnumerator;
00942           rv =trashFolder->GetSubFolders(getter_AddRefs(aEnumerator));
00943           NS_ENSURE_SUCCESS(rv,rv);
00944           rv = aEnumerator->First();    //will fail if no subfolders 
00945           if (NS_FAILED(rv)) return NS_OK;
00946         }
00947         nsCOMPtr<nsIMsgFolder> parentFolder;
00948         rv = trashFolder->GetParentMsgFolder(getter_AddRefs(parentFolder));
00949         if (NS_SUCCEEDED(rv) && parentFolder)
00950         {
00951           nsCOMPtr <nsIDBFolderInfo> transferInfo;
00952           trashFolder->GetDBTransferInfo(getter_AddRefs(transferInfo)); 
00953 
00954           trashFolder->SetParent(nsnull);
00955           parentFolder->PropagateDelete(trashFolder, PR_TRUE, msgWindow);
00956           parentFolder->CreateSubfolder(NS_LITERAL_STRING("Trash").get(),nsnull);
00957           nsCOMPtr<nsIMsgFolder> newTrashFolder;
00958           rv = GetTrashFolder(getter_AddRefs(newTrashFolder));
00959           if (NS_SUCCEEDED(rv) && newTrashFolder) 
00960           {
00961             nsCOMPtr <nsIMsgLocalMailFolder> localTrash = do_QueryInterface(newTrashFolder);
00962             newTrashFolder->SetDBTransferInfo(transferInfo);
00963             if (localTrash)
00964               localTrash->RefreshSizeOnDisk();
00965             // update the summary totals so the front end will
00966             // show the right thing for the new trash folder
00967             // see bug #161999
00968             nsCOMPtr<nsIDBFolderInfo> dbFolderInfo;
00969             nsCOMPtr<nsIMsgDatabase> db; 
00970             newTrashFolder->GetDBFolderInfoAndDB(getter_AddRefs(dbFolderInfo), getter_AddRefs(db));
00971             if (dbFolderInfo)
00972             {
00973               dbFolderInfo->SetNumUnreadMessages(0);
00974               dbFolderInfo->SetNumMessages(0);
00975             }
00976             newTrashFolder->UpdateSummaryTotals(PR_TRUE);
00977           }
00978         }
00979     }
00980     return rv;
00981 }
00982 
00983 nsresult nsMsgLocalMailFolder::IsChildOfTrash(PRBool *result)
00984 {
00985   nsresult rv = NS_ERROR_NULL_POINTER;
00986   PRBool isServer = PR_FALSE;
00987   PRUint32 parentFlags = 0;
00988 
00989   if (!result) return rv;
00990   *result = PR_FALSE;
00991 
00992   rv = GetIsServer(&isServer);
00993   if (NS_FAILED(rv) || isServer) return rv;
00994 
00995   rv= GetFlags(&parentFlags);  //this is the parent folder
00996   if (parentFlags & MSG_FOLDER_FLAG_TRASH) 
00997   {
00998       *result = PR_TRUE;
00999       return rv;
01000   }
01001 
01002   nsCOMPtr<nsIMsgFolder> parentFolder;
01003   nsCOMPtr<nsIMsgFolder> thisFolder;
01004   rv = QueryInterface(NS_GET_IID(nsIMsgFolder), (void **)
01005                       getter_AddRefs(thisFolder));
01006 
01007   while (!isServer && thisFolder) 
01008   {
01009     rv = thisFolder->GetParentMsgFolder(getter_AddRefs(parentFolder));
01010     if (NS_FAILED(rv)) return rv;
01011     rv = parentFolder->GetIsServer(&isServer);
01012     if (NS_FAILED(rv) || isServer) return rv;
01013     rv = parentFolder->GetFlags(&parentFlags);
01014     if (NS_FAILED(rv)) return rv;
01015     if (parentFlags & MSG_FOLDER_FLAG_TRASH) 
01016     {
01017       *result = PR_TRUE;
01018       return rv;
01019     }
01020     thisFolder = parentFolder;
01021   }
01022   return rv;
01023 }
01024 
01025 NS_IMETHODIMP nsMsgLocalMailFolder::Delete()
01026 {
01027   nsresult rv;
01028   
01029   if(mDatabase)
01030   {
01031     mDatabase->ForceClosed();
01032     mDatabase = nsnull;
01033   }
01034   
01035   nsCOMPtr<nsIFileSpec> pathSpec;
01036   rv = GetPath(getter_AddRefs(pathSpec));
01037   if (NS_FAILED(rv)) return rv;
01038   
01039   nsFileSpec path;
01040   rv = pathSpec->GetFileSpec(&path);
01041   if (NS_FAILED(rv)) return rv;
01042   
01043   nsLocalFolderSummarySpec summarySpec(path);
01044 
01045   //Clean up .sbd folder if it exists.
01046   if(NS_SUCCEEDED(rv))
01047   {
01048     // Remove summary file.
01049     summarySpec.Delete(PR_FALSE);
01050       
01051     //Delete mailbox
01052     path.Delete(PR_FALSE);
01053       
01054     if (!path.IsDirectory())
01055       AddDirectorySeparator(path);
01056       
01057     //If this is a directory, then remove it.
01058     if (path.IsDirectory())
01059       path.Delete(PR_TRUE);
01060   }
01061   return rv;
01062 }
01063 
01064 NS_IMETHODIMP nsMsgLocalMailFolder::DeleteSubFolders(
01065   nsISupportsArray *folders, nsIMsgWindow *msgWindow)
01066 {
01067   nsresult rv = NS_ERROR_FAILURE;
01068   PRBool isChildOfTrash;
01069   rv = IsChildOfTrash(&isChildOfTrash);
01070 
01071   // we don't allow multiple folder selection so this is ok.
01072   nsCOMPtr<nsIMsgFolder> folder = do_QueryElementAt(folders, 0);
01073   PRUint32 folderFlags = 0;
01074   if (folder)
01075     folder->GetFlags(&folderFlags);
01076   // when deleting from trash, or virtual folder, just delete it.
01077   if (isChildOfTrash || folderFlags & MSG_FOLDER_FLAG_VIRTUAL)
01078     return nsMsgDBFolder::DeleteSubFolders(folders, msgWindow);
01079 
01080   if (!msgWindow) 
01081     return NS_ERROR_NULL_POINTER;
01082  
01083   nsCOMPtr<nsIMsgFolder> trashFolder;
01084   rv = GetTrashFolder(getter_AddRefs(trashFolder));
01085   if (NS_SUCCEEDED(rv))
01086   {
01087     if (folder)
01088       trashFolder->CopyFolder(folder, PR_TRUE, msgWindow, nsnull);
01089   }
01090   return rv;
01091 }
01092 
01093 nsresult nsMsgLocalMailFolder::ConfirmFolderDeletion(nsIMsgWindow *aMsgWindow, PRBool *aResult)
01094 {
01095   NS_ENSURE_ARG(aResult);
01096   NS_ENSURE_ARG(aMsgWindow);
01097   nsCOMPtr<nsIDocShell> docShell;
01098   aMsgWindow->GetRootDocShell(getter_AddRefs(docShell));
01099   if (docShell)
01100   {
01101     PRBool confirmDeletion = PR_TRUE;
01102     nsresult rv;
01103     nsCOMPtr<nsIPrefBranch> pPrefBranch(do_GetService(NS_PREFSERVICE_CONTRACTID, &rv));
01104     if (pPrefBranch)
01105        pPrefBranch->GetBoolPref("mailnews.confirm.moveFoldersToTrash", &confirmDeletion);
01106     if (confirmDeletion)
01107     {
01108       if (!mMsgStringService)
01109         mMsgStringService = do_GetService(NS_MSG_POPSTRINGSERVICE_CONTRACTID);
01110       if (!mMsgStringService) 
01111         return NS_ERROR_FAILURE;
01112       nsXPIDLString alertString;
01113       mMsgStringService->GetStringByID(POP3_MOVE_FOLDER_TO_TRASH, getter_Copies(alertString));
01114       nsCOMPtr<nsIPrompt> dialog(do_GetInterface(docShell));
01115       if (dialog)
01116         dialog->Confirm(nsnull, alertString.get(), aResult);
01117     }
01118     else
01119       *aResult = PR_TRUE;
01120   }
01121   return NS_OK;
01122 }
01123 
01124 NS_IMETHODIMP nsMsgLocalMailFolder::Rename(const PRUnichar *aNewName, nsIMsgWindow *msgWindow)
01125 {
01126   nsCOMPtr<nsIFileSpec> oldPathSpec;
01127   nsCOMPtr<nsIAtom> folderRenameAtom;
01128   nsresult rv = GetPath(getter_AddRefs(oldPathSpec));
01129   if (NS_FAILED(rv)) 
01130     return rv;
01131   nsCOMPtr<nsIMsgFolder> parentFolder;
01132   rv = GetParentMsgFolder(getter_AddRefs(parentFolder));
01133   if (NS_FAILED(rv)) 
01134     return rv;
01135   nsCOMPtr<nsISupports> parentSupport = do_QueryInterface(parentFolder);
01136   
01137   nsFileSpec fileSpec;
01138   oldPathSpec->GetFileSpec(&fileSpec);
01139   nsLocalFolderSummarySpec oldSummarySpec(fileSpec);
01140   nsFileSpec dirSpec;
01141   
01142   PRUint32 cnt = 0;
01143   if (mSubFolders)
01144     mSubFolders->Count(&cnt);
01145   
01146   if (cnt > 0)
01147     rv = CreateDirectoryForFolder(dirSpec);
01148   
01149   // convert from PRUnichar* to char* due to not having Rename(PRUnichar*)
01150   // function in nsIFileSpec
01151   
01152   nsAutoString safeName(aNewName);
01153   NS_MsgHashIfNecessary(safeName);
01154   nsCAutoString newDiskName;
01155   if (NS_FAILED(NS_CopyUnicodeToNative(safeName, newDiskName)))
01156     return NS_ERROR_FAILURE;
01157   
01158   nsXPIDLCString oldLeafName;
01159   oldPathSpec->GetLeafName(getter_Copies(oldLeafName));
01160   
01161   if (mName.Equals(aNewName, nsCaseInsensitiveStringComparator()))
01162   {
01163     if(msgWindow)
01164       rv = ThrowAlertMsg("folderExists", msgWindow);
01165     return NS_MSG_FOLDER_EXISTS;
01166   }
01167   else
01168   {
01169     nsCOMPtr <nsIFileSpec> parentPathSpec;
01170     parentFolder->GetPath(getter_AddRefs(parentPathSpec));
01171     NS_ENSURE_SUCCESS(rv,rv);
01172     
01173     nsFileSpec parentPath;
01174     parentPathSpec->GetFileSpec(&parentPath);
01175     NS_ENSURE_SUCCESS(rv,rv);
01176     
01177     if (!parentPath.IsDirectory())
01178       AddDirectorySeparator(parentPath);
01179     
01180     rv = CheckIfFolderExists(aNewName, parentFolder, msgWindow);
01181     if (NS_FAILED(rv)) 
01182       return rv;
01183   }
01184   
01185   ForceDBClosed();
01186   
01187   nsCAutoString newNameDirStr(newDiskName.get());  //save of dir name before appending .msf 
01188   
01189   rv = oldPathSpec->Rename(newDiskName.get());
01190   if (NS_SUCCEEDED(rv))
01191   {
01192     newDiskName += ".msf";
01193     oldSummarySpec.Rename(newDiskName.get());
01194   }
01195   else
01196   {
01197     ThrowAlertMsg("folderRenameFailed", msgWindow);
01198     return rv;
01199   }
01200   
01201   if (NS_SUCCEEDED(rv) && cnt > 0) 
01202   {
01203     // rename "*.sbd" directory
01204     newNameDirStr += ".sbd";
01205     dirSpec.Rename(newNameDirStr.get());
01206   }
01207   
01208   nsCOMPtr<nsIMsgFolder> newFolder;
01209   if (parentSupport)
01210   {
01211     rv = parentFolder->AddSubfolder(safeName, getter_AddRefs(newFolder));
01212     if (newFolder) 
01213     {
01214       // Because we just renamed the db, w/o setting the pretty name in it, 
01215       // we need to force the pretty name to be correct. 
01216       // SetPrettyName won't write the name to the db if it doesn't think the 
01217       // name has changed. This hack forces the pretty name to get set in the db.
01218       // We could set the new pretty name on the db before renaming the .msf file,
01219       // but if the rename failed, it would be out of sync.
01220       newFolder->SetPrettyName(NS_LITERAL_STRING("").get());
01221       newFolder->SetPrettyName(aNewName);
01222       PRBool changed = PR_FALSE;
01223       MatchOrChangeFilterDestination(newFolder, PR_TRUE /*caseInsenstive*/, &changed);
01224       if (changed)
01225         AlertFilterChanged(msgWindow);
01226       
01227       if (cnt > 0)
01228         newFolder->RenameSubFolders(msgWindow, this);
01229       
01230       // the newFolder should have the same flags
01231       newFolder->SetFlags(mFlags);
01232       if (parentFolder)
01233       {
01234         SetParent(nsnull);
01235         parentFolder->PropagateDelete(this, PR_FALSE, msgWindow);
01236         parentFolder->NotifyItemAdded(newFolder);
01237       }
01238       SetPath(nsnull); // forget our path, since this folder object renamed itself
01239       folderRenameAtom = do_GetAtom("RenameCompleted");
01240       newFolder->NotifyFolderEvent(folderRenameAtom);
01241     }
01242   }
01243   return rv;
01244 }
01245 
01246 NS_IMETHODIMP nsMsgLocalMailFolder::RenameSubFolders(nsIMsgWindow *msgWindow, nsIMsgFolder *oldFolder)
01247 {
01248   nsresult rv =NS_OK;
01249   mInitialized = PR_TRUE;
01250 
01251   PRUint32 flags;
01252   oldFolder->GetFlags(&flags);
01253   SetFlags(flags);
01254 
01255   nsCOMPtr<nsIEnumerator> aEnumerator;
01256   oldFolder->GetSubFolders(getter_AddRefs(aEnumerator));
01257   nsCOMPtr<nsISupports> aSupport;
01258   rv = aEnumerator->First();
01259   while (NS_SUCCEEDED(rv))
01260   {
01261      rv = aEnumerator->CurrentItem(getter_AddRefs(aSupport));
01262      nsCOMPtr<nsIMsgFolder>msgFolder = do_QueryInterface(aSupport);
01263      nsXPIDLString folderName;
01264      rv = msgFolder->GetName(getter_Copies(folderName));
01265      nsCOMPtr <nsIMsgFolder> newFolder;
01266      AddSubfolder(folderName, getter_AddRefs(newFolder));
01267      if (newFolder)
01268      {
01269        newFolder->SetPrettyName(folderName.get());
01270        PRBool changed = PR_FALSE;
01271        msgFolder->MatchOrChangeFilterDestination(newFolder, PR_TRUE /*caseInsenstive*/, &changed);
01272        if (changed)
01273          msgFolder->AlertFilterChanged(msgWindow);
01274 
01275        newFolder->RenameSubFolders(msgWindow, msgFolder);
01276      }
01277      rv = aEnumerator->Next();
01278   }
01279   return NS_OK;
01280 }
01281 
01282 NS_IMETHODIMP nsMsgLocalMailFolder::GetPrettyName(PRUnichar ** prettyName)
01283 {
01284   return nsMsgDBFolder::GetPrettyName(prettyName);
01285 }
01286 
01287 NS_IMETHODIMP nsMsgLocalMailFolder::SetPrettyName(const PRUnichar *aName)
01288 {
01289   NS_ENSURE_ARG_POINTER(aName);
01290   nsresult rv = nsMsgDBFolder::SetPrettyName(aName);
01291   NS_ENSURE_SUCCESS(rv, rv);
01292   nsXPIDLCString folderName;
01293   rv = GetStringProperty("folderName", getter_Copies(folderName));
01294   NS_ConvertUCS2toUTF8 utf8FolderName(mName);
01295   if (!NS_SUCCEEDED(rv) || !folderName.Equals(utf8FolderName.get()))
01296     return SetStringProperty("folderName", utf8FolderName.get());
01297   else
01298     return rv;
01299 }
01300 
01301 NS_IMETHODIMP nsMsgLocalMailFolder::GetName(PRUnichar **aName)
01302 {
01303   ReadDBFolderInfo(PR_FALSE);
01304   return nsMsgDBFolder::GetName(aName);
01305 }
01306 
01307 NS_IMETHODIMP
01308 nsMsgLocalMailFolder::GetDBFolderInfoAndDB(nsIDBFolderInfo **folderInfo, nsIMsgDatabase **db)
01309 {
01310   nsresult openErr=NS_ERROR_UNEXPECTED;
01311   if(!db || !folderInfo || !mPath || mIsServer)
01312     return NS_ERROR_NULL_POINTER;   //ducarroz: should we use NS_ERROR_INVALID_ARG?
01313 
01314   nsresult rv;
01315   if (mDatabase)
01316   {
01317     openErr = NS_OK;
01318   }
01319   else
01320   {
01321     nsCOMPtr<nsIMsgDBService> msgDBService = do_GetService(NS_MSGDB_SERVICE_CONTRACTID, &rv);
01322     if (msgDBService)
01323     {
01324       PRBool folderEmpty = PR_FALSE;
01325       nsCOMPtr <nsIFileSpec> fileSpec;
01326       rv = GetPath(getter_AddRefs(fileSpec));
01327       // check for case of trying to open db for 0 byte folder (i.e., new folder), 
01328       // and in that case, tell msg db to create a new db and set it valid after opening it.
01329       if (fileSpec)
01330       {
01331         PRUint32 mailboxSize;
01332         if (NS_SUCCEEDED(fileSpec->GetFileSize(&mailboxSize)))
01333           folderEmpty = !mailboxSize;
01334       }
01335 
01336       openErr = msgDBService->OpenFolderDB(this, folderEmpty, PR_FALSE, getter_AddRefs(mDatabase));
01337       if (folderEmpty)
01338       {
01339         if (openErr == NS_MSG_ERROR_FOLDER_SUMMARY_MISSING)
01340         {
01341           if (mDatabase)
01342             mDatabase->SetSummaryValid(PR_TRUE);
01343           openErr = NS_OK;
01344         }
01345         else if (NS_FAILED(openErr))
01346         {
01347           mDatabase = nsnull;
01348         }
01349       }
01350     }
01351   }
01352 
01353   *db = mDatabase;
01354   NS_IF_ADDREF(*db);
01355   if (NS_SUCCEEDED(openErr)&& *db)
01356     openErr = (*db)->GetDBFolderInfo(folderInfo);
01357   return openErr;
01358 }
01359 
01360 NS_IMETHODIMP nsMsgLocalMailFolder::ReadFromFolderCacheElem(nsIMsgFolderCacheElement *element)
01361 {
01362   NS_ENSURE_ARG_POINTER(element);
01363   nsresult rv = nsMsgDBFolder::ReadFromFolderCacheElem(element);
01364   NS_ENSURE_SUCCESS(rv, rv);
01365   nsXPIDLCString utf8Name;
01366   rv = element->GetStringProperty("folderName", getter_Copies(utf8Name));
01367   NS_ENSURE_SUCCESS(rv, rv);
01368   CopyUTF8toUTF16(utf8Name, mName);
01369   return rv;
01370 }
01371 
01372 NS_IMETHODIMP nsMsgLocalMailFolder::WriteToFolderCacheElem(nsIMsgFolderCacheElement *element)
01373 {
01374   NS_ENSURE_ARG_POINTER(element);
01375   nsMsgDBFolder::WriteToFolderCacheElem(element);
01376   return element->SetStringProperty("folderName", NS_ConvertUCS2toUTF8(mName).get());
01377 }
01378 
01379 NS_IMETHODIMP nsMsgLocalMailFolder::UpdateSummaryTotals(PRBool force)
01380 {
01381   if (!mNotifyCountChanges)
01382     return NS_OK;
01383   PRInt32 oldUnreadMessages = mNumUnreadMessages;
01384   PRInt32 oldTotalMessages = mNumTotalMessages;
01385   //We need to read this info from the database
01386   ReadDBFolderInfo(force);
01387   
01388   //Need to notify listeners that total count changed.
01389   if(oldTotalMessages != mNumTotalMessages)
01390   {
01391     NotifyIntPropertyChanged(kTotalMessagesAtom, oldTotalMessages, mNumTotalMessages);
01392   }
01393   
01394   if(oldUnreadMessages != mNumUnreadMessages)
01395   {
01396     NotifyIntPropertyChanged(kTotalUnreadMessagesAtom, oldUnreadMessages, mNumUnreadMessages);
01397   }
01398   
01399   FlushToFolderCache();
01400   return NS_OK;
01401 }
01402 
01403 NS_IMETHODIMP nsMsgLocalMailFolder::GetDeletable(PRBool *deletable)
01404 {
01405   if(!deletable)
01406     return NS_ERROR_NULL_POINTER;
01407 
01408   PRBool isServer;
01409   GetIsServer(&isServer);
01410   // These are specified in the "Mail/News Windows" UI spe 
01411   if (isServer)
01412     *deletable = PR_FALSE;
01413   else if (mFlags & MSG_FOLDER_FLAG_INBOX || 
01414     mFlags & MSG_FOLDER_FLAG_DRAFTS || 
01415     mFlags & MSG_FOLDER_FLAG_TRASH ||
01416     mFlags & MSG_FOLDER_FLAG_TEMPLATES ||
01417     mFlags & MSG_FOLDER_FLAG_JUNK)
01418     *deletable = PR_FALSE;
01419   else *deletable =  PR_TRUE;
01420 
01421   return NS_OK;
01422 }
01423 
01424 NS_IMETHODIMP nsMsgLocalMailFolder::GetRequiresCleanup(PRBool *requiresCleanup)
01425 {
01426 #ifdef HAVE_PORT
01427   if (m_expungedBytes > 0)
01428   {
01429     PRInt32 purgeThreshhold = m_master->GetPrefs()->GetPurgeThreshhold();
01430     PRBool purgePrompt = m_master->GetPrefs()->GetPurgeThreshholdEnabled();
01431     return (purgePrompt && m_expungedBytes / 1000L > purgeThreshhold);
01432   }
01433   return PR_FALSE;
01434 #endif
01435   return NS_OK;
01436 }
01437 
01438 NS_IMETHODIMP nsMsgLocalMailFolder::RefreshSizeOnDisk()
01439 {
01440   PRUint32 oldFolderSize = mFolderSize;
01441   mFolderSize = 0; // we set this to 0 to force it to get recalculated from disk
01442   if (NS_SUCCEEDED(GetSizeOnDisk(&mFolderSize)))
01443     NotifyIntPropertyChanged(kFolderSizeAtom, oldFolderSize, mFolderSize);
01444   return NS_OK;
01445 }
01446 
01447 NS_IMETHODIMP nsMsgLocalMailFolder::GetSizeOnDisk(PRUint32* aSize)
01448 {
01449     NS_ENSURE_ARG_POINTER(aSize);
01450     nsresult rv = NS_OK;
01451     if (!mFolderSize)
01452     {
01453       nsCOMPtr <nsIFileSpec> fileSpec;
01454       rv = GetPath(getter_AddRefs(fileSpec));
01455       NS_ENSURE_SUCCESS(rv, rv);
01456       rv = fileSpec->GetFileSize(&mFolderSize);
01457     }
01458     *aSize = mFolderSize;
01459     return rv;
01460 }
01461 
01462 nsresult
01463 nsMsgLocalMailFolder::GetTrashFolder(nsIMsgFolder** result)
01464 {
01465   nsresult rv = NS_ERROR_NULL_POINTER;
01466   
01467   if (!result) return rv;
01468   
01469   nsCOMPtr<nsIMsgFolder> rootFolder;
01470   rv = GetRootFolder(getter_AddRefs(rootFolder));
01471   if(NS_SUCCEEDED(rv))
01472   {
01473     PRUint32 numFolders;
01474     rv = rootFolder->GetFoldersWithFlag(MSG_FOLDER_FLAG_TRASH,
01475       1, &numFolders, result);
01476     if (NS_SUCCEEDED(rv) && numFolders != 1)
01477       rv = NS_ERROR_FAILURE;
01478   }
01479   return rv;
01480 }
01481 
01482 NS_IMETHODIMP
01483 nsMsgLocalMailFolder::DeleteMessages(nsISupportsArray *messages,
01484                                      nsIMsgWindow *msgWindow, 
01485                                      PRBool deleteStorage, PRBool isMove,
01486                                      nsIMsgCopyServiceListener* listener, PRBool allowUndo)
01487 {
01488   NS_ENSURE_ARG_POINTER(messages);
01489 
01490   PRUint32 messageCount;
01491   nsresult rv = messages->Count(&messageCount);
01492   if (!messageCount)
01493     return rv;
01494     
01495   // shift delete case - (delete to trash is handled in EndMove)
01496   // this is also the case when applying retention settings.
01497   if (deleteStorage && !isMove)
01498   {
01499     MarkMsgsOnPop3Server(messages, POP3_DELETE);
01500     nsCOMPtr <nsIMsgFolderNotificationService> notifier = do_GetService(NS_MSGNOTIFICATIONSERVICE_CONTRACTID);
01501     if (notifier)
01502         notifier->NotifyItemDeleted(messages);    
01503   }
01504   
01505   PRBool isTrashFolder = mFlags & MSG_FOLDER_FLAG_TRASH;
01506   if (!deleteStorage && !isTrashFolder)
01507   {
01508       nsCOMPtr<nsIMsgFolder> trashFolder;
01509       rv = GetTrashFolder(getter_AddRefs(trashFolder));
01510       if (NS_SUCCEEDED(rv))
01511       {
01512           nsCOMPtr<nsIMsgCopyService> copyService = 
01513                    do_GetService(NS_MSGCOPYSERVICE_CONTRACTID, &rv);
01514           if (NS_SUCCEEDED(rv))
01515           {
01516             return copyService->CopyMessages(this, messages, trashFolder,
01517                                       PR_TRUE, listener, msgWindow, allowUndo);
01518           }
01519       }
01520       return rv;
01521   }
01522   else
01523   {   
01524       nsCOMPtr <nsIMsgDatabase> msgDB;
01525       rv = GetDatabaseWOReparse(getter_AddRefs(msgDB));
01526       if(NS_SUCCEEDED(rv))
01527       {
01528         if (deleteStorage && isMove && GetDeleteFromServerOnMove())
01529             MarkMsgsOnPop3Server(messages, POP3_DELETE);
01530 
01531         nsCOMPtr<nsISupports> msgSupport;
01532         rv = EnableNotifications(allMessageCountNotifications, PR_FALSE, PR_TRUE /*dbBatching*/);
01533         if (NS_SUCCEEDED(rv))
01534         {
01535           for(PRUint32 i = 0; i < messageCount; i++)
01536           {
01537             msgSupport = getter_AddRefs(messages->ElementAt(i));
01538             if (msgSupport)
01539               DeleteMessage(msgSupport, msgWindow, PR_TRUE, PR_FALSE);
01540           }
01541         }
01542         else if (rv == NS_MSG_FOLDER_BUSY)
01543           ThrowAlertMsg("deletingMsgsFailed", msgWindow);
01544 
01545         // we are the source folder here for a move or shift delete
01546         //enable notifications because that will close the file stream
01547         // we've been caching, mark the db as valid, and commit it.
01548         EnableNotifications(allMessageCountNotifications, PR_TRUE, PR_TRUE /*dbBatching*/);
01549         if(!isMove)
01550           NotifyFolderEvent(NS_SUCCEEDED(rv) ? mDeleteOrMoveMsgCompletedAtom : mDeleteOrMoveMsgFailedAtom);
01551         if (msgWindow && !isMove)
01552           AutoCompact(msgWindow);
01553       }
01554   }
01555   return rv;
01556 }
01557 
01558 nsresult
01559 nsMsgLocalMailFolder::InitCopyState(nsISupports* aSupport, 
01560                                     nsISupportsArray* messages,
01561                                     PRBool isMove,
01562                                     nsIMsgCopyServiceListener* listener, 
01563                                     nsIMsgWindow *msgWindow, PRBool isFolder, 
01564                                     PRBool allowUndo)
01565 {
01566   nsresult rv = NS_OK;
01567   nsFileSpec path;
01568   nsCOMPtr<nsIFileSpec> pathSpec;
01569   
01570   NS_ASSERTION(!mCopyState, "already copying a msg into this folder");
01571   if (mCopyState) 
01572     return NS_ERROR_FAILURE; // already has a  copy in progress
01573   
01574   // get mDatabase set, so we can use it to add new hdrs to this db.
01575   // calling GetDatabase will set mDatabase - we use the comptr
01576   // here to avoid doubling the refcnt on mDatabase. We don't care if this
01577   // fails - we just want to give it a chance. It will definitely fail in
01578   // nsLocalMailFolder::EndCopy because we will have written data to the folder
01579   // and changed its size.
01580   nsCOMPtr <nsIMsgDatabase> msgDB;
01581   GetDatabaseWOReparse(getter_AddRefs(msgDB));
01582   PRBool isLocked;
01583   
01584   GetLocked(&isLocked);
01585   if(!isLocked)
01586     AcquireSemaphore(NS_STATIC_CAST(nsIMsgLocalMailFolder*, this));
01587   else
01588     return NS_MSG_FOLDER_BUSY;
01589   
01590   rv = GetPath(getter_AddRefs(pathSpec));
01591   NS_ENSURE_SUCCESS(rv, rv);
01592   
01593   rv = pathSpec->GetFileSpec(&path);
01594   NS_ENSURE_SUCCESS(rv, rv);
01595   
01596   mCopyState = new nsLocalMailCopyState();
01597   if(!mCopyState)
01598     return NS_ERROR_OUT_OF_MEMORY;
01599   
01600   mCopyState->m_dataBuffer = (char*) PR_CALLOC(COPY_BUFFER_SIZE+1);
01601   if (!mCopyState->m_dataBuffer)
01602     return NS_ERROR_OUT_OF_MEMORY;
01603   
01604   mCopyState->m_dataBufferSize = COPY_BUFFER_SIZE;
01605   mCopyState->m_destDB = msgDB;
01606   
01607   //Before we continue we should verify that there is enough diskspace.
01608   //XXX How do we do this?
01609   mCopyState->m_fileStream = new nsOutputFileStream(path, PR_WRONLY |
01610     PR_CREATE_FILE);
01611   if(!mCopyState->m_fileStream)
01612     return NS_ERROR_OUT_OF_MEMORY;
01613   
01614   //The new key is the end of the file
01615   mCopyState->m_fileStream->seek(PR_SEEK_END, 0);
01616   mCopyState->m_srcSupport = do_QueryInterface(aSupport, &rv);
01617   NS_ENSURE_SUCCESS(rv, rv);
01618   mCopyState->m_messages = do_QueryInterface(messages, &rv);
01619   NS_ENSURE_SUCCESS(rv, rv);
01620   mCopyState->m_curCopyIndex = 0;
01621   mCopyState->m_isMove = isMove;
01622   mCopyState->m_isFolder = isFolder;
01623   mCopyState->m_allowUndo = allowUndo;
01624   mCopyState->m_msgWindow = msgWindow;
01625   rv = messages->Count(&mCopyState->m_totalMsgCount);
01626   if (listener)
01627     mCopyState->m_listener = do_QueryInterface(listener, &rv);
01628   mCopyState->m_copyingMultipleMessages = PR_FALSE;
01629   return rv;
01630 }
01631 
01632 NS_IMETHODIMP nsMsgLocalMailFolder::OnAnnouncerGoingAway(nsIDBChangeAnnouncer *instigator)
01633 {
01634   if (mCopyState)
01635     mCopyState->m_destDB = nsnull;
01636   return nsMsgDBFolder::OnAnnouncerGoingAway(instigator);
01637 }
01638 
01639 NS_IMETHODIMP
01640 nsMsgLocalMailFolder::OnCopyCompleted(nsISupports *srcSupport, PRBool moveCopySucceeded)
01641 {
01642   if (mCopyState && mCopyState->m_notifyFolderLoaded)
01643     NotifyFolderEvent(mFolderLoadedAtom);
01644 
01645   delete mCopyState;
01646   mCopyState = nsnull;
01647  
01648   (void) RefreshSizeOnDisk();
01649   // we are the destination folder for a move/copy
01650   if (moveCopySucceeded && mDatabase)
01651   {
01652     mDatabase->SetSummaryValid(PR_TRUE);
01653     mDatabase->Commit(nsMsgDBCommitType::kLargeCommit);
01654     (void) CloseDBIfFolderNotOpen();
01655   }
01656 
01657   PRBool haveSemaphore;
01658   nsresult result;
01659   result = TestSemaphore(NS_STATIC_CAST(nsIMsgLocalMailFolder*, this),
01660     &haveSemaphore);
01661   if(NS_SUCCEEDED(result) && haveSemaphore)
01662     ReleaseSemaphore(NS_STATIC_CAST(nsIMsgLocalMailFolder*, this));
01663 
01664   nsCOMPtr<nsIMsgCopyService> copyService = 
01665      do_GetService(NS_MSGCOPYSERVICE_CONTRACTID, &result);
01666 
01667   if (NS_SUCCEEDED(result))  //copyService will do listener->OnStopCopy()
01668     copyService->NotifyCompletion(srcSupport, this, moveCopySucceeded ? NS_OK : NS_ERROR_FAILURE); 
01669   return NS_OK;
01670 }
01671 
01672 nsresult 
01673 nsMsgLocalMailFolder::SortMessagesBasedOnKey(nsISupportsArray *messages, nsMsgKeyArray *aKeyArray, nsIMsgFolder *srcFolder)
01674 {
01675   nsresult rv = NS_OK;
01676   PRUint32 numMessages = 0;
01677   rv = messages->Count(&numMessages);
01678   NS_ENSURE_SUCCESS(rv,rv);
01679   NS_ASSERTION ((numMessages == aKeyArray->GetSize()), "message array and key array size are not same");
01680   rv = messages->Clear();
01681   NS_ENSURE_SUCCESS(rv,rv);
01682   nsCOMPtr <nsIMsgDBHdr> msgHdr;
01683   nsCOMPtr<nsIDBFolderInfo> folderInfo;
01684   nsCOMPtr<nsIMsgDatabase> db; 
01685   rv = srcFolder->GetDBFolderInfoAndDB(getter_AddRefs(folderInfo), getter_AddRefs(db));
01686   if (NS_SUCCEEDED(rv) && db)
01687     for (PRUint32 i=0;i < numMessages; i++)
01688     {
01689       rv = db->GetMsgHdrForKey(aKeyArray->GetAt(i), getter_AddRefs(msgHdr));
01690       NS_ENSURE_SUCCESS(rv,rv);
01691       if (msgHdr)
01692         messages->AppendElement(msgHdr);
01693     }
01694   return rv;
01695 }
01696                 
01697 NS_IMETHODIMP
01698 nsMsgLocalMailFolder::CopyMessages(nsIMsgFolder* srcFolder, nsISupportsArray*
01699                                    messages, PRBool isMove,
01700                                    nsIMsgWindow *msgWindow,
01701                                    nsIMsgCopyServiceListener* listener, 
01702                                    PRBool isFolder, PRBool allowUndo)
01703 {
01704   nsCOMPtr<nsISupports> srcSupport = do_QueryInterface(srcFolder);
01705   PRBool isServer;
01706   nsresult rv = GetIsServer(&isServer);
01707   if (NS_SUCCEEDED(rv) && isServer)
01708   {
01709     NS_ASSERTION(0, "Destination is the root folder. Cannot move/copy here");
01710     if (isMove)
01711       srcFolder->NotifyFolderEvent(mDeleteOrMoveMsgFailedAtom);
01712     return OnCopyCompleted(srcSupport, PR_FALSE);
01713   }
01714   
01715   PRBool mailboxTooLarge;
01716 
01717   (void) WarnIfLocalFileTooBig(msgWindow, &mailboxTooLarge);
01718   if (mailboxTooLarge)
01719   {
01720     if (isMove)
01721       srcFolder->NotifyFolderEvent(mDeleteOrMoveMsgFailedAtom);
01722     return OnCopyCompleted(srcSupport, PR_FALSE);
01723   }
01724 
01725   if (!(mFlags & (MSG_FOLDER_FLAG_TRASH|MSG_FOLDER_FLAG_JUNK)))
01726     SetMRUTime();
01727 
01728   nsXPIDLCString uri;
01729   rv = srcFolder->GetURI(getter_Copies(uri));
01730   nsCAutoString protocolType(uri);
01731   protocolType.SetLength(protocolType.FindChar(':'));
01732   
01733   if (WeAreOffline() && (protocolType.LowerCaseEqualsLiteral("imap") || protocolType.LowerCaseEqualsLiteral("news")))
01734   {
01735     PRUint32 numMessages = 0;
01736     messages->Count(&numMessages);
01737     for (PRUint32 i = 0; i < numMessages; i++)
01738     {
01739       nsCOMPtr<nsIMsgDBHdr> message;
01740       messages->QueryElementAt(i, NS_GET_IID(nsIMsgDBHdr),(void **)getter_AddRefs(message));
01741       if(NS_SUCCEEDED(rv) && message)
01742       {
01743         nsMsgKey key;
01744         PRBool hasMsgOffline = PR_FALSE;
01745         message->GetMessageKey(&key);
01746         srcFolder->HasMsgOffline(key, &hasMsgOffline);
01747         if (!hasMsgOffline)
01748         {
01749           if (isMove)
01750             srcFolder->NotifyFolderEvent(mDeleteOrMoveMsgFailedAtom);
01751           ThrowAlertMsg("cantMoveMsgWOBodyOffline", msgWindow);
01752           return OnCopyCompleted(srcSupport, PR_FALSE);
01753         }
01754       }
01755     }
01756   }
01757   
01758   // don't update the counts in the dest folder until it is all over
01759   EnableNotifications(allMessageCountNotifications, PR_FALSE, PR_FALSE /*dbBatching*/);  //dest folder doesn't need db batching
01760   
01761   rv = InitCopyState(srcSupport, messages, isMove, listener, msgWindow, isFolder, allowUndo);
01762   if (NS_FAILED(rv))
01763   {
01764     ThrowAlertMsg("operationFailedFolderBusy", msgWindow);
01765     (void) OnCopyCompleted(srcSupport, PR_FALSE);
01766     return rv;
01767   }
01768 
01769   if (!protocolType.LowerCaseEqualsLiteral("mailbox"))
01770   {
01771     mCopyState->m_dummyEnvelopeNeeded = PR_TRUE;
01772     nsParseMailMessageState* parseMsgState = new nsParseMailMessageState();
01773     if (parseMsgState)
01774     {
01775       nsCOMPtr<nsIMsgDatabase> msgDb;
01776       mCopyState->m_parseMsgState = do_QueryInterface(parseMsgState, &rv);
01777       GetDatabaseWOReparse(getter_AddRefs(msgDb));   
01778       if (msgDb)
01779         parseMsgState->SetMailDB(msgDb);
01780     }
01781   }
01782   
01783   // undo stuff
01784   if (allowUndo)    //no undo for folder move/copy or or move/copy from search window
01785   {
01786     nsLocalMoveCopyMsgTxn* msgTxn = nsnull;
01787     
01788     msgTxn = new nsLocalMoveCopyMsgTxn;
01789     
01790     if (msgTxn && NS_SUCCEEDED(msgTxn->Init(srcFolder, this, isMove)))
01791       rv = msgTxn->QueryInterface(NS_GET_IID(nsLocalMoveCopyMsgTxn),
01792                                   getter_AddRefs(mCopyState->m_undoMsgTxn));
01793     else
01794     {
01795       delete msgTxn;
01796       rv = NS_ERROR_OUT_OF_MEMORY;
01797     }
01798     if (NS_FAILED(rv))
01799     {
01800       (void) OnCopyCompleted(srcSupport, PR_FALSE);
01801     }
01802     else
01803     {
01804       msgTxn->SetMsgWindow(msgWindow);
01805       if (isMove)
01806       {
01807         if (mFlags & MSG_FOLDER_FLAG_TRASH)
01808           msgTxn->SetTransactionType(nsIMessenger::eDeleteMsg);
01809         else
01810           msgTxn->SetTransactionType(nsIMessenger::eMoveMsg);
01811       }
01812       else
01813       {
01814         msgTxn->SetTransactionType(nsIMessenger::eCopyMsg);
01815       }
01816     }
01817   }
01818   PRUint32 numMsgs = 0;
01819   mCopyState->m_messages->Count(&numMsgs);
01820   if (numMsgs > 1 && ((protocolType.LowerCaseEqualsLiteral("imap") && !WeAreOffline()) || protocolType.LowerCaseEqualsLiteral("mailbox")))
01821   {
01822     mCopyState->m_copyingMultipleMessages = PR_TRUE;
01823     rv = CopyMessagesTo(mCopyState->m_messages, msgWindow, this, isMove);
01824     if (NS_FAILED(rv))
01825     {
01826       NS_ERROR("copy message failed");
01827       (void) OnCopyCompleted(srcSupport, PR_FALSE);
01828     }
01829   }
01830   else
01831   {
01832     nsCOMPtr<nsISupports> msgSupport;
01833     msgSupport = getter_AddRefs(mCopyState->m_messages->ElementAt(0));
01834     if (msgSupport)
01835     {
01836       rv = CopyMessageTo(msgSupport, this, msgWindow, isMove);
01837       if (NS_FAILED(rv))
01838       {
01839         NS_ASSERTION(PR_FALSE, "copy message failed");
01840         (void) OnCopyCompleted(srcSupport, PR_FALSE);
01841       }
01842     }
01843   }
01844   // if this failed immediately, need to turn back on notifications and inform FE.
01845   if (NS_FAILED(rv))
01846   {
01847     if (isMove)
01848       srcFolder->NotifyFolderEvent(mDeleteOrMoveMsgFailedAtom);
01849     EnableNotifications(allMessageCountNotifications, PR_TRUE, PR_FALSE /*dbBatching*/);  //dest folder doesn't need db batching
01850   }
01851   return rv;
01852 }
01853 // for srcFolder that are on different server than the dstFolder. 
01854 // "this" is the parent of the new dest folder.
01855 nsresult
01856 nsMsgLocalMailFolder::CopyFolderAcrossServer(nsIMsgFolder* srcFolder, nsIMsgWindow *msgWindow,
01857                   nsIMsgCopyServiceListener *listener )
01858 {
01859   mInitialized = PR_TRUE;
01860     
01861   nsXPIDLString folderName;
01862   srcFolder->GetName(getter_Copies(folderName));
01863   
01864   nsresult rv = CreateSubfolder(folderName, msgWindow);
01865   if (NS_FAILED(rv)) return rv;
01866 
01867   nsCAutoString escapedFolderName;
01868   rv = NS_MsgEscapeEncodeURLPath(folderName, escapedFolderName);
01869   NS_ENSURE_SUCCESS(rv,rv);
01870 
01871   nsCOMPtr<nsIMsgFolder> newFolder;
01872   nsCOMPtr<nsIMsgFolder> newMsgFolder;
01873 
01874   rv = FindSubFolder(escapedFolderName, getter_AddRefs(newMsgFolder));
01875   NS_ENSURE_SUCCESS(rv,rv);
01876   
01877   nsCOMPtr<nsISimpleEnumerator> messages;
01878   rv = srcFolder->GetMessages(msgWindow, getter_AddRefs(messages));
01879 
01880   nsCOMPtr<nsISupportsArray> msgSupportsArray;
01881   NS_NewISupportsArray(getter_AddRefs(msgSupportsArray));
01882 
01883   PRBool hasMoreElements;
01884   nsCOMPtr<nsISupports> aSupport;
01885 
01886   if (messages)
01887     messages->HasMoreElements(&hasMoreElements);
01888   
01889   while (hasMoreElements && NS_SUCCEEDED(rv))
01890   {
01891     rv = messages->GetNext(getter_AddRefs(aSupport));
01892     rv = msgSupportsArray->AppendElement(aSupport);
01893     messages->HasMoreElements(&hasMoreElements);
01894   }
01895   
01896   PRUint32 numMsgs=0;
01897   msgSupportsArray->Count(&numMsgs);
01898 
01899   if (numMsgs > 0 )   //if only srcFolder has messages..
01900     newMsgFolder->CopyMessages(srcFolder, msgSupportsArray, PR_FALSE, msgWindow, listener, PR_TRUE /* is folder*/, PR_FALSE /* allowUndo */);
01901   else
01902   {
01903     nsCOMPtr <nsIMsgLocalMailFolder> localFolder = do_QueryInterface(newMsgFolder);
01904     if (localFolder)
01905     {
01906       // normally these would get called from ::EndCopy when the last message
01907       // was finished copying. But since there are no messages, we have to call
01908       // them explicitly.
01909       nsCOMPtr<nsISupports> srcSupports = do_QueryInterface(newMsgFolder);
01910       localFolder->CopyAllSubFolders(srcFolder, msgWindow, listener);
01911       return localFolder->OnCopyCompleted(srcSupports, PR_TRUE);
01912     }
01913   }     
01914   return NS_OK;  // otherwise the front-end will say Exception::CopyFolder
01915 }
01916 
01917 nsresult    //copy the sub folders
01918 nsMsgLocalMailFolder::CopyAllSubFolders(nsIMsgFolder *srcFolder, 
01919                                       nsIMsgWindow *msgWindow, 
01920                                       nsIMsgCopyServiceListener *listener )
01921 {
01922   nsresult rv;
01923   nsCOMPtr<nsIEnumerator> aEnumerator;
01924   srcFolder->GetSubFolders(getter_AddRefs(aEnumerator));
01925   nsCOMPtr<nsIMsgFolder>folder;
01926   nsCOMPtr<nsISupports> aSupports;
01927   rv = aEnumerator->First();
01928   while (NS_SUCCEEDED(rv))
01929   {
01930     rv = aEnumerator->CurrentItem(getter_AddRefs(aSupports));
01931     folder = do_QueryInterface(aSupports);
01932     rv = aEnumerator->Next();
01933     if (folder)
01934       CopyFolderAcrossServer(folder, msgWindow, listener);  
01935     
01936   }  
01937   return rv;
01938 }
01939 
01940 NS_IMETHODIMP
01941 nsMsgLocalMailFolder::CopyFolder( nsIMsgFolder* srcFolder, PRBool isMoveFolder,
01942                                    nsIMsgWindow *msgWindow,
01943                                    nsIMsgCopyServiceListener* listener)
01944 {
01945   nsresult rv;
01946   NS_ENSURE_ARG_POINTER(srcFolder);
01947   
01948   if (isMoveFolder)   // isMoveFolder == true when "this" and srcFolder are on same server
01949     rv = CopyFolderLocal(srcFolder, isMoveFolder, msgWindow, listener );
01950   else
01951     rv = CopyFolderAcrossServer(srcFolder, msgWindow, listener );
01952     
01953   return rv;
01954 }
01955 
01956 NS_IMETHODIMP
01957 nsMsgLocalMailFolder::CopyFolderLocal(nsIMsgFolder *srcFolder, 
01958                                       PRBool isMoveFolder,
01959                                       nsIMsgWindow *msgWindow,
01960                                       nsIMsgCopyServiceListener *listener )
01961 {
01962   nsresult rv;
01963   mInitialized = PR_TRUE;
01964   nsCOMPtr<nsIMsgFolder> newMsgFolder;
01965   PRBool isChildOfTrash=PR_FALSE;
01966   rv = IsChildOfTrash(&isChildOfTrash);
01967   if (isChildOfTrash)  
01968   {
01969     // do it just for the parent folder (isMoveFolder is true for parent only) if we are deleting/moving a folder tree
01970     // don't confirm for rss folders.
01971     if (isMoveFolder) 
01972     {
01973       PRBool okToDelete = PR_FALSE;
01974       ConfirmFolderDeletion(msgWindow, &okToDelete);
01975       if (!okToDelete)
01976         return NS_MSG_ERROR_COPY_FOLDER_ABORTED;
01977       // if we are moving a favorite folder to trash, we should clear the favorites flag
01978       // so it gets removed from the view.
01979       srcFolder->ClearFlag(MSG_FOLDER_FLAG_FAVORITE);
01980     }
01981 
01982     PRBool match = PR_FALSE;
01983     rv = srcFolder->MatchOrChangeFilterDestination(nsnull, PR_FALSE, &match);
01984     if (match)
01985     {
01986       PRBool confirmed = PR_FALSE;
01987       srcFolder->ConfirmFolderDeletionForFilter(msgWindow, &confirmed);
01988       if (!confirmed) 
01989         return NS_MSG_ERROR_COPY_FOLDER_ABORTED;
01990     }
01991   }
01992   
01993   nsXPIDLString folderName;
01994   srcFolder->GetName(getter_Copies(folderName));
01995   nsAutoString safeFolderName(folderName);
01996   NS_MsgHashIfNecessary(safeFolderName);
01997   nsCOMPtr <nsIMsgLocalMailFolder> localFolder = do_QueryInterface(srcFolder);
01998   nsCOMPtr <nsIMsgDatabase> srcDB;
01999   if (localFolder)
02000     localFolder->GetDatabaseWOReparse(getter_AddRefs(srcDB));
02001   PRBool summaryValid = (srcDB != nsnull);
02002   srcDB = nsnull;
02003   srcFolder->ForceDBClosed();   
02004   
02005   nsCOMPtr<nsIFileSpec> oldPathSpec;
02006   rv = srcFolder->GetPath(getter_AddRefs(oldPathSpec));
02007   NS_ENSURE_SUCCESS(rv,rv);
02008   
02009   nsFileSpec oldPath;
02010   rv = oldPathSpec->GetFileSpec(&oldPath);
02011   NS_ENSURE_SUCCESS(rv,rv);
02012   
02013   nsLocalFolderSummarySpec  summarySpec(oldPath);
02014   
02015   nsCOMPtr<nsIFileSpec> newPathSpec;
02016   rv = GetPath(getter_AddRefs(newPathSpec));
02017   NS_ENSURE_SUCCESS(rv,rv);
02018   
02019   nsFileSpec newPath;
02020   rv = newPathSpec->GetFileSpec(&newPath);
02021   NS_ENSURE_SUCCESS(rv,rv);
02022   
02023   if (!newPath.IsDirectory())
02024   {
02025     AddDirectorySeparator(newPath);
02026     newPath.CreateDirectory();
02027   }
02028   
02029   rv = CheckIfFolderExists(folderName.get(), this, msgWindow);
02030   if(NS_FAILED(rv)) 
02031     return rv;
02032   
02033   nsFileSpec path = oldPath;
02034   
02035   rv = path.CopyToDir(newPath);   //copying necessary for aborting.... if failure return
02036   NS_ENSURE_SUCCESS(rv, rv);      //would fail if a file by that name exists
02037 
02038   // Copy to dir can fail if filespec does not exist. If copy fails, we test
02039   // if the filespec exist or not, if it does not that's ok, we continue 
02040   // without copying it. If it fails and filespec exist and is not zero sized
02041   // there is real problem
02042   rv = summarySpec.CopyToDir(newPath);      // Copy the filespec to the new dir
02043   if (! NS_SUCCEEDED(rv))                   // Test if the copy is successfull
02044   {                                       
02045     // Test if the filespec has data
02046     if (summarySpec.Exists() && (summarySpec.GetFileSize() > 0))
02047       NS_ENSURE_SUCCESS(rv, rv);          // Yes, it should have worked !
02048     // else case is filespec is zero sized, no need to copy it, 
02049     // not an error
02050     // else case is filespec does not exist - not an error
02051   }
02052   
02053   // linux and mac are not good about maintaining the file stamp when copying folders
02054   // around. So if the source folder db is good, set the dest db as good too.
02055   nsCOMPtr <nsIMsgDatabase> destDB;
02056   if (summaryValid)
02057   {
02058     nsCAutoString folderLeafName;
02059     folderLeafName.Adopt(path.GetLeafName());
02060     newPath += folderLeafName.get();
02061     nsCOMPtr<nsIMsgDBService> msgDBService = do_GetService(NS_MSGDB_SERVICE_CONTRACTID, &rv);
02062     if (NS_SUCCEEDED(rv) && msgDBService)
02063     {
02064       nsCOMPtr <nsIFileSpec> dbFileSpec;
02065       NS_NewFileSpecWithSpec(newPath, getter_AddRefs(dbFileSpec));
02066       rv = msgDBService->OpenMailDBFromFileSpec(dbFileSpec, PR_FALSE, PR_TRUE, getter_AddRefs(destDB));
02067       if (rv == NS_MSG_ERROR_FOLDER_SUMMARY_OUT_OF_DATE && destDB)
02068         destDB->SetSummaryValid(PR_TRUE);
02069     }
02070   }
02071   rv = AddSubfolder(safeFolderName, getter_AddRefs(newMsgFolder));  
02072   NS_ENSURE_SUCCESS(rv, rv);
02073 
02074   newMsgFolder->SetPrettyName(folderName.get());
02075   
02076   PRUint32 flags;
02077   srcFolder->GetFlags(&flags);
02078   newMsgFolder->SetFlags(flags);
02079   PRBool changed = PR_FALSE;
02080   rv = srcFolder->MatchOrChangeFilterDestination(newMsgFolder, PR_TRUE, &changed);
02081   if (changed)
02082     srcFolder->AlertFilterChanged(msgWindow);
02083   
02084   nsCOMPtr<nsIEnumerator> aEnumerator;
02085   srcFolder->GetSubFolders(getter_AddRefs(aEnumerator));
02086   nsCOMPtr<nsIMsgFolder>folder;
02087   nsCOMPtr<nsISupports> supports;
02088   rv = aEnumerator->First();
02089   nsresult copyStatus = NS_OK;
02090   while (NS_SUCCEEDED(rv) && NS_SUCCEEDED(copyStatus))
02091   {
02092     rv = aEnumerator->CurrentItem(getter_AddRefs(supports));
02093     folder = do_QueryInterface(supports);
02094     rv = aEnumerator->Next();
02095     if (folder)
02096     {
02097       nsCOMPtr <nsIMsgLocalMailFolder> localFolder =
02098         do_QueryInterface(newMsgFolder);
02099       if (localFolder)
02100       {
02101         // PR_FALSE needed to avoid un-necessary deletions
02102         copyStatus = localFolder->CopyFolderLocal(folder, PR_FALSE, msgWindow, listener);
02103         // Test if the call succeeded, if not we have to stop recursive call
02104         if (NS_FAILED(copyStatus))
02105         {
02106           // Copy failed we have to notify caller to handle the error and stop
02107           // moving the folders. In case this happens to the topmost level of
02108           // recursive call, then we just need to break from the while loop and
02109           // go to error handling code.
02110           if (!isMoveFolder)
02111             return copyStatus;
02112           break;
02113         }
02114       }
02115     }
02116   }  
02117 
02118   nsCOMPtr <nsIMsgFolderNotificationService> notifier = do_GetService(NS_MSGNOTIFICATIONSERVICE_CONTRACTID);
02119   if (notifier)
02120   {
02121     PRBool hasListeners;
02122     notifier->GetHasListeners(&hasListeners);
02123     if (hasListeners)
02124     {
02125       nsCOMPtr <nsISupportsArray> supportsArray = do_CreateInstance(NS_SUPPORTSARRAY_CONTRACTID);
02126       if (supportsArray)
02127       {
02128         
02129         supportsArray->AppendElement(srcFolder);
02130         notifier->NotifyItemMoveCopyCompleted(isMoveFolder, supportsArray, this);
02131       }
02132     }
02133   }
02134         
02135   if (isMoveFolder && NS_SUCCEEDED(copyStatus))
02136   {
02137     //notifying the "folder" that was dragged and dropped has been created.
02138     //no need to do this for its subfolders - isMoveFolder will be true for "folder"
02139     NotifyItemAdded(newMsgFolder);
02140     
02141     nsCOMPtr<nsIMsgFolder> msgParent;
02142     srcFolder->GetParentMsgFolder(getter_AddRefs(msgParent));
02143     srcFolder->SetParent(nsnull);
02144     if (msgParent) 
02145     {
02146       msgParent->PropagateDelete(srcFolder, PR_FALSE, msgWindow);  // The files have already been moved, so delete storage PR_FALSE 
02147       oldPath.Delete(PR_FALSE);  //berkeley mailbox
02148       nsCOMPtr <nsIMsgDatabase> srcDB; // we need to force closed the source db
02149       srcFolder->Delete();
02150 
02151       nsCOMPtr<nsIFileSpec> parentPathSpec;
02152       rv = msgParent->GetPath(getter_AddRefs(parentPathSpec));
02153       NS_ENSURE_SUCCESS(rv,rv);
02154   
02155       nsFileSpec parentPath;
02156       rv = parentPathSpec->GetFileSpec(&parentPath);
02157       NS_ENSURE_SUCCESS(rv,rv);
02158 
02159       AddDirectorySeparator(parentPath); 
02160       nsDirectoryIterator i(parentPath, PR_FALSE);
02161       // i.Exists() checks if the directory is empty or not 
02162       if (parentPath.IsDirectory() && !i.Exists())
02163         parentPath.Delete(PR_TRUE);
02164     }
02165   }
02166   else
02167   {
02168     // This is the case where the copy of a subfolder failed.
02169     // We have to delete the newDirectory tree to make a "rollback".
02170     // Someone should add a popup to warn the user that the move was not
02171     // possible.
02172     if (isMoveFolder && NS_FAILED(copyStatus))
02173     {
02174       nsCOMPtr<nsIMsgFolder> msgParent;
02175       newMsgFolder->ForceDBClosed();
02176       newMsgFolder->GetParentMsgFolder(getter_AddRefs(msgParent));
02177       newMsgFolder->SetParent(nsnull);
02178       if (msgParent)
02179       {
02180         msgParent->PropagateDelete(newMsgFolder, PR_FALSE, msgWindow);
02181         newMsgFolder->Delete();
02182         newMsgFolder->ForceDBClosed();
02183         AddDirectorySeparator(newPath);
02184         newPath.Delete(PR_TRUE);  //berkeley mailbox
02185       }
02186       return NS_ERROR_FAILURE;
02187     }
02188   }
02189 
02190   return NS_OK;
02191 }
02192 
02193 NS_IMETHODIMP
02194 nsMsgLocalMailFolder::CopyFileMessage(nsIFileSpec* fileSpec, nsIMsgDBHdr*
02195                                       msgToReplace, PRBool isDraftOrTemplate,
02196                                       PRUint32 newMsgFlags,
02197                                       nsIMsgWindow *msgWindow,
02198                                       nsIMsgCopyServiceListener* listener)
02199 {
02200   nsresult rv = NS_ERROR_NULL_POINTER;
02201   nsCOMPtr<nsIInputStream> inputStream;
02202   nsParseMailMessageState* parseMsgState = nsnull;
02203   PRUint32 fileSize = 0;
02204   nsCOMPtr<nsISupports> fileSupport(do_QueryInterface(fileSpec, &rv));
02205 
02206   nsCOMPtr<nsISupportsArray> messages;
02207   rv = NS_NewISupportsArray(getter_AddRefs(messages));
02208   
02209   if (msgToReplace)
02210   {
02211     nsCOMPtr<nsISupports> msgSupport(do_QueryInterface(msgToReplace, &rv));
02212     if (NS_SUCCEEDED(rv))
02213       messages->AppendElement(msgSupport);
02214   }
02215 
02216   rv = InitCopyState(fileSupport, messages, msgToReplace ? PR_TRUE:PR_FALSE,
02217                      listener, msgWindow, PR_FALSE, PR_FALSE);
02218   if (NS_FAILED(rv)) goto done;
02219 
02220   parseMsgState = new nsParseMailMessageState();
02221   if (parseMsgState)
02222   {
02223     nsCOMPtr<nsIMsgDatabase> msgDb;
02224     mCopyState->m_parseMsgState = do_QueryInterface(parseMsgState, &rv);
02225     GetDatabaseWOReparse(getter_AddRefs(msgDb));
02226     if (msgDb)
02227       parseMsgState->SetMailDB(msgDb);
02228   }
02229 
02230   rv = fileSpec->OpenStreamForReading();
02231   if (NS_FAILED(rv)) goto done;
02232 
02233   rv = fileSpec->GetInputStream(getter_AddRefs(inputStream));
02234   if (NS_FAILED(rv)) goto done;
02235   
02236   rv = NS_ERROR_NULL_POINTER;
02237   if (inputStream)
02238     rv = inputStream->Available(&fileSize);
02239   if (NS_FAILED(rv)) goto done;
02240   rv = BeginCopy(nsnull);
02241   if (NS_FAILED(rv)) goto done;
02242   rv = CopyData(inputStream, (PRInt32) fileSize);
02243   if (NS_FAILED(rv)) goto done;
02244   rv = EndCopy(PR_TRUE);
02245   if (NS_FAILED(rv)) goto done;
02246 
02247   if (msgToReplace && mDatabase)  //mDatabase should have been initialized above - if we got msgDb
02248     rv = DeleteMessage(msgToReplace, msgWindow, PR_TRUE, PR_TRUE);
02249 
02250 done:
02251   if(NS_FAILED(rv))
02252     (void) OnCopyCompleted(fileSupport, PR_FALSE);
02253 
02254   fileSpec->CloseStream();
02255   return rv;
02256 }
02257 
02258 nsresult nsMsgLocalMailFolder::DeleteMessage(nsISupports *message,
02259                                              nsIMsgWindow *msgWindow,
02260                                              PRBool deleteStorage, PRBool commit)
02261 {
02262   nsresult rv = NS_OK;
02263   if (deleteStorage)
02264   {
02265     nsCOMPtr <nsIMsgDBHdr> msgDBHdr(do_QueryInterface(message, &rv));
02266     
02267     if(NS_SUCCEEDED(rv))
02268       rv = mDatabase->DeleteHeader(msgDBHdr, nsnull, commit, PR_TRUE);
02269   }
02270   return rv;
02271 }
02272 
02273 NS_IMETHODIMP nsMsgLocalMailFolder::GetNewMessages(nsIMsgWindow *aWindow, nsIUrlListener *aListener)
02274 {
02275   nsCOMPtr<nsIMsgIncomingServer> server;
02276   nsresult rv = GetServer(getter_AddRefs(server)); 
02277   if (NS_FAILED(rv)) return rv;
02278   if (!server) return NS_MSG_INVALID_OR_MISSING_SERVER;
02279   
02280   nsCOMPtr<nsILocalMailIncomingServer> localMailServer = do_QueryInterface(server);
02281   if (!localMailServer) 
02282       return NS_MSG_INVALID_OR_MISSING_SERVER;
02283   
02284   // XXX todo, move all this into nsILocalMailIncomingServer's GetNewMail
02285   // so that we don't have to have RSS foo here.
02286   nsCOMPtr<nsIRssIncomingServer> rssServer = do_QueryInterface(server);
02287   if (rssServer)
02288   {
02289       return localMailServer->GetNewMail(aWindow, aListener, this, nsnull); 
02290   }
02291   
02292   nsCOMPtr<nsIMsgFolder> inbox;
02293   nsCOMPtr<nsIMsgFolder> rootFolder;
02294   rv = server->GetRootMsgFolder(getter_AddRefs(rootFolder));
02295   if(NS_SUCCEEDED(rv) && rootFolder)
02296   {
02297     PRUint32 numFolders;
02298     rv = rootFolder->GetFoldersWithFlag(MSG_FOLDER_FLAG_INBOX, 1, &numFolders, getter_AddRefs(inbox));
02299   }
02300   nsCOMPtr<nsIMsgLocalMailFolder> localInbox = do_QueryInterface(inbox, &rv);
02301   if (NS_SUCCEEDED(rv) && localInbox)
02302   {
02303     PRBool valid = PR_FALSE;
02304     nsCOMPtr <nsIMsgDatabase> db;
02305     // this will kick off a reparse if the db is out of date.
02306     rv = localInbox->GetDatabaseWithReparse(nsnull, aWindow, getter_AddRefs(db));
02307     if (NS_SUCCEEDED(rv) && db)
02308     {
02309       rv = db->GetSummaryValid(&valid);
02310       if (valid)
02311         rv = localMailServer->GetNewMail(aWindow, aListener, inbox, nsnull); 
02312       else
02313         rv = localInbox->SetCheckForNewMessagesAfterParsing(PR_TRUE);
02314     }
02315   }
02316   return rv;
02317 }
02318 
02319 nsresult nsMsgLocalMailFolder::WriteStartOfNewMessage()
02320 {
02321   mCopyState->m_curDstKey = mCopyState->m_fileStream->tell();
02322   
02323   // CopyFileMessage() and CopyMessages() from servers other than pop3
02324   if (mCopyState->m_parseMsgState)
02325   {
02326     mCopyState->m_parseMsgState->SetEnvelopePos(mCopyState->m_curDstKey);
02327     mCopyState->m_parseMsgState->SetState(nsIMsgParseMailMsgState::ParseHeadersState);
02328   }
02329   if (mCopyState->m_dummyEnvelopeNeeded)
02330   {
02331     nsCString result;
02332     nsCAutoString nowStr;
02333     MsgGenerateNowStr(nowStr);
02334     result.Append("From - ");
02335     result.Append(nowStr);
02336     result.Append(MSG_LINEBREAK);
02337 
02338     // *** jt - hard code status line for now; come back later
02339 
02340     nsresult rv;
02341     nsCOMPtr <nsIMsgDBHdr> curSourceMessage; 
02342     curSourceMessage = do_QueryElementAt(mCopyState->m_messages,
02343                                          mCopyState->m_curCopyIndex, &rv);
02344 
02345     char statusStrBuf[50];
02346     if (curSourceMessage)
02347     {
02348       PRUint32 dbFlags = 0;
02349       curSourceMessage->GetFlags(&dbFlags);
02350 
02351       // write out x-mozilla-status, but make sure we don't write out MSG_FLAG_OFFLINE
02352       PR_snprintf(statusStrBuf, sizeof(statusStrBuf), X_MOZILLA_STATUS_FORMAT MSG_LINEBREAK, 
02353         dbFlags & ~(MSG_FLAG_RUNTIME_ONLY | MSG_FLAG_OFFLINE) & 0x0000FFFF);
02354       // need to carry the new flag over to the new header.
02355       if (dbFlags & MSG_FLAG_NEW)
02356       {
02357       }
02358     }
02359     else
02360     {
02361       strcpy(statusStrBuf, "X-Mozilla-Status: 0001" MSG_LINEBREAK);
02362     }
02363     *(mCopyState->m_fileStream) << result.get();
02364     if (mCopyState->m_parseMsgState)
02365         mCopyState->m_parseMsgState->ParseAFolderLine(
02366           result.get(), result.Length());
02367     *(mCopyState->m_fileStream) << statusStrBuf;
02368     if (mCopyState->m_parseMsgState)
02369         mCopyState->m_parseMsgState->ParseAFolderLine(
02370         statusStrBuf, strlen(statusStrBuf));
02371     result = "X-Mozilla-Status2: 00000000" MSG_LINEBREAK;
02372     *(mCopyState->m_fileStream) << result.get();
02373     if (mCopyState->m_parseMsgState)
02374         mCopyState->m_parseMsgState->ParseAFolderLine(
02375           result.get(), result.Length());
02376     mCopyState->m_fromLineSeen = PR_TRUE;
02377   }
02378   else 
02379   {
02380     mCopyState->m_fromLineSeen = PR_FALSE;
02381   }
02382 
02383   mCopyState->m_curCopyIndex++;
02384 
02385   return NS_OK;
02386 }
02387 
02388 //nsICopyMessageListener
02389 NS_IMETHODIMP nsMsgLocalMailFolder::BeginCopy(nsIMsgDBHdr *message)
02390 {
02391   if (!mCopyState) 
02392     return NS_ERROR_NULL_POINTER;
02393 
02394   nsresult rv = NS_OK;
02395   mCopyState->m_fileStream->seek(PR_SEEK_END, 0);
02396  
02397   PRInt32 messageIndex = (mCopyState->m_copyingMultipleMessages) ? mCopyState->m_curCopyIndex - 1 : mCopyState->m_curCopyIndex;
02398   NS_ASSERTION(!mCopyState->m_copyingMultipleMessages || mCopyState->m_curCopyIndex >= 0, "mCopyState->m_curCopyIndex invalid");
02399   // by the time we get here, m_curCopyIndex is 1 relative because WriteStartOfNewMessage increments it
02400   mCopyState->m_messages->QueryElementAt(messageIndex, NS_GET_IID(nsIMsgDBHdr),
02401                                   (void **)getter_AddRefs(mCopyState->m_message));
02402 
02403   DisplayMoveCopyStatusMsg();
02404   // if we're copying more than one message, StartMessage will handle this.
02405   if (!mCopyState->m_copyingMultipleMessages)
02406   rv = WriteStartOfNewMessage();
02407   return rv;
02408 }
02409 
02410 NS_IMETHODIMP nsMsgLocalMailFolder::CopyData(nsIInputStream *aIStream, PRInt32 aLength)
02411 {
02412   //check to make sure we have control of the write.
02413   PRBool haveSemaphore;
02414   nsresult rv = NS_OK;
02415   
02416   rv = TestSemaphore(NS_STATIC_CAST(nsIMsgLocalMailFolder*, this),
02417     &haveSemaphore);
02418   if(NS_FAILED(rv))
02419     return rv;
02420   if(!haveSemaphore)
02421     return NS_MSG_FOLDER_BUSY;
02422   
02423   if (!mCopyState) 
02424     return NS_ERROR_OUT_OF_MEMORY;
02425   
02426   PRUint32 readCount;
02427   if ( aLength + mCopyState->m_leftOver > mCopyState->m_dataBufferSize )
02428   {
02429     mCopyState->m_dataBuffer = (char *) PR_REALLOC(mCopyState->m_dataBuffer, aLength + mCopyState->m_leftOver+ 1);
02430     if (!mCopyState->m_dataBuffer)
02431       return NS_ERROR_OUT_OF_MEMORY;
02432     mCopyState->m_dataBufferSize = aLength + mCopyState->m_leftOver;
02433   }
02434   
02435   mCopyState->m_fileStream->seek(PR_SEEK_END, 0);
02436   char *start, *end;
02437   PRUint32 linebreak_len = 0;
02438   
02439   rv = aIStream->Read(mCopyState->m_dataBuffer + mCopyState->m_leftOver,
02440     aLength, &readCount);
02441   NS_ENSURE_SUCCESS(rv, rv);
02442   mCopyState->m_leftOver += readCount;
02443   mCopyState->m_dataBuffer[mCopyState->m_leftOver] ='\0';
02444   start = mCopyState->m_dataBuffer;
02445   end = (char *) memchr(start, '\r', mCopyState->m_leftOver);
02446   if (!end)
02447     end = (char *) memchr(start, '\n', mCopyState->m_leftOver);
02448   else if (*(end+1) == nsCRT::LF && linebreak_len == 0)
02449     linebreak_len = 2;
02450   
02451   if (linebreak_len == 0) // not set yet
02452     linebreak_len = 1;
02453   
02454   nsCString line;
02455   char tmpChar = 0;
02456   PRInt32 lineLength, bytesWritten;
02457   
02458   while (start && end)
02459   {
02460     if (mCopyState->m_fromLineSeen)
02461     {
02462       if (strncmp(start, "From ", 5) == 0)
02463       {
02464         line = ">";
02465         
02466         tmpChar = *end;
02467         *end = 0;
02468         line += start;
02469         *end = tmpChar;
02470         line += MSG_LINEBREAK;
02471         
02472         lineLength = line.Length();
02473         bytesWritten = mCopyState->m_fileStream->write(line.get(), lineLength); 
02474         if (bytesWritten != lineLength)
02475         {
02476           ThrowAlertMsg("copyMsgWriteFailed", mCopyState->m_msgWindow);
02477           mCopyState->m_writeFailed = PR_TRUE;
02478           return NS_MSG_ERROR_WRITING_MAIL_FOLDER;
02479         }
02480         
02481         if (mCopyState->m_parseMsgState)
02482           mCopyState->m_parseMsgState->ParseAFolderLine(line.get(),
02483           line.Length());
02484         goto keepGoing;
02485       }
02486     }
02487     else
02488     {
02489       mCopyState->m_fromLineSeen = PR_TRUE;
02490       NS_ASSERTION(strncmp(start, "From ", 5) == 0, 
02491         "Fatal ... bad message format\n");
02492     }
02493     
02494     lineLength = end-start+linebreak_len;
02495     bytesWritten = mCopyState->m_fileStream->write(start, lineLength);
02496     if (bytesWritten != lineLength)
02497     {
02498       ThrowAlertMsg("copyMsgWriteFailed", mCopyState->m_msgWindow);
02499       mCopyState->m_writeFailed = PR_TRUE;
02500       return NS_MSG_ERROR_WRITING_MAIL_FOLDER;
02501     }
02502     
02503     if (mCopyState->m_parseMsgState)
02504       mCopyState->m_parseMsgState->ParseAFolderLine(start,
02505       end-start+linebreak_len);
02506 keepGoing:
02507     start = end+linebreak_len;
02508     if (start >=
02509       &mCopyState->m_dataBuffer[mCopyState->m_leftOver])
02510     {
02511       mCopyState->m_leftOver = 0;
02512       break;
02513     }
02514     char *endBuffer = mCopyState->m_dataBuffer + mCopyState->m_leftOver;
02515     end = (char *) memchr(start, '\r', endBuffer - start + 1);
02516     if (end)
02517     {
02518       if (*(end+1) == nsCRT::LF)  //need to set the linebreak_len each time
02519         linebreak_len = 2;  //CRLF
02520       else
02521         linebreak_len = 1;  //only CR
02522     }
02523     if (!end)
02524     {
02525       end = (char *) memchr(start, '\n', endBuffer - start + 1);
02526       if (end)
02527         linebreak_len = 1;   //LF
02528       else
02529         linebreak_len =0;  //no LF
02530     }
02531     if (start && !end)
02532     {
02533       mCopyState->m_leftOver -= (start - mCopyState->m_dataBuffer);
02534       memcpy (mCopyState->m_dataBuffer, start,
02535         mCopyState->m_leftOver+1);
02536     }
02537   }
02538   return rv;
02539 }
02540 
02541 void nsMsgLocalMailFolder::CopyPropertiesToMsgHdr(nsIMsgDBHdr *destHdr, nsIMsgDBHdr *srcHdr)
02542 {
02543   nsXPIDLCString sourceString;
02544   srcHdr->GetStringProperty("junkscore", getter_Copies(sourceString));
02545   destHdr->SetStringProperty("junkscore", sourceString);
02546   srcHdr->GetStringProperty("junkscoreorigin", getter_Copies(sourceString));
02547   destHdr->SetStringProperty("junkscoreorigin", sourceString);
02548   srcHdr->GetStringProperty("keywords", getter_Copies(sourceString));
02549   destHdr->SetStringProperty("keywords", sourceString);
02550   
02551   nsMsgLabelValue label = 0;
02552   srcHdr->GetLabel(&label);
02553   destHdr->SetLabel(label);
02554 }
02555 
02556 
02557 NS_IMETHODIMP nsMsgLocalMailFolder::EndCopy(PRBool copySucceeded)
02558 {
02559   // we are the destination folder for a move/copy
02560   nsresult rv = copySucceeded ? NS_OK : NS_ERROR_FAILURE;
02561   if (!mCopyState) return NS_OK;
02562   if (!copySucceeded || mCopyState->m_writeFailed)
02563   {
02564     if (mCopyState->m_fileStream)
02565       mCopyState->m_fileStream->close();
02566     
02567     nsCOMPtr <nsIFileSpec> pathSpec;
02568     rv = GetPath(getter_AddRefs(pathSpec));
02569     
02570     if (NS_SUCCEEDED(rv) && pathSpec)
02571       pathSpec->Truncate(mCopyState->m_curDstKey);
02572     
02573     if (!mCopyState->m_isMove)
02574     {
02575     /*passing PR_TRUE because the messages that have been successfully copied have their corresponding
02576     hdrs in place. The message that has failed has been truncated so the msf file and berkeley mailbox
02577       are in sync*/
02578       
02579       (void) OnCopyCompleted(mCopyState->m_srcSupport, PR_TRUE);
02580       
02581       // enable the dest folder
02582       EnableNotifications(allMessageCountNotifications, PR_TRUE, PR_FALSE /*dbBatching*/); //dest folder doesn't need db batching
02583     }
02584     return NS_OK;
02585   }
02586   
02587   nsRefPtr<nsLocalMoveCopyMsgTxn> localUndoTxn;
02588   PRBool multipleCopiesFinished = (mCopyState->m_curCopyIndex >= mCopyState->m_totalMsgCount);
02589   
02590   if (mCopyState->m_undoMsgTxn)
02591     mCopyState->m_undoMsgTxn->QueryInterface(NS_GET_IID(nsLocalMoveCopyMsgTxn), getter_AddRefs(localUndoTxn));
02592 
02593   
02594   if (mCopyState)
02595   {
02596     NS_ASSERTION(mCopyState->m_leftOver == 0, "whoops, something wrong with previous copy");
02597     mCopyState->m_leftOver = 0; // reset to 0.
02598     // need to reset this in case we're move/copying multiple msgs.
02599     mCopyState->m_fromLineSeen = PR_FALSE;
02600     // flush the copied message.
02601     if (mCopyState->m_fileStream)
02602       mCopyState->m_fileStream->seek(PR_SEEK_CUR, 0); // seeking causes a flush, w/o syncing
02603   }
02604   //Copy the header to the new database
02605   if (copySucceeded && mCopyState->m_message)
02606   { //  CopyMessages() goes here; CopyFileMessage() never gets in here because
02607     //  the mCopyState->m_message will be always null for file message
02608     
02609     nsCOMPtr<nsIMsgDBHdr> newHdr;
02610     
02611     if(!mCopyState->m_parseMsgState)
02612     {
02613       if(mCopyState->m_destDB)
02614       {
02615         rv = mCopyState->m_destDB->CopyHdrFromExistingHdr(mCopyState->m_curDstKey,
02616           mCopyState->m_message, PR_TRUE,
02617           getter_AddRefs(newHdr));
02618         PRUint32 newHdrFlags;
02619         // turn off offline flag - it's not valid for local mail folders.
02620         if (newHdr)
02621           newHdr->AndFlags(~MSG_FLAG_OFFLINE, &newHdrFlags);
02622       }
02623       // we can do undo with the dest folder db, see bug #198909
02624       //else
02625       //  mCopyState->m_undoMsgTxn = nsnull; //null out the transaction because we can't undo w/o the msg db
02626     }
02627     
02628     // if we plan on allowing undo, (if we have a mCopyState->m_parseMsgState or not)
02629     // we need to save the source and dest keys on the undo txn.
02630     // see bug #179856 for details
02631     PRBool isImap;
02632     if (NS_SUCCEEDED(rv) && localUndoTxn) {
02633       localUndoTxn->GetSrcIsImap(&isImap);
02634       if (!isImap || !mCopyState->m_copyingMultipleMessages)
02635       {
02636         nsMsgKey aKey;
02637         PRUint32 statusOffset;
02638         mCopyState->m_message->GetMessageKey(&aKey);
02639         mCopyState->m_message->GetStatusOffset(&statusOffset);
02640         localUndoTxn->AddSrcKey(aKey);
02641         localUndoTxn->AddSrcStatusOffset(statusOffset);
02642         localUndoTxn->AddDstKey(mCopyState->m_curDstKey);
02643       }
02644     }
02645   }
02646   if (mCopyState->m_dummyEnvelopeNeeded)
02647   {
02648     mCopyState->m_fileStream->seek(PR_SEEK_END, 0);
02649     *(mCopyState->m_fileStream) << MSG_LINEBREAK;
02650     if (mCopyState->m_parseMsgState)
02651       mCopyState->m_parseMsgState->ParseAFolderLine(CRLF, MSG_LINEBREAK_LEN);
02652   }
02653   
02654   // CopyFileMessage() and CopyMessages() from servers other than mailbox
02655   if (mCopyState->m_parseMsgState)
02656   {
02657     nsCOMPtr<nsIMsgDatabase> msgDb;
02658     nsCOMPtr<nsIMsgDBHdr> newHdr;
02659     
02660     mCopyState->m_parseMsgState->FinishHeader();
02661     
02662     GetDatabaseWOReparse(getter_AddRefs(msgDb));
02663     if (msgDb)
02664     {
02665       nsresult result = mCopyState->m_parseMsgState->GetNewMsgHdr(getter_AddRefs(newHdr));
02666       if (NS_SUCCEEDED(result) && newHdr)
02667       {
02668         // need to copy junk score and label from mCopyState->m_message to newHdr.
02669         if (mCopyState->m_message)
02670         {
02671           // deal with propagating the new flag on an imap to local folder filter action
02672           PRUint32 msgFlags;
02673           mCopyState->m_message->GetFlags(&msgFlags);
02674           if (!(msgFlags & MSG_FLAG_READ))
02675           {
02676             nsCOMPtr <nsIMsgFolder> srcFolder;
02677             mCopyState->m_message->GetFolder(getter_AddRefs(srcFolder));
02678             if (srcFolder)
02679             {
02680               PRUint32 folderFlags;
02681               srcFolder->GetFlags(&folderFlags);
02682               // check if the src folder is an imap inbox.
02683               if ((folderFlags & (MSG_FOLDER_FLAG_INBOX|MSG_FOLDER_FLAG_IMAPBOX))
02684                             == (MSG_FOLDER_FLAG_INBOX|MSG_FOLDER_FLAG_IMAPBOX))
02685               {
02686                 nsCOMPtr <nsIMsgDatabase> db;
02687                 srcFolder->GetMsgDatabase(nsnull, getter_AddRefs(db));
02688                 if (db)
02689                 {
02690                   nsMsgKey srcKey;
02691                   PRBool containsKey;
02692                   mCopyState->m_message->GetMessageKey(&srcKey);
02693                   db->ContainsKey(srcKey, &containsKey);
02694                   // if the db doesn't have the key, it must be a filtered imap
02695                   // message, getting moved to a local folder.
02696                   if (!containsKey)
02697                     newHdr->OrFlags(MSG_FLAG_NEW, &msgFlags);
02698                 }
02699               }
02700             }
02701           }
02702           CopyPropertiesToMsgHdr(newHdr, mCopyState->m_message);
02703         }
02704         msgDb->AddNewHdrToDB(newHdr, PR_TRUE);
02705         if (localUndoTxn)
02706         { 
02707           // ** jt - recording the message size for possible undo use; the
02708           // message size is different for pop3 and imap4 messages
02709           PRUint32 msgSize;
02710           newHdr->GetMessageSize(&msgSize);
02711           localUndoTxn->AddDstMsgSize(msgSize);
02712         }
02713       }
02714       //      msgDb->SetSummaryValid(PR_TRUE);
02715       //      msgDb->Commit(nsMsgDBCommitType::kLargeCommit);
02716     }
02717     else
02718       mCopyState->m_undoMsgTxn = nsnull; //null out the transaction because we can't undo w/o the msg db
02719     
02720     mCopyState->m_parseMsgState->Clear();
02721     
02722     if (mCopyState->m_listener) // CopyFileMessage() only
02723       mCopyState->m_listener->SetMessageKey((PRUint32) mCopyState->m_curDstKey);
02724   }
02725   
02726   if (!multipleCopiesFinished && !mCopyState->m_copyingMultipleMessages)
02727   { // CopyMessages() goes here; CopyFileMessage() never gets in here because
02728     // curCopyIndex will always be less than the mCopyState->m_totalMsgCount
02729     nsCOMPtr<nsISupports> aSupport =
02730       getter_AddRefs(mCopyState->m_messages->ElementAt
02731       (mCopyState->m_curCopyIndex));
02732     rv = CopyMessageTo(aSupport, this, mCopyState->m_msgWindow, mCopyState->m_isMove);
02733   }
02734   else
02735   { // both CopyMessages() & CopyFileMessage() go here if they have
02736     // done copying operation; notify completion to copy service
02737     
02738     // notify the global msg folder listeners
02739     if (multipleCopiesFinished)
02740     {
02741       // we need to send this notification before we delete the source messages,
02742       // because deleting the source messages clears out the src msg db hdr.
02743       nsCOMPtr <nsIMsgFolderNotificationService> notifier = do_GetService(NS_MSGNOTIFICATIONSERVICE_CONTRACTID);
02744       if (notifier)
02745       {
02746         PRBool hasListeners;
02747         notifier->GetHasListeners(&hasListeners);
02748         if (hasListeners)
02749             notifier->NotifyItemMoveCopyCompleted(mCopyState->m_isMove, mCopyState->m_messages, this);
02750       }
02751       
02752     }
02753     if(!mCopyState->m_isMove)
02754     {
02755       if (multipleCopiesFinished)
02756       {
02757         nsCOMPtr<nsIMsgFolder> srcFolder;
02758         srcFolder = do_QueryInterface(mCopyState->m_srcSupport);
02759         if (mCopyState->m_isFolder)
02760           CopyAllSubFolders(srcFolder, nsnull, nsnull);  //Copy all subfolders then notify completion
02761         
02762         if (mCopyState->m_msgWindow && mCopyState->m_undoMsgTxn)
02763         {
02764           nsCOMPtr<nsITransactionManager> txnMgr;
02765           mCopyState->m_msgWindow->GetTransactionManager(getter_AddRefs(txnMgr));
02766           if (txnMgr)
02767             txnMgr->DoTransaction(mCopyState->m_undoMsgTxn);
02768         }
02769         if (srcFolder && !mCopyState->m_isFolder)
02770           srcFolder->NotifyFolderEvent(mDeleteOrMoveMsgCompletedAtom);   
02771         
02772         (void) OnCopyCompleted(mCopyState->m_srcSupport, PR_TRUE);
02773         // enable the dest folder
02774         EnableNotifications(allMessageCountNotifications, PR_TRUE, PR_FALSE /*dbBatching*/); //dest folder doesn't need db batching
02775       }
02776     }
02777   }
02778   return rv;
02779 }
02780 
02781 static PRBool gGotGlobalPrefs;
02782 static PRBool gDeleteFromServerOnMove;
02783 
02784 PRBool nsMsgLocalMailFolder::GetDeleteFromServerOnMove()
02785 {
02786   PRBool deleteFromServerOnMove = PR_FALSE;
02787   if (!gGotGlobalPrefs)
02788   {
02789     nsCOMPtr<nsIPrefBranch> pPrefBranch(do_GetService(NS_PREFSERVICE_CONTRACTID));
02790     if (pPrefBranch)
02791     {
02792       pPrefBranch->GetBoolPref("mail.pop3.deleteFromServerOnMove", &gDeleteFromServerOnMove);
02793       gGotGlobalPrefs = PR_TRUE;
02794     }   
02795   }
02796   return gDeleteFromServerOnMove;
02797 }
02798 
02799 NS_IMETHODIMP nsMsgLocalMailFolder::EndMove(PRBool moveSucceeded)
02800 {
02801   nsresult result = NS_OK;
02802 
02803   if (!mCopyState)
02804     return NS_OK;
02805   if (!moveSucceeded || mCopyState->m_writeFailed)
02806   {
02807     //Notify that a completion finished.
02808     nsCOMPtr<nsIMsgFolder> srcFolder = do_QueryInterface(mCopyState->m_srcSupport);
02809     srcFolder->NotifyFolderEvent(mDeleteOrMoveMsgFailedAtom);
02810 
02811     /*passing PR_TRUE because the messages that have been successfully copied have their corressponding
02812     hdrs in place. The message that has failed has been truncated so the msf file and berkeley mailbox
02813     are in sync*/
02814 
02815     (void) OnCopyCompleted(mCopyState->m_srcSupport, PR_TRUE);
02816 
02817     // enable the dest folder
02818     EnableNotifications(allMessageCountNotifications, PR_TRUE, PR_FALSE /*dbBatching*/ );  //dest folder doesn't need db batching
02819    
02820     return NS_OK;
02821   }
02822   
02823   if (mCopyState && mCopyState->m_curCopyIndex >= mCopyState->m_totalMsgCount)
02824   {
02825     //Notify that a completion finished.
02826     nsCOMPtr<nsIMsgFolder> srcFolder = do_QueryInterface(mCopyState->m_srcSupport);
02827     if(srcFolder)
02828     {
02829       nsCOMPtr <nsIMsgLocalMailFolder> localSrcFolder = do_QueryInterface(srcFolder);
02830       if (localSrcFolder)
02831       {
02832         // if we are the trash and a local msg is being moved to us, mark the source
02833         // for delete from server, if so configured.
02834         if (mFlags & MSG_FOLDER_FLAG_TRASH)
02835         {
02836           // if we're deleting on all moves, we'll mark this message for deletion when
02837           // we call DeleteMessages on the source folder. So don't mark it for deletion
02838           // here, in that case.
02839           if (!GetDeleteFromServerOnMove())
02840             localSrcFolder->MarkMsgsOnPop3Server(mCopyState->m_messages, POP3_DELETE);
02841         }
02842 
02843       }
02844       // lets delete these all at once - much faster that way
02845       result = srcFolder->DeleteMessages(mCopyState->m_messages, mCopyState->m_msgWindow, PR_TRUE, PR_TRUE, nsnull, mCopyState->m_allowUndo);
02846       srcFolder->NotifyFolderEvent(NS_SUCCEEDED(result) ? mDeleteOrMoveMsgCompletedAtom : mDeleteOrMoveMsgFailedAtom);
02847       AutoCompact(mCopyState->m_msgWindow);
02848     }
02849       
02850     // enable the dest folder
02851     EnableNotifications(allMessageCountNotifications, PR_TRUE, PR_FALSE /*dbBatching*/); //dest folder doesn't need db batching
02852       
02853     if (NS_SUCCEEDED(result) && mCopyState->m_msgWindow && mCopyState->m_undoMsgTxn)
02854     {
02855       nsCOMPtr<nsITransactionManager> txnMgr;
02856       mCopyState->m_msgWindow->GetTransactionManager(getter_AddRefs(txnMgr));
02857       if (txnMgr)
02858         txnMgr->DoTransaction(mCopyState->m_undoMsgTxn);
02859     }
02860     (void) OnCopyCompleted(mCopyState->m_srcSupport, NS_SUCCEEDED(result) ? PR_TRUE : PR_FALSE);  //clear the copy state so that the next message from a different folder can be move
02861   }
02862   
02863   return NS_OK;
02864 
02865 }
02866 
02867 // this is the beginning of the next message copied
02868 NS_IMETHODIMP nsMsgLocalMailFolder::StartMessage()
02869 {
02870   return WriteStartOfNewMessage();
02871 }
02872 
02873 // just finished the current message.
02874 NS_IMETHODIMP nsMsgLocalMailFolder::EndMessage(nsMsgKey key)
02875 {
02876   nsRefPtr<nsLocalMoveCopyMsgTxn> localUndoTxn;
02877   nsCOMPtr<nsIMsgWindow> msgWindow;
02878   nsresult rv;
02879   
02880   if (mCopyState->m_undoMsgTxn)
02881   {
02882     rv = mCopyState->m_undoMsgTxn->QueryInterface(NS_GET_IID(nsLocalMoveCopyMsgTxn), getter_AddRefs(localUndoTxn));
02883     if (localUndoTxn)
02884       localUndoTxn->GetMsgWindow(getter_AddRefs(msgWindow));
02885   }
02886   
02887   if (localUndoTxn)
02888   {
02889     localUndoTxn->AddSrcKey(key);
02890     localUndoTxn->AddDstKey(mCopyState->m_curDstKey);
02891   }
02892   
02893   // I think this is always true for online to offline copy
02894   mCopyState->m_dummyEnvelopeNeeded = PR_TRUE;
02895   
02896   if (mCopyState->m_dummyEnvelopeNeeded)
02897   {
02898     mCopyState->m_fileStream->seek(PR_SEEK_END, 0);
02899     *(mCopyState->m_fileStream) << MSG_LINEBREAK;
02900     if (mCopyState->m_parseMsgState)
02901       mCopyState->m_parseMsgState->ParseAFolderLine(CRLF, MSG_LINEBREAK_LEN);
02902   }
02903   
02904   // CopyFileMessage() and CopyMessages() from servers other than mailbox
02905   if (mCopyState->m_parseMsgState)
02906   {
02907     nsresult result;
02908     nsCOMPtr<nsIMsgDatabase> msgDb;
02909     nsCOMPtr<nsIMsgDBHdr> newHdr;
02910     
02911     mCopyState->m_parseMsgState->FinishHeader();
02912     
02913     result = mCopyState->m_parseMsgState->GetNewMsgHdr(getter_AddRefs(newHdr));
02914     if (NS_SUCCEEDED(result) && newHdr)
02915     {
02916       nsCOMPtr<nsIMsgFolder> srcFolder = do_QueryInterface(mCopyState->m_srcSupport);
02917       nsCOMPtr<nsIMsgDatabase> srcDB;
02918       if (srcFolder)
02919       {
02920         srcFolder->GetMsgDatabase(nsnull, getter_AddRefs(srcDB));
02921         if (srcDB)
02922         {
02923           nsCOMPtr <nsIMsgDBHdr> srcMsgHdr;
02924           srcDB->GetMsgHdrForKey(key, getter_AddRefs(srcMsgHdr));
02925           if (srcMsgHdr)
02926             CopyPropertiesToMsgHdr(newHdr, srcMsgHdr);
02927         }
02928       }
02929       
02930       result = GetDatabaseWOReparse(getter_AddRefs(msgDb));
02931       if (NS_SUCCEEDED(result) && msgDb)
02932       {
02933         msgDb->AddNewHdrToDB(newHdr, PR_TRUE);
02934         if (localUndoTxn)
02935         { // ** jt - recording the message size for possible undo use; the
02936           // message size is different for pop3 and imap4 messages
02937           PRUint32 msgSize;
02938           newHdr->GetMessageSize(&msgSize);
02939           localUndoTxn->AddDstMsgSize(msgSize);
02940         }
02941       }
02942       else
02943         mCopyState->m_undoMsgTxn = nsnull; //null out the transaction because we can't undo w/o the msg db
02944     }
02945     mCopyState->m_parseMsgState->Clear();
02946     
02947     if (mCopyState->m_listener) // CopyFileMessage() only
02948       mCopyState->m_listener->SetMessageKey((PRUint32) mCopyState->m_curDstKey);
02949   }
02950   
02951   if (mCopyState->m_fileStream)
02952     mCopyState->m_fileStream->flush();
02953   return NS_OK;
02954 }
02955 
02956 
02957 nsresult nsMsgLocalMailFolder::CopyMessagesTo(nsISupportsArray *messages, 
02958                                              nsIMsgWindow *aMsgWindow, nsIMsgFolder *dstFolder,
02959                                              PRBool isMove)
02960 {
02961   if (!mCopyState) return NS_ERROR_OUT_OF_MEMORY;
02962    
02963   nsresult rv;
02964 
02965   nsCOMPtr<nsICopyMessageStreamListener> copyStreamListener = do_CreateInstance(NS_COPYMESSAGESTREAMLISTENER_CONTRACTID, &rv);
02966   NS_ENSURE_SUCCESS(rv,rv);
02967   
02968   nsCOMPtr<nsICopyMessageListener> copyListener(do_QueryInterface(dstFolder));
02969   if(!copyListener)
02970     return NS_ERROR_NO_INTERFACE;
02971   
02972   nsCOMPtr<nsIMsgFolder> srcFolder(do_QueryInterface(mCopyState->m_srcSupport));
02973   if(!srcFolder)
02974     return NS_ERROR_NO_INTERFACE;
02975   
02976   rv = copyStreamListener->Init(srcFolder, copyListener, nsnull);
02977   if(NS_FAILED(rv))
02978     return rv;
02979   
02980   if (!mCopyState->m_messageService)
02981   {
02982     nsXPIDLCString uri;
02983     srcFolder->GetURI(getter_Copies(uri));
02984     rv = GetMessageServiceFromURI(uri, getter_AddRefs(mCopyState->m_messageService));
02985   }
02986   
02987   if (NS_SUCCEEDED(rv) && mCopyState->m_messageService)
02988   {
02989     nsMsgKeyArray keyArray;
02990     PRUint32 numMessages = 0;
02991     messages->Count(&numMessages);
02992     for (PRUint32 i = 0; i < numMessages; i++)
02993     {
02994       nsCOMPtr<nsIMsgDBHdr> aMessage = do_QueryElementAt(messages, i, &rv);
02995       if(NS_SUCCEEDED(rv) && aMessage)
02996       {
02997         nsMsgKey key;
02998         aMessage->GetMessageKey(&key);
02999         keyArray.Add(key);
03000       }
03001     }
03002     keyArray.QuickSort();
03003     rv = SortMessagesBasedOnKey(messages, &keyArray, srcFolder);
03004     NS_ENSURE_SUCCESS(rv,rv);
03005     
03006     nsCOMPtr<nsIStreamListener> streamListener(do_QueryInterface(copyStreamListener));
03007     if(!streamListener)
03008       return NS_ERROR_NO_INTERFACE;
03009     mCopyState->m_curCopyIndex = 0; 
03010     // we need to kick off the first message - subsequent messages
03011     // are kicked off by nsMailboxProtocol when it finishes a message
03012     // before starting the next message. Only do this if the source folder
03013     // is a local folder, however. IMAP will handle calling StartMessage for
03014     // each message that gets downloaded, and news doesn't go through here
03015     // because news only downloads one message at a time, and this routine
03016     // is for multiple message copy.
03017     nsCOMPtr <nsIMsgLocalMailFolder> srcLocalFolder = do_QueryInterface(srcFolder);
03018     if (srcLocalFolder)
03019       StartMessage();
03020     mCopyState->m_messageService->CopyMessages(&keyArray, srcFolder, streamListener, isMove,
03021       nsnull, aMsgWindow, nsnull);
03022   }
03023   
03024   return rv;
03025 }
03026 
03027 nsresult nsMsgLocalMailFolder::CopyMessageTo(nsISupports *message, 
03028                                              nsIMsgFolder *dstFolder /* dst same as "this" */,
03029                                              nsIMsgWindow *aMsgWindow,
03030                                              PRBool isMove)
03031 {
03032   if (!mCopyState) return NS_ERROR_OUT_OF_MEMORY;
03033   
03034   nsresult rv;
03035   nsCOMPtr<nsIMsgDBHdr> msgHdr(do_QueryInterface(message));
03036   if(!msgHdr)
03037     return NS_ERROR_FAILURE;
03038   
03039   mCopyState->m_message = do_QueryInterface(msgHdr, &rv);
03040   
03041   nsCOMPtr<nsIMsgFolder> srcFolder(do_QueryInterface(mCopyState->m_srcSupport));
03042   if(!srcFolder)
03043     return NS_ERROR_NO_INTERFACE;
03044   nsXPIDLCString uri;
03045   srcFolder->GetUriForMsg(msgHdr, getter_Copies(uri));
03046   
03047   nsCOMPtr<nsICopyMessageStreamListener> copyStreamListener = do_CreateInstance(NS_COPYMESSAGESTREAMLISTENER_CONTRACTID, &rv);
03048   NS_ENSURE_SUCCESS(rv,rv);
03049   
03050   nsCOMPtr<nsICopyMessageListener> copyListener(do_QueryInterface(dstFolder));
03051   if(!copyListener)
03052     return NS_ERROR_NO_INTERFACE;
03053   
03054   rv = copyStreamListener->Init(srcFolder, copyListener, nsnull);
03055   if(NS_FAILED(rv))
03056     return rv;
03057   
03058   if (!mCopyState->m_messageService)
03059   {
03060     rv = GetMessageServiceFromURI(uri, getter_AddRefs(mCopyState->m_messageService));
03061   }
03062   
03063   if (NS_SUCCEEDED(rv) && mCopyState->m_messageService)
03064   {
03065     nsCOMPtr<nsIStreamListener>
03066       streamListener(do_QueryInterface(copyStreamListener));
03067     if(!streamListener)
03068       return NS_ERROR_NO_INTERFACE;
03069     mCopyState->m_messageService->CopyMessage(uri, streamListener, isMove,
03070       nsnull, aMsgWindow, nsnull);
03071   }
03072   
03073   return rv;
03074 }
03075 
03076 // A message is being deleted from a POP3 mail file, so check and see if we have the message
03077 // being deleted in the server. If so, then we need to remove the message from the server as well.
03078 // We have saved the UIDL of the message in the popstate.dat file and we must match this uidl, so
03079 // read the message headers and see if we have it, then mark the message for deletion from the server.
03080 // The next time we look at mail the message will be deleted from the server.
03081 
03082 NS_IMETHODIMP
03083 nsMsgLocalMailFolder::MarkMsgsOnPop3Server(nsISupportsArray *aMessages, PRInt32 aMark)
03084 {
03085   nsLocalFolderScanState folderScanState;
03086   nsCOMPtr<nsIPop3IncomingServer> curFolderPop3MailServer;
03087   nsCOMPtr<nsIFileSpec> mailboxSpec;
03088   nsFileSpec realSpec;
03089   nsCOMArray<nsIPop3IncomingServer> pop3Servers; // servers with msgs deleted...
03090 
03091   nsCOMPtr<nsIMsgIncomingServer> incomingServer;
03092   nsresult rv = GetServer(getter_AddRefs(incomingServer));
03093   if (!incomingServer) 
03094     return NS_MSG_INVALID_OR_MISSING_SERVER;
03095 
03096   nsCOMPtr <nsIMsgAccountManager> accountManager = do_GetService(NS_MSGACCOUNTMANAGER_CONTRACTID, &rv);
03097   NS_ENSURE_SUCCESS(rv,rv);
03098 
03099   // I wonder if we should run through the pop3 accounts and see if any of them have
03100   // leave on server set. If not, we could short-circuit some of this.
03101 
03102   curFolderPop3MailServer = do_QueryInterface(incomingServer, &rv);
03103   rv = GetPath(getter_AddRefs(mailboxSpec));
03104   NS_ENSURE_SUCCESS(rv, rv);
03105 
03106   mailboxSpec->GetFileSpec(&realSpec);
03107   folderScanState.m_fileSpec = &realSpec;
03108   rv = GetFolderScanState(&folderScanState);
03109   NS_ENSURE_SUCCESS(rv,rv);
03110   
03111   PRUint32 srcCount;
03112   aMessages->Count(&srcCount);
03113   
03114   // Filter delete requests are always honored, others are subject
03115   // to the deleteMailLeftOnServer preference.
03116   PRInt32 mark;
03117   mark = (aMark == POP3_FORCE_DEL) ? POP3_DELETE : aMark;
03118 
03119   for (PRUint32 i = 0; i < srcCount; i++)
03120   {
03121     /* get uidl for this message */
03122     nsCOMPtr<nsIMsgDBHdr> msgDBHdr (do_QueryElementAt(aMessages, i, &rv));
03123     
03124     PRUint32 flags = 0;
03125 
03126     if (msgDBHdr)
03127     {
03128       msgDBHdr->GetFlags(&flags);
03129       nsCOMPtr <nsIPop3IncomingServer> msgPop3Server = curFolderPop3MailServer;
03130       PRBool leaveOnServer = PR_FALSE;
03131       PRBool deleteMailLeftOnServer = PR_FALSE;
03132       // set up defaults, in case there's no x-mozilla-account header
03133       if (curFolderPop3MailServer)
03134       {
03135         curFolderPop3MailServer->GetDeleteMailLeftOnServer(&deleteMailLeftOnServer);
03136         curFolderPop3MailServer->GetLeaveMessagesOnServer(&leaveOnServer);
03137       }
03138 
03139       rv = GetUidlFromFolder(&folderScanState, msgDBHdr);
03140       if (!NS_SUCCEEDED(rv))
03141         continue;
03142 
03143       if (folderScanState.m_uidl)
03144       {
03145         nsCOMPtr <nsIMsgAccount> account;
03146         rv = accountManager->GetAccount(folderScanState.m_accountKey.get(), getter_AddRefs(account));
03147         if (NS_SUCCEEDED(rv) && account)
03148         {
03149           account->GetIncomingServer(getter_AddRefs(incomingServer));
03150           nsCOMPtr<nsIPop3IncomingServer> curMsgPop3MailServer = do_QueryInterface(incomingServer);
03151           if (curMsgPop3MailServer)
03152           {
03153             msgPop3Server = curMsgPop3MailServer;
03154             msgPop3Server->GetDeleteMailLeftOnServer(&deleteMailLeftOnServer);
03155             msgPop3Server->GetLeaveMessagesOnServer(&leaveOnServer);
03156           }
03157         }
03158       }
03159       // ignore this header if not partial and leaveOnServer not set...
03160       // or if we can't find the pop3 server.
03161       if (!msgPop3Server || (! (flags & MSG_FLAG_PARTIAL) && !leaveOnServer))
03162         continue;
03163       // if marking deleted, ignore header if we're not deleting from
03164       // server when deleting locally.
03165       if (aMark == POP3_DELETE && leaveOnServer && !deleteMailLeftOnServer)
03166         continue;
03167       if (folderScanState.m_uidl)
03168       {
03169         msgPop3Server->AddUidlToMark(folderScanState.m_uidl, mark);
03170         // remember this pop server in list of servers with msgs deleted
03171         if (pop3Servers.IndexOfObject(msgPop3Server) == kNotFound)
03172           pop3Servers.AppendObject(msgPop3Server);
03173       }
03174     }
03175   }
03176 
03177   // need to do this for all pop3 mail servers that had messages deleted.
03178   PRUint32 serverCount = pop3Servers.Count();
03179   for (PRUint32 index = 0; index < serverCount; index++)
03180     pop3Servers[index]->MarkMessages();
03181 
03182   mailboxSpec->CloseStream();
03183   return rv;
03184 }
03185 
03186 NS_IMETHODIMP nsMsgLocalMailFolder::DeleteDownloadMsg(nsIMsgDBHdr *aMsgHdr,
03187   PRBool *aDoSelect)
03188 {
03189   PRUint32 numMsgs;
03190   char *newMsgId;
03191 
03192   // This method is only invoked thru DownloadMessagesForOffline()
03193   if (mDownloadState != DOWNLOAD_STATE_NONE)
03194   {
03195     // We only remember the first key, no matter how many
03196     // messages were originally selected.
03197     if (mDownloadState == DOWNLOAD_STATE_INITED)
03198     {
03199       aMsgHdr->GetMessageKey(&mDownloadSelectKey);
03200       mDownloadState = DOWNLOAD_STATE_GOTMSG;
03201     }
03202   
03203     aMsgHdr->GetMessageId(&newMsgId);
03204   
03205     // Walk through all the selected headers, looking for a matching
03206     // Message-ID.
03207     mDownloadMessages->Count(&numMsgs);
03208     for (PRUint32 i = 0; i < numMsgs; i++)
03209     {
03210       nsresult rv;
03211       nsCOMPtr<nsIMsgDBHdr> msgDBHdr (do_QueryElementAt(mDownloadMessages, i, &rv));
03212       char *oldMsgId = nsnull;
03213       msgDBHdr->GetMessageId(&oldMsgId);
03214   
03215       // Delete the first match and remove it from the array
03216       if (!PL_strcmp(newMsgId, oldMsgId))
03217       {
03218 #if DOWNLOAD_NOTIFY_STYLE == DOWNLOAD_NOTIFY_LAST
03219         msgDBHdr->GetMessageKey(&mDownloadOldKey);
03220         msgDBHdr->GetThreadParent(&mDownloadOldParent);
03221         msgDBHdr->GetFlags(&mDownloadOldFlags);
03222         mDatabase->DeleteHeader(msgDBHdr, nsnull, PR_FALSE, PR_FALSE);
03223         // Tell caller we want to select this message
03224         if (aDoSelect)
03225           *aDoSelect = PR_TRUE;
03226 #else
03227         mDatabase->DeleteHeader(msgDBHdr, nsnull, PR_FALSE, PR_TRUE);
03228         // Tell caller we want to select this message
03229         if (aDoSelect && mDownloadState == DOWNLOAD_STATE_GOTMSG)
03230           *aDoSelect = PR_TRUE;
03231 #endif
03232         mDownloadMessages->DeleteElementAt(i);
03233         break;
03234       }
03235     }
03236   }
03237 
03238   return NS_OK;
03239 }
03240 
03241 NS_IMETHODIMP nsMsgLocalMailFolder::SelectDownloadMsg()
03242 {
03243 
03244 #if DOWNLOAD_NOTIFY_STYLE == DOWNLOAD_NOTIFY_LAST
03245   if (mDownloadState >= DOWNLOAD_STATE_GOTMSG)
03246     mDatabase->NotifyKeyDeletedAll(mDownloadOldKey, mDownloadOldParent, mDownloadOldFlags, nsnull);
03247 #endif
03248 
03249   if (mDownloadState == DOWNLOAD_STATE_GOTMSG && mDownloadWindow)
03250   {
03251     nsCAutoString newuri;
03252     nsBuildLocalMessageURI(mBaseMessageURI, mDownloadSelectKey, newuri);
03253     mDownloadWindow->SelectMessage(newuri.get());
03254     mDownloadState = DOWNLOAD_STATE_DIDSEL;
03255   }
03256   return NS_OK;
03257 }
03258 
03259 NS_IMETHODIMP nsMsgLocalMailFolder::DownloadMessagesForOffline(
03260   nsISupportsArray *aMessages, nsIMsgWindow *aWindow)
03261 {
03262   if (mDownloadState != DOWNLOAD_STATE_NONE) 
03263     return NS_ERROR_FAILURE; // already has a download in progress
03264 
03265   // We're starting a download...
03266   mDownloadState = DOWNLOAD_STATE_INITED;
03267 
03268   MarkMsgsOnPop3Server(aMessages, POP3_FETCH_BODY);
03269 
03270   // Pull out all the PARTIAL messages into a new array
03271   PRUint32 srcCount;
03272   aMessages->Count(&srcCount);
03273 
03274   NS_NewISupportsArray(getter_AddRefs(mDownloadMessages));
03275   for (PRUint32 i = 0; i < srcCount; i++)
03276   {
03277     nsresult rv;
03278     nsCOMPtr<nsIMsgDBHdr> msgDBHdr (do_QueryElementAt(aMessages, i, &rv));
03279     if (msgDBHdr)
03280     {
03281       PRUint32 flags = 0;
03282       msgDBHdr->GetFlags(&flags);
03283       if (flags & MSG_FLAG_PARTIAL)
03284       {
03285         mDownloadMessages->AppendElement(msgDBHdr);
03286       }
03287     }
03288   }
03289   mDownloadWindow = aWindow;
03290 
03291   nsCOMPtr<nsIMsgIncomingServer> server;
03292   nsresult rv = GetServer(getter_AddRefs(server)); 
03293   if (NS_FAILED(rv)) return rv;
03294   if (!server) return NS_MSG_INVALID_OR_MISSING_SERVER;
03295   
03296   nsCOMPtr<nsILocalMailIncomingServer> localMailServer = do_QueryInterface(server);
03297   if (!localMailServer) 
03298       return NS_MSG_INVALID_OR_MISSING_SERVER;
03299   
03300   return localMailServer->GetNewMail(aWindow, this, this, nsnull); 
03301 }
03302 
03303 NS_IMETHODIMP nsMsgLocalMailFolder::NotifyDelete()
03304 {
03305   NotifyFolderEvent(mDeleteOrMoveMsgCompletedAtom);
03306   return NS_OK;
03307 }
03308 
03309 // TODO:  once we move certain code into the IncomingServer (search for TODO)
03310 // this method will go away.
03311 // sometimes this gets called when we don't have the server yet, so
03312 // that's why we're not calling GetServer()
03313 const char *
03314 nsMsgLocalMailFolder::GetIncomingServerType()
03315 {
03316   nsresult rv;
03317 
03318   if (mType) return mType;
03319 
03320   nsCOMPtr<nsIURL> url = do_CreateInstance(NS_STANDARDURL_CONTRACTID, &rv);
03321   if (NS_FAILED(rv)) return "";
03322 
03323   rv = url->SetSpec(nsDependentCString(mURI));
03324   if (NS_FAILED(rv)) return "";
03325 
03326   nsCOMPtr<nsIMsgAccountManager> accountManager = 
03327            do_GetService(NS_MSGACCOUNTMANAGER_CONTRACTID, &rv);
03328   if (NS_FAILED(rv)) return "";
03329 
03330   nsCOMPtr<nsIMsgIncomingServer> server;
03331 
03332   // try "none" first
03333   url->SetScheme(NS_LITERAL_CSTRING("none"));
03334   rv = accountManager->FindServerByURI(url, PR_FALSE,
03335                                   getter_AddRefs(server));
03336   if (NS_SUCCEEDED(rv) && server) 
03337   {
03338     mType = "none";
03339     return mType;
03340   }
03341 
03342   // next try "pop3"
03343   url->SetScheme(NS_LITERAL_CSTRING("pop3"));
03344   rv = accountManager->FindServerByURI(url, PR_FALSE,
03345                                   getter_AddRefs(server));
03346   if (NS_SUCCEEDED(rv) && server) 
03347   {
03348     mType = "pop3";
03349     return mType;
03350   }
03351 
03352   // next try "rss"
03353   url->SetScheme(NS_LITERAL_CSTRING("rss"));
03354   rv = accountManager->FindServerByURI(url, PR_FALSE,
03355                                   getter_AddRefs(server));
03356   if (NS_SUCCEEDED(rv) && server) 
03357   {
03358     mType = "rss";
03359     return mType;
03360   }
03361 
03362 #ifdef HAVE_MOVEMAIL
03363   // next try "movemail"
03364   url->SetScheme(NS_LITERAL_CSTRING("movemail"));
03365   rv = accountManager->FindServerByURI(url, PR_FALSE,
03366                                   getter_AddRefs(server));
03367   if (NS_SUCCEEDED(rv) && server) 
03368   {
03369     mType = "movemail";
03370     return mType;
03371   }
03372 #endif /* HAVE_MOVEMAIL */
03373 
03374   return "";
03375 }
03376 
03377 nsresult nsMsgLocalMailFolder::CreateBaseMessageURI(const char *aURI)
03378 {
03379   return nsCreateLocalBaseMessageURI(aURI, &mBaseMessageURI);
03380 }
03381 
03382 NS_IMETHODIMP
03383 nsMsgLocalMailFolder::OnStartRunningUrl(nsIURI * aUrl)
03384 {
03385   nsresult rv;
03386   nsCOMPtr<nsIPop3URL> popurl = do_QueryInterface(aUrl, &rv);
03387   if (NS_SUCCEEDED(rv))
03388   {
03389     nsCAutoString aSpec;
03390     aUrl->GetSpec(aSpec);
03391     if (strstr(aSpec.get(), "uidl="))
03392     {
03393       nsCOMPtr<nsIPop3Sink> popsink;
03394       rv = popurl->GetPop3Sink(getter_AddRefs(popsink));
03395       if (NS_SUCCEEDED(rv))
03396         popsink->SetBaseMessageUri(mBaseMessageURI);
03397     }
03398   }
03399   return nsMsgDBFolder::OnStartRunningUrl(aUrl);
03400 }
03401 
03402 NS_IMETHODIMP
03403 nsMsgLocalMailFolder::OnStopRunningUrl(nsIURI * aUrl, nsresult aExitCode)
03404 {
03405   // If we just finished a DownloadMessages call, reset...
03406   if (mDownloadState != DOWNLOAD_STATE_NONE)
03407   {
03408     mDownloadState = DOWNLOAD_STATE_NONE;
03409     mDownloadMessages = nsnull;
03410     mDownloadWindow = nsnull;
03411     return nsMsgDBFolder::OnStopRunningUrl(aUrl, aExitCode);
03412   }
03413   nsresult rv;
03414   if (NS_SUCCEEDED(aExitCode))
03415   {
03416     nsCOMPtr<nsIMsgMailSession> mailSession = do_GetService(NS_MSGMAILSESSION_CONTRACTID, &rv);
03417     if (NS_FAILED(rv)) return rv; 
03418     nsCOMPtr<nsIMsgWindow> msgWindow;
03419     rv = mailSession->GetTopmostMsgWindow(getter_AddRefs(msgWindow));
03420     nsCAutoString aSpec;
03421     aUrl->GetSpec(aSpec);
03422     
03423     if (strstr(aSpec.get(), "uidl="))
03424     {
03425       nsCOMPtr<nsIPop3URL> popurl = do_QueryInterface(aUrl, &rv);
03426       if (NS_SUCCEEDED(rv))
03427       {
03428         nsXPIDLCString messageuri;
03429         rv = popurl->GetMessageUri(getter_Copies(messageuri));
03430         if (NS_SUCCEEDED(rv))
03431         {
03432           nsCOMPtr<nsIRDFService> rdfService = 
03433                    do_GetService("@mozilla.org/rdf/rdf-service;1", &rv); 
03434           if(NS_SUCCEEDED(rv))
03435           {
03436             nsCOMPtr <nsIMsgDBHdr> msgDBHdr;
03437             rv = GetMsgDBHdrFromURI(messageuri, getter_AddRefs(msgDBHdr));
03438             if(NS_SUCCEEDED(rv))
03439                 rv = mDatabase->DeleteHeader(msgDBHdr, nsnull, PR_TRUE,
03440                                              PR_TRUE);
03441             nsCOMPtr<nsIPop3Sink> pop3sink;
03442             nsXPIDLCString newMessageUri;
03443             rv = popurl->GetPop3Sink(getter_AddRefs(pop3sink));
03444             if (NS_SUCCEEDED(rv))
03445             {
03446               pop3sink->GetMessageUri(getter_Copies(newMessageUri));
03447               if(msgWindow)
03448               {
03449                 msgWindow->SelectMessage(newMessageUri);
03450               }
03451             }
03452           }
03453         }
03454       }
03455     }
03456     
03457     if (mFlags & MSG_FOLDER_FLAG_INBOX)
03458     {
03459       if (mDatabase)
03460       {
03461         if (mCheckForNewMessagesAfterParsing)
03462         {
03463           PRBool valid;
03464           mDatabase->GetSummaryValid(&valid);
03465           if (valid)
03466           {
03467             if (msgWindow)
03468              rv = GetNewMessages(msgWindow, nsnull);
03469           }
03470           mCheckForNewMessagesAfterParsing = PR_FALSE;
03471         }
03472       }
03473     }
03474   }
03475 
03476   if (m_parsingFolder && mReparseListener)
03477   {
03478     nsCOMPtr<nsIUrlListener> saveReparseListener = mReparseListener;
03479     mReparseListener = nsnull;
03480     saveReparseListener->OnStopRunningUrl(aUrl, aExitCode);
03481   }
03482   if (mFlags & MSG_FOLDER_FLAG_INBOX)
03483   {
03484     // if we are the inbox and running pop url 
03485     nsCOMPtr<nsIPop3URL> popurl = do_QueryInterface(aUrl, &rv);
03486     if (NS_SUCCEEDED(rv))
03487     {
03488       nsCOMPtr<nsIMsgIncomingServer> server;
03489       GetServer(getter_AddRefs(server));
03490       // this is the deferred to account, in the global inbox case
03491       if (server)
03492         server->SetPerformingBiff(PR_FALSE);  //biff is over
03493     }
03494   }
03495   m_parsingFolder = PR_FALSE;
03496   return nsMsgDBFolder::OnStopRunningUrl(aUrl, aExitCode);
03497 }
03498 
03499 nsresult nsMsgLocalMailFolder::DisplayMoveCopyStatusMsg()
03500 {
03501   nsresult rv = NS_OK;
03502   if (mCopyState)
03503   {
03504     if (!mCopyState->m_statusFeedback)
03505     {
03506       // get msgWindow from undo txn
03507       nsCOMPtr<nsIMsgWindow> msgWindow;
03508       nsresult rv;
03509 
03510       if (mCopyState->m_undoMsgTxn)
03511       {
03512         nsRefPtr<nsLocalMoveCopyMsgTxn> localUndoTxn;
03513         rv = mCopyState->m_undoMsgTxn->QueryInterface(NS_GET_IID(nsLocalMoveCopyMsgTxn), getter_AddRefs(localUndoTxn));
03514         if (NS_SUCCEEDED(rv))
03515           localUndoTxn->GetMsgWindow(getter_AddRefs(msgWindow));
03516       NS_ASSERTION(msgWindow, "no msg window");
03517       }
03518       if (!msgWindow)
03519         return NS_OK; // not a fatal error.
03520       msgWindow->GetStatusFeedback(getter_AddRefs(mCopyState->m_statusFeedback));
03521     }
03522 
03523     if (!mCopyState->m_stringBundle)
03524     {
03525       nsCOMPtr <nsIMsgStringService> stringService = do_GetService(NS_MSG_MAILBOXSTRINGSERVICE_CONTRACTID);
03526 
03527       rv = stringService->GetBundle(getter_AddRefs(mCopyState->m_stringBundle));
03528 
03529       NS_ASSERTION(NS_SUCCEEDED(rv), "GetBundle failed");
03530       NS_ENSURE_SUCCESS(rv, rv);
03531     }
03532     if (mCopyState->m_statusFeedback && mCopyState->m_stringBundle)
03533     {
03534       nsXPIDLString finalString;
03535       nsXPIDLString folderName;
03536       GetName(getter_Copies(folderName));
03537       PRInt32 statusMsgId = (mCopyState->m_isMove) ? MOVING_MSGS_STATUS : COPYING_MSGS_STATUS;
03538       nsAutoString numMsgSoFarString;
03539       numMsgSoFarString.AppendInt((mCopyState->m_copyingMultipleMessages) ? mCopyState->m_curCopyIndex : 1);
03540 
03541       nsAutoString totalMessagesString;
03542       totalMessagesString.AppendInt(mCopyState->m_totalMsgCount);
03543 
03544       const PRUnichar * stringArray[] = { numMsgSoFarString.get(), totalMessagesString.get(), folderName.get() };
03545       rv = mCopyState->m_stringBundle->FormatStringFromID(statusMsgId, stringArray, 3,
03546                                                getter_Copies(finalString));
03547       PRInt64 minIntervalBetweenProgress;
03548       PRInt64 nowMS = LL_ZERO;
03549 
03550       // only update status/progress every half second
03551       LL_I2L(minIntervalBetweenProgress, 500);
03552       PRInt64 diffSinceLastProgress;
03553       LL_I2L(nowMS, PR_IntervalToMilliseconds(PR_IntervalNow()));
03554       LL_SUB(diffSinceLastProgress, nowMS, mCopyState->m_lastProgressTime); // r = a - b
03555       LL_SUB(diffSinceLastProgress, diffSinceLastProgress, minIntervalBetweenProgress); // r = a - b
03556       if (!LL_GE_ZERO(diffSinceLastProgress) && mCopyState->m_curCopyIndex < mCopyState->m_totalMsgCount)
03557         return NS_OK;
03558 
03559       mCopyState->m_lastProgressTime = nowMS;
03560       mCopyState->m_statusFeedback->ShowStatusString(finalString);
03561       mCopyState->m_statusFeedback->ShowProgress(mCopyState->m_curCopyIndex * 100 / mCopyState->m_totalMsgCount);
03562     }
03563   }
03564   return rv;
03565 }
03566 
03567 NS_IMETHODIMP
03568 nsMsgLocalMailFolder::SetFlagsOnDefaultMailboxes(PRUint32 flags)
03569 {
03570   if (flags & MSG_FOLDER_FLAG_INBOX)
03571     setSubfolderFlag(NS_LITERAL_STRING("Inbox").get(), MSG_FOLDER_FLAG_INBOX);
03572 
03573   if (flags & MSG_FOLDER_FLAG_SENTMAIL)
03574     setSubfolderFlag(NS_LITERAL_STRING("Sent").get(), MSG_FOLDER_FLAG_SENTMAIL);
03575   
03576   if (flags & MSG_FOLDER_FLAG_DRAFTS)
03577     setSubfolderFlag(NS_LITERAL_STRING("Drafts").get(), MSG_FOLDER_FLAG_DRAFTS);
03578 
03579   if (flags & MSG_FOLDER_FLAG_TEMPLATES)
03580     setSubfolderFlag(NS_LITERAL_STRING("Templates").get(), MSG_FOLDER_FLAG_TEMPLATES);
03581   
03582   if (flags & MSG_FOLDER_FLAG_TRASH)
03583     setSubfolderFlag(NS_LITERAL_STRING("Trash").get(), MSG_FOLDER_FLAG_TRASH);
03584 
03585   if (flags & MSG_FOLDER_FLAG_QUEUE)
03586     setSubfolderFlag(NS_LITERAL_STRING("Unsent Messages").get(), MSG_FOLDER_FLAG_QUEUE);
03587   
03588   if (flags & MSG_FOLDER_FLAG_JUNK)
03589     setSubfolderFlag(NS_LITERAL_STRING("Junk").get(), MSG_FOLDER_FLAG_JUNK);
03590 
03591   return NS_OK;
03592 }
03593 
03594 nsresult
03595 nsMsgLocalMailFolder::setSubfolderFlag(const PRUnichar *aFolderName,
03596                                        PRUint32 flags)
03597 {
03598   // FindSubFolder() expects the folder name to be escaped
03599   // see bug #192043
03600   nsCAutoString escapedFolderName;
03601   nsresult rv = NS_MsgEscapeEncodeURLPath(nsDependentString(aFolderName), escapedFolderName);
03602   NS_ENSURE_SUCCESS(rv,rv);
03603   nsCOMPtr<nsIMsgFolder> msgFolder;
03604   rv = FindSubFolder(escapedFolderName, getter_AddRefs(msgFolder));
03605   
03606   if (NS_FAILED(rv)) 
03607     return rv;
03608   if (!msgFolder) 
03609     return NS_ERROR_FAILURE;
03610 
03611   // we only want to do this if the folder *really* exists, 
03612   // so check if it has a parent. Otherwise, we'll create the
03613   // .msf file when we don't want to.
03614   nsCOMPtr <nsIMsgFolder> parent;
03615   msgFolder->GetParent(getter_AddRefs(parent));
03616   if (!parent)
03617     return NS_ERROR_FAILURE;
03618 
03619   rv = msgFolder->SetFlag(flags);
03620   if (NS_FAILED(rv)) 
03621     return rv;
03622   
03623   msgFolder->SetPrettyName(aFolderName);
03624   
03625   return NS_OK;
03626 }
03627 
03628 NS_IMETHODIMP
03629 nsMsgLocalMailFolder::GetCheckForNewMessagesAfterParsing(PRBool *aCheckForNewMessagesAfterParsing)
03630 {
03631   NS_ENSURE_ARG(aCheckForNewMessagesAfterParsing);
03632   *aCheckForNewMessagesAfterParsing = mCheckForNewMessagesAfterParsing;
03633   return NS_OK;
03634 }
03635 
03636 NS_IMETHODIMP
03637 nsMsgLocalMailFolder::SetCheckForNewMessagesAfterParsing(PRBool aCheckForNewMessagesAfterParsing)
03638 {
03639   mCheckForNewMessagesAfterParsing = aCheckForNewMessagesAfterParsing;
03640   return NS_OK;
03641 }
03642 
03643 NS_IMETHODIMP
03644 nsMsgLocalMailFolder::NotifyCompactCompleted()
03645 {
03646   mExpungedBytes = 0;
03647   m_newMsgs.RemoveAll(); // if compacted, m_newMsgs probably aren't valid.
03648   (void) RefreshSizeOnDisk();
03649   (void) CloseDBIfFolderNotOpen();
03650   nsCOMPtr <nsIAtom> compactCompletedAtom;
03651   compactCompletedAtom = do_GetAtom("CompactCompleted");
03652   NotifyFolderEvent(compactCompletedAtom);
03653   return NS_OK;
03654 }
03655 
03656 NS_IMETHODIMP nsMsgLocalMailFolder::Shutdown(PRBool shutdownChildren)
03657 {
03658   mInitialized = PR_FALSE;
03659   return nsMsgDBFolder::Shutdown(shutdownChildren);
03660 }
03661 
03662 nsresult
03663 nsMsgLocalMailFolder::SpamFilterClassifyMessage(const char *aURI, nsIMsgWindow *aMsgWindow, nsIJunkMailPlugin *aJunkMailPlugin)
03664 {
03665   ++mNumFilterClassifyRequests;
03666   return aJunkMailPlugin->ClassifyMessage(aURI, aMsgWindow, this);   
03667 }
03668 
03669 nsresult
03670 nsMsgLocalMailFolder::SpamFilterClassifyMessages(const char **aURIArray, PRUint32 aURICount, nsIMsgWindow *aMsgWindow, nsIJunkMailPlugin *aJunkMailPlugin)
03671 {
03672   NS_ASSERTION(!mNumFilterClassifyRequests, "shouldn't call this when already classifying messages");
03673   mNumFilterClassifyRequests = aURICount;
03674   return aJunkMailPlugin->ClassifyMessages(aURICount, aURIArray, aMsgWindow, this);   
03675 }
03676 
03677 
03678 NS_IMETHODIMP
03679 nsMsgLocalMailFolder::OnMessageClassified(const char *aMsgURI, nsMsgJunkStatus aClassification)
03680 {
03681   if (mNumFilterClassifyRequests > 0)
03682     --mNumFilterClassifyRequests;
03683 
03684   nsCOMPtr<nsIMsgIncomingServer> server;
03685   nsresult rv = GetServer(getter_AddRefs(server));
03686   NS_ENSURE_SUCCESS(rv, rv);
03687   nsCOMPtr <nsIMsgDBHdr> msgHdr;
03688   rv = GetMsgDBHdrFromURI(aMsgURI, getter_AddRefs(msgHdr));
03689   NS_ENSURE_SUCCESS(rv, rv);
03690 
03691   nsMsgKey msgKey;
03692   rv = msgHdr->GetMessageKey(&msgKey);
03693   NS_ENSURE_SUCCESS(rv, rv);
03694 
03695   mDatabase->SetStringProperty(msgKey, "junkscore", (aClassification == nsIJunkMailPlugin::JUNK) ? "100" : "0");
03696   mDatabase->SetStringProperty(msgKey, "junkscoreorigin", "plugin");
03697 
03698   nsCOMPtr<nsISpamSettings> spamSettings;
03699   PRBool moveOnSpam = PR_FALSE;
03700   
03701   rv = GetServer(getter_AddRefs(server));
03702   NS_ENSURE_SUCCESS(rv, rv); 
03703   
03704   rv = server->GetSpamSettings(getter_AddRefs(spamSettings));
03705   NS_ENSURE_SUCCESS(rv, rv); 
03706 
03707   if (aClassification == nsIJunkMailPlugin::JUNK)
03708   {
03709     PRBool markAsReadOnSpam;
03710     (void)spamSettings->GetMarkAsReadOnSpam(&markAsReadOnSpam);
03711     if (markAsReadOnSpam)
03712     {
03713       rv = mDatabase->MarkRead(msgKey, true, this);
03714         if (!NS_SUCCEEDED(rv))
03715           NS_WARNING("failed marking spam message as read");
03716     }
03717 
03718     PRBool willMoveMessage = PR_FALSE;
03719 
03720     // don't do the move when we are opening up 
03721     // the junk mail folder or the trash folder
03722     // or when manually classifying messages in those folders
03723     if (!(mFlags & MSG_FOLDER_FLAG_JUNK || mFlags & MSG_FOLDER_FLAG_TRASH))
03724     {
03725       rv = spamSettings->GetMoveOnSpam(&moveOnSpam);
03726       NS_ENSURE_SUCCESS(rv,rv);
03727       if (moveOnSpam)
03728       {
03729         nsXPIDLCString uriStr;
03730         rv = spamSettings->GetSpamFolderURI(getter_Copies(uriStr));
03731         NS_ENSURE_SUCCESS(rv,rv);
03732         mSpamFolderURI = uriStr;
03733 
03734         nsCOMPtr<nsIMsgFolder> folder;
03735         rv = GetExistingFolder(mSpamFolderURI.get(), getter_AddRefs(folder));
03736         if (NS_SUCCEEDED(rv) && folder) 
03737         {
03738           rv = folder->SetFlag(MSG_FOLDER_FLAG_JUNK);
03739           NS_ENSURE_SUCCESS(rv,rv);
03740           mSpamKeysToMove.Add(msgKey);
03741           willMoveMessage = PR_TRUE;
03742         }
03743         else
03744         {
03745           // XXX TODO
03746           // JUNK MAIL RELATED
03747           // the listener should do
03748           // rv = folder->SetFlag(MSG_FOLDER_FLAG_JUNK);
03749           // NS_ENSURE_SUCCESS(rv,rv);
03750           // mSpamKeysToMove.Add(msgKey);
03751           // willMoveMessage = PR_TRUE;
03752           rv = GetOrCreateFolder(mSpamFolderURI, nsnull /* aListener */);
03753           NS_ASSERTION(NS_SUCCEEDED(rv), "GetOrCreateFolder failed");
03754         }
03755       }
03756     }
03757     rv = spamSettings->LogJunkHit(msgHdr, willMoveMessage);
03758     NS_ENSURE_SUCCESS(rv,rv);
03759   }
03760 
03761   if (mNumFilterClassifyRequests == 0)
03762   {
03763     if (mSpamKeysToMove.GetSize() > 0)
03764     {
03765       if (!mSpamFolderURI.IsEmpty())
03766       {
03767         nsCOMPtr<nsIMsgFolder> folder;
03768         rv = GetExistingFolder(mSpamFolderURI.get(), getter_AddRefs(folder));
03769         if (NS_SUCCEEDED(rv) && folder) {      
03770           nsCOMPtr<nsISupportsArray> messages;
03771           NS_NewISupportsArray(getter_AddRefs(messages));
03772           for (PRUint32 keyIndex = 0; keyIndex < mSpamKeysToMove.GetSize(); keyIndex++)
03773           {
03774             nsCOMPtr<nsIMsgDBHdr> mailHdr = nsnull;
03775             rv = GetMessageHeader(mSpamKeysToMove.ElementAt(keyIndex), getter_AddRefs(mailHdr));
03776             if (NS_SUCCEEDED(rv) && mailHdr)
03777             {
03778               nsCOMPtr<nsISupports> iSupports = do_QueryInterface(mailHdr);
03779               messages->AppendElement(iSupports);
03780             }
03781           }
03782         
03783           nsCOMPtr<nsIMsgCopyService> copySvc = do_GetService(NS_MSGCOPYSERVICE_CONTRACTID, &rv);
03784           NS_ENSURE_SUCCESS(rv,rv);
03785         
03786           rv = copySvc->CopyMessages(this, messages, folder, PR_TRUE,
03787             /*nsIMsgCopyServiceListener* listener*/ nsnull, nsnull, PR_FALSE /*allowUndo*/);
03788           NS_ASSERTION(NS_SUCCEEDED(rv), "CopyMessages failed");
03789           if (NS_FAILED(rv))
03790           {
03791             nsCAutoString logMsg("failed to copy junk messages to junk folder rv = ");
03792             logMsg.AppendInt(rv, 16);
03793             spamSettings->LogJunkString(logMsg.get());
03794           }
03795         }
03796       }
03797     }
03798     PRInt32 numNewMessages;
03799     GetNumNewMessages(PR_FALSE, &numNewMessages);
03800     SetNumNewMessages(numNewMessages - mSpamKeysToMove.GetSize());
03801     mSpamKeysToMove.RemoveAll();
03802     // check if this is the inbox first...
03803     if (mFlags & MSG_FOLDER_FLAG_INBOX)
03804       PerformBiffNotifications();
03805 
03806   }
03807 
03808   return NS_OK;
03809 }
03810 
03811 NS_IMETHODIMP
03812 nsMsgLocalMailFolder::GetFolderScanState(nsLocalFolderScanState *aState)
03813 {
03814   nsresult rv;
03815 
03816   NS_FileSpecToIFile(aState->m_fileSpec, getter_AddRefs(aState->m_localFile));
03817   aState->m_fileStream = do_CreateInstance(NS_LOCALFILEINPUTSTREAM_CONTRACTID, &rv);
03818   NS_ENSURE_SUCCESS(rv, rv);
03819 
03820   rv = aState->m_fileStream->Init(aState->m_localFile, PR_RDONLY, 0664, PR_FALSE);
03821   if (NS_SUCCEEDED(rv))
03822   {
03823     aState->m_inputStream = do_QueryInterface(aState->m_fileStream);
03824     aState->m_seekableStream = do_QueryInterface(aState->m_inputStream);
03825     aState->m_fileLineStream = do_QueryInterface(aState->m_inputStream);
03826     aState->m_uidl = nsnull;
03827   }
03828 
03829   return rv;
03830 }
03831 
03832 NS_IMETHODIMP
03833 nsMsgLocalMailFolder::GetUidlFromFolder(nsLocalFolderScanState *aState,
03834        nsIMsgDBHdr *aMsgDBHdr)
03835 {
03836   nsresult rv;
03837   PRUint32 messageOffset;
03838   PRBool more = PR_FALSE;
03839   PRUint32 size = 0, len = 0;
03840   const char *accountKey = nsnull;
03841 
03842   aMsgDBHdr->GetMessageOffset(&messageOffset);
03843   rv = aState->m_seekableStream->Seek(PR_SEEK_SET, messageOffset);
03844   NS_ENSURE_SUCCESS(rv,rv);
03845 
03846   aState->m_uidl = nsnull;
03847 
03848   aMsgDBHdr->GetMessageSize(&len);
03849   while (len > 0)
03850   {
03851     rv = aState->m_fileLineStream->ReadLine(aState->m_header, &more);
03852     if (NS_SUCCEEDED(rv))
03853     {
03854       size = aState->m_header.Length();
03855       if (!size)
03856         break;
03857       // this isn't quite right - need to account for line endings
03858       len -= size;
03859       // account key header will always be before X_UIDL header
03860       if (!accountKey)
03861       {
03862         accountKey = strstr(aState->m_header.get(), HEADER_X_MOZILLA_ACCOUNT_KEY);
03863         if (accountKey)
03864         {
03865           accountKey += strlen(HEADER_X_MOZILLA_ACCOUNT_KEY) + 2;
03866           aState->m_accountKey = accountKey;
03867         }
03868       }
03869       else
03870       {
03871         aState->m_uidl = strstr(aState->m_header.get(), X_UIDL);
03872         if (aState->m_uidl)
03873         {
03874           aState->m_uidl += X_UIDL_LEN + 2; // skip UIDL: header
03875           break;
03876         }
03877       }
03878     }
03879   }
03880   return rv;
03881 }
03882 
03883   // this adds a message to the end of the folder, parsing it as it goes, and
03884   // applying filters, if applicable.
03885 NS_IMETHODIMP
03886 nsMsgLocalMailFolder::AddMessage(const char *aMessage)
03887 {
03888   nsCOMPtr<nsIFileSpec> pathSpec;
03889   nsresult rv = GetPath(getter_AddRefs(pathSpec));
03890   if (NS_FAILED(rv)) return rv;
03891   
03892   nsFileSpec fileSpec;
03893   rv = pathSpec->GetFileSpec(&fileSpec);
03894   if (NS_FAILED(rv)) return rv;
03895   
03896   nsIOFileStream outFileStream(fileSpec);
03897   outFileStream.seek(fileSpec.GetFileSize());
03898       
03899   // create a new mail parser
03900   nsRefPtr<nsParseNewMailState> newMailParser = new nsParseNewMailState;
03901   if (newMailParser == nsnull)
03902       return NS_ERROR_OUT_OF_MEMORY;
03903 
03904   nsCOMPtr<nsIMsgFolder> rootFolder;
03905   rv = GetRootFolder(getter_AddRefs(rootFolder));
03906   NS_ENSURE_SUCCESS(rv, rv);
03907 
03908   PRBool isLocked;
03909   
03910   GetLocked(&isLocked);
03911   if(!isLocked)
03912     AcquireSemaphore(NS_STATIC_CAST(nsIMsgLocalMailFolder*, this));
03913   else
03914     return NS_MSG_FOLDER_BUSY;
03915 
03916   rv = newMailParser->Init(rootFolder, this, 
03917                            fileSpec, &outFileStream, nsnull, PR_FALSE);
03918                            
03919   if (!mGettingNewMessages)
03920        newMailParser->DisableFilters();
03921        
03922   if (NS_SUCCEEDED(rv))
03923   {
03924     outFileStream << aMessage;
03925     newMailParser->BufferInput(aMessage, strlen(aMessage));
03926 
03927     outFileStream.flush();
03928     newMailParser->SetDBFolderStream(&outFileStream); 
03929     newMailParser->OnStopRequest(nsnull, nsnull, NS_OK);
03930     newMailParser->SetDBFolderStream(nsnull); // stream is going away
03931     if (outFileStream.is_open())
03932         outFileStream.close();
03933     newMailParser->EndMsgDownload();
03934   }
03935   ReleaseSemaphore(NS_STATIC_CAST(nsIMsgLocalMailFolder*, this));
03936   return rv;
03937 }
03938 
03939 NS_IMETHODIMP
03940 nsMsgLocalMailFolder::WarnIfLocalFileTooBig(nsIMsgWindow *aWindow, PRBool *aTooBig)
03941 {
03942   NS_ENSURE_ARG_POINTER(aTooBig);
03943   *aTooBig = PR_FALSE;
03944   PRInt64 sizeOnDisk;
03945   nsCOMPtr <nsILocalFile> filePath;
03946   nsresult rv = GetFilePath(getter_AddRefs(filePath));
03947   NS_ENSURE_SUCCESS(rv, rv);
03948   rv = filePath->GetFileSize(&sizeOnDisk);
03949   if (NS_SUCCEEDED(rv))
03950   {
03951     const nsInt64 kMaxFolderSize = 0xFFF00000;
03952     nsInt64 folderSize(sizeOnDisk);
03953     if (folderSize > kMaxFolderSize)
03954     {
03955       ThrowAlertMsg("mailboxTooLarge", aWindow);
03956       *aTooBig = PR_TRUE;
03957     }
03958   }
03959   return NS_OK;
03960 }
03961 
03962 NS_IMETHODIMP nsMsgLocalMailFolder::FetchMsgPreviewText(nsMsgKey *aKeysToFetch, PRUint32 aNumKeys,
03963                                                  PRBool aLocalOnly, nsIUrlListener *aUrlListener, 
03964                                                  PRBool *aAsyncResults)
03965 {
03966   NS_ENSURE_ARG_POINTER(aKeysToFetch);
03967   NS_ENSURE_ARG_POINTER(aAsyncResults);
03968 
03969   *aAsyncResults = PR_FALSE;
03970   nsXPIDLCString nativePath;
03971   mPath->GetNativePath(getter_Copies(nativePath));
03972 
03973   nsCOMPtr <nsILocalFile> localStore;
03974   nsCOMPtr <nsIInputStream> inputStream;
03975 
03976   nsresult rv = NS_NewNativeLocalFile(nativePath, PR_TRUE, getter_AddRefs(localStore));
03977   NS_ENSURE_SUCCESS(rv, rv);
03978   rv = NS_NewLocalFileInputStream(getter_AddRefs(inputStream), localStore);
03979   NS_ENSURE_SUCCESS(rv, rv);
03980 
03981   for (PRUint32 i = 0; i < aNumKeys; i++)
03982   {
03983     nsCOMPtr <nsIMsgDBHdr> msgHdr;
03984     nsXPIDLCString prevBody;
03985     rv = GetMessageHeader(aKeysToFetch[i], getter_AddRefs(msgHdr));
03986     NS_ENSURE_SUCCESS(rv, rv);
03987     // ignore messages that already have a preview body.
03988     msgHdr->GetStringProperty("preview", getter_Copies(prevBody));
03989     if (!prevBody.IsEmpty())
03990       continue;
03991     PRUint32 messageOffset;
03992 
03993     msgHdr->GetMessageOffset(&messageOffset);
03994     nsCOMPtr <nsISeekableStream> seekableStream = do_QueryInterface(inputStream);
03995     if (seekableStream)
03996       rv = seekableStream->Seek(nsISeekableStream::NS_SEEK_CUR, messageOffset);
03997     NS_ENSURE_SUCCESS(rv,rv);
03998     rv = GetMsgPreviewTextFromStream(msgHdr, inputStream);
03999 
04000   }
04001   return rv;
04002 }
04003 
04004 NS_IMETHODIMP nsMsgLocalMailFolder::AddKeywordsToMessages(nsISupportsArray *aMessages, const char *aKeywords)
04005 {
04006   return ChangeKeywordForMessages(aMessages, aKeywords, PR_TRUE /* add */);
04007 }
04008 nsresult nsMsgLocalMailFolder::ChangeKeywordForMessages(nsISupportsArray *aMessages, const char *aKeywords, PRBool add)
04009 {
04010   nsresult rv = (add) ? nsMsgDBFolder::AddKeywordsToMessages(aMessages, aKeywords)
04011                       : nsMsgDBFolder::RemoveKeywordsFromMessages(aMessages, aKeywords);
04012 
04013   if (NS_SUCCEEDED(rv))
04014   {
04015     rv = GetDatabase(nsnull);
04016     NS_ENSURE_SUCCESS(rv, rv);
04017     // this will fail if the folder is locked.
04018     rv = mDatabase->StartBatch();
04019     NS_ENSURE_SUCCESS(rv, rv);
04020     nsIOFileStream *fileStream;
04021     rv = mDatabase->GetFolderStream(&fileStream);
04022     NS_ENSURE_SUCCESS(rv, rv);
04023     PRUint32 count;
04024     NS_ENSURE_ARG(aMessages);
04025     nsresult rv = aMessages->Count(&count);
04026     NS_ENSURE_SUCCESS(rv, rv);
04027     nsXPIDLCString keywords;
04028     // for each message, we seek to the beginning of the x-mozilla-status header, and 
04029     // start reading lines, looking for x-mozilla-keys: headers; If we're adding
04030     // the keyword and we find
04031     // a header with the desired keyword already in it, we don't need to 
04032     // do anything. Likewise, if removing keyword and we don't find it,
04033     // we don't need to do anything. Otherwise, if adding, we need to 
04034     // see if there's an x-mozilla-keys
04035     // header with room for the new keyword. If so, we replace the 
04036     // corresponding number of spaces with the keyword. If no room,
04037     // we can't do anything until the folder is compacted and another
04038     // x-mozilla-keys header is added. In that case, we set a property
04039     // on the header, which the compaction code will check.
04040 
04041     // don't return out of the for loop - otherwise, we won't call EndBatch();
04042     for(PRUint32 i = 0; i < count; i++) // for each message
04043     {
04044       char lineBuff[500];
04045 
04046       nsCOMPtr<nsIMsgDBHdr> message = do_QueryElementAt(aMessages, i, &rv);
04047       NS_ENSURE_SUCCESS(rv, rv);
04048       PRUint32 messageOffset;
04049       message->GetMessageOffset(&messageOffset);
04050       PRUint32 statusOffset = 0;
04051       (void)message->GetStatusOffset(&statusOffset);
04052       PRUint32 desiredOffset = messageOffset + statusOffset;
04053 
04054       nsCStringArray keywordArray;
04055       keywordArray.ParseString(aKeywords, " ");
04056       for (PRInt32 j = 0; j < keywordArray.Count(); j++)
04057       {
04058         nsCAutoString header;
04059         nsCAutoString keywords;
04060         PRBool done = PR_FALSE;
04061         PRUint32 len = 0;
04062         nsCAutoString keywordToWrite(" ");
04063 
04064         keywordToWrite.Append(*(keywordArray[j]));
04065         fileStream->seek(PR_SEEK_SET, desiredOffset);
04066         PRBool inKeywordHeader = PR_FALSE;
04067         PRBool foundKeyword = PR_FALSE;
04068         PRUint32 offsetToAddKeyword = 0;
04069         message->GetMessageSize(&len);
04070         // loop through 
04071         while (!done)
04072         {
04073           lineBuff[0] = '\0';
04074           PRInt32 lineStartPos = fileStream->tell();
04075           // readLine won't return line termination chars.
04076           if (fileStream->readline(lineBuff, sizeof(lineBuff)))
04077           {
04078             if (EMPTY_MESSAGE_LINE(lineBuff))
04079               break; // passed headers; no x-mozilla-keywords header; give up.
04080             nsCString keywordHeaders;
04081             if (!strncmp(lineBuff, HEADER_X_MOZILLA_KEYWORDS, sizeof(HEADER_X_MOZILLA_KEYWORDS) - 1))
04082             {
04083               inKeywordHeader = PR_TRUE;
04084               keywordHeaders = lineBuff;
04085             }
04086             else if (inKeywordHeader && (lineBuff[0] == ' ' || lineBuff[0] == '\t'))
04087               keywordHeaders = lineBuff;
04088             else if (inKeywordHeader)
04089               break;
04090             else
04091               continue;
04092 
04093             PRInt32 keywordHdrLength = keywordHeaders.Length();
04094             nsACString::const_iterator start, end;
04095             nsACString::const_iterator keywordHdrStart;
04096             keywordHeaders.BeginReading(keywordHdrStart);
04097             // check if we have the keyword
04098             if (MsgFindKeyword(*(keywordArray[j]), keywordHeaders, start, end))
04099             {
04100               foundKeyword = PR_TRUE;
04101               if (!add) // if we're removing, remove it, and break;
04102               {
04103                 PRInt32 keywordStartOffset = Distance(keywordHdrStart, start);
04104                 keywordHeaders.Cut(keywordStartOffset, Distance(start, end));
04105                 for (PRInt32 i = Distance(start, end); i > 0; i--)
04106                   keywordHeaders.Append(' ');
04107                 fileStream->seek(PR_SEEK_SET, lineStartPos);
04108                 fileStream->write(keywordHeaders.get(), keywordHeaders.Length());
04109               }
04110               offsetToAddKeyword = 0;
04111               // if adding and we already have the keyword, done
04112               done = PR_TRUE;
04113               break;
04114             }
04115             // argh, we need to check all the lines to see if we already have the
04116             // keyword, but if we don't find it, we want to remember the line and
04117             // position where we have room to add the keyword.
04118             if (add)
04119             {
04120               nsCAutoString curKeywordHdr(lineBuff);
04121               // strip off line ending spaces.
04122               curKeywordHdr.Trim(" ", PR_FALSE, PR_TRUE);
04123               if (!offsetToAddKeyword && curKeywordHdr.Length() + keywordToWrite.Length() < keywordHdrLength)
04124                 offsetToAddKeyword = lineStartPos + curKeywordHdr.Length();
04125             }
04126           }
04127         }
04128         if (add && !foundKeyword)
04129         {
04130           if (!offsetToAddKeyword)
04131            message->SetUint32Property("growKeywords", 1);
04132           else
04133           {
04134             fileStream->seek(PR_SEEK_SET, offsetToAddKeyword);
04135             fileStream->write(keywordToWrite.get(), keywordToWrite.Length());
04136           }
04137         }
04138       }
04139     }
04140     mDatabase->EndBatch();
04141   }
04142   return rv;
04143 }
04144 
04145 NS_IMETHODIMP nsMsgLocalMailFolder::RemoveKeywordsFromMessages(nsISupportsArray *aMessages, const char *aKeywords)
04146 {
04147   return ChangeKeywordForMessages(aMessages, aKeywords, PR_FALSE /* remove */);
04148 }