Back to index

lightning-sunbird  0.9+nobinonly
nsMsgDBFolder.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) 1999
00020  * the Initial Developer. All Rights Reserved.
00021  *
00022  * Contributor(s):
00023  *   Pierre Phaneuf <pp@ludusdesign.com>
00024  *
00025  * Alternatively, the contents of this file may be used under the terms of
00026  * either of the GNU General Public License Version 2 or later (the "GPL"),
00027  * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
00028  * in which case the provisions of the GPL or the LGPL are applicable instead
00029  * of those above. If you wish to allow use of your version of this file only
00030  * under the terms of either the GPL or the LGPL, and not to allow others to
00031  * use your version of this file under the terms of the MPL, indicate your
00032  * decision by deleting the provisions above and replace them with the notice
00033  * and other provisions required by the GPL or the LGPL. If you do not delete
00034  * the provisions above, a recipient may use your version of this file under
00035  * the terms of any one of the MPL, the GPL or the LGPL.
00036  *
00037  * ***** END LICENSE BLOCK ***** */
00038 
00039 #include "msgCore.h"
00040 #include "nsReadableUtils.h"
00041 #include "nsUnicharUtils.h"
00042 #include "nsMsgDBFolder.h"
00043 #include "nsMsgFolderFlags.h"
00044 #include "nsIPrefBranch.h"
00045 #include "nsIPrefService.h"
00046 #include "nsRDFCID.h"
00047 #include "nsNetUtil.h"
00048 #include "nsIMsgFolderCache.h"
00049 #include "nsIMsgFolderCacheElement.h"
00050 #include "nsMsgBaseCID.h"
00051 #include "nsIMsgMailNewsUrl.h"
00052 #include "nsMsgDatabase.h"
00053 #include "nsIMsgAccountManager.h"
00054 #include "nsXPIDLString.h"
00055 #include "nsEscape.h"
00056 #include "nsLocalFolderSummarySpec.h"
00057 #include "nsMsgI18N.h"
00058 #include "nsNativeCharsetUtils.h"
00059 #include "nsIFileStream.h"
00060 #include "nsIChannel.h"
00061 #include "nsITransport.h"
00062 #include "nsIMsgFolderCompactor.h"
00063 #include "nsIDocShell.h"
00064 #include "nsIMsgWindow.h"
00065 #include "nsIPrompt.h"
00066 #include "nsIInterfaceRequestor.h"
00067 #include "nsIInterfaceRequestorUtils.h"
00068 #include "nsILocale.h"
00069 #include "nsILocaleService.h"
00070 #include "nsCollationCID.h"
00071 #include "nsAbBaseCID.h"
00072 #include "nsIAbMDBDirectory.h"
00073 #include "nsISpamSettings.h"
00074 #include "nsIMsgFilterPlugin.h"
00075 #include "nsIMsgMailSession.h"
00076 #include "nsIRDFService.h"
00077 #include "nsTextFormatter.h"
00078 #include "nsCPasswordManager.h"
00079 #include "nsMsgDBCID.h"
00080 #include "nsInt64.h"
00081 #include "nsReadLine.h"
00082 #include "nsParserCIID.h"
00083 #include "nsIParser.h"
00084 #include "nsIHTMLContentSink.h"
00085 #include "nsIContentSerializer.h"
00086 #include "nsLayoutCID.h"
00087 #include "nsIHTMLToTextSink.h"
00088 #include "nsIDocumentEncoder.h" 
00089 #include "nsIMIMEHeaderParam.h"
00090 #include "plbase64.h"
00091 #include <time.h>
00092 #include "nsIMsgFolderNotificationService.h"
00093 
00094 #define oneHour 3600000000U
00095 #include "nsMsgUtils.h"
00096 
00097 static PRTime gtimeOfLastPurgeCheck;    //variable to know when to check for purge_threshhold
00098 
00099 #define PREF_MAIL_PROMPT_PURGE_THRESHOLD "mail.prompt_purge_threshhold"
00100 #define PREF_MAIL_PURGE_THRESHOLD "mail.purge_threshhold"
00101 #define PREF_MAIL_PURGE_ASK "mail.purge.ask"
00102 #define PREF_MAIL_WARN_FILTER_CHANGED "mail.warn_filter_changed"
00103 
00104 static NS_DEFINE_CID(kRDFServiceCID, NS_RDFSERVICE_CID);
00105 static NS_DEFINE_CID(kCollationFactoryCID, NS_COLLATIONFACTORY_CID);
00106 static NS_DEFINE_CID(kCMailDB, NS_MAILDB_CID);
00107 static NS_DEFINE_CID(kParserCID, NS_PARSER_CID);
00108 static NS_DEFINE_CID(kNavDTDCID, NS_CNAVDTD_CID);
00109 
00110 nsIAtom* nsMsgDBFolder::mFolderLoadedAtom=nsnull;
00111 nsIAtom* nsMsgDBFolder::mDeleteOrMoveMsgCompletedAtom=nsnull;
00112 nsIAtom* nsMsgDBFolder::mDeleteOrMoveMsgFailedAtom=nsnull;
00113 nsIAtom* nsMsgDBFolder::mJunkStatusChangedAtom=nsnull;
00114 nsIAtom* nsMsgDBFolder::kTotalMessagesAtom=nsnull;
00115 nsIAtom* nsMsgDBFolder::kFolderSizeAtom=nsnull;
00116 nsIAtom* nsMsgDBFolder::kBiffStateAtom=nsnull;
00117 nsIAtom* nsMsgDBFolder::kNewMessagesAtom=nsnull;
00118 nsIAtom* nsMsgDBFolder::kInVFEditSearchScopeAtom=nsnull;
00119 nsIAtom* nsMsgDBFolder::kNumNewBiffMessagesAtom=nsnull;
00120 nsIAtom* nsMsgDBFolder::kTotalUnreadMessagesAtom=nsnull;
00121 nsIAtom* nsMsgDBFolder::kFlaggedAtom=nsnull;
00122 nsIAtom* nsMsgDBFolder::kStatusAtom=nsnull;
00123 nsIAtom* nsMsgDBFolder::kNameAtom=nsnull;
00124 nsIAtom* nsMsgDBFolder::kSynchronizeAtom=nsnull;
00125 nsIAtom* nsMsgDBFolder::kOpenAtom=nsnull;
00126 nsIAtom* nsMsgDBFolder::kIsDeferred=nsnull;
00127 
00128 nsICollation * nsMsgDBFolder::gCollationKeyGenerator = nsnull;
00129 
00130 PRUnichar *nsMsgDBFolder::kLocalizedInboxName;
00131 PRUnichar *nsMsgDBFolder::kLocalizedTrashName;
00132 PRUnichar *nsMsgDBFolder::kLocalizedSentName;
00133 PRUnichar *nsMsgDBFolder::kLocalizedDraftsName;
00134 PRUnichar *nsMsgDBFolder::kLocalizedTemplatesName;
00135 PRUnichar *nsMsgDBFolder::kLocalizedUnsentName;
00136 PRUnichar *nsMsgDBFolder::kLocalizedJunkName;
00137 PRUnichar *nsMsgDBFolder::kLocalizedBrandShortName;
00138 
00139 nsrefcnt nsMsgDBFolder::mInstanceCount=0;
00140 
00141 NS_IMPL_ISUPPORTS_INHERITED6(nsMsgDBFolder, nsRDFResource,
00142                                    nsISupportsWeakReference,
00143                                    nsIMsgFolder,
00144                                    nsICollection,
00145                                    nsISerializable,
00146                                    nsIDBChangeListener,
00147                                    nsIUrlListener)
00148 
00149 const nsStaticAtom nsMsgDBFolder::folder_atoms[] = {
00150   { "FolderLoaded", &nsMsgDBFolder::mFolderLoadedAtom },
00151   { "DeleteOrMoveMsgCompleted", &nsMsgDBFolder::mDeleteOrMoveMsgCompletedAtom },
00152   { "DeleteOrMoveMsgFailed", &nsMsgDBFolder::mDeleteOrMoveMsgFailedAtom },
00153   { "JunkStatusChanged", &nsMsgDBFolder::mJunkStatusChangedAtom },
00154   { "BiffState", &nsMsgDBFolder::kBiffStateAtom },
00155   { "NewMessages", &nsMsgDBFolder::kNewMessagesAtom },
00156   { "inVFEditSearchScope", &nsMsgDBFolder::kInVFEditSearchScopeAtom },
00157   { "NumNewBiffMessages", &nsMsgDBFolder::kNumNewBiffMessagesAtom },
00158   { "Name", &nsMsgDBFolder::kNameAtom },
00159   { "TotalUnreadMessages", &nsMsgDBFolder::kTotalUnreadMessagesAtom },
00160   { "TotalMessages", &nsMsgDBFolder::kTotalMessagesAtom },
00161   { "FolderSize", &nsMsgDBFolder::kFolderSizeAtom },
00162   { "Status", &nsMsgDBFolder::kStatusAtom },
00163   { "Flagged", &nsMsgDBFolder::kFlaggedAtom },
00164   { "Synchronize", &nsMsgDBFolder::kSynchronizeAtom },
00165   { "open", &nsMsgDBFolder::kOpenAtom },
00166   { "isDeferred", &nsMsgDBFolder::kIsDeferred }
00167 };
00168 
00169 nsMsgDBFolder::nsMsgDBFolder(void)
00170 : mAddListener(PR_TRUE),
00171   mNewMessages(PR_FALSE),
00172   mGettingNewMessages(PR_FALSE),
00173   mLastMessageLoaded(nsMsgKey_None),
00174   mFlags(0),
00175   mNumUnreadMessages(-1),
00176   mNumTotalMessages(-1),
00177   mNotifyCountChanges(PR_TRUE),
00178   mExpungedBytes(0),
00179   mInitializedFromCache(PR_FALSE),
00180   mSemaphoreHolder(nsnull),
00181   mNumPendingUnreadMessages(0),
00182   mNumPendingTotalMessages(0),
00183   mFolderSize(0),
00184   mNumNewBiffMessages(0),
00185   mIsCachable(PR_TRUE),
00186   mHaveParsedURI(PR_FALSE),
00187   mIsServerIsValid(PR_FALSE),
00188   mIsServer(PR_FALSE),
00189   mBaseMessageURI(nsnull),
00190   mInVFEditSearchScope (PR_FALSE)
00191 {
00192   NS_NewISupportsArray(getter_AddRefs(mSubFolders));
00193   if (mInstanceCount++ <=0) {
00194     NS_RegisterStaticAtoms(folder_atoms, NS_ARRAY_LENGTH(folder_atoms));
00195     initializeStrings();
00196     createCollationKeyGenerator();
00197 #ifdef MSG_FASTER_URI_PARSING
00198     mParsingURL = do_CreateInstance(NS_STANDARDURL_CONTRACTID);
00199 #endif
00200     LL_I2L(gtimeOfLastPurgeCheck, 0);
00201   }
00202 }
00203 
00204 nsMsgDBFolder::~nsMsgDBFolder(void)
00205 {
00206   CRTFREEIF(mBaseMessageURI);
00207 
00208   if (--mInstanceCount == 0) {
00209     NS_IF_RELEASE(gCollationKeyGenerator);
00210     CRTFREEIF(kLocalizedInboxName);
00211     CRTFREEIF(kLocalizedTrashName);
00212     CRTFREEIF(kLocalizedSentName);
00213     CRTFREEIF(kLocalizedDraftsName);
00214     CRTFREEIF(kLocalizedTemplatesName);
00215     CRTFREEIF(kLocalizedUnsentName);
00216     CRTFREEIF(kLocalizedJunkName);
00217     CRTFREEIF(kLocalizedBrandShortName);
00218 #ifdef MSG_FASTER_URI_PARSING
00219     mParsingURL = nsnull;
00220 #endif
00221   }
00222   //shutdown but don't shutdown children.
00223   Shutdown(PR_FALSE);
00224 }
00225 
00226 NS_IMETHODIMP nsMsgDBFolder::Shutdown(PRBool shutdownChildren)
00227 {
00228   if(mDatabase)
00229   {
00230     mDatabase->RemoveListener(this);
00231     mDatabase->Close(PR_TRUE);
00232     mDatabase = nsnull;
00233     
00234   }
00235   
00236   if(shutdownChildren)
00237   {
00238     PRUint32 count;
00239     nsresult rv = mSubFolders->Count(&count);
00240     if(NS_SUCCEEDED(rv))
00241     {
00242       for (PRUint32 i = 0; i < count; i++)
00243       {
00244         nsCOMPtr<nsIMsgFolder> childFolder = do_QueryElementAt(mSubFolders, i);
00245         if(childFolder)
00246           childFolder->Shutdown(PR_TRUE);
00247       }
00248     }
00249     // Reset incoming server pointer and pathname.
00250     mServer = nsnull;
00251     mPath = nsnull;
00252     mHaveParsedURI = PR_FALSE;
00253     mName.SetLength(0);
00254     mSubFolders->Clear();
00255   }
00256   return NS_OK;
00257 }
00258 
00259 
00260 NS_IMETHODIMP nsMsgDBFolder::ForceDBClosed()
00261 {
00262     PRUint32 cnt = 0, i;
00263     if (mSubFolders)
00264     {
00265         nsCOMPtr<nsIMsgFolder> child;
00266         mSubFolders->Count(&cnt);
00267         if (cnt > 0)
00268             for (i = 0; i < cnt; i++)
00269             {
00270                 child = do_QueryElementAt(mSubFolders, i);
00271                 if (child)
00272                     child->ForceDBClosed();
00273             }
00274     }
00275     if (mDatabase)
00276     {
00277         mDatabase->ForceClosed();
00278         mDatabase = nsnull;
00279     }
00280     else
00281     {
00282       nsCOMPtr<nsIMsgDatabase> mailDBFactory = do_CreateInstance(kCMailDB);
00283       if (mailDBFactory)
00284         mailDBFactory->ForceFolderDBClosed(this);
00285     }
00286     return NS_OK;
00287 }
00288 
00289 
00290 NS_IMETHODIMP nsMsgDBFolder::StartFolderLoading(void)
00291 {
00292   if(mDatabase)
00293     mDatabase->RemoveListener(this);
00294   mAddListener = PR_FALSE;
00295   return NS_OK;
00296 }
00297 
00298 NS_IMETHODIMP nsMsgDBFolder::EndFolderLoading(void)
00299 {
00300   if(mDatabase)
00301     mDatabase->AddListener(this);
00302   mAddListener = PR_TRUE;
00303   UpdateSummaryTotals(PR_TRUE);
00304 
00305   //GGGG       check for new mail here and call SetNewMessages...?? -- ONE OF THE 2 PLACES
00306   if(mDatabase)
00307     m_newMsgs.RemoveAll();
00308 
00309   return NS_OK;
00310 }
00311 
00312 NS_IMETHODIMP
00313 nsMsgDBFolder::GetExpungedBytes(PRUint32 *count)
00314 {
00315   NS_ENSURE_ARG_POINTER(count);
00316 
00317   if (mDatabase)
00318   {
00319     nsresult rv;
00320     nsCOMPtr<nsIDBFolderInfo> folderInfo;
00321     rv = mDatabase->GetDBFolderInfo(getter_AddRefs(folderInfo));
00322     if (NS_FAILED(rv)) return rv;
00323     rv = folderInfo->GetExpungedBytes((PRInt32 *) count);
00324     if (NS_SUCCEEDED(rv))
00325       mExpungedBytes = *count; // sync up with the database
00326     return rv;
00327   }
00328   else
00329   {
00330     ReadDBFolderInfo(PR_FALSE);
00331     *count = mExpungedBytes;
00332   }
00333   return NS_OK;
00334 }
00335 
00336 
00337 NS_IMETHODIMP nsMsgDBFolder::GetCharset(char * *aCharset)
00338 {
00339   NS_ENSURE_ARG_POINTER(aCharset);
00340 
00341   nsCOMPtr<nsIDBFolderInfo> folderInfo;
00342   nsCOMPtr<nsIMsgDatabase> db; 
00343   nsresult rv = GetDBFolderInfoAndDB(getter_AddRefs(folderInfo), getter_AddRefs(db));
00344   if(NS_SUCCEEDED(rv))
00345     return folderInfo->GetCharPtrCharacterSet(aCharset);
00346 
00347   return rv;
00348 }
00349 
00350 NS_IMETHODIMP nsMsgDBFolder::SetCharset(const char * aCharset)
00351 {
00352   nsresult rv;
00353 
00354   nsCOMPtr<nsIDBFolderInfo> folderInfo;
00355   nsCOMPtr<nsIMsgDatabase> db; 
00356   rv = GetDBFolderInfoAndDB(getter_AddRefs(folderInfo), getter_AddRefs(db));
00357   if(NS_SUCCEEDED(rv))
00358   {
00359     rv = folderInfo->SetCharacterSet(aCharset);
00360     db->Commit(nsMsgDBCommitType::kLargeCommit);
00361     mCharset.AssignASCII(aCharset);  // synchronize member variable
00362   }
00363   return rv;
00364 }
00365 
00366 NS_IMETHODIMP nsMsgDBFolder::GetCharsetOverride(PRBool *aCharsetOverride)
00367 {
00368   nsCOMPtr<nsIDBFolderInfo> folderInfo;
00369   nsCOMPtr<nsIMsgDatabase> db; 
00370   nsresult rv = GetDBFolderInfoAndDB(getter_AddRefs(folderInfo), getter_AddRefs(db));
00371   if(NS_SUCCEEDED(rv))
00372     rv = folderInfo->GetCharacterSetOverride(aCharsetOverride);
00373   return rv;
00374 }
00375 
00376 NS_IMETHODIMP nsMsgDBFolder::SetCharsetOverride(PRBool aCharsetOverride)
00377 {
00378   nsresult rv;
00379 
00380   nsCOMPtr<nsIDBFolderInfo> folderInfo;
00381   nsCOMPtr<nsIMsgDatabase> db; 
00382   rv = GetDBFolderInfoAndDB(getter_AddRefs(folderInfo), getter_AddRefs(db));
00383   if(NS_SUCCEEDED(rv))
00384   {
00385     rv = folderInfo->SetCharacterSetOverride(aCharsetOverride);
00386     db->Commit(nsMsgDBCommitType::kLargeCommit);
00387     mCharsetOverride = aCharsetOverride;  // synchronize member variable
00388   }
00389   return rv;
00390 }
00391 
00392 NS_IMETHODIMP nsMsgDBFolder::GetHasNewMessages(PRBool *hasNewMessages)
00393 {
00394   NS_ENSURE_ARG_POINTER(hasNewMessages);
00395   
00396   *hasNewMessages = mNewMessages;
00397   return NS_OK;
00398 }
00399 
00400 NS_IMETHODIMP nsMsgDBFolder::SetHasNewMessages(PRBool curNewMessages)
00401 {
00402   if (curNewMessages != mNewMessages) 
00403   {
00404     // Only change mru time if we're going from doesn't have new to has new.
00405     // technically, we should probably update mru time for every new message
00406     // but we would pay a performance penalty for that. If the user
00407     // opens the folder, the mrutime will get updated anyway.
00408     if (curNewMessages) 
00409       SetMRUTime();
00413     PRBool oldNewMessages = mNewMessages;
00414     mNewMessages = curNewMessages;
00415     NotifyBoolPropertyChanged(kNewMessagesAtom, oldNewMessages, curNewMessages);
00416   }
00417   
00418   return NS_OK;
00419 }
00420 
00421 NS_IMETHODIMP nsMsgDBFolder::GetGettingNewMessages(PRBool *gettingNewMessages)
00422 {
00423   NS_ENSURE_ARG_POINTER(gettingNewMessages);
00424   
00425   *gettingNewMessages = mGettingNewMessages;
00426   return NS_OK;
00427 }
00428 
00429 NS_IMETHODIMP nsMsgDBFolder::SetGettingNewMessages(PRBool gettingNewMessages)
00430 {
00431   mGettingNewMessages = gettingNewMessages;
00432   return NS_OK;
00433 }
00434 
00435 NS_IMETHODIMP nsMsgDBFolder::GetFirstNewMessage(nsIMsgDBHdr **firstNewMessage)
00436 {
00437   //If there's not a db then there can't be new messages.  Return failure since you
00438   //should use HasNewMessages first.
00439   if(!mDatabase)
00440     return NS_ERROR_FAILURE;
00441   
00442   nsresult rv;
00443   nsMsgKey key;
00444   rv = mDatabase->GetFirstNew(&key);
00445   if(NS_FAILED(rv))
00446     return rv;
00447   
00448   nsCOMPtr<nsIMsgDBHdr> hdr;
00449   rv = mDatabase->GetMsgHdrForKey(key, getter_AddRefs(hdr));
00450   if(NS_FAILED(rv))
00451     return rv;
00452   
00453   return  mDatabase->GetMsgHdrForKey(key, firstNewMessage);
00454 }
00455 
00456 NS_IMETHODIMP nsMsgDBFolder::ClearNewMessages()
00457 {
00458   nsresult rv = NS_OK;
00459   //If there's no db then there's nothing to clear.
00460   if(mDatabase)
00461   {
00462     PRUint32 numNewKeys;
00463     PRUint32 *newMessageKeys;
00464     rv = mDatabase->GetNewList(&numNewKeys, &newMessageKeys);
00465     if (NS_SUCCEEDED(rv) && newMessageKeys)
00466     {
00467       m_saveNewMsgs.RemoveAll();
00468       m_saveNewMsgs.Add(newMessageKeys, numNewKeys);
00469     }
00470     mDatabase->ClearNewList(PR_TRUE);
00471   }
00472   m_newMsgs.RemoveAll();
00473   mNumNewBiffMessages = 0;
00474   return rv;
00475 }
00476 
00477 void nsMsgDBFolder::UpdateNewMessages()
00478 {
00479   if (! (mFlags & MSG_FOLDER_FLAG_VIRTUAL))
00480   {
00481     PRBool hasNewMessages = PR_FALSE;
00482     for (PRUint32 keyIndex = 0; keyIndex < m_newMsgs.GetSize(); keyIndex++)
00483     {
00484       PRBool containsKey = PR_FALSE;
00485       mDatabase->ContainsKey(m_newMsgs[keyIndex], &containsKey);
00486       if (!containsKey)
00487         continue;
00488       PRBool isRead = PR_FALSE;
00489       nsresult rv2 = mDatabase->IsRead(m_newMsgs[keyIndex], &isRead);
00490       if (NS_SUCCEEDED(rv2) && !isRead)
00491       {
00492         hasNewMessages = PR_TRUE;
00493         mDatabase->AddToNewList(m_newMsgs[keyIndex]);
00494       }
00495     }
00496     SetHasNewMessages(hasNewMessages);
00497   }
00498 }
00499 
00500 // helper function that gets the cache element that corresponds to the passed in file spec.
00501 // This could be static, or could live in another class - it's not specific to the current
00502 // nsMsgDBFolder. If it lived at a higher level, we could cache the account manager and folder cache.
00503 nsresult nsMsgDBFolder::GetFolderCacheElemFromFileSpec(nsIFileSpec *fileSpec, nsIMsgFolderCacheElement **cacheElement)
00504 {
00505   nsresult result;
00506   if (!fileSpec || !cacheElement)
00507     return NS_ERROR_NULL_POINTER;
00508   nsCOMPtr <nsIMsgFolderCache> folderCache;
00509 #ifdef DEBUG_bienvenu1
00510   PRBool exists;
00511   NS_ASSERTION(NS_SUCCEEDED(fileSpec->Exists(&exists)) && exists, "whoops, file doesn't exist, mac will break");
00512 #endif
00513   nsCOMPtr<nsIMsgAccountManager> accountMgr = 
00514     do_GetService(NS_MSGACCOUNTMANAGER_CONTRACTID, &result); 
00515   if(NS_SUCCEEDED(result))
00516   {
00517     result = accountMgr->GetFolderCache(getter_AddRefs(folderCache));
00518     if (NS_SUCCEEDED(result) && folderCache)
00519     {
00520       nsXPIDLCString persistentPath;
00521       fileSpec->GetPersistentDescriptorString(getter_Copies(persistentPath));
00522       result = folderCache->GetCacheElement(persistentPath, PR_FALSE, cacheElement);
00523     }
00524   }
00525   return result;
00526 }
00527 
00528 nsresult nsMsgDBFolder::ReadDBFolderInfo(PRBool force)
00529 {
00530   // Since it turns out to be pretty expensive to open and close
00531   // the DBs all the time, if we have to open it once, get everything
00532   // we might need while we're here
00533   
00534   nsresult result=NS_ERROR_FAILURE;
00535   
00536   // don't need to reload from cache if we've already read from cache,
00537   // and, we might get stale info, so don't do it.
00538   if (!mInitializedFromCache)
00539   {
00540     nsCOMPtr <nsIFileSpec> dbPath;
00541     
00542     result = GetFolderCacheKey(getter_AddRefs(dbPath), PR_TRUE /* createDBIfMissing */);
00543     
00544     if (dbPath)
00545     {
00546       nsCOMPtr <nsIMsgFolderCacheElement> cacheElement;
00547       result = GetFolderCacheElemFromFileSpec(dbPath, getter_AddRefs(cacheElement));
00548       if (NS_SUCCEEDED(result) && cacheElement)
00549       {
00550         result = ReadFromFolderCacheElem(cacheElement);
00551       }
00552     }
00553   }
00554   //  if (m_master->InitFolderFromCache (this))
00555   //    return err;
00556   
00557   if (force || !mInitializedFromCache)
00558   {
00559     nsCOMPtr<nsIDBFolderInfo> folderInfo;
00560     nsCOMPtr<nsIMsgDatabase> db; 
00561     result = GetDBFolderInfoAndDB(getter_AddRefs(folderInfo), getter_AddRefs(db));
00562     if(NS_SUCCEEDED(result))
00563     {
00564       mIsCachable = PR_TRUE;
00565       if (folderInfo)
00566       {
00567         if (!mInitializedFromCache)
00568         {
00569           folderInfo->GetFlags((PRInt32 *)&mFlags);
00570 #ifdef DEBUG_bienvenu1
00571           nsXPIDLString name;
00572           GetName(getter_Copies(name));
00573           NS_ASSERTION(Compare(name, kLocalizedTrashName) || (mFlags & MSG_FOLDER_FLAG_TRASH), "lost trash flag");
00574 #endif
00575           mInitializedFromCache = PR_TRUE;
00576         }
00577         
00578         folderInfo->GetNumMessages(&mNumTotalMessages);
00579         folderInfo->GetNumUnreadMessages(&mNumUnreadMessages);
00580         folderInfo->GetExpungedBytes((PRInt32 *)&mExpungedBytes);
00581         
00582         nsXPIDLCString utf8Name;
00583         folderInfo->GetFolderName(getter_Copies(utf8Name));
00584         if (!utf8Name.IsEmpty())
00585           CopyUTF8toUTF16(utf8Name, mName);
00586         
00587         //These should be put in IMAP folder only.
00588         //folderInfo->GetImapTotalPendingMessages(&mNumPendingTotalMessages);
00589         //folderInfo->GetImapUnreadPendingMessages(&mNumPendingUnreadMessages);
00590         
00591         PRBool defaultUsed;
00592         folderInfo->GetCharacterSet(mCharset, &defaultUsed);
00593         if (defaultUsed)
00594           mCharset.Truncate();
00595         folderInfo->GetCharacterSetOverride(&mCharsetOverride);
00596         
00597         if (db) {
00598           PRBool hasnew;
00599           nsresult rv;
00600           rv = db->HasNew(&hasnew);
00601           if (NS_FAILED(rv)) return rv;
00602           if (!hasnew && mNumPendingUnreadMessages <= 0) {
00603             ClearFlag(MSG_FOLDER_FLAG_GOT_NEW);
00604           }
00605         }
00606       }
00607       
00608     }
00609     folderInfo = nsnull;
00610     if (db)
00611       db->Close(PR_FALSE);
00612   }
00613   
00614   return result;
00615   
00616 }
00617 
00618 nsresult nsMsgDBFolder::SendFlagNotifications(nsIMsgDBHdr *item, PRUint32 oldFlags, PRUint32 newFlags)
00619 {
00620   nsresult rv = NS_OK;
00621   
00622   PRUint32 changedFlags = oldFlags ^ newFlags;
00623   
00624   if((changedFlags & MSG_FLAG_READ)  && (changedFlags & MSG_FLAG_NEW))
00625   {
00626     //..so..if the msg is read in the folder and the folder has new msgs clear the account level and status bar biffs.
00627     rv = NotifyPropertyFlagChanged(item, kStatusAtom, oldFlags, newFlags);
00628     rv = SetBiffState(nsMsgBiffState_NoMail);
00629   }
00630   else if(changedFlags & (MSG_FLAG_READ | MSG_FLAG_REPLIED | MSG_FLAG_FORWARDED
00631     | MSG_FLAG_IMAP_DELETED | MSG_FLAG_NEW | MSG_FLAG_OFFLINE))
00632   {
00633     rv = NotifyPropertyFlagChanged(item, kStatusAtom, oldFlags, newFlags);
00634   }
00635   else if((changedFlags & MSG_FLAG_MARKED))
00636   {
00637     rv = NotifyPropertyFlagChanged(item, kFlaggedAtom, oldFlags, newFlags);
00638   }
00639   return rv;
00640 }
00641 
00642 NS_IMETHODIMP nsMsgDBFolder::DownloadMessagesForOffline(nsISupportsArray *messages, nsIMsgWindow *)
00643 {
00644   NS_ASSERTION(PR_FALSE, "imap and news need to override this");
00645   return NS_OK;
00646 }
00647 
00648 NS_IMETHODIMP nsMsgDBFolder::DownloadAllForOffline(nsIUrlListener *listener, nsIMsgWindow *msgWindow)
00649 {
00650   NS_ASSERTION(PR_FALSE, "imap and news need to override this");
00651   return NS_OK;
00652 }
00653 
00654 NS_IMETHODIMP nsMsgDBFolder::GetOfflineStoreInputStream(nsIInputStream **stream)
00655 {
00656   nsresult rv = NS_ERROR_NULL_POINTER;
00657   if (mPath)
00658     rv = mPath->GetInputStream(stream);
00659   return rv;
00660 }
00661 
00662 NS_IMETHODIMP nsMsgDBFolder::GetOfflineFileStream(nsMsgKey msgKey, PRUint32 *offset, PRUint32 *size, nsIInputStream **aFileStream)
00663 {
00664   NS_ENSURE_ARG(aFileStream);
00665 
00666   *offset = *size = 0;
00667   
00668   nsXPIDLCString nativePath;
00669   mPath->GetNativePath(getter_Copies(nativePath));
00670 
00671   nsCOMPtr <nsILocalFile> localStore;
00672   nsresult rv = NS_NewNativeLocalFile(nativePath, PR_TRUE, getter_AddRefs(localStore));
00673   if (NS_SUCCEEDED(rv) && localStore)
00674   {
00675     rv = NS_NewLocalFileInputStream(aFileStream, localStore);
00676 
00677     if (NS_SUCCEEDED(rv))
00678     {
00679 
00680       rv = GetDatabase(nsnull);
00681       NS_ENSURE_SUCCESS(rv, NS_OK);
00682       nsCOMPtr<nsIMsgDBHdr> hdr;
00683       rv = mDatabase->GetMsgHdrForKey(msgKey, getter_AddRefs(hdr));
00684       if (hdr && NS_SUCCEEDED(rv))
00685       {
00686         hdr->GetMessageOffset(offset);
00687         hdr->GetOfflineMessageSize(size);
00688       }
00689       // check if offline store really has the correct offset into the offline 
00690       // store by reading the first few bytes. If it doesn't, clear the offline
00691       // flag on the msg and return false, which will fall back to reading the message
00692       // from the server.
00693       nsCOMPtr <nsISeekableStream> seekableStream = do_QueryInterface(*aFileStream);
00694       if (seekableStream)
00695       {
00696         rv = seekableStream->Seek(nsISeekableStream::NS_SEEK_CUR, *offset);
00697         char startOfMsg[10];
00698         PRUint32 bytesRead;
00699         if (NS_SUCCEEDED(rv))
00700           rv = (*aFileStream)->Read(startOfMsg, sizeof(startOfMsg), &bytesRead);
00701 
00702         // check if message starts with From, or is a draft and starts with FCC
00703         if (NS_FAILED(rv) || bytesRead != sizeof(startOfMsg) || 
00704           (strncmp(startOfMsg, "From ", 5) && (! (mFlags & MSG_FOLDER_FLAG_DRAFTS) || strncmp(startOfMsg, "FCC", 3))))
00705           rv = NS_ERROR_FAILURE;
00706       }
00707     }
00708     if (NS_FAILED(rv) && mDatabase)
00709       mDatabase->MarkOffline(msgKey, PR_FALSE, nsnull);
00710   }
00711   return rv;
00712 }
00713 
00714 NS_IMETHODIMP nsMsgDBFolder::GetOfflineStoreOutputStream(nsIOutputStream **outputStream)
00715 {
00716   nsresult rv = NS_ERROR_NULL_POINTER;
00717   if (mPath)
00718   {
00719     // the following code doesn't work for a host of reasons - the transfer offset
00720     // is ignored for output streams. The buffering used by file channels does not work
00721     // if transfer offsets are coerced to work, etc.
00722 #if 0
00723     nsCOMPtr<nsIFileChannel> fileChannel = do_CreateInstance(NS_LOCALFILECHANNEL_CONTRACTID);
00724     if (fileChannel)
00725     {
00726       nsCOMPtr <nsILocalFile> localStore;
00727       rv = NS_NewLocalFile(nativePath, PR_TRUE, getter_AddRefs(localStore));
00728       if (NS_SUCCEEDED(rv) && localStore)
00729       {
00730         rv = fileChannel->Init(localStore, PR_CREATE_FILE | PR_RDWR, 0);
00731         if (NS_FAILED(rv)) 
00732           return rv; 
00733         rv = fileChannel->Open(outputStream);
00734         if (NS_FAILED(rv)) 
00735           return rv; 
00736       }
00737     }
00738 #endif
00739     nsCOMPtr<nsISupports>  supports;
00740     nsFileSpec fileSpec;
00741     mPath->GetFileSpec(&fileSpec);
00742     rv = NS_NewIOFileStream(getter_AddRefs(supports), fileSpec, PR_WRONLY | PR_CREATE_FILE, 00700);
00743     NS_ENSURE_SUCCESS(rv, rv);
00744     supports->QueryInterface(NS_GET_IID(nsIOutputStream), (void **) outputStream);
00745 
00746     nsCOMPtr <nsISeekableStream> seekable = do_QueryInterface(supports);
00747     if (seekable)
00748       seekable->Seek(nsISeekableStream::NS_SEEK_END, 0);
00749   }
00750   return rv;
00751 }
00752 
00753 // XXX todo
00754 // move these to a common location and remove all the hard coded ".msf"
00755 #define SUMMARY_SUFFIX ".msf"
00756 #define SUMMARY_SUFFIX_LEN 4
00757 
00758 // path coming in is the root path without the leaf name,
00759 // on the way out, it's the whole path.
00760 nsresult nsMsgDBFolder::CreateFileSpecForDB(const char *userLeafName, nsFileSpec &path, nsIFileSpec **dbFileSpec)
00761 {
00762   NS_ENSURE_ARG_POINTER(dbFileSpec);
00763   NS_ENSURE_ARG_POINTER(userLeafName);
00764 
00765   // XXX : This function is only called by nsImapMailFolder which calls
00766   // this function with UTF-7 (ASCII only) userLeafName so that we can
00767   // use 'char' version of NS_MsgHasIfNcessary (bug 264071). 
00768   // If this becomes not the case any more, we should use PRUnichar-version,
00769   // instead.
00770   nsCAutoString proposedDBName(userLeafName);
00771   NS_MsgHashIfNecessary(proposedDBName);
00772 
00773   // (note, the caller of this will be using the dbFileSpec to call db->Open() 
00774   // will turn the path into summary spec, and append the ".msf" extension)
00775   //
00776   // we want db->Open() to create a new summary file
00777   // so we have to jump through some hoops to make sure the .msf it will
00778   // create is unique.  now that we've got the "safe" proposedDBName,
00779   // we append ".msf" to see if the file exists.  if so, we make the name
00780   // unique and then string off the ".msf" so that we pass the right thing
00781   // into Open().  this isn't ideal, since this is not atomic
00782   // but it will make do.
00783   proposedDBName+= SUMMARY_SUFFIX;
00784   path += proposedDBName.get();
00785   if (path.Exists()) 
00786   {
00787     path.MakeUnique();
00788     proposedDBName = path.GetLeafName();
00789   }
00790   // now, take the ".msf" off
00791   proposedDBName.Truncate(proposedDBName.Length() - SUMMARY_SUFFIX_LEN);
00792   path.SetLeafName(proposedDBName.get());
00793 
00794   NS_NewFileSpecWithSpec(path, dbFileSpec);
00795   return NS_OK;
00796 }
00797 
00798 NS_IMETHODIMP
00799 nsMsgDBFolder::GetMsgDatabase(nsIMsgWindow *aMsgWindow,
00800                               nsIMsgDatabase** aMsgDatabase)
00801 {
00802   GetDatabase(aMsgWindow);
00803   
00804   if (!aMsgDatabase || !mDatabase)
00805     return NS_ERROR_NULL_POINTER;
00806   
00807   NS_ADDREF(*aMsgDatabase = mDatabase);
00808   return NS_OK;
00809 }
00810 
00811 NS_IMETHODIMP
00812 nsMsgDBFolder::SetMsgDatabase(nsIMsgDatabase *aMsgDatabase)
00813 {
00814   if (mDatabase)
00815   {
00816     // commit here - db might go away when all these refs are released.
00817     mDatabase->Commit(nsMsgDBCommitType::kLargeCommit);
00818     mDatabase->RemoveListener(this);
00819     mDatabase->ClearCachedHdrs();
00820     if (!aMsgDatabase)
00821     {
00822       PRUint32 numNewKeys;
00823       PRUint32 *newMessageKeys;
00824       nsresult rv = mDatabase->GetNewList(&numNewKeys, &newMessageKeys);
00825       if (NS_SUCCEEDED(rv) && newMessageKeys)
00826       {
00827         m_newMsgs.RemoveAll();
00828         m_newMsgs.Add(newMessageKeys, numNewKeys);
00829       }
00830       nsMemory::Free (newMessageKeys);
00831     }
00832   }
00833   mDatabase = aMsgDatabase;
00834 
00835   if (aMsgDatabase)
00836     aMsgDatabase->AddListener(this);
00837   return NS_OK;
00838 }
00839 
00840 NS_IMETHODIMP
00841 nsMsgDBFolder::GetDBFolderInfoAndDB(nsIDBFolderInfo **folderInfo, nsIMsgDatabase **database)
00842 {
00843   return NS_ERROR_NOT_IMPLEMENTED;
00844 }
00845 
00846 NS_IMETHODIMP
00847 nsMsgDBFolder::OnReadChanged(nsIDBChangeListener * aInstigator)
00848 {
00849   /* do nothing.  if you care about this, override it.  see nsNewsFolder.cpp */
00850   return NS_OK;
00851 }
00852 
00853 NS_IMETHODIMP
00854 nsMsgDBFolder::OnJunkScoreChanged(nsIDBChangeListener * aInstigator)
00855 {
00856   NotifyFolderEvent(mJunkStatusChangedAtom);
00857   return NS_OK;
00858 }
00859 
00860 
00861 // 1.  When the status of a message changes.
00862 NS_IMETHODIMP nsMsgDBFolder::OnHdrChange(nsIMsgDBHdr *aHdrChanged, PRUint32 aOldFlags, PRUint32 aNewFlags, 
00863                                          nsIDBChangeListener * aInstigator)
00864 {
00865   if(aHdrChanged)
00866   {
00867     SendFlagNotifications(aHdrChanged, aOldFlags, aNewFlags);
00868     UpdateSummaryTotals(PR_TRUE);
00869   }
00870   
00871   // The old state was new message state
00872   // We check and see if this state has changed
00873   if(aOldFlags & MSG_FLAG_NEW) 
00874   {
00875     // state changing from new to something else
00876     if (!(aNewFlags  & MSG_FLAG_NEW)) 
00877     {
00878       CheckWithNewMessagesStatus(PR_FALSE);
00879     }
00880   }
00881   
00882   return NS_OK;
00883 }
00884 
00885 nsresult nsMsgDBFolder::CheckWithNewMessagesStatus(PRBool messageAdded)
00886 {
00887   nsresult rv;
00888   
00889   PRBool hasNewMessages;
00890   
00891   if (messageAdded)
00892   {
00893     SetHasNewMessages(PR_TRUE);
00894   }
00895   else // message modified or deleted
00896   {
00897     if(mDatabase)
00898     {
00899       rv = mDatabase->HasNew(&hasNewMessages);
00900       SetHasNewMessages(hasNewMessages);
00901     }
00902   }
00903   
00904   return NS_OK;
00905 }
00906 
00907 // 3.  When a message gets deleted, we need to see if it was new
00908 //     When we lose a new message we need to check if there are still new messages 
00909 NS_IMETHODIMP nsMsgDBFolder::OnHdrDeleted(nsIMsgDBHdr *aHdrChanged, nsMsgKey  aParentKey, PRInt32 aFlags, 
00910                           nsIDBChangeListener * aInstigator)
00911 {
00912     // check to see if a new message is being deleted
00913     // as in this case, if there is only one new message and it's being deleted
00914     // the folder newness has to be cleared.
00915     CheckWithNewMessagesStatus(PR_FALSE);
00916 
00917     return OnHdrAddedOrDeleted(aHdrChanged, PR_FALSE);
00918 }
00919 
00920 // 2.  When a new messages gets added, we need to see if it's new.
00921 NS_IMETHODIMP nsMsgDBFolder::OnHdrAdded(nsIMsgDBHdr *aHdrChanged, nsMsgKey  aParentKey , PRInt32 aFlags, 
00922                         nsIDBChangeListener * aInstigator)
00923 {
00924   if(aFlags & MSG_FLAG_NEW) 
00925     CheckWithNewMessagesStatus(PR_TRUE);
00926   
00927   return OnHdrAddedOrDeleted(aHdrChanged, PR_TRUE);
00928 }
00929 
00930 nsresult nsMsgDBFolder::OnHdrAddedOrDeleted(nsIMsgDBHdr *aHdrChanged, PRBool added)
00931 {
00932   if(added)
00933     NotifyItemAdded(aHdrChanged);
00934   else
00935     NotifyItemRemoved(aHdrChanged);
00936   UpdateSummaryTotals(PR_TRUE);
00937   return NS_OK;
00938   
00939 }
00940 
00941 
00942 NS_IMETHODIMP nsMsgDBFolder::OnParentChanged(nsMsgKey aKeyChanged, nsMsgKey oldParent, nsMsgKey newParent, 
00943             nsIDBChangeListener * aInstigator)
00944 {
00945   nsCOMPtr<nsIMsgDBHdr> hdrChanged;
00946   mDatabase->GetMsgHdrForKey(aKeyChanged, getter_AddRefs(hdrChanged));
00947   //In reality we probably want to just change the parent because otherwise we will lose things like
00948   //selection.
00949 
00950   if (hdrChanged)
00951   {
00952     //First delete the child from the old threadParent
00953     OnHdrAddedOrDeleted(hdrChanged, PR_FALSE);
00954     //Then add it to the new threadParent
00955     OnHdrAddedOrDeleted(hdrChanged, PR_TRUE);
00956   }
00957   return NS_OK;
00958 }
00959 
00960 
00961 NS_IMETHODIMP nsMsgDBFolder::OnAnnouncerGoingAway(nsIDBChangeAnnouncer *instigator)
00962 {
00963     if (mDatabase)
00964     {
00965         mDatabase->RemoveListener(this);
00966         mDatabase = nsnull;
00967     }
00968     return NS_OK;
00969 }
00970 
00971 NS_IMETHODIMP nsMsgDBFolder::GetManyHeadersToDownload(PRBool *retval)
00972 {
00973   PRInt32 numTotalMessages;
00974 
00975   NS_ENSURE_ARG_POINTER(retval);
00976 
00977   // is there any reason to return false?
00978   if (!mDatabase)
00979     *retval = PR_TRUE;
00980   else if (NS_SUCCEEDED(GetTotalMessages(PR_FALSE, &numTotalMessages)) && numTotalMessages <= 0)
00981     *retval = PR_TRUE;
00982   else
00983     *retval = PR_FALSE;
00984   return NS_OK;
00985 }
00986 
00987 nsresult nsMsgDBFolder::MsgFitsDownloadCriteria(nsMsgKey msgKey, PRBool *result)
00988 {
00989   if(!mDatabase)
00990     return NS_ERROR_FAILURE;
00991   
00992   nsresult rv;
00993   nsCOMPtr<nsIMsgDBHdr> hdr;
00994   rv = mDatabase->GetMsgHdrForKey(msgKey, getter_AddRefs(hdr));
00995   if(NS_FAILED(rv))
00996     return rv;
00997   
00998   if (hdr)
00999   {
01000     PRUint32 msgFlags = 0;
01001     
01002     hdr->GetFlags(&msgFlags);
01003     // check if we already have this message body offline
01004     if (! (msgFlags & MSG_FLAG_OFFLINE))
01005     {
01006       *result = PR_TRUE;
01007       // check against the server download size limit .
01008       nsCOMPtr <nsIMsgIncomingServer> incomingServer;
01009       rv = GetServer(getter_AddRefs(incomingServer));
01010       if (NS_SUCCEEDED(rv) && incomingServer)
01011       {
01012         PRBool limitDownloadSize = PR_FALSE;
01013         rv = incomingServer->GetLimitOfflineMessageSize(&limitDownloadSize);
01014         NS_ENSURE_SUCCESS(rv, rv);
01015         if (limitDownloadSize)
01016         {
01017           PRInt32 maxDownloadMsgSize = 0;
01018           PRUint32 msgSize;
01019           hdr->GetMessageSize(&msgSize);
01020           rv = incomingServer->GetMaxMessageSize(&maxDownloadMsgSize);
01021           NS_ENSURE_SUCCESS(rv, rv);
01022           maxDownloadMsgSize *= 1024;
01023           if (msgSize > (PRUint32) maxDownloadMsgSize)
01024             *result = PR_FALSE;
01025         }
01026       }
01027     }
01028   }
01029   return NS_OK;
01030 }
01031 
01032 NS_IMETHODIMP nsMsgDBFolder::GetSupportsOffline(PRBool *aSupportsOffline)
01033 {  
01034    NS_ENSURE_ARG_POINTER(aSupportsOffline);
01035 
01036    if (mFlags & MSG_FOLDER_FLAG_VIRTUAL)
01037    {
01038       *aSupportsOffline = PR_FALSE;
01039       return NS_OK;
01040    }
01041 
01042    nsCOMPtr<nsIMsgIncomingServer> server;
01043    nsresult rv = GetServer(getter_AddRefs(server));
01044    NS_ENSURE_SUCCESS(rv,rv);
01045    if (!server) return NS_ERROR_FAILURE;
01046   
01047    PRInt32 offlineSupportLevel;
01048    rv = server->GetOfflineSupportLevel(&offlineSupportLevel);
01049    NS_ENSURE_SUCCESS(rv,rv);
01050 
01051    *aSupportsOffline = (offlineSupportLevel >= OFFLINE_SUPPORT_LEVEL_REGULAR);
01052    return NS_OK;
01053 }
01054 
01055 NS_IMETHODIMP nsMsgDBFolder::ShouldStoreMsgOffline(nsMsgKey msgKey, PRBool *result)
01056 {
01057   NS_ENSURE_ARG(result);
01058   PRUint32 flags = 0;
01059   *result = PR_FALSE;
01060   GetFlags(&flags);
01061 
01062   if (flags & MSG_FOLDER_FLAG_OFFLINE)
01063     return MsgFitsDownloadCriteria(msgKey, result);
01064   return NS_OK;
01065 }
01066 
01067 NS_IMETHODIMP nsMsgDBFolder::HasMsgOffline(nsMsgKey msgKey, PRBool *result)
01068 {
01069   NS_ENSURE_ARG(result);
01070   *result = PR_FALSE;
01071   if(!mDatabase)
01072     return NS_ERROR_FAILURE;
01073 
01074   nsresult rv;
01075   nsCOMPtr<nsIMsgDBHdr> hdr;
01076   rv = mDatabase->GetMsgHdrForKey(msgKey, getter_AddRefs(hdr));
01077   if(NS_FAILED(rv))
01078     return rv;
01079 
01080   if (hdr)
01081   {
01082     PRUint32 msgFlags = 0;
01083 
01084     hdr->GetFlags(&msgFlags);
01085     // check if we already have this message body offline
01086     if ((msgFlags & MSG_FLAG_OFFLINE))
01087       *result = PR_TRUE;
01088   }
01089   return NS_OK;
01090 }
01091 
01092 
01093 NS_IMETHODIMP nsMsgDBFolder::GetFlags(PRUint32 *_retval)
01094 {
01095   ReadDBFolderInfo(PR_FALSE);
01096   *_retval = mFlags;
01097   return NS_OK;
01098 }
01099 
01100 
01101 NS_IMETHODIMP nsMsgDBFolder::ReadFromFolderCacheElem(nsIMsgFolderCacheElement *element)
01102 {
01103   nsresult rv = NS_OK;
01104   nsXPIDLCString charset;
01105 
01106   element->GetInt32Property("flags", (PRInt32 *) &mFlags);
01107 
01108   PRBool persistElided = PR_TRUE;
01109   rv = GetPersistElided(&persistElided);
01110   NS_ENSURE_SUCCESS(rv,rv);
01111 
01112   // we aren't persisting elided, set the folder as closed
01113   if (!persistElided) {
01114     mFlags |= MSG_FOLDER_FLAG_ELIDED;
01115   }
01116 
01117   element->GetInt32Property("totalMsgs", &mNumTotalMessages);
01118   element->GetInt32Property("totalUnreadMsgs", &mNumUnreadMessages);
01119   element->GetInt32Property("pendingUnreadMsgs", &mNumPendingUnreadMessages);
01120   element->GetInt32Property("pendingMsgs", &mNumPendingTotalMessages);
01121   element->GetInt32Property("expungedBytes", (PRInt32 *) &mExpungedBytes);
01122   element->GetInt32Property("folderSize", (PRInt32 *) &mFolderSize);
01123 
01124   element->GetStringProperty("charset", getter_Copies(charset));
01125 
01126 #ifdef DEBUG_bienvenu1
01127   char *uri;
01128 
01129   GetURI(&uri);
01130   printf("read total %ld for %s\n", mNumTotalMessages, uri);
01131   PR_Free(uri);
01132 #endif
01133   mCharset.AssignASCII(charset);
01134 
01135   mInitializedFromCache = PR_TRUE;
01136   return rv;
01137 }
01138 
01139 nsresult nsMsgDBFolder::GetFolderCacheKey(nsIFileSpec **aFileSpec, PRBool createDBIfMissing /* = PR_FALSE */)
01140 {
01141   nsresult rv;
01142   nsCOMPtr <nsIFileSpec> path;
01143   rv = GetPath(getter_AddRefs(path));
01144   
01145   // now we put a new file spec in aFileSpec, because we're going to change it.
01146   rv = NS_NewFileSpec(aFileSpec);
01147   
01148   if (NS_SUCCEEDED(rv) && *aFileSpec)
01149   {
01150     nsIFileSpec *dbPath = *aFileSpec;
01151     dbPath->FromFileSpec(path);
01152     // if not a server, we need to convert to a db Path with .msf on the end
01153     PRBool isServer = PR_FALSE;
01154     GetIsServer(&isServer);
01155     
01156     // if it's a server, we don't need the .msf appended to the name
01157     if (!isServer)
01158     {
01159       nsFileSpec            folderName;
01160       dbPath->GetFileSpec(&folderName);
01161       nsLocalFolderSummarySpec summarySpec(folderName);
01162       
01163       dbPath->SetFromFileSpec(summarySpec);
01164       
01165       // create the .msf file
01166       // see bug #244217 for details
01167       PRBool exists;
01168       if (createDBIfMissing && NS_SUCCEEDED(dbPath->Exists(&exists)) && !exists)
01169         dbPath->Touch();
01170     }
01171   }
01172   return rv;
01173 }
01174 
01175 nsresult nsMsgDBFolder::FlushToFolderCache()
01176 {
01177   nsresult rv;
01178   nsCOMPtr<nsIMsgAccountManager> accountManager = 
01179            do_GetService(NS_MSGACCOUNTMANAGER_CONTRACTID, &rv);
01180   if (NS_SUCCEEDED(rv) && accountManager)
01181   {
01182     nsCOMPtr<nsIMsgFolderCache> folderCache;
01183 
01184     rv = accountManager->GetFolderCache(getter_AddRefs(folderCache));
01185     if (NS_SUCCEEDED(rv) && folderCache)
01186       rv = WriteToFolderCache(folderCache, PR_FALSE);
01187   }
01188   return rv;
01189 }
01190 
01191 NS_IMETHODIMP nsMsgDBFolder::WriteToFolderCache(nsIMsgFolderCache *folderCache, PRBool deep)
01192 {
01193   nsCOMPtr <nsIEnumerator> aEnumerator;
01194   nsresult rv;
01195 
01196   if (folderCache)
01197   {
01198     nsCOMPtr <nsIMsgFolderCacheElement> cacheElement;
01199     nsCOMPtr <nsIFileSpec> dbPath;
01200 
01201     rv = GetFolderCacheKey(getter_AddRefs(dbPath));
01202 #ifdef DEBUG_bienvenu1
01203     PRBool exists;
01204     NS_ASSERTION(NS_SUCCEEDED(dbPath->Exists(&exists)) && exists, "file spec we're adding to cache should exist");
01205 #endif
01206     if (NS_SUCCEEDED(rv) && dbPath)
01207     {
01208       nsXPIDLCString persistentPath;
01209       dbPath->GetPersistentDescriptorString(getter_Copies(persistentPath));
01210       rv = folderCache->GetCacheElement(persistentPath, PR_TRUE, getter_AddRefs(cacheElement));
01211       if (NS_SUCCEEDED(rv) && cacheElement)
01212         rv = WriteToFolderCacheElem(cacheElement);
01213     }
01214   }
01215 
01216   if (!deep)
01217     return rv;
01218 
01219   rv = GetSubFolders(getter_AddRefs(aEnumerator));
01220   if(NS_FAILED(rv)) 
01221     return rv;
01222 
01223   nsCOMPtr<nsISupports> aItem;
01224 
01225   rv = aEnumerator->First();
01226   if (NS_FAILED(rv))
01227     return NS_OK; // it's OK, there are no sub-folders.
01228 
01229   while(NS_SUCCEEDED(rv))
01230   {
01231     rv = aEnumerator->CurrentItem(getter_AddRefs(aItem));
01232     if (NS_FAILED(rv)) break;
01233     nsCOMPtr<nsIMsgFolder> aMsgFolder(do_QueryInterface(aItem, &rv));
01234     if (NS_SUCCEEDED(rv))
01235     {
01236       if (folderCache)
01237       {
01238         rv = aMsgFolder->WriteToFolderCache(folderCache, PR_TRUE);
01239         if (NS_FAILED(rv))
01240           break;
01241       }
01242     }
01243     rv = aEnumerator->Next();
01244     if (NS_FAILED(rv))
01245     {
01246       rv = NS_OK;
01247       break;
01248     }
01249   }
01250   return rv;
01251 }
01252 
01253 NS_IMETHODIMP nsMsgDBFolder::WriteToFolderCacheElem(nsIMsgFolderCacheElement *element)
01254 {
01255   nsresult rv = NS_OK;
01256 
01257   element->SetInt32Property("flags", (PRInt32) mFlags);
01258   element->SetInt32Property("totalMsgs", mNumTotalMessages);
01259   element->SetInt32Property("totalUnreadMsgs", mNumUnreadMessages);
01260   element->SetInt32Property("pendingUnreadMsgs", mNumPendingUnreadMessages);
01261   element->SetInt32Property("pendingMsgs", mNumPendingTotalMessages);
01262   element->SetInt32Property("expungedBytes", mExpungedBytes);
01263   element->SetInt32Property("folderSize", mFolderSize);
01264 
01265   element->SetStringProperty("charset", mCharset.get());
01266 
01267 #ifdef DEBUG_bienvenu1
01268   char *uri;
01269 
01270   GetURI(&uri);
01271   printf("writing total %ld for %s\n", mNumTotalMessages, uri);
01272   PR_Free(uri);
01273 #endif
01274   return rv;
01275 }
01276 
01277 NS_IMETHODIMP
01278 nsMsgDBFolder::AddMessageDispositionState(nsIMsgDBHdr *aMessage, nsMsgDispositionState aDispositionFlag)
01279 {
01280   NS_ENSURE_ARG_POINTER(aMessage);
01281 
01282   nsresult rv = GetDatabase(nsnull);
01283   NS_ENSURE_SUCCESS(rv, NS_OK);
01284   
01285   nsMsgKey msgKey;
01286   aMessage->GetMessageKey(&msgKey);
01287   
01288   if (aDispositionFlag == nsIMsgFolder::nsMsgDispositionState_Replied)
01289     mDatabase->MarkReplied(msgKey, PR_TRUE, nsnull);
01290   else if (aDispositionFlag == nsIMsgFolder::nsMsgDispositionState_Forwarded)
01291     mDatabase->MarkForwarded(msgKey, PR_TRUE, nsnull);
01292   return NS_OK;
01293 }
01294 
01295 NS_IMETHODIMP
01296 nsMsgDBFolder::MarkAllMessagesRead(void)
01297 {
01298   // ### fix me need nsIMsgWindow
01299   nsresult rv = GetDatabase(nsnull);
01300   
01301   m_newMsgs.RemoveAll();
01302   if(NS_SUCCEEDED(rv))
01303   {
01304     EnableNotifications(allMessageCountNotifications, PR_FALSE, PR_TRUE /*dbBatching*/);
01305     rv = mDatabase->MarkAllRead(nsnull);
01306     EnableNotifications(allMessageCountNotifications, PR_TRUE, PR_TRUE /*dbBatching*/);
01307   }
01308   SetHasNewMessages(PR_FALSE);
01309   return rv;
01310 }
01311 
01312 NS_IMETHODIMP nsMsgDBFolder::MarkThreadRead(nsIMsgThread *thread)
01313 {
01314   
01315   nsresult rv = GetDatabase(nsnull);
01316   if(NS_SUCCEEDED(rv))
01317     return mDatabase->MarkThreadRead(thread, nsnull, nsnull);
01318   
01319   return rv;
01320 }
01321 
01322 NS_IMETHODIMP
01323 nsMsgDBFolder::OnStartRunningUrl(nsIURI *aUrl)
01324 {
01325   NS_PRECONDITION(aUrl, "just a sanity check");
01326   return NS_OK;
01327 }
01328 
01329 NS_IMETHODIMP
01330 nsMsgDBFolder::OnStopRunningUrl(nsIURI *aUrl, nsresult aExitCode)
01331 {
01332   NS_PRECONDITION(aUrl, "just a sanity check");
01333   nsCOMPtr<nsIMsgMailNewsUrl> mailUrl = do_QueryInterface(aUrl);
01334   if (mailUrl)
01335   {
01336     PRBool updatingFolder = PR_FALSE;
01337     if (NS_SUCCEEDED(mailUrl->GetUpdatingFolder(&updatingFolder)) && updatingFolder)
01338       NotifyFolderEvent(mFolderLoadedAtom);
01339       
01340     // be sure to remove ourselves as a url listener
01341     mailUrl->UnRegisterListener(this);
01342   }
01343   return NS_OK;
01344 }
01345 
01346 NS_IMETHODIMP 
01347 nsMsgDBFolder::GetRetentionSettings(nsIMsgRetentionSettings **settings)
01348 {
01349   NS_ENSURE_ARG_POINTER(settings);
01350   nsresult rv = NS_OK;
01351   if (!m_retentionSettings)
01352   {
01353     GetDatabase(nsnull);
01354     if (mDatabase)
01355     {
01356       // get the settings from the db - if the settings from the db say the folder
01357       // is not overriding the incoming server settings, get the settings from the
01358       // server.
01359       rv = mDatabase->GetMsgRetentionSettings(getter_AddRefs(m_retentionSettings));
01360       if (NS_SUCCEEDED(rv) && m_retentionSettings)
01361       {
01362         PRBool useServerDefaults;
01363         m_retentionSettings->GetUseServerDefaults(&useServerDefaults);
01364 
01365         if (useServerDefaults)
01366         {
01367           nsCOMPtr <nsIMsgIncomingServer> incomingServer;
01368           rv = GetServer(getter_AddRefs(incomingServer));
01369           if (NS_SUCCEEDED(rv) && incomingServer)
01370             incomingServer->GetRetentionSettings(getter_AddRefs(m_retentionSettings));
01371         }
01372 
01373       }
01374     }
01375   }
01376   *settings = m_retentionSettings;
01377   NS_IF_ADDREF(*settings);
01378   return rv;
01379 }
01380 
01381 NS_IMETHODIMP nsMsgDBFolder::SetRetentionSettings(nsIMsgRetentionSettings *settings)
01382 {
01383   m_retentionSettings = settings;
01384   GetDatabase(nsnull);
01385   if (mDatabase)
01386     mDatabase->SetMsgRetentionSettings(settings);
01387   return NS_OK;
01388 }
01389 
01390 NS_IMETHODIMP nsMsgDBFolder::GetDownloadSettings(nsIMsgDownloadSettings **settings)
01391 {
01392   NS_ENSURE_ARG_POINTER(settings);
01393   nsresult rv = NS_OK;
01394   if (!m_downloadSettings)
01395   {
01396     GetDatabase(nsnull);
01397     if (mDatabase)
01398     {
01399       // get the settings from the db - if the settings from the db say the folder
01400       // is not overriding the incoming server settings, get the settings from the
01401       // server.
01402       rv = mDatabase->GetMsgDownloadSettings(getter_AddRefs(m_downloadSettings));
01403       if (NS_SUCCEEDED(rv) && m_downloadSettings)
01404       {
01405         PRBool useServerDefaults;
01406         m_downloadSettings->GetUseServerDefaults(&useServerDefaults);
01407 
01408         if (useServerDefaults)
01409         {
01410           nsCOMPtr <nsIMsgIncomingServer> incomingServer;
01411           rv = GetServer(getter_AddRefs(incomingServer));
01412           if (NS_SUCCEEDED(rv) && incomingServer)
01413             incomingServer->GetDownloadSettings(getter_AddRefs(m_downloadSettings));
01414         }
01415 
01416       }
01417     }
01418   }
01419   *settings = m_downloadSettings;
01420   NS_IF_ADDREF(*settings);
01421   return rv;
01422 }
01423 
01424 NS_IMETHODIMP nsMsgDBFolder::SetDownloadSettings(nsIMsgDownloadSettings *settings)
01425 {
01426   m_downloadSettings = settings;
01427   return NS_OK;
01428 }
01429 
01430 NS_IMETHODIMP nsMsgDBFolder::IsCommandEnabled(const char *command, PRBool *result)
01431 {
01432   NS_ENSURE_ARG_POINTER(result);
01433   *result = PR_TRUE;
01434   return NS_OK;
01435 }
01436 
01437 
01438 nsresult nsMsgDBFolder::WriteStartOfNewLocalMessage()
01439 {
01440   nsCAutoString result;
01441   char *ct;
01442   PRUint32 writeCount;
01443   time_t now = time ((time_t*) 0);
01444   ct = ctime(&now);
01445   ct[24] = 0;
01446   result = "From - ";
01447   result += ct;
01448   result += MSG_LINEBREAK;
01449   
01450   nsCOMPtr <nsISeekableStream> seekable;
01451   nsInt64 curStorePos;
01452 
01453   if (m_offlineHeader)
01454     seekable = do_QueryInterface(m_tempMessageStream);
01455 
01456   if (seekable)
01457   {
01458     PRInt64 tellPos;
01459     seekable->Tell(&tellPos);
01460     curStorePos = tellPos;
01461     // ### todo - need to convert this to 64 bits
01462     m_offlineHeader->SetMessageOffset((PRUint32) curStorePos);
01463   }
01464   m_tempMessageStream->Write(result.get(), result.Length(),
01465                              &writeCount);
01466   if (seekable)
01467   {
01468     PRInt64 tellPos;
01469     seekable->Seek(PR_SEEK_CUR, 0); // seeking causes a flush, w/o syncing
01470     seekable->Tell(&tellPos);
01471     curStorePos = tellPos;
01472     m_offlineHeader->SetStatusOffset((PRUint32) curStorePos);
01473   }
01474 
01475   result = "X-Mozilla-Status: 0001";
01476   result += MSG_LINEBREAK;
01477   m_tempMessageStream->Write(result.get(), result.Length(),
01478                              &writeCount);
01479   result =  "X-Mozilla-Status2: 00000000";
01480   result += MSG_LINEBREAK;
01481   nsresult rv = m_tempMessageStream->Write(result.get(), result.Length(),
01482                              &writeCount);
01483   return rv;
01484 }
01485 
01486 nsresult nsMsgDBFolder::StartNewOfflineMessage()
01487 {
01488   nsresult rv = NS_OK;
01489   if (!m_tempMessageStream)
01490     rv = GetOfflineStoreOutputStream(getter_AddRefs(m_tempMessageStream));
01491   else
01492   {
01493     nsCOMPtr <nsISeekableStream> seekable;
01494 
01495     seekable = do_QueryInterface(m_tempMessageStream);
01496 
01497     if (seekable)
01498       seekable->Seek(PR_SEEK_END, 0);
01499   }
01500   if (NS_SUCCEEDED(rv))
01501     WriteStartOfNewLocalMessage();
01502   m_numOfflineMsgLines = 0;
01503   return rv;
01504 }
01505 
01506 nsresult nsMsgDBFolder::EndNewOfflineMessage()
01507 {
01508   nsCOMPtr <nsISeekableStream> seekable;
01509   nsInt64 curStorePos;
01510   PRUint32 messageOffset;
01511   nsMsgKey messageKey;
01512 
01513   nsresult rv = GetDatabase(nsnull);
01514   NS_ENSURE_SUCCESS(rv, rv);
01515 
01516   m_offlineHeader->GetMessageKey(&messageKey);
01517   if (m_tempMessageStream)
01518     seekable = do_QueryInterface(m_tempMessageStream);
01519 
01520   mDatabase->MarkOffline(messageKey, PR_TRUE, nsnull);
01521   if (seekable)
01522   {
01523     seekable->Seek(PR_SEEK_CUR, 0); // seeking causes a flush, w/o syncing
01524     PRInt64 tellPos;
01525     seekable->Tell(&tellPos);
01526     curStorePos = tellPos;
01527     
01528     m_offlineHeader->GetMessageOffset(&messageOffset);
01529     curStorePos -= messageOffset;
01530     m_offlineHeader->SetOfflineMessageSize(curStorePos);
01531     m_offlineHeader->SetLineCount(m_numOfflineMsgLines);
01532   }
01533   m_offlineHeader = nsnull;
01534   return NS_OK;
01535 }
01536 
01537 nsresult nsMsgDBFolder::CompactOfflineStore(nsIMsgWindow *inWindow)
01538 {
01539   nsresult rv;
01540   nsCOMPtr <nsIMsgFolderCompactor> folderCompactor =  do_CreateInstance(NS_MSGOFFLINESTORECOMPACTOR_CONTRACTID, &rv);
01541   if (NS_SUCCEEDED(rv) && folderCompactor)
01542       rv = folderCompactor->Compact(this, PR_TRUE, inWindow);
01543   return rv;
01544 }
01545 
01546 nsresult
01547 nsMsgDBFolder::AutoCompact(nsIMsgWindow *aWindow) 
01548 {
01549    NS_ENSURE_ARG_POINTER(aWindow);
01550    PRBool prompt;
01551    nsresult rv = GetPromptPurgeThreshold(&prompt);
01552    NS_ENSURE_SUCCESS(rv, rv);
01553    PRTime timeNow = PR_Now();   //time in microseconds
01554    PRTime timeAfterOneHourOfLastPurgeCheck;
01555    LL_ADD(timeAfterOneHourOfLastPurgeCheck, gtimeOfLastPurgeCheck, oneHour);
01556    if (LL_CMP(timeAfterOneHourOfLastPurgeCheck, <, timeNow) && prompt)
01557    {
01558      gtimeOfLastPurgeCheck = timeNow;
01559      nsCOMPtr<nsIMsgAccountManager> accountMgr = do_GetService(NS_MSGACCOUNTMANAGER_CONTRACTID, &rv);
01560      if (NS_SUCCEEDED(rv))
01561      {
01562        nsCOMPtr<nsISupportsArray> allServers;
01563        accountMgr->GetAllServers(getter_AddRefs(allServers));
01564        NS_ENSURE_SUCCESS(rv,rv);
01565        PRUint32 numServers, serverIndex=0;
01566        rv = allServers->Count(&numServers);
01567        PRInt32 offlineSupportLevel;
01568        if ( numServers > 0 )
01569        {
01570          nsCOMPtr<nsIMsgIncomingServer> server =
01571            do_QueryElementAt(allServers, serverIndex);
01572          NS_ENSURE_SUCCESS(rv,rv);
01573          nsCOMPtr<nsISupportsArray> folderArray;
01574          nsCOMPtr<nsISupportsArray> offlineFolderArray;
01575          NS_NewISupportsArray(getter_AddRefs(folderArray));
01576          NS_NewISupportsArray(getter_AddRefs(offlineFolderArray));
01577          PRInt32 totalExpungedBytes=0;
01578          PRInt32 offlineExpungedBytes =0;
01579          PRInt32 localExpungedBytes = 0;
01580          do 
01581          {
01582            nsCOMPtr<nsIMsgFolder> rootFolder;
01583            rv = server->GetRootFolder(getter_AddRefs(rootFolder));
01584            if(NS_SUCCEEDED(rv) && rootFolder)
01585            {  
01586              rv = server->GetOfflineSupportLevel(&offlineSupportLevel);
01587              NS_ENSURE_SUCCESS(rv,rv);
01588              nsCOMPtr<nsISupportsArray> allDescendents;
01589              NS_NewISupportsArray(getter_AddRefs(allDescendents));
01590              rootFolder->ListDescendents(allDescendents);
01591              PRUint32 cnt=0;
01592              rv = allDescendents->Count(&cnt);
01593              NS_ENSURE_SUCCESS(rv,rv);
01594              PRUint32 expungedBytes=0;
01595          
01596              if (offlineSupportLevel > 0)
01597              {
01598                PRUint32 flags;
01599                for (PRUint32 i=0; i< cnt;i++)
01600                {
01601                  nsCOMPtr<nsISupports> supports = getter_AddRefs(allDescendents->ElementAt(i));
01602                  nsCOMPtr<nsIMsgFolder> folder = do_QueryInterface(supports, &rv);
01603                  expungedBytes = 0;
01604                  folder->GetFlags(&flags);
01605                  if (flags & MSG_FOLDER_FLAG_OFFLINE)
01606                    folder->GetExpungedBytes(&expungedBytes);
01607                  if (expungedBytes > 0 )
01608                  { 
01609                    offlineFolderArray->AppendElement(supports);
01610                    offlineExpungedBytes += expungedBytes;
01611                  }
01612                }
01613              }
01614              else  //pop or local
01615              {
01616                for (PRUint32 i=0; i< cnt;i++)
01617                {
01618                  nsCOMPtr<nsISupports> supports = getter_AddRefs(allDescendents->ElementAt(i));
01619                  nsCOMPtr<nsIMsgFolder> folder = do_QueryInterface(supports, &rv);
01620                  folder->GetExpungedBytes(&expungedBytes);
01621                  if (expungedBytes > 0 )
01622                  {
01623                    folderArray->AppendElement(supports);
01624                    localExpungedBytes += expungedBytes;
01625                  }
01626                }
01627              }
01628            }
01629            server = do_QueryElementAt(allServers, ++serverIndex);
01630          }
01631          while (serverIndex < numServers);
01632          totalExpungedBytes = localExpungedBytes + offlineExpungedBytes;
01633          PRInt32 purgeThreshold;
01634          rv = GetPurgeThreshold(&purgeThreshold);
01635          NS_ENSURE_SUCCESS(rv, rv);
01636          if (totalExpungedBytes > (purgeThreshold*1024))
01637          {
01638            PRBool okToCompact = PR_FALSE;
01639 
01640            nsCOMPtr<nsIPrefService> pref = do_GetService(NS_PREFSERVICE_CONTRACTID);
01641            nsCOMPtr<nsIPrefBranch> branch;
01642            pref->GetBranch("", getter_AddRefs(branch));
01643           
01644            PRBool askBeforePurge;
01645            branch->GetBoolPref(PREF_MAIL_PURGE_ASK, &askBeforePurge);
01646            if (askBeforePurge)
01647            {
01648              nsCOMPtr <nsIStringBundle> bundle;
01649              rv = GetBaseStringBundle(getter_AddRefs(bundle));
01650              NS_ENSURE_SUCCESS(rv, rv);
01651              nsXPIDLString dialogTitle;
01652              nsXPIDLString confirmString;
01653              nsXPIDLString checkboxText;
01654 
01655              rv = bundle->GetStringFromName(NS_LITERAL_STRING("autoCompactAllFoldersTitle").get(), getter_Copies(dialogTitle));
01656              NS_ENSURE_SUCCESS(rv, rv);
01657              rv = bundle->GetStringFromName(NS_LITERAL_STRING("autoCompactAllFolders").get(), getter_Copies(confirmString));
01658              NS_ENSURE_SUCCESS(rv, rv);
01659              rv = bundle->GetStringFromName(NS_LITERAL_STRING("autoCompactAllFoldersCheckbox").get(), getter_Copies(checkboxText));
01660              NS_ENSURE_SUCCESS(rv, rv);
01661 
01662              PRBool checkValue = PR_FALSE;
01663              PRInt32 buttonPressed = 0;
01664 
01665              nsCOMPtr<nsIPrompt> dialog;
01666              rv = aWindow->GetPromptDialog(getter_AddRefs(dialog));
01667              NS_ENSURE_SUCCESS(rv, rv);
01668 
01669              rv = dialog->ConfirmEx(dialogTitle.get(), confirmString.get(), nsIPrompt::STD_OK_CANCEL_BUTTONS,
01670                                     nsnull, nsnull, nsnull, checkboxText.get(), &checkValue, &buttonPressed);
01671              NS_ENSURE_SUCCESS(rv, rv);
01672              if (!buttonPressed)
01673              {
01674                okToCompact = PR_TRUE;
01675 
01676                if (checkValue)
01677                  branch->SetBoolPref(PREF_MAIL_PURGE_ASK, PR_FALSE);
01678              }
01679            }
01680            else
01681              okToCompact = PR_TRUE;
01682 
01683            if (okToCompact)
01684            {
01685               nsCOMPtr <nsIAtom> aboutToCompactAtom = do_GetAtom("AboutToCompact");
01686               NotifyFolderEvent(aboutToCompactAtom);
01687              
01688              if ( localExpungedBytes > 0)
01689              {
01690                nsCOMPtr <nsIMsgFolder> msgFolder =
01691                  do_QueryElementAt(folderArray, 0, &rv);
01692                if (msgFolder && NS_SUCCEEDED(rv))
01693                  if (offlineExpungedBytes > 0)
01694                    msgFolder->CompactAll(nsnull, aWindow, folderArray, PR_TRUE, offlineFolderArray);
01695                  else
01696                    msgFolder->CompactAll(nsnull, aWindow, folderArray, PR_FALSE, nsnull);
01697              }
01698              else if (offlineExpungedBytes > 0)
01699                CompactAllOfflineStores(aWindow, offlineFolderArray);
01700            }
01701          }
01702        }
01703      }  
01704   }
01705   return rv;
01706 }
01707  
01708 NS_IMETHODIMP
01709 nsMsgDBFolder::CompactAllOfflineStores(nsIMsgWindow *aWindow, nsISupportsArray *aOfflineFolderArray)
01710 {
01711   nsresult rv= NS_OK;
01712   nsCOMPtr <nsIMsgFolderCompactor> folderCompactor;
01713   folderCompactor = do_CreateInstance(NS_MSGOFFLINESTORECOMPACTOR_CONTRACTID, &rv);
01714 
01715   if (NS_SUCCEEDED(rv) && folderCompactor)
01716     rv = folderCompactor->CompactAll(nsnull, aWindow, PR_TRUE, aOfflineFolderArray);
01717   return rv;
01718 }
01719 
01720 nsresult
01721 nsMsgDBFolder::GetPromptPurgeThreshold(PRBool *aPrompt)
01722 {
01723   NS_ENSURE_ARG(aPrompt);
01724   nsresult rv;
01725   nsCOMPtr<nsIPrefBranch> prefBranch = do_GetService(NS_PREFSERVICE_CONTRACTID, &rv);
01726   if (NS_SUCCEEDED(rv) && prefBranch)
01727   {
01728     rv = prefBranch->GetBoolPref(PREF_MAIL_PROMPT_PURGE_THRESHOLD, aPrompt);
01729     if (NS_FAILED(rv)) 
01730     {
01731       *aPrompt = PR_FALSE;
01732       rv = NS_OK;
01733     }
01734   }
01735   return rv;
01736 }
01737 
01738 nsresult
01739 nsMsgDBFolder::GetPurgeThreshold(PRInt32 *aThreshold)
01740 {
01741   NS_ENSURE_ARG(aThreshold);
01742   nsresult rv;
01743   nsCOMPtr<nsIPrefBranch> prefBranch = do_GetService(NS_PREFSERVICE_CONTRACTID, &rv);
01744   if (NS_SUCCEEDED(rv) && prefBranch)
01745   {
01746     rv = prefBranch->GetIntPref(PREF_MAIL_PURGE_THRESHOLD, aThreshold);
01747     if (NS_FAILED(rv)) 
01748     {
01749       *aThreshold = 0;
01750       rv = NS_OK;
01751     }
01752   }
01753   return rv;
01754 }
01755 
01756 NS_IMETHODIMP //called on the folder that is renamed or about to be deleted
01757 nsMsgDBFolder::MatchOrChangeFilterDestination(nsIMsgFolder *newFolder, PRBool caseInsensitive, PRBool *found)
01758 {
01759   nsresult rv = NS_OK;
01760   nsXPIDLCString oldUri;
01761   rv = GetURI(getter_Copies(oldUri));
01762   NS_ENSURE_SUCCESS(rv,rv);
01763   
01764   nsXPIDLCString newUri;
01765   if (newFolder) //for matching uri's this will be null
01766   {
01767     rv = newFolder->GetURI(getter_Copies(newUri));
01768     NS_ENSURE_SUCCESS(rv,rv);
01769   }
01770   
01771   nsCOMPtr<nsIMsgFilterList> filterList;
01772   nsCOMPtr<nsIMsgAccountManager> accountMgr = do_GetService(NS_MSGACCOUNTMANAGER_CONTRACTID, &rv);
01773   if (NS_SUCCEEDED(rv))
01774   {
01775     nsCOMPtr<nsISupportsArray> allServers;
01776     rv = accountMgr->GetAllServers(getter_AddRefs(allServers));
01777     if (NS_SUCCEEDED(rv) && allServers)
01778     {
01779       PRUint32 numServers;
01780       rv = allServers->Count(&numServers);
01781       for (PRUint32 serverIndex=0; serverIndex < numServers; serverIndex++)
01782       {
01783         nsCOMPtr <nsIMsgIncomingServer> server =
01784           do_QueryElementAt(allServers, serverIndex, &rv);
01785         if (server && NS_SUCCEEDED(rv))
01786         {
01787           PRBool canHaveFilters;
01788           rv = server->GetCanHaveFilters(&canHaveFilters);
01789           if (NS_SUCCEEDED(rv) && canHaveFilters) 
01790           {
01791             rv = server->GetFilterList(nsnull, getter_AddRefs(filterList));
01792             if (filterList && NS_SUCCEEDED(rv))
01793             {
01794               rv = filterList->MatchOrChangeFilterTarget(oldUri, newUri, caseInsensitive, found);
01795               if (found && newFolder && newUri)
01796                 rv = filterList->SaveToDefaultFile();
01797             }
01798           }
01799         }
01800       }
01801     }
01802   }
01803 
01804   return rv;
01805 }
01806 
01807 NS_IMETHODIMP
01808 nsMsgDBFolder::GetDBTransferInfo(nsIDBFolderInfo **aTransferInfo)
01809 {
01810   nsCOMPtr <nsIDBFolderInfo> dbFolderInfo;
01811   nsCOMPtr <nsIMsgDatabase> db;
01812   GetDBFolderInfoAndDB(getter_AddRefs(dbFolderInfo), getter_AddRefs(db));
01813   if (dbFolderInfo)
01814     dbFolderInfo->GetTransferInfo(aTransferInfo);
01815   return NS_OK;
01816 }
01817 
01818 NS_IMETHODIMP
01819 nsMsgDBFolder::SetDBTransferInfo(nsIDBFolderInfo *aTransferInfo)
01820 {
01821   NS_ENSURE_ARG(aTransferInfo);
01822   nsCOMPtr <nsIDBFolderInfo> dbFolderInfo;
01823   nsCOMPtr <nsIMsgDatabase> db;
01824   GetMsgDatabase(nsnull, getter_AddRefs(db));
01825   if (db)
01826   {
01827     db->GetDBFolderInfo(getter_AddRefs(dbFolderInfo));
01828     if(dbFolderInfo)
01829       dbFolderInfo->InitFromTransferInfo(aTransferInfo);
01830     db->SetSummaryValid(PR_TRUE);
01831   }
01832   return NS_OK;
01833 }
01834 
01835 NS_IMETHODIMP
01836 nsMsgDBFolder::GetStringProperty(const char *propertyName, char **propertyValue)
01837 {
01838   NS_ENSURE_ARG_POINTER(propertyName);
01839   NS_ENSURE_ARG_POINTER(propertyValue);
01840   nsCOMPtr <nsIFileSpec> dbPath;
01841   nsresult rv = GetFolderCacheKey(getter_AddRefs(dbPath));
01842   if (dbPath)
01843   {
01844     nsCOMPtr <nsIMsgFolderCacheElement> cacheElement;
01845     rv = GetFolderCacheElemFromFileSpec(dbPath, getter_AddRefs(cacheElement));
01846     if (cacheElement)  //try to get from cache
01847       rv = cacheElement->GetStringProperty(propertyName, propertyValue);
01848     if (NS_FAILED(rv))  //if failed, then try to get from db
01849     {
01850       nsCOMPtr<nsIDBFolderInfo> folderInfo;
01851       nsCOMPtr<nsIMsgDatabase> db; 
01852       PRBool exists;
01853       rv = dbPath->Exists(&exists);
01854       if (NS_FAILED(rv) || !exists)
01855         return NS_MSG_ERROR_FOLDER_MISSING;
01856       rv = GetDBFolderInfoAndDB(getter_AddRefs(folderInfo), getter_AddRefs(db));
01857       if (NS_SUCCEEDED(rv))
01858         rv = folderInfo->GetCharPtrProperty(propertyName, propertyValue);
01859     }
01860   }
01861   return rv;
01862 }
01863 
01864 NS_IMETHODIMP
01865 nsMsgDBFolder::SetStringProperty(const char *propertyName, const char *propertyValue)
01866 {
01867   NS_ENSURE_ARG_POINTER(propertyName);
01868   NS_ENSURE_ARG_POINTER(propertyValue);
01869 
01870   nsCOMPtr <nsIFileSpec> dbPath;
01871   GetFolderCacheKey(getter_AddRefs(dbPath));
01872   if (dbPath)
01873   {
01874     nsCOMPtr <nsIMsgFolderCacheElement> cacheElement;
01875     GetFolderCacheElemFromFileSpec(dbPath, getter_AddRefs(cacheElement));
01876     if (cacheElement)  //try to set in the cache
01877       cacheElement->SetStringProperty(propertyName, propertyValue);
01878   }  
01879   nsCOMPtr<nsIDBFolderInfo> folderInfo;
01880   nsCOMPtr<nsIMsgDatabase> db; 
01881   nsresult rv = GetDBFolderInfoAndDB(getter_AddRefs(folderInfo), getter_AddRefs(db));
01882   if(NS_SUCCEEDED(rv))
01883   {
01884     folderInfo->SetCharPtrProperty(propertyName, propertyValue);
01885     db->Commit(nsMsgDBCommitType::kLargeCommit);  //commiting the db also commits the cache
01886   }
01887   return NS_OK;
01888 }
01889 
01890 // sub-classes need to override
01891 nsresult
01892 nsMsgDBFolder::SpamFilterClassifyMessage(const char *aURI, nsIMsgWindow *aMsgWindow, nsIJunkMailPlugin *aJunkMailPlugin)
01893 {
01894   return aJunkMailPlugin->ClassifyMessage(aURI, aMsgWindow, nsnull);   
01895 }
01896 
01897 nsresult
01898 nsMsgDBFolder::SpamFilterClassifyMessages(const char **aURIArray, PRUint32 aURICount, nsIMsgWindow *aMsgWindow, nsIJunkMailPlugin *aJunkMailPlugin)
01899 {
01900   return aJunkMailPlugin->ClassifyMessages(aURICount, aURIArray, aMsgWindow, nsnull);   
01901 }
01902 
01906 NS_IMETHODIMP
01907 nsMsgDBFolder::CallFilterPlugins(nsIMsgWindow *aMsgWindow, PRBool *aFiltersRun)
01908 {
01909   NS_ENSURE_ARG_POINTER(aFiltersRun);
01910   *aFiltersRun = PR_FALSE;
01911   nsCOMPtr<nsIMsgIncomingServer> server;
01912   nsCOMPtr<nsISpamSettings> spamSettings;
01913   nsCOMPtr<nsIAbMDBDirectory> whiteListDirectory;
01914   nsCOMPtr<nsIMsgHeaderParser> headerParser;
01915   PRBool useWhiteList = PR_FALSE;
01916   PRInt32 spamLevel = 0;
01917   nsXPIDLCString whiteListAbURI;
01918 
01919   nsresult rv = GetServer(getter_AddRefs(server));
01920   NS_ENSURE_SUCCESS(rv, rv); 
01921   
01922   nsXPIDLCString serverType; 
01923   server->GetType(getter_Copies(serverType));
01924   
01925 
01926   // if this is the junk folder, or the trash folder
01927   // don't analyze for spam, because we don't care
01928   //
01929   // if it's the sent, unsent, templates, or drafts, 
01930   // don't analyze for spam, because the user
01931   // created that message
01932   //
01933   // if it's a public imap folder, or another users
01934   // imap folder, don't analyze for spam, because
01935   // it's not ours to analyze
01936   if ( !(nsCRT::strcmp(serverType.get(), "rss")) || 
01937        (mFlags & (MSG_FOLDER_FLAG_JUNK | MSG_FOLDER_FLAG_TRASH |
01938                MSG_FOLDER_FLAG_SENTMAIL | MSG_FOLDER_FLAG_QUEUE |
01939                MSG_FOLDER_FLAG_DRAFTS | MSG_FOLDER_FLAG_TEMPLATES |
01940                MSG_FOLDER_FLAG_IMAP_PUBLIC | MSG_FOLDER_FLAG_IMAP_OTHER_USER)
01941        && !(mFlags & MSG_FOLDER_FLAG_INBOX)) )
01942     return NS_OK;
01943 
01944 
01945   rv = server->GetSpamSettings(getter_AddRefs(spamSettings));
01946   nsCOMPtr <nsIMsgFilterPlugin> filterPlugin;
01947   server->GetSpamFilterPlugin(getter_AddRefs(filterPlugin));
01948   if (!filterPlugin) // it's not an error not to have the filter plugin.
01949     return NS_OK;
01950   NS_ENSURE_SUCCESS(rv, rv); 
01951 
01952   nsCOMPtr <nsIJunkMailPlugin> junkMailPlugin = do_QueryInterface(filterPlugin);
01953 
01954   if (junkMailPlugin)
01955   {
01956     PRBool userHasClassified = PR_FALSE;
01957     // if the user has not classified any messages yet, then we shouldn't bother
01958     // running the junk mail controls. This creates a better first use experience.
01959     // See Bug #250084.
01960     junkMailPlugin->GetUserHasClassified(&userHasClassified);
01961     if (!userHasClassified)
01962       return NS_OK;
01963   }
01964 
01965   spamSettings->GetLevel(&spamLevel);
01966   if (!spamLevel)
01967     return NS_OK;
01968 
01969   nsCOMPtr<nsIMsgMailSession> mailSession = 
01970       do_GetService(NS_MSGMAILSESSION_CONTRACTID, &rv);
01971   NS_ENSURE_SUCCESS(rv, rv);
01972   
01973   if (!mDatabase) 
01974   {
01975       rv = GetDatabase(nsnull);   // XXX is nsnull a reasonable arg here?
01976       NS_ENSURE_SUCCESS(rv, rv);
01977   }
01978 
01979   // get the list of new messages
01980   //
01981   PRUint32 numNewKeys;
01982   PRUint32 *newKeys;
01983   rv = mDatabase->GetNewList(&numNewKeys, &newKeys);
01984   NS_ENSURE_SUCCESS(rv, rv);
01985 
01986   nsMsgKeyArray newMessageKeys;
01987   if (numNewKeys)
01988     newMessageKeys.Add(newKeys, numNewKeys);
01989 
01990   newMessageKeys.InsertAt(0, &m_saveNewMsgs);
01991   // if there weren't any, just return 
01992   //
01993   if (!newMessageKeys.GetSize()) 
01994       return NS_OK;
01995 
01996   spamSettings->GetUseWhiteList(&useWhiteList);
01997   if (useWhiteList)
01998   {
01999     spamSettings->GetWhiteListAbURI(getter_Copies(whiteListAbURI));
02000     NS_ENSURE_SUCCESS(rv, rv);
02001     if (!whiteListAbURI.IsEmpty())
02002     {
02003       nsCOMPtr <nsIRDFService> rdfService = do_GetService("@mozilla.org/rdf/rdf-service;1",&rv);
02004       NS_ENSURE_SUCCESS(rv, rv);
02005 
02006       nsCOMPtr <nsIRDFResource> resource;
02007       rv = rdfService->GetResource(whiteListAbURI, getter_AddRefs(resource));
02008       NS_ENSURE_SUCCESS(rv, rv);
02009 
02010       whiteListDirectory = do_QueryInterface(resource, &rv);
02011       NS_ENSURE_SUCCESS(rv, rv);
02012     }
02013     // if we can't get the db, we probably want to continue firing spam filters.
02014   }
02015 
02016   nsXPIDLCString trustedMailDomains;
02017   nsCOMPtr<nsIPrefBranch> prefBranch(do_GetService(NS_PREFSERVICE_CONTRACTID, &rv));
02018   if (prefBranch)
02019     prefBranch->GetCharPref("mail.trusteddomains", getter_Copies(trustedMailDomains));
02020 
02021   if (whiteListDirectory || !trustedMailDomains.IsEmpty())
02022   {
02023     headerParser = do_GetService(NS_MAILNEWS_MIME_HEADER_PARSER_CONTRACTID, &rv);
02024     NS_ENSURE_SUCCESS(rv, rv);
02025   }
02026 
02027   // build up list of keys to classify
02028   //
02029   nsXPIDLCString uri;
02030   nsMsgKeyArray keysToClassify;
02031 
02032   PRUint32 numNewMessages = newMessageKeys.GetSize();
02033   for ( PRUint32 i=0 ; i < numNewMessages ; ++i ) 
02034   {
02035       nsXPIDLCString junkScore;
02036       nsCOMPtr <nsIMsgDBHdr> msgHdr;
02037       nsMsgKey msgKey = newMessageKeys.GetAt(i);
02038       rv = mDatabase->GetMsgHdrForKey(msgKey, getter_AddRefs(msgHdr));
02039       if (!NS_SUCCEEDED(rv))
02040         continue;
02041       nsXPIDLCString author;
02042       nsXPIDLCString authorEmailAddress;
02043       if (whiteListDirectory || !trustedMailDomains.IsEmpty())
02044       {
02045         msgHdr->GetAuthor(getter_Copies(author));
02046         rv = headerParser->ExtractHeaderAddressMailboxes(nsnull, author.get(), getter_Copies(authorEmailAddress));
02047       }
02048       
02049       if (!trustedMailDomains.IsEmpty())
02050       {
02051         nsCAutoString domain;
02052         PRInt32 atPos = authorEmailAddress.FindChar('@');
02053         if (atPos >= 0)
02054           authorEmailAddress.Right(domain, authorEmailAddress.Length() - atPos - 1);
02055         if (!domain.IsEmpty() && MsgHostDomainIsTrusted(domain, trustedMailDomains))
02056         {
02057           // mark this msg as non-junk, because we whitelisted it.
02058           mDatabase->SetStringProperty(msgKey, "junkscore", "0");
02059           mDatabase->SetStringProperty(msgKey, "junkscoreorigin", "plugin");
02060           continue; // skip this msg since it's in the white list
02061         }
02062       }
02063       msgHdr->GetStringProperty("junkscore", getter_Copies(junkScore));
02064       if (!junkScore.IsEmpty()) // ignore already scored messages.
02065         continue;
02066     // check whitelist first:
02067       if (whiteListDirectory)
02068       {
02069         if (NS_SUCCEEDED(rv))
02070         {
02071           PRBool cardExists = PR_FALSE;
02072           // don't want to abort the rest of the scoring.
02073           if (!authorEmailAddress.IsEmpty())
02074             rv = whiteListDirectory->HasCardForEmailAddress(authorEmailAddress, &cardExists);
02075           if (NS_SUCCEEDED(rv) && cardExists)
02076           {
02077             // mark this msg as non-junk, because we whitelisted it.
02078             mDatabase->SetStringProperty(msgKey, "junkscore", "0");
02079             mDatabase->SetStringProperty(msgKey, "junkscoreorigin", "plugin");
02080             continue; // skip this msg since it's in the white list
02081           }
02082         }
02083       }
02084 
02085       keysToClassify.Add(newMessageKeys.GetAt(i));
02086 
02087   }
02088 
02089   if (keysToClassify.GetSize() > 0)
02090   {
02091     PRUint32 numMessagesToClassify = keysToClassify.GetSize();
02092     char ** messageURIs = (char **) PR_MALLOC(sizeof(const char *) * numMessagesToClassify);
02093     if (!messageURIs)
02094       return NS_ERROR_OUT_OF_MEMORY;
02095 
02096     for ( PRUint32 msgIndex=0 ; msgIndex < numMessagesToClassify ; ++msgIndex ) 
02097     {
02098         // generate a URI for the message
02099         //
02100         rv = GenerateMessageURI(keysToClassify.GetAt(msgIndex), &messageURIs[msgIndex]);
02101         if (NS_FAILED(rv)) 
02102             NS_WARNING("nsMsgDBFolder::CallFilterPlugins(): could not"
02103                        " generate URI for message");
02104     }
02105     // filterMsgs
02106     //
02107     *aFiltersRun = PR_TRUE;
02108     rv = SpamFilterClassifyMessages((const char **) messageURIs, numMessagesToClassify, aMsgWindow, junkMailPlugin); 
02109 
02110     for ( PRUint32 freeIndex=0 ; freeIndex < numMessagesToClassify ; ++freeIndex ) 
02111       PR_Free(messageURIs[freeIndex]);
02112     PR_Free(messageURIs);
02113 
02114   }
02115   m_saveNewMsgs.RemoveAll();
02116   return rv;
02117 }
02118 
02119 NS_IMETHODIMP
02120 nsMsgDBFolder::GetLastMessageLoaded(nsMsgKey *aMsgKey) 
02121 {
02122   NS_ENSURE_ARG_POINTER(aMsgKey);
02123   *aMsgKey = mLastMessageLoaded;
02124   return NS_OK;
02125 }
02126 
02127 NS_IMETHODIMP
02128 nsMsgDBFolder::SetLastMessageLoaded(nsMsgKey aMsgKey)
02129 {
02130   mLastMessageLoaded = aMsgKey;
02131   return NS_OK;
02132 }
02133 
02134 nsresult nsMsgDBFolder::PromptForCachePassword(nsIMsgIncomingServer *server, nsIMsgWindow *aWindow, PRBool &passwordCorrect)
02135 {
02136   PRBool userDidntCancel;
02137   passwordCorrect = PR_FALSE;
02138   nsCOMPtr <nsIStringBundle> bundle;
02139   nsresult rv = GetBaseStringBundle(getter_AddRefs(bundle));
02140   NS_ENSURE_SUCCESS(rv, rv);
02141   nsXPIDLCString hostName;
02142   nsXPIDLCString userName;
02143   nsXPIDLString passwordTemplate;
02144   nsXPIDLCString password;
02145   nsXPIDLString passwordTitle;
02146   nsXPIDLString passwordPromptString;
02147 
02148   server->GetRealHostName(getter_Copies(hostName));
02149   server->GetRealUsername(getter_Copies(userName));
02150   bundle->GetStringFromName(NS_LITERAL_STRING("passwordTitle").get(), getter_Copies(passwordTitle));
02151   bundle->GetStringFromName(NS_LITERAL_STRING("passwordPrompt").get(), getter_Copies(passwordTemplate));
02152 
02153   NS_ConvertASCIItoUCS2 userNameStr(userName);
02154   NS_ConvertASCIItoUCS2 hostNameStr(hostName);
02155 
02156   const PRUnichar *stringParams[2] = { userNameStr.get(), hostNameStr.get() };
02157 
02158   rv = bundle->FormatStringFromName(
02159         NS_LITERAL_STRING("passwordPrompt").get(), stringParams, 2, 
02160         getter_Copies(passwordPromptString ));
02161   NS_ENSURE_SUCCESS(rv, rv);
02162 
02163   do
02164   {
02165     rv = server->GetPasswordWithUI(passwordPromptString,
02166                                    passwordTitle, 
02167                                    aWindow,
02168                                    &userDidntCancel,
02169                                    getter_Copies(password));
02170     if (rv != NS_MSG_PASSWORD_PROMPT_CANCELLED && !password.IsEmpty()) 
02171     {
02172       nsCOMPtr <nsIPasswordManagerInternal> passwordMgrInt = do_GetService(NS_PASSWORDMANAGER_CONTRACTID, &rv);
02173       if(passwordMgrInt) 
02174       {
02175 
02176         // Get the current server URI
02177         nsXPIDLCString currServerUri;
02178         rv = server->GetServerURI(getter_Copies(currServerUri));
02179         NS_ENSURE_SUCCESS(rv, rv);
02180 
02181         currServerUri.Insert('x', 0);
02182         nsCAutoString hostFound;
02183         nsAutoString userNameFound;
02184         nsAutoString passwordFound;
02185 
02186         const nsAFlatString& empty = EmptyString();
02187 
02188         // Get password entry corresponding to the host URI we are passing in.
02189         rv = passwordMgrInt->FindPasswordEntry(currServerUri, empty, empty,
02190                                                hostFound, userNameFound,
02191                                                passwordFound);
02192         if (NS_FAILED(rv)) 
02193           break;
02194         // compare the user-entered password with the saved password with
02195         // the munged uri.
02196         passwordCorrect = password.Equals(NS_ConvertUCS2toUTF8(passwordFound).get());
02197         if (!passwordCorrect)
02198           server->SetPassword("");
02199         else
02200         {
02201           nsCOMPtr<nsIMsgAccountManager> accountManager = do_GetService(NS_MSGACCOUNTMANAGER_CONTRACTID);
02202           if (accountManager)
02203             accountManager->SetUserNeedsToAuthenticate(PR_FALSE);
02204         }
02205       }
02206     }
02207   }
02208   while (NS_SUCCEEDED(rv) && rv != NS_MSG_PASSWORD_PROMPT_CANCELLED && userDidntCancel && !passwordCorrect);
02209   return (!passwordCorrect) ? NS_ERROR_FAILURE : rv;
02210 }
02211 
02212 // this gets called after the last junk mail classification has run.
02213 nsresult nsMsgDBFolder::PerformBiffNotifications(void)
02214 {
02215   nsCOMPtr<nsIMsgIncomingServer> server;
02216   nsresult rv = GetServer(getter_AddRefs(server));
02217   NS_ENSURE_SUCCESS(rv, rv);
02218   PRInt32  numBiffMsgs = 0;
02219   nsCOMPtr<nsIMsgFolder> root;
02220   rv = GetRootFolder(getter_AddRefs(root));
02221   root->GetNumNewMessages(PR_TRUE, &numBiffMsgs);
02222   if (numBiffMsgs > 0) 
02223   {
02224     server->SetPerformingBiff(true);
02225     SetBiffState(nsIMsgFolder::nsMsgBiffState_NewMail);
02226     server->SetPerformingBiff(false);
02227   }
02228   return NS_OK;
02229 }
02230 
02231 nsresult
02232 nsMsgDBFolder::initializeStrings()
02233 {
02234   nsresult rv;
02235   nsCOMPtr<nsIStringBundleService> bundleService =
02236       do_GetService(NS_STRINGBUNDLE_CONTRACTID, &rv);
02237   NS_ENSURE_SUCCESS(rv, rv);
02238   nsCOMPtr<nsIStringBundle> bundle;
02239   rv = bundleService->CreateBundle("chrome://messenger/locale/messenger.properties",
02240                                    getter_AddRefs(bundle));
02241   NS_ENSURE_SUCCESS(rv, rv);
02242 
02243   bundle->GetStringFromName(NS_LITERAL_STRING("inboxFolderName").get(),
02244                             &kLocalizedInboxName);
02245   bundle->GetStringFromName(NS_LITERAL_STRING("trashFolderName").get(),
02246                             &kLocalizedTrashName);
02247   bundle->GetStringFromName(NS_LITERAL_STRING("sentFolderName").get(),
02248                             &kLocalizedSentName);
02249   bundle->GetStringFromName(NS_LITERAL_STRING("draftsFolderName").get(),
02250                             &kLocalizedDraftsName);
02251   bundle->GetStringFromName(NS_LITERAL_STRING("templatesFolderName").get(),
02252                             &kLocalizedTemplatesName);
02253   bundle->GetStringFromName(NS_LITERAL_STRING("junkFolderName").get(),
02254                             &kLocalizedJunkName);
02255   bundle->GetStringFromName(NS_LITERAL_STRING("unsentFolderName").get(),
02256                             &kLocalizedUnsentName);
02257 
02258   nsCOMPtr<nsIStringBundle> brandBundle;
02259   rv = bundleService->CreateBundle("chrome://branding/locale/brand.properties", getter_AddRefs(bundle));
02260   NS_ENSURE_SUCCESS(rv, rv);
02261 
02262   bundle->GetStringFromName(NS_LITERAL_STRING("brandShortName").get(),
02263                             &kLocalizedBrandShortName);
02264 
02265   return NS_OK;
02266 }
02267 
02268 nsresult
02269 nsMsgDBFolder::createCollationKeyGenerator()
02270 {
02271   nsresult rv = NS_OK;
02272 
02273   nsCOMPtr<nsILocaleService> localeSvc = do_GetService(NS_LOCALESERVICE_CONTRACTID,&rv);
02274   NS_ENSURE_SUCCESS(rv, rv);
02275 
02276   nsCOMPtr<nsILocale> locale;
02277   rv = localeSvc->GetApplicationLocale(getter_AddRefs(locale));
02278   NS_ENSURE_SUCCESS(rv, rv);
02279 
02280   nsCOMPtr <nsICollationFactory> factory = do_CreateInstance(kCollationFactoryCID, &rv);
02281   NS_ENSURE_SUCCESS(rv, rv);
02282 
02283   rv = factory->CreateCollation(locale, &gCollationKeyGenerator);
02284   return NS_OK;
02285 }
02286 
02287 NS_IMETHODIMP
02288 nsMsgDBFolder::Init(const char* aURI)
02289 {
02290   // for now, just initialize everything during Init()
02291 
02292   nsresult rv;
02293 
02294   rv = nsRDFResource::Init(aURI);
02295   if (NS_FAILED(rv))
02296     return rv;
02297 
02298   rv = CreateBaseMessageURI(aURI);
02299 
02300   return NS_OK;
02301 }
02302 
02303 nsresult nsMsgDBFolder::CreateBaseMessageURI(const char *aURI)
02304 {
02305   // Each folder needs to implement this.
02306   return NS_OK;
02307 }
02308 
02309   // nsISerializable methods:
02310 NS_IMETHODIMP
02311 nsMsgDBFolder::Read(nsIObjectInputStream *aStream)
02312 {
02313   NS_NOTREACHED("nsMsgDBFolder::Read");
02314   return NS_ERROR_NOT_IMPLEMENTED;
02315 }
02316 
02317 NS_IMETHODIMP
02318 nsMsgDBFolder::Write(nsIObjectOutputStream *aStream)
02319 {
02320   NS_NOTREACHED("nsMsgDBFolder::Write");
02321   return NS_ERROR_NOT_IMPLEMENTED;
02322 }
02323 
02324   // nsICollection methods:
02325 NS_IMETHODIMP
02326 nsMsgDBFolder::Count(PRUint32 *result)
02327 {
02328   return mSubFolders->Count(result);
02329 }
02330 
02331 NS_IMETHODIMP
02332 nsMsgDBFolder::GetElementAt(PRUint32 i, nsISupports* *result)
02333 {
02334   return mSubFolders->GetElementAt(i, result);
02335 }
02336 
02337 NS_IMETHODIMP
02338 nsMsgDBFolder::QueryElementAt(PRUint32 i, const nsIID & iid, void * *result) 
02339 {
02340   return mSubFolders->QueryElementAt(i, iid, result);
02341 }
02342 
02343 NS_IMETHODIMP
02344 nsMsgDBFolder::SetElementAt(PRUint32 i, nsISupports* value) 
02345 {
02346   return mSubFolders->SetElementAt(i, value);
02347 }
02348 
02349 NS_IMETHODIMP
02350 nsMsgDBFolder::AppendElement(nsISupports *aElement) 
02351 {
02352   return mSubFolders->AppendElement(aElement);
02353 }
02354 
02355 NS_IMETHODIMP
02356 nsMsgDBFolder::RemoveElement(nsISupports *aElement) 
02357 {
02358   return mSubFolders->RemoveElement(aElement);
02359 }
02360 
02361 NS_IMETHODIMP
02362 nsMsgDBFolder::Enumerate(nsIEnumerator* *result) 
02363 {
02364   // nsMsgDBFolders only have subfolders, no message elements
02365   return mSubFolders->Enumerate(result);
02366 }
02367 
02368 NS_IMETHODIMP
02369 nsMsgDBFolder::Clear(void) 
02370 {
02371   return mSubFolders->Clear();
02372 }
02373 
02374 NS_IMETHODIMP
02375 nsMsgDBFolder::GetURI(char* *name) 
02376 {
02377   return nsRDFResource::GetValue(name);
02378 }
02379 
02380 
02382 
02383 typedef PRBool
02384 (*nsArrayFilter)(nsISupports* element, void* data);
02385 
02387 
02388 
02389 NS_IMETHODIMP
02390 nsMsgDBFolder::GetSubFolders(nsIEnumerator* *result)
02391 {
02392   return mSubFolders->Enumerate(result);
02393 }
02394 
02395 NS_IMETHODIMP
02396 nsMsgDBFolder::FindSubFolder(const nsACString& aEscapedSubFolderName, nsIMsgFolder **aFolder)
02397 {
02398   nsresult rv = NS_OK;
02399   nsCOMPtr<nsIRDFService> rdf(do_GetService(kRDFServiceCID, &rv));
02400 
02401   if (NS_FAILED(rv))
02402     return rv;
02403 
02404   // XXX use necko here
02405   nsCAutoString uri;
02406   uri.Append(mURI);
02407   uri.Append('/');
02408   uri.Append(aEscapedSubFolderName);
02409 
02410   nsCOMPtr<nsIRDFResource> res;
02411   rv = rdf->GetResource(uri, getter_AddRefs(res));
02412   if (NS_FAILED(rv))
02413     return rv;
02414 
02415   nsCOMPtr<nsIMsgFolder> folder(do_QueryInterface(res, &rv));
02416   if (NS_FAILED(rv))
02417     return rv;
02418   if (!aFolder)
02419     return NS_ERROR_UNEXPECTED;
02420 
02421   *aFolder = folder;
02422   NS_ADDREF(*aFolder);
02423   return NS_OK;
02424 }
02425 
02426 NS_IMETHODIMP
02427 nsMsgDBFolder::GetHasSubFolders(PRBool *_retval)
02428 {
02429   PRUint32 cnt;
02430   nsresult rv = mSubFolders->Count(&cnt);
02431   if (NS_FAILED(rv)) return rv;
02432   *_retval = (cnt > 0);
02433   return NS_OK;
02434 }
02435 
02436 NS_IMETHODIMP nsMsgDBFolder::AddFolderListener(nsIFolderListener * listener)
02437 {
02438   return mListeners.AppendElement(listener) ? NS_OK : NS_ERROR_OUT_OF_MEMORY;
02439 }
02440 
02441 NS_IMETHODIMP nsMsgDBFolder::RemoveFolderListener(nsIFolderListener * listener)
02442 {
02443 
02444   mListeners.RemoveElement(listener);
02445   return NS_OK;
02446 
02447 }
02448 
02449 NS_IMETHODIMP nsMsgDBFolder::SetParent(nsIMsgFolder *aParent)
02450 {
02451   mParent = do_GetWeakReference(aParent);
02452 
02453   if (aParent) 
02454   {
02455     nsresult rv;
02456     nsCOMPtr<nsIMsgFolder> parentMsgFolder = do_QueryInterface(aParent, &rv);
02457 
02458     if (NS_SUCCEEDED(rv)) 
02459     {
02460 
02461       // servers do not have parents, so we must not be a server
02462       mIsServer = PR_FALSE;
02463       mIsServerIsValid = PR_TRUE;
02464 
02465       // also set the server itself while we're here.
02466 
02467       nsCOMPtr<nsIMsgIncomingServer> server;
02468       rv = parentMsgFolder->GetServer(getter_AddRefs(server));
02469       if (NS_SUCCEEDED(rv) && server)
02470         mServer = do_GetWeakReference(server);
02471     }
02472   }
02473 
02474   return NS_OK;
02475 }
02476 
02477 
02478 NS_IMETHODIMP nsMsgDBFolder::GetParent(nsIMsgFolder **aParent)
02479 {
02480   NS_ENSURE_ARG_POINTER(aParent);
02481 
02482   nsCOMPtr<nsIMsgFolder> parent = do_QueryReferent(mParent);
02483 
02484   *aParent = parent;
02485   NS_IF_ADDREF(*aParent);
02486   return NS_OK;
02487 }
02488 
02489 NS_IMETHODIMP nsMsgDBFolder::GetParentMsgFolder(nsIMsgFolder **aParentMsgFolder)
02490 {
02491   NS_ENSURE_ARG_POINTER(aParentMsgFolder);
02492 
02493   nsCOMPtr<nsIMsgFolder> parent = do_QueryReferent(mParent);
02494 
02495   NS_IF_ADDREF(*aParentMsgFolder = parent);
02496   return NS_OK;
02497 }
02498 
02499 NS_IMETHODIMP
02500 nsMsgDBFolder::GetMessages(nsIMsgWindow *aMsgWindow, nsISimpleEnumerator* *result)
02501 {
02502   // XXX should this return an empty enumeration?
02503   return NS_ERROR_FAILURE;
02504 }
02505 
02506 NS_IMETHODIMP
02507 nsMsgDBFolder::UpdateFolder(nsIMsgWindow *)
02508 {
02509   return NS_OK;
02510 }
02511 
02513 
02514 NS_IMETHODIMP nsMsgDBFolder::GetFolderURL(char **url)
02515 {
02516   NS_ENSURE_ARG_POINTER(url);
02517   *url = nsnull;
02518   return NS_OK;
02519 }
02520 
02521 
02522 NS_IMETHODIMP nsMsgDBFolder::GetServer(nsIMsgIncomingServer ** aServer)
02523 {
02524   NS_ENSURE_ARG_POINTER(aServer);
02525 
02526   nsresult rv;
02527 
02528   // short circut the server if we have it.
02529   nsCOMPtr<nsIMsgIncomingServer> server = do_QueryReferent(mServer, &rv);
02530   if (NS_FAILED(rv) || !server) 
02531   {
02532     // try again after parsing the URI
02533     rv = parseURI(PR_TRUE);
02534     server = do_QueryReferent(mServer);
02535   }
02536 
02537   *aServer = server;
02538   NS_IF_ADDREF(*aServer);
02539 
02540   return (server) ? NS_OK : NS_ERROR_NULL_POINTER;
02541 }
02542 
02543 #ifdef MSG_FASTER_URI_PARSING
02544 class nsMsgAutoBool {
02545 public:
02546   nsMsgAutoBool() : mValue(nsnull) {}
02547   void autoReset(PRBool *aValue) { mValue = aValue; }
02548   ~nsMsgAutoBool() { if (mValue) *mValue = PR_FALSE; }
02549 private:
02550   PRBool *mValue;
02551 };
02552 #endif
02553 
02554 nsresult
02555 nsMsgDBFolder::parseURI(PRBool needServer)
02556 {
02557   nsresult rv;
02558   nsCOMPtr<nsIURL> url;
02559 
02560 #ifdef MSG_FASTER_URI_PARSING
02561   nsMsgAutoBool parsingUrlState;
02562   if (mParsingURLInUse) 
02563   {
02564     url = do_CreateInstance(NS_STANDARDURL_CONTRACTID, &rv);
02565   }
02566   else 
02567   {
02568     url = mParsingURL;
02569     mParsingURLInUse = PR_TRUE;
02570     parsingUrlState.autoReset(&mParsingURLInUse);
02571   }
02572 
02573 #else
02574   url = do_CreateInstance(NS_STANDARDURL_CONTRACTID, &rv);
02575   if (NS_FAILED(rv)) return rv;
02576 #endif
02577 
02578   rv = url->SetSpec(nsDependentCString(mURI));
02579   if (NS_FAILED(rv)) return rv;
02580 
02581   //
02582   // pull some info out of the URI
02583   //
02584 
02585   // empty path tells us it's a server.
02586   if (!mIsServerIsValid) 
02587   {
02588     nsCAutoString path;
02589     rv = url->GetPath(path);
02590     if (NS_SUCCEEDED(rv)) 
02591     {
02592       if (!strcmp(path.get(), "/"))
02593         mIsServer = PR_TRUE;
02594       else
02595         mIsServer = PR_FALSE;
02596     }
02597     mIsServerIsValid = PR_TRUE;
02598   }
02599 
02600   // grab the name off the leaf of the server
02601   if (mName.IsEmpty()) 
02602   {
02603     // mName:
02604     // the name is the trailing directory in the path
02605     nsCAutoString fileName;
02606     url->GetFileName(fileName);
02607     if (!fileName.IsEmpty()) 
02608     {
02609       // XXX conversion to unicode here? is fileName in UTF8?
02610       // yes, let's say it is in utf8
02611       NS_UnescapeURL((char *)fileName.get());
02612       NS_ASSERTION(IsUTF8(fileName), "fileName is not in UTF-8");
02613       CopyUTF8toUTF16(fileName, mName);
02614     }
02615   }
02616 
02617   // grab the server by parsing the URI and looking it up
02618   // in the account manager...
02619   // But avoid this extra work by first asking the parent, if any
02620 
02621   nsCOMPtr<nsIMsgIncomingServer> server = do_QueryReferent(mServer, &rv);
02622   if (NS_FAILED(rv) || !server) 
02623   {
02624     // first try asking the parent instead of the URI
02625     nsCOMPtr<nsIMsgFolder> parentMsgFolder;
02626     rv = GetParentMsgFolder(getter_AddRefs(parentMsgFolder));
02627 
02628     if (NS_SUCCEEDED(rv) && parentMsgFolder)
02629       rv = parentMsgFolder->GetServer(getter_AddRefs(server));
02630 
02631     // no parent. do the extra work of asking
02632     if (!server && needServer) 
02633     {
02634       nsCOMPtr<nsIMsgAccountManager> accountManager =
02635                do_GetService(NS_MSGACCOUNTMANAGER_CONTRACTID, &rv);
02636       if (NS_FAILED(rv)) return rv;
02637 
02638       url->SetScheme(nsDependentCString(GetIncomingServerType()));
02639       rv = accountManager->FindServerByURI(url, PR_FALSE,
02640                                       getter_AddRefs(server));
02641 
02642       if (NS_FAILED(rv)) return rv;
02643 
02644     }
02645 
02646     mServer = do_GetWeakReference(server);
02647 
02648   } /* !mServer */
02649 
02650   // now try to find the local path for this folder
02651   if (server) 
02652   {
02653     nsCAutoString newPath;
02654 
02655     nsCAutoString urlPath;
02656     url->GetFilePath(urlPath);
02657     if (!urlPath.IsEmpty()) 
02658     {
02659       NS_UnescapeURL((char *) urlPath.get());
02660 
02661       // transform the filepath from the URI, such as
02662       // "/folder1/folder2/foldern"
02663       // to
02664       // "folder1.sbd/folder2.sbd/foldern"
02665       // (remove leading / and add .sbd to first n-1 folders)
02666       // to be appended onto the server's path
02667       
02668       PRBool isNewsFolder = PR_FALSE;
02669       nsCAutoString scheme;
02670       if (NS_SUCCEEDED(url->GetScheme(scheme)))
02671       {
02672         isNewsFolder = scheme.EqualsLiteral("news") ||  
02673                        scheme.EqualsLiteral("snews") ||  
02674                        scheme.EqualsLiteral("nntp");
02675       }
02676 
02677       NS_MsgCreatePathStringFromFolderURI(urlPath.get(), newPath, isNewsFolder);
02678     }
02679 
02680     // now append munged path onto server path
02681     nsCOMPtr<nsIFileSpec> serverPath;
02682     rv = server->GetLocalPath(getter_AddRefs(serverPath));
02683     if (NS_FAILED(rv)) return rv;
02684 
02685     if (serverPath) 
02686     {
02687       if (!newPath.IsEmpty())
02688       {
02689         rv = serverPath->AppendRelativeUnixPath(newPath.get());
02690         NS_ASSERTION(NS_SUCCEEDED(rv),"failed to append to the serverPath");
02691         if (NS_FAILED(rv)) 
02692         {
02693           mPath = nsnull;
02694           return rv;
02695         }
02696       }
02697       mPath = serverPath;
02698     }
02699 
02700     // URI is completely parsed when we've attempted to get the server
02701     mHaveParsedURI=PR_TRUE;
02702   }
02703 
02704   return NS_OK;
02705 }
02706 
02707 NS_IMETHODIMP
02708 nsMsgDBFolder::GetIsServer(PRBool *aResult)
02709 {
02710   NS_ENSURE_ARG_POINTER(aResult);
02711 
02712   // make sure we've parsed the URI
02713   if (!mIsServerIsValid) 
02714   {
02715     nsresult rv = parseURI();
02716     if (NS_FAILED(rv) || !mIsServerIsValid)
02717       return NS_ERROR_FAILURE;
02718   }
02719 
02720   *aResult = mIsServer;
02721   return NS_OK;
02722 }
02723 
02724 NS_IMETHODIMP
02725 nsMsgDBFolder::GetNoSelect(PRBool *aResult)
02726 {
02727   NS_ENSURE_ARG_POINTER(aResult);
02728   *aResult = PR_FALSE;
02729   return NS_OK;
02730 }
02731 
02732 NS_IMETHODIMP
02733 nsMsgDBFolder::GetImapShared(PRBool *aResult)
02734 {
02735   NS_ENSURE_ARG_POINTER(aResult);
02736   return GetFlag(MSG_FOLDER_FLAG_PERSONAL_SHARED, aResult);
02737 }
02738 
02739 NS_IMETHODIMP
02740 nsMsgDBFolder::GetCanSubscribe(PRBool *aResult)
02741 {
02742   NS_ENSURE_ARG_POINTER(aResult);
02743   // by default, you can't subscribe.
02744   // if otherwise, override it.
02745   *aResult = PR_FALSE;
02746   return NS_OK;
02747 }
02748 
02749 NS_IMETHODIMP
02750 nsMsgDBFolder::GetCanFileMessages(PRBool *aResult)
02751 {
02752   NS_ENSURE_ARG_POINTER(aResult);
02753 
02754   //varada - checking folder flag to see if it is the "Unsent Messages"
02755   //and if so return FALSE
02756   if (mFlags & (MSG_FOLDER_FLAG_QUEUE | MSG_FOLDER_FLAG_VIRTUAL))
02757   {
02758     *aResult = PR_FALSE;
02759     return NS_OK;
02760   }
02761 
02762   PRBool isServer = PR_FALSE;
02763   nsresult rv = GetIsServer(&isServer);
02764   if (NS_FAILED(rv)) return rv;
02765 
02766   // by default, you can't file messages into servers, only to folders
02767   // if otherwise, override it.
02768   *aResult = !isServer;
02769   return NS_OK;
02770 }
02771 
02772 NS_IMETHODIMP
02773 nsMsgDBFolder::GetCanDeleteMessages(PRBool *aResult)
02774 {
02775   NS_ENSURE_ARG_POINTER(aResult);
02776   
02777   *aResult = PR_TRUE;
02778   return NS_OK;
02779 }
02780 
02781 NS_IMETHODIMP
02782 nsMsgDBFolder::GetCanCreateSubfolders(PRBool *aResult)
02783 {
02784   NS_ENSURE_ARG_POINTER(aResult);
02785 
02786   //Checking folder flag to see if it is the "Unsent Messages"
02787   //or a virtual folder, and if so return FALSE
02788   if (mFlags & (MSG_FOLDER_FLAG_QUEUE | MSG_FOLDER_FLAG_VIRTUAL))
02789   {
02790     *aResult = PR_FALSE;
02791     return NS_OK;
02792   }
02793 
02794   // by default, you can create subfolders on server and folders
02795   // if otherwise, override it.
02796   *aResult = PR_TRUE;
02797   return NS_OK;
02798 }
02799 
02800 NS_IMETHODIMP
02801 nsMsgDBFolder::GetCanRename(PRBool *aResult)
02802 {
02803   NS_ENSURE_ARG_POINTER(aResult);
02804 
02805   PRBool isServer = PR_FALSE;
02806   nsresult rv = GetIsServer(&isServer);
02807   if (NS_FAILED(rv)) return rv;
02808 
02809   // by default, you can't rename servers, only folders
02810   // if otherwise, override it.
02811   if (isServer) 
02812   {
02813     *aResult = PR_FALSE;
02814   }
02815   //
02816   // check if the folder is a special folder
02817   // (Trash, Drafts, Unsent Messages, Inbox, Sent, Templates, Junk)
02818   // if it is, don't allow the user to rename it
02819   // (which includes dnd moving it with in the same server)
02820   //
02821   // this errors on the side of caution.  we'll return false a lot
02822   // more often if we use flags,
02823   // instead of checking if the folder really is being used as a
02824   // special folder by looking at the "copies and folders" prefs on the
02825   // identities.
02826   //
02827   // one day...
02828   else if (mFlags & MSG_FOLDER_FLAG_TRASH ||
02829            mFlags & MSG_FOLDER_FLAG_DRAFTS ||
02830            mFlags & MSG_FOLDER_FLAG_QUEUE ||
02831            mFlags & MSG_FOLDER_FLAG_INBOX ||
02832            mFlags & MSG_FOLDER_FLAG_SENTMAIL ||
02833            mFlags & MSG_FOLDER_FLAG_TEMPLATES ||
02834            mFlags & MSG_FOLDER_FLAG_JUNK) 
02835   {
02836     *aResult = PR_FALSE;
02837   }
02838   else 
02839   {
02840     *aResult = PR_TRUE;
02841   }
02842   return NS_OK;
02843 }
02844 
02845 NS_IMETHODIMP
02846 nsMsgDBFolder::GetCanCompact(PRBool *aResult)
02847 {
02848   NS_ENSURE_ARG_POINTER(aResult);
02849 
02850   PRBool isServer = PR_FALSE;
02851   nsresult rv = GetIsServer(&isServer);
02852   NS_ENSURE_SUCCESS(rv,rv);
02853   // servers cannot be compacted --> 4.x
02854   // virtual search folders cannot be compacted
02855   *aResult = !isServer && !(mFlags & MSG_FOLDER_FLAG_VIRTUAL);
02856   return NS_OK;
02857 }
02858 
02859 
02860 NS_IMETHODIMP nsMsgDBFolder::GetPrettyName(PRUnichar ** name)
02861 {
02862   return GetName(name);
02863 }
02864 
02865 NS_IMETHODIMP nsMsgDBFolder::SetPrettyName(const PRUnichar *name)
02866 {
02867   nsresult rv;
02868   nsAutoString unicodeName(name);
02869 
02870   //Set pretty name only if special flag is set and if it the default folder name
02871   if (mFlags & MSG_FOLDER_FLAG_INBOX && unicodeName.LowerCaseEqualsLiteral("inbox"))
02872     rv = SetName(kLocalizedInboxName);
02873   else if (mFlags & MSG_FOLDER_FLAG_SENTMAIL && unicodeName.LowerCaseEqualsLiteral("sent"))
02874     rv = SetName(kLocalizedSentName);
02875   //netscape webmail uses "Draft" instead of "Drafts"
02876   else if (mFlags & MSG_FOLDER_FLAG_DRAFTS && (unicodeName.LowerCaseEqualsLiteral("drafts") 
02877                                                 || unicodeName.LowerCaseEqualsLiteral("draft")))  
02878     rv = SetName(kLocalizedDraftsName);
02879   else if (mFlags & MSG_FOLDER_FLAG_TEMPLATES && unicodeName.LowerCaseEqualsLiteral("templates"))
02880     rv = SetName(kLocalizedTemplatesName);
02881   else if (mFlags & MSG_FOLDER_FLAG_TRASH && unicodeName.LowerCaseEqualsLiteral("trash"))
02882     rv = SetName(kLocalizedTrashName);
02883   else if (mFlags & MSG_FOLDER_FLAG_QUEUE && unicodeName.LowerCaseEqualsLiteral("unsent messages"))
02884     rv = SetName(kLocalizedUnsentName);
02885   else if (mFlags & MSG_FOLDER_FLAG_JUNK && unicodeName.LowerCaseEqualsLiteral("junk"))
02886     rv = SetName(kLocalizedJunkName);
02887   else
02888     rv = SetName(name);
02889 
02890   return rv;
02891 }
02892 
02893 NS_IMETHODIMP nsMsgDBFolder::GetName(PRUnichar **name)
02894 {
02895   NS_ENSURE_ARG_POINTER(name);
02896 
02897   nsresult rv;
02898   if (!mHaveParsedURI && mName.IsEmpty()) 
02899   {
02900     rv = parseURI();
02901     if (NS_FAILED(rv)) return rv;
02902   }
02903 
02904   // if it's a server, just forward the call
02905   if (mIsServer) 
02906   {
02907     nsCOMPtr<nsIMsgIncomingServer> server;
02908     rv = GetServer(getter_AddRefs(server));
02909     if (NS_SUCCEEDED(rv) && server)
02910       return server->GetPrettyName(name);
02911   }
02912 
02913   *name = ToNewUnicode(mName);
02914 
02915   if (!(*name)) return NS_ERROR_OUT_OF_MEMORY;
02916     return NS_OK;
02917 }
02918 
02919 NS_IMETHODIMP nsMsgDBFolder::SetName(const PRUnichar * name)
02920 {
02921   // override the URI-generated name
02922   if (!mName.Equals(name))
02923   {
02924     mName = name;
02925 
02926     // old/new value doesn't matter here
02927     NotifyUnicharPropertyChanged(kNameAtom, name, name);
02928   }
02929   return NS_OK;
02930 }
02931 
02932 //For default, just return name
02933 NS_IMETHODIMP nsMsgDBFolder::GetAbbreviatedName(PRUnichar * *aAbbreviatedName)
02934 {
02935   return GetName(aAbbreviatedName);
02936 }
02937 
02938 NS_IMETHODIMP nsMsgDBFolder::GetChildNamed(const PRUnichar *name, nsISupports ** aChild)
02939 {
02940   NS_ASSERTION(aChild, "NULL child");
02941   nsresult rv;
02942   // will return nsnull if we can't find it
02943   *aChild = nsnull;
02944 
02945   PRUint32 count;
02946   rv = mSubFolders->Count(&count);
02947   if (NS_FAILED(rv)) return rv;
02948 
02949   for (PRUint32 i = 0; i < count; i++)
02950   {
02951     nsCOMPtr<nsIMsgFolder> folder(do_QueryElementAt(mSubFolders, i, &rv));
02952     if (NS_SUCCEEDED(rv))
02953     {
02954       nsXPIDLString folderName;
02955 
02956       rv = folder->GetName(getter_Copies(folderName));
02957       // case-insensitive compare is probably LCD across OS filesystems
02958       if (NS_SUCCEEDED(rv) &&
02959           folderName.Equals(name, nsCaseInsensitiveStringComparator()))
02960       {
02961         NS_ADDREF(*aChild = folder);
02962         return NS_OK;
02963       }
02964     }
02965   }
02966   // don't return NS_OK if we didn't find the folder
02967   // see http://bugzilla.mozilla.org/show_bug.cgi?id=210089#c15
02968   // and http://bugzilla.mozilla.org/show_bug.cgi?id=210089#c17
02969   return NS_ERROR_FAILURE;
02970 }
02971 
02972 NS_IMETHODIMP nsMsgDBFolder::GetChildWithURI(const char *uri, PRBool deep, PRBool caseInsensitive, nsIMsgFolder ** child)
02973 {
02974   NS_ASSERTION(child, "NULL child");
02975   nsresult rv;
02976   // will return nsnull if we can't find it
02977   *child = nsnull;
02978 
02979   nsCOMPtr <nsIEnumerator> aEnumerator;
02980 
02981   rv = GetSubFolders(getter_AddRefs(aEnumerator));
02982   if (NS_FAILED(rv))
02983     return rv;
02984 
02985   nsCOMPtr<nsISupports> aItem;
02986 
02987   rv = aEnumerator->First();
02988   if (NS_FAILED(rv))
02989     return NS_OK; // it's OK, there are no sub-folders.
02990 
02991   while(NS_SUCCEEDED(rv))
02992   {
02993     rv = aEnumerator->CurrentItem(getter_AddRefs(aItem));
02994     if (NS_FAILED(rv)) break;
02995     nsCOMPtr<nsIRDFResource> folderResource = do_QueryInterface(aItem);
02996     nsCOMPtr<nsIMsgFolder> folder = do_QueryInterface(aItem);
02997     if (folderResource  && folder)
02998     {
02999       const char *folderURI;
03000       rv = folderResource->GetValueConst(&folderURI);
03001       if (NS_FAILED(rv)) return rv;
03002       PRBool equal;
03003       equal = folderURI &&
03004               (caseInsensitive
03005                ? nsCRT::strcasecmp(folderURI, uri)
03006                : nsCRT::strcmp(folderURI, uri)) == 0;
03007       if (equal)
03008       {
03009         *child = folder;
03010         NS_ADDREF(*child);
03011         return NS_OK;
03012       }
03013       if (deep)
03014       {
03015         rv = folder->GetChildWithURI(uri, deep, caseInsensitive, child);
03016         if (NS_FAILED(rv))
03017           return rv;
03018 
03019         if (*child)
03020           return NS_OK;
03021       }
03022     }
03023     rv = aEnumerator->Next();
03024     if (NS_FAILED(rv))
03025     {
03026       rv = NS_OK;
03027       break;
03028     }
03029   }
03030 
03031   return NS_OK;
03032 }
03033 
03034 NS_IMETHODIMP nsMsgDBFolder::GetPrettiestName(PRUnichar **name)
03035 {
03036   if (NS_SUCCEEDED(GetPrettyName(name)))
03037     return NS_OK;
03038   return GetName(name);
03039 }
03040 
03041 
03042 NS_IMETHODIMP nsMsgDBFolder::GetShowDeletedMessages(PRBool *showDeletedMessages)
03043 {
03044   NS_ENSURE_ARG_POINTER(showDeletedMessages);
03045 
03046   *showDeletedMessages = PR_FALSE;
03047   return NS_OK;
03048 }
03049 
03050 NS_IMETHODIMP nsMsgDBFolder::Delete()
03051 {
03052   return NS_OK;
03053 }
03054 
03055 NS_IMETHODIMP nsMsgDBFolder::DeleteSubFolders(nsISupportsArray *folders,
03056                                             nsIMsgWindow *msgWindow)
03057 {
03058   nsresult rv;
03059 
03060   PRUint32 count;
03061   rv = folders->Count(&count);
03062   for(PRUint32 i = 0; i < count; i++)
03063   {
03064     nsCOMPtr<nsIMsgFolder> folder(do_QueryElementAt(folders, i, &rv));
03065     if (folder)
03066       PropagateDelete(folder, PR_TRUE, msgWindow);
03067   }
03068   return rv;
03069 
03070 }
03071 
03072 NS_IMETHODIMP nsMsgDBFolder::CreateStorageIfMissing(nsIUrlListener* /* urlListener */)
03073 {
03074   NS_ASSERTION(PR_FALSE, "needs to be overridden");
03075   return NS_OK;
03076 }
03077 
03078 
03079 NS_IMETHODIMP nsMsgDBFolder::PropagateDelete(nsIMsgFolder *folder, PRBool deleteStorage, nsIMsgWindow *msgWindow)
03080 {
03081   nsresult status = NS_OK;
03082 
03083   nsCOMPtr<nsIMsgFolder> child;
03084 
03085   // first, find the folder we're looking to delete
03086   PRUint32 cnt;
03087   nsresult rv = mSubFolders->Count(&cnt);
03088   if (NS_FAILED(rv)) return rv;
03089   for (PRUint32 i = 0; i < cnt; i++)
03090   {
03091     nsCOMPtr<nsISupports> supports = getter_AddRefs(mSubFolders->ElementAt(i));
03092     child = do_QueryInterface(supports, &status);
03093     if (NS_SUCCEEDED(status))
03094     {
03095       if (folder == child.get())
03096       {
03097         //Remove self as parent
03098         child->SetParent(nsnull);
03099 
03100         // maybe delete disk storage for it, and its subfolders
03101         status = child->RecursiveDelete(deleteStorage, msgWindow);
03102 
03103         if (status == NS_OK)
03104         {
03105           //Remove from list of subfolders.
03106           mSubFolders->RemoveElement(supports);
03107           NotifyItemRemoved(supports);
03108           break;
03109         }
03110         else
03111         {  // setting parent back if we failed
03112           child->SetParent(this);
03113         }
03114       }
03115       else
03116       {
03117         status = child->PropagateDelete (folder, deleteStorage, msgWindow);
03118       }
03119     }
03120   }
03121 
03122   return status;
03123 }
03124 
03125 NS_IMETHODIMP nsMsgDBFolder::RecursiveDelete(PRBool deleteStorage, nsIMsgWindow *msgWindow)
03126 {
03127   // If deleteStorage is PR_TRUE, recursively deletes disk storage for this folder
03128   // and all its subfolders.
03129   // Regardless of deleteStorage, always unlinks them from the children lists and
03130   // frees memory for the subfolders but NOT for _this_
03131 
03132   nsresult status = NS_OK;
03133   nsCOMPtr <nsIFileSpec> dbPath;
03134   
03135   // first remove the deleted folder from the folder cache;
03136   nsresult result = GetFolderCacheKey(getter_AddRefs(dbPath));
03137 
03138   nsCOMPtr<nsIMsgAccountManager> accountMgr = 
03139     do_GetService(NS_MSGACCOUNTMANAGER_CONTRACTID, &result); 
03140   if(NS_SUCCEEDED(result))
03141   {
03142     nsCOMPtr <nsIMsgFolderCache> folderCache;
03143     result = accountMgr->GetFolderCache(getter_AddRefs(folderCache));
03144     if (NS_SUCCEEDED(result) && folderCache)
03145     {
03146       nsXPIDLCString persistentPath;
03147       dbPath->GetPersistentDescriptorString(getter_Copies(persistentPath));
03148       folderCache->RemoveElement(persistentPath.get());
03149     }
03150   }
03151 
03152   PRUint32 cnt;
03153   nsresult rv = mSubFolders->Count(&cnt);
03154   if (NS_FAILED(rv)) return rv;
03155   while (cnt > 0)
03156   {
03157     nsCOMPtr<nsISupports> supports = getter_AddRefs(mSubFolders->ElementAt(0));
03158     nsCOMPtr<nsIMsgFolder> child(do_QueryInterface(supports, &status));
03159 
03160     if (NS_SUCCEEDED(status))
03161     {
03162       child->SetParent(nsnull);
03163       status = child->RecursiveDelete(deleteStorage,msgWindow);  // recur
03164       if (NS_SUCCEEDED(status))
03165         mSubFolders->RemoveElement(supports);  // unlink it from this's child list
03166       else
03167       { // setting parent back if we failed for some reason
03168           child->SetParent(this);
03169       }
03170     }
03171     cnt--;
03172   }
03173 
03174   // now delete the disk storage for _this_
03175   if (deleteStorage && (status == NS_OK))
03176   {
03177     status = Delete();
03178     nsCOMPtr <nsISupports> supports;
03179     QueryInterface(NS_GET_IID(nsISupports), getter_AddRefs(supports));
03180     nsCOMPtr <nsIMsgFolderNotificationService> notifier = do_GetService(NS_MSGNOTIFICATIONSERVICE_CONTRACTID);
03181     if (notifier)
03182       notifier->NotifyItemDeleted(supports);    
03183     
03184   }
03185   return status;
03186 }
03187 
03188 NS_IMETHODIMP nsMsgDBFolder::CreateSubfolder(const PRUnichar *folderName, nsIMsgWindow *msgWindow )
03189 {
03190   return NS_ERROR_NOT_IMPLEMENTED;
03191 }
03192 
03193 NS_IMETHODIMP nsMsgDBFolder::AddSubfolder(const nsAString& name,
03194                                    nsIMsgFolder** child)
03195 {
03196   NS_ENSURE_ARG_POINTER(child);
03197   
03198   PRInt32 flags = 0;
03199   nsresult rv;
03200   nsCOMPtr<nsIRDFService> rdf = do_GetService("@mozilla.org/rdf/rdf-service;1", &rv);
03201   NS_ENSURE_SUCCESS(rv,rv);
03202   
03203   nsCAutoString uri(mURI);
03204   uri.Append('/');
03205   
03206   // URI should use UTF-8
03207   // (see RFC2396 Uniform Resource Identifiers (URI): Generic Syntax)
03208   nsCAutoString escapedName;
03209   rv = NS_MsgEscapeEncodeURLPath(name, escapedName);
03210   NS_ENSURE_SUCCESS(rv, rv);
03211   
03212   // fix for #192780
03213   // if this is the root folder
03214   // make sure the the special folders
03215   // have the right uri.
03216   // on disk, host\INBOX should be a folder with the uri mailbox://user@host/Inbox"
03217   // as mailbox://user@host/Inbox != mailbox://user@host/INBOX
03218   nsCOMPtr<nsIMsgFolder> rootFolder;
03219   rv = GetRootFolder(getter_AddRefs(rootFolder));
03220   if (NS_SUCCEEDED(rv) && rootFolder && (rootFolder.get() == (nsIMsgFolder *)this))
03221   {
03222     if (nsCRT::strcasecmp(escapedName.get(), "INBOX") == 0)
03223       uri += "Inbox";
03224     else if (nsCRT::strcasecmp(escapedName.get(), "UNSENT%20MESSAGES") == 0)
03225       uri += "Unsent%20Messages";
03226     else if (nsCRT::strcasecmp(escapedName.get(), "DRAFTS") == 0)
03227       uri += "Drafts";
03228     else if (nsCRT::strcasecmp(escapedName.get(), "TRASH") == 0)
03229       uri += "Trash";
03230     else if (nsCRT::strcasecmp(escapedName.get(), "SENT") == 0)
03231       uri += "Sent";
03232     else if (nsCRT::strcasecmp(escapedName.get(), "TEMPLATES") == 0)
03233       uri +="Templates";
03234     else
03235       uri += escapedName.get();
03236   }
03237   else
03238     uri += escapedName.get();
03239   
03240   nsCOMPtr <nsIMsgFolder> msgFolder;
03241   rv = GetChildWithURI(uri.get(), PR_FALSE/*deep*/, PR_TRUE /*case Insensitive*/, getter_AddRefs(msgFolder));  
03242   if (NS_SUCCEEDED(rv) && msgFolder)
03243     return NS_MSG_FOLDER_EXISTS;
03244   
03245   nsCOMPtr<nsIRDFResource> res;
03246   rv = rdf->GetResource(uri, getter_AddRefs(res));
03247   if (NS_FAILED(rv))
03248     return rv;
03249   
03250   nsCOMPtr<nsIMsgFolder> folder(do_QueryInterface(res, &rv));
03251   if (NS_FAILED(rv))
03252     return rv;
03253 
03254   nsFileSpec path;
03255   // we just need to do this for the parent folder, i.e., "this".
03256   rv = CreateDirectoryForFolder(path);
03257   NS_ENSURE_SUCCESS(rv, rv);
03258 
03259   folder->GetFlags((PRUint32 *)&flags);
03260   
03261   flags |= MSG_FOLDER_FLAG_MAIL;
03262   
03263   folder->SetParent(this);
03264   
03265   PRBool isServer;
03266   rv = GetIsServer(&isServer);
03267   
03268   //Only set these is these are top level children.
03269   if(NS_SUCCEEDED(rv) && isServer)
03270   {
03271     if(name.LowerCaseEqualsLiteral("inbox"))
03272     {
03273       flags |= MSG_FOLDER_FLAG_INBOX;
03274       SetBiffState(nsIMsgFolder::nsMsgBiffState_Unknown);
03275     }
03276     else if (name.LowerCaseEqualsLiteral("trash"))
03277       flags |= MSG_FOLDER_FLAG_TRASH;
03278     else if (name.LowerCaseEqualsLiteral("unsent messages") ||
03279       name.LowerCaseEqualsLiteral("outbox"))
03280       flags |= MSG_FOLDER_FLAG_QUEUE;
03281 #if 0
03282     // the logic for this has been moved into 
03283     // SetFlagsOnDefaultMailboxes()
03284     else if(name.EqualsIgnoreCase(NS_LITERAL_STRING("Sent"), nsCaseInsensitiveStringComparator()))
03285       folder->SetFlag(MSG_FOLDER_FLAG_SENTMAIL);
03286     else if(name.EqualsIgnoreCase(NS_LITERAL_STRING("Drafts"), nsCaseInsensitiveStringComparator()))
03287       folder->SetFlag(MSG_FOLDER_FLAG_DRAFTS);
03288     else if(name.EqualsIgnoreCase(NS_LITERAL_STRING("Templates"), nsCaseInsensitiveStringComparator()))
03289       folder->SetFlag(MSG_FOLDER_FLAG_TEMPLATES);
03290 #endif 
03291   }
03292   
03293   folder->SetFlags(flags);
03294   
03295   //at this point we must be ok and we don't want to return failure in case GetIsServer failed.
03296   rv = NS_OK;
03297   
03298   nsCOMPtr<nsISupports> supports = do_QueryInterface(folder);
03299   if(folder)
03300     mSubFolders->AppendElement(supports);
03301   *child = folder;
03302   NS_ADDREF(*child);
03303   
03304   return rv;
03305 }
03306 
03307 NS_IMETHODIMP nsMsgDBFolder::Compact(nsIUrlListener *aListener, nsIMsgWindow *aMsgWindow)
03308 {
03309   return NS_ERROR_NOT_IMPLEMENTED;
03310 }
03311 
03312 NS_IMETHODIMP nsMsgDBFolder::CompactAll(nsIUrlListener *aListener, nsIMsgWindow *aMsgWindow, nsISupportsArray *aFolderArray, PRBool aCompactOfflineAlso, nsISupportsArray *aCompactOfflineArray)
03313 {
03314   NS_ASSERTION(PR_FALSE, "should be overridden by child class");
03315   return NS_ERROR_NOT_IMPLEMENTED;
03316 }
03317 
03318 NS_IMETHODIMP nsMsgDBFolder::EmptyTrash(nsIMsgWindow *msgWindow, nsIUrlListener *aListener)
03319 {
03320   return NS_ERROR_NOT_IMPLEMENTED;
03321 }
03322 
03323 nsresult 
03324 nsMsgDBFolder::CheckIfFolderExists(const PRUnichar *newFolderName, nsIMsgFolder *parentFolder, nsIMsgWindow *msgWindow)
03325 {
03326   NS_ENSURE_ARG_POINTER(newFolderName);
03327   NS_ENSURE_ARG_POINTER(parentFolder);
03328   nsCOMPtr<nsIEnumerator> subfolders;
03329   nsresult rv = parentFolder->GetSubFolders(getter_AddRefs(subfolders));
03330   NS_ENSURE_SUCCESS(rv, rv);
03331   rv = subfolders->First();    //will fail if no subfolders 
03332   while (NS_SUCCEEDED(rv))
03333   {
03334     nsCOMPtr<nsISupports> supports;
03335     subfolders->CurrentItem(getter_AddRefs(supports));
03336     nsCOMPtr<nsIMsgFolder> msgFolder = do_QueryInterface(supports);
03337     nsAutoString folderNameString;
03338     PRUnichar *folderName;
03339     if (msgFolder)
03340       msgFolder->GetName(&folderName);
03341     folderNameString.Adopt(folderName);
03342     if (folderNameString.Equals(newFolderName, nsCaseInsensitiveStringComparator()))
03343     {
03344       if (msgWindow)
03345         ThrowAlertMsg("folderExists", msgWindow);
03346       return NS_MSG_FOLDER_EXISTS;
03347     }
03348     rv = subfolders->Next();
03349   }
03350   return NS_OK;
03351 }
03352 
03353 
03354 nsresult
03355 nsMsgDBFolder::AddDirectorySeparator(nsFileSpec &path)
03356 {
03357     nsAutoString sep;
03358     nsresult rv = nsGetMailFolderSeparator(sep);
03359     if (NS_FAILED(rv)) return rv;
03360     
03361     // see if there's a dir with the same name ending with .sbd
03362     // unfortunately we can't just say:
03363     //          path += sep;
03364     // here because of the way nsFileSpec concatenates
03365  
03366     nsCAutoString str(path.GetNativePathCString());
03367     str.AppendWithConversion(sep);
03368     path = str.get();
03369 
03370     return rv;
03371 }
03372 
03373 /* Finds the directory associated with this folder.  That is if the path is
03374    c:\Inbox, it will return c:\Inbox.sbd if it succeeds.  If that path doesn't
03375    currently exist then it will create it. Path is strictly an out parameter.
03376   */
03377 nsresult nsMsgDBFolder::CreateDirectoryForFolder(nsFileSpec &path)
03378 {
03379   nsresult rv = NS_OK;
03380   
03381   nsCOMPtr<nsIFileSpec> pathSpec;
03382   rv = GetPath(getter_AddRefs(pathSpec));
03383   if (NS_FAILED(rv)) return rv;
03384   
03385   rv = pathSpec->GetFileSpec(&path);
03386   if (NS_FAILED(rv)) return rv;
03387   
03388   if(!path.IsDirectory())
03389   {
03390     //If the current path isn't a directory, add directory separator
03391     //and test it out.
03392     rv = AddDirectorySeparator(path);
03393     if(NS_FAILED(rv))
03394       return rv;
03395     
03396     //If that doesn't exist, then we have to create this directory
03397     if(!path.IsDirectory())
03398     {
03399       //If for some reason there's a file with the directory separator
03400       //then we are going to fail.
03401       if(path.Exists())
03402       {
03403         return NS_MSG_COULD_NOT_CREATE_DIRECTORY;
03404       }
03405       //otherwise we need to create a new directory.
03406       else
03407       {
03408         nsFileSpec tempPath(path.GetNativePathCString(), PR_TRUE); // create intermediate directories
03409         path.CreateDirectory();
03410         //Above doesn't return an error value so let's see if
03411         //it was created.
03412         if(!path.IsDirectory())
03413           return NS_MSG_COULD_NOT_CREATE_DIRECTORY;
03414       }
03415     }
03416   }
03417   
03418   return rv;
03419 }
03420 
03421 
03422 
03423 NS_IMETHODIMP nsMsgDBFolder::Rename(const PRUnichar *aNewName, nsIMsgWindow *msgWindow)
03424 {
03425   nsCOMPtr<nsIFileSpec> oldPathSpec;
03426   nsCOMPtr<nsIAtom> folderRenameAtom;
03427   nsresult rv = GetPath(getter_AddRefs(oldPathSpec));
03428   if (NS_FAILED(rv)) 
03429     return rv;
03430   nsCOMPtr<nsIMsgFolder> parentFolder;
03431   rv = GetParentMsgFolder(getter_AddRefs(parentFolder));
03432   if (NS_FAILED(rv)) 
03433     return rv;
03434   nsCOMPtr<nsISupports> parentSupport = do_QueryInterface(parentFolder);
03435   
03436   nsFileSpec fileSpec;
03437   oldPathSpec->GetFileSpec(&fileSpec);
03438   nsLocalFolderSummarySpec oldSummarySpec(fileSpec);
03439   nsFileSpec dirSpec;
03440   
03441   PRUint32 cnt = 0;
03442   if (mSubFolders)
03443     mSubFolders->Count(&cnt);
03444   
03445   if (cnt > 0)
03446     rv = CreateDirectoryForFolder(dirSpec);
03447   
03448   // convert from PRUnichar* to char* due to not having Rename(PRUnichar*)
03449   // function in nsIFileSpec
03450   
03451   nsAutoString safeName(aNewName);
03452   NS_MsgHashIfNecessary(safeName);
03453   nsCAutoString newDiskName;
03454   if (NS_FAILED(NS_CopyUnicodeToNative(safeName, newDiskName)))
03455     return NS_ERROR_FAILURE;
03456   
03457   nsXPIDLCString oldLeafName;
03458   oldPathSpec->GetLeafName(getter_Copies(oldLeafName));
03459   
03460   if (mName.Equals(aNewName, nsCaseInsensitiveStringComparator()))
03461   {
03462     if(msgWindow)
03463       rv = ThrowAlertMsg("folderExists", msgWindow);
03464     return NS_MSG_FOLDER_EXISTS;
03465   }
03466   else
03467   {
03468     nsCOMPtr <nsIFileSpec> parentPathSpec;
03469     parentFolder->GetPath(getter_AddRefs(parentPathSpec));
03470     NS_ENSURE_SUCCESS(rv,rv);
03471     
03472     nsFileSpec parentPath;
03473     parentPathSpec->GetFileSpec(&parentPath);
03474     NS_ENSURE_SUCCESS(rv,rv);
03475     
03476     if (!parentPath.IsDirectory())
03477       AddDirectorySeparator(parentPath);
03478     
03479     rv = CheckIfFolderExists(aNewName, parentFolder, msgWindow);
03480     if (NS_FAILED(rv)) 
03481       return rv;
03482   }
03483   
03484   ForceDBClosed();
03485   
03486   nsCAutoString newNameDirStr(newDiskName);  //save of dir name before appending .msf 
03487   
03488   if (! (mFlags & MSG_FOLDER_FLAG_VIRTUAL))
03489     rv = oldPathSpec->Rename(newDiskName.get());
03490   if (NS_SUCCEEDED(rv))
03491   {
03492     newDiskName += ".msf";
03493     oldSummarySpec.Rename(newDiskName.get());
03494   }
03495   else
03496   {
03497     ThrowAlertMsg("folderRenameFailed", msgWindow);
03498     return rv;
03499   }
03500   
03501   if (NS_SUCCEEDED(rv) && cnt > 0) 
03502   {
03503     // rename "*.sbd" directory
03504     newNameDirStr += ".sbd";
03505     dirSpec.Rename(newNameDirStr.get());
03506   }
03507   
03508   nsCOMPtr<nsIMsgFolder> newFolder;
03509   if (parentSupport)
03510   {
03511     rv = parentFolder->AddSubfolder(nsDependentString(aNewName), getter_AddRefs(newFolder));
03512     if (newFolder) 
03513     {
03514       newFolder->SetPrettyName(aNewName);
03515       newFolder->SetFlags(mFlags);
03516       PRBool changed = PR_FALSE;
03517       MatchOrChangeFilterDestination(newFolder, PR_TRUE /*caseInsenstive*/, &changed);
03518       if (changed)
03519         AlertFilterChanged(msgWindow);
03520       
03521       if (cnt > 0)
03522         newFolder->RenameSubFolders(msgWindow, this);
03523       
03524       if (parentFolder)
03525       {
03526         SetParent(nsnull);
03527         parentFolder->PropagateDelete(this, PR_FALSE, msgWindow);
03528         parentFolder->NotifyItemAdded(newFolder);
03529       }
03530       folderRenameAtom = do_GetAtom("RenameCompleted");
03531       newFolder->NotifyFolderEvent(folderRenameAtom);
03532     }
03533   }
03534   return rv;
03535 
03536 }
03537 
03538 NS_IMETHODIMP nsMsgDBFolder::RenameSubFolders(nsIMsgWindow *msgWindow, nsIMsgFolder *oldFolder)
03539 {
03540   return NS_ERROR_NOT_IMPLEMENTED;
03541 }
03542 
03543 NS_IMETHODIMP nsMsgDBFolder::ContainsChildNamed(const PRUnichar *name, PRBool* containsChild)
03544 {
03545   NS_ENSURE_ARG_POINTER(containsChild);
03546 
03547   nsCOMPtr<nsISupports> child;
03548   GetChildNamed(name, getter_AddRefs(child));
03549   *containsChild = child != nsnull;
03550   return NS_OK;
03551 }
03552 
03553 
03554 
03555 NS_IMETHODIMP nsMsgDBFolder::IsAncestorOf(nsIMsgFolder *child, PRBool *isAncestor)
03556 {
03557   NS_ENSURE_ARG_POINTER(isAncestor);
03558 
03559   nsresult rv = NS_OK;
03560 
03561   PRUint32 count;
03562   rv = mSubFolders->Count(&count);
03563   if (NS_FAILED(rv)) return rv;
03564 
03565   for (PRUint32 i = 0; i < count; i++)
03566   {
03567     nsCOMPtr<nsIMsgFolder> folder(do_QueryElementAt(mSubFolders, i, &rv));
03568     if (NS_SUCCEEDED(rv))
03569     {
03570       if (folder.get() == child )
03571       {
03572         *isAncestor = PR_TRUE;
03573       }
03574       else
03575         folder->IsAncestorOf(child, isAncestor);
03576 
03577     }
03578     if (*isAncestor)
03579       return NS_OK;
03580   }
03581   *isAncestor = PR_FALSE;
03582   return rv;
03583 
03584 }
03585 
03586 
03587 NS_IMETHODIMP nsMsgDBFolder::GenerateUniqueSubfolderName(const PRUnichar *prefix,
03588                                                        nsIMsgFolder *otherFolder,
03589                                                        PRUnichar **name)
03590 {
03591   NS_ENSURE_ARG_POINTER(name);
03592 
03593   /* only try 256 times */
03594   for (int count = 0; (count < 256); count++)
03595   {
03596     nsAutoString uniqueName;
03597     uniqueName.Assign(prefix);
03598     uniqueName.AppendInt(count);
03599     PRBool containsChild;
03600     PRBool otherContainsChild = PR_FALSE;
03601 
03602     ContainsChildNamed(uniqueName.get(), &containsChild);
03603     if (otherFolder)
03604     {
03605       ((nsIMsgFolder*)otherFolder)->ContainsChildNamed(uniqueName.get(), &otherContainsChild);
03606     }
03607 
03608     if (!containsChild && !otherContainsChild)
03609     {
03610       *name = nsCRT::strdup(uniqueName.get());
03611       return NS_OK;
03612     }
03613   }
03614   *name = nsnull;
03615   return NS_OK;
03616 }
03617 
03618 NS_IMETHODIMP nsMsgDBFolder::UpdateSummaryTotals(PRBool /* force */)
03619 {
03620   //We don't support this
03621   return NS_OK;
03622 }
03623 
03624 NS_IMETHODIMP nsMsgDBFolder::SummaryChanged()
03625 {
03626   UpdateSummaryTotals(PR_FALSE);
03627   return NS_OK;
03628 }
03629 
03630 NS_IMETHODIMP nsMsgDBFolder::GetNumUnread(PRBool deep, PRInt32 *numUnread)
03631 {
03632   NS_ENSURE_ARG_POINTER(numUnread);
03633 
03634   nsresult rv;
03635   PRInt32 total = mNumUnreadMessages + mNumPendingUnreadMessages;
03636   if (deep)
03637   {
03638     if (total < 0) // deep search never returns negative counts
03639       total = 0;
03640     PRUint32 count;
03641     rv = mSubFolders->Count(&count);
03642     if (NS_SUCCEEDED(rv)) 
03643     {
03644       for (PRUint32 i = 0; i < count; i++)
03645       {
03646         nsCOMPtr<nsIMsgFolder> folder(do_QueryElementAt(mSubFolders, i, &rv));
03647         if (NS_SUCCEEDED(rv))
03648         {
03649           PRInt32 num;
03650           PRUint32 folderFlags;
03651           folder->GetFlags(&folderFlags);
03652           if (!(folderFlags & MSG_FOLDER_FLAG_VIRTUAL))
03653           {
03654             folder->GetNumUnread(deep, &num);
03655             total += num;
03656           }
03657         }
03658       }
03659     }
03660   }
03661   *numUnread = total;
03662   return NS_OK;
03663 
03664 }
03665 
03666 NS_IMETHODIMP nsMsgDBFolder::GetTotalMessages(PRBool deep, PRInt32 *totalMessages)
03667 {
03668   NS_ENSURE_ARG_POINTER(totalMessages);
03669 
03670   nsresult rv;
03671   PRInt32 total = mNumTotalMessages + mNumPendingTotalMessages;
03672   if (deep)
03673   {
03674     if (total < 0) // deep search never returns negative counts
03675       total = 0;
03676     PRUint32 count;
03677     rv = mSubFolders->Count(&count);
03678     if (NS_SUCCEEDED(rv)) 
03679     {
03680 
03681       for (PRUint32 i = 0; i < count; i++)
03682       {
03683         nsCOMPtr<nsIMsgFolder> folder(do_QueryElementAt(mSubFolders, i, &rv));
03684         if (NS_SUCCEEDED(rv))
03685         {
03686           PRInt32 num;
03687           PRUint32 folderFlags;
03688           folder->GetFlags(&folderFlags);
03689           if (!(folderFlags & MSG_FOLDER_FLAG_VIRTUAL))
03690           {
03691             folder->GetTotalMessages (deep, &num);
03692             total += num;
03693           }
03694         }
03695       }
03696     }
03697   }
03698   *totalMessages = total;
03699   return NS_OK;
03700 }
03701 
03702 PRInt32 nsMsgDBFolder::GetNumPendingUnread()
03703 {
03704   return mNumPendingUnreadMessages;
03705 }
03706 
03707 PRInt32 nsMsgDBFolder::GetNumPendingTotalMessages()
03708 {
03709   return mNumPendingTotalMessages;
03710 }
03711 
03712 void nsMsgDBFolder::ChangeNumPendingUnread(PRInt32 delta)
03713 {
03714   if (delta)
03715   {
03716     PRInt32 oldUnreadMessages = mNumUnreadMessages + mNumPendingUnreadMessages;
03717     mNumPendingUnreadMessages += delta;
03718     PRInt32 newUnreadMessages = mNumUnreadMessages + mNumPendingUnreadMessages;
03719     NS_ASSERTION(newUnreadMessages >= 0, "shouldn't have negative unread message count");
03720     if (newUnreadMessages >= 0)
03721     {
03722       nsCOMPtr<nsIMsgDatabase> db;
03723       nsCOMPtr<nsIDBFolderInfo> folderInfo;
03724       nsresult rv = GetDBFolderInfoAndDB(getter_AddRefs(folderInfo), getter_AddRefs(db));
03725       if (NS_SUCCEEDED(rv) && folderInfo)
03726         folderInfo->SetImapUnreadPendingMessages(mNumPendingUnreadMessages);
03727 
03728       NotifyIntPropertyChanged(kTotalUnreadMessagesAtom, oldUnreadMessages, newUnreadMessages);
03729     }
03730   }
03731 }
03732 
03733 void nsMsgDBFolder::ChangeNumPendingTotalMessages(PRInt32 delta)
03734 {
03735   if (delta)
03736   {
03737     PRInt32 oldTotalMessages = mNumTotalMessages + mNumPendingTotalMessages;
03738     mNumPendingTotalMessages += delta;
03739     PRInt32 newTotalMessages = mNumTotalMessages + mNumPendingTotalMessages;
03740 
03741     nsCOMPtr<nsIMsgDatabase> db;
03742     nsCOMPtr<nsIDBFolderInfo> folderInfo;
03743     nsresult rv = GetDBFolderInfoAndDB(getter_AddRefs(folderInfo), getter_AddRefs(db));
03744     if (NS_SUCCEEDED(rv) && folderInfo)
03745       folderInfo->SetImapTotalPendingMessages(mNumPendingTotalMessages);
03746     NotifyIntPropertyChanged(kTotalMessagesAtom, oldTotalMessages, newTotalMessages);
03747   }
03748 }
03749 
03750 NS_IMETHODIMP nsMsgDBFolder::SetPrefFlag()
03751 {
03752   // *** Note: this method should only be called when we done with the folder
03753   // discovery. GetResource() may return a node which is not in the folder
03754   // tree hierarchy but in the rdf cache in case of the non-existing default
03755   // Sent, Drafts, and Templates folders. The resouce will be eventually
03756   // released when the rdf service shuts down. When we create the default
03757   // folders later on in the imap server, the subsequent GetResource() of the
03758   // same uri will get us the cached rdf resource which should have the folder
03759   // flag set appropriately.
03760   nsresult rv;
03761   nsCOMPtr<nsIRDFService> rdf(do_GetService(kRDFServiceCID, &rv));
03762   if (NS_FAILED(rv)) return rv;
03763 
03764   nsCOMPtr<nsIMsgAccountManager> accountMgr = do_GetService(NS_MSGACCOUNTMANAGER_CONTRACTID, &rv);
03765   NS_ENSURE_SUCCESS(rv, rv);
03766 
03767   nsCOMPtr<nsIMsgIncomingServer> server;
03768   rv = GetServer(getter_AddRefs(server));
03769   NS_ENSURE_SUCCESS(rv, rv);
03770 
03771   nsCOMPtr<nsIMsgIdentity> identity;
03772   rv = accountMgr->GetFirstIdentityForServer(server, getter_AddRefs(identity));
03773   if (NS_SUCCEEDED(rv) && identity)
03774   {
03775     nsXPIDLCString folderUri;
03776     nsCOMPtr<nsIRDFResource> res;
03777     nsCOMPtr<nsIMsgFolder> folder;
03778     identity->GetFccFolder(getter_Copies(folderUri));
03779     if (folderUri && NS_SUCCEEDED(rdf->GetResource(folderUri, getter_AddRefs(res))))
03780     {
03781       folder = do_QueryInterface(res, &rv);
03782       if (NS_SUCCEEDED(rv))
03783         rv = folder->SetFlag(MSG_FOLDER_FLAG_SENTMAIL);
03784     }
03785     identity->GetDraftFolder(getter_Copies(folderUri));
03786     if (folderUri && NS_SUCCEEDED(rdf->GetResource(folderUri, getter_AddRefs(res))))
03787     {
03788       folder = do_QueryInterface(res, &rv);
03789       if (NS_SUCCEEDED(rv))
03790         rv = folder->SetFlag(MSG_FOLDER_FLAG_DRAFTS);
03791     }
03792     identity->GetStationeryFolder(getter_Copies(folderUri));
03793     if (folderUri && NS_SUCCEEDED(rdf->GetResource(folderUri, getter_AddRefs(res))))
03794     {
03795       folder = do_QueryInterface(res, &rv);
03796       if (NS_SUCCEEDED(rv))
03797         rv = folder->SetFlag(MSG_FOLDER_FLAG_TEMPLATES);
03798     }
03799   }
03800 
03801   // XXX TODO
03802   // JUNK RELATED
03803   // should we be using the spam settings to set the JUNK flag?
03804   return rv;
03805 }
03806 
03807 NS_IMETHODIMP nsMsgDBFolder::SetFlag(PRUint32 flag)
03808 {
03809   ReadDBFolderInfo(PR_FALSE);
03810   // OnFlagChange can be expensive, so don't call it if we don't need to
03811   PRBool flagSet;
03812   nsresult rv;
03813 
03814   PRBool dbWasOpen = mDatabase != nsnull;
03815 
03816   if (NS_FAILED(rv = GetFlag(flag, &flagSet)))
03817     return rv;
03818 
03819   if (!flagSet)
03820   {
03821     mFlags |= flag;
03822     OnFlagChange(flag);
03823   }
03824   if (!dbWasOpen && mDatabase)
03825     SetMsgDatabase(nsnull);
03826 
03827   return NS_OK;
03828 }
03829 
03830 NS_IMETHODIMP nsMsgDBFolder::ClearFlag(PRUint32 flag)
03831 {
03832   // OnFlagChange can be expensive, so don't call it if we don't need to
03833   PRBool flagSet;
03834   nsresult rv;
03835 
03836   if (NS_FAILED(rv = GetFlag(flag, &flagSet)))
03837     return rv;
03838 
03839   if (flagSet)
03840   {
03841     mFlags &= ~flag;
03842     OnFlagChange (flag);
03843   }
03844 
03845   return NS_OK;
03846 }
03847 
03848 NS_IMETHODIMP nsMsgDBFolder::GetFlag(PRUint32 flag, PRBool *_retval)
03849 {
03850   *_retval = ((mFlags & flag) != 0);
03851   return NS_OK;
03852 }
03853 
03854 NS_IMETHODIMP nsMsgDBFolder::ToggleFlag(PRUint32 flag)
03855 {
03856   mFlags ^= flag;
03857   OnFlagChange (flag);
03858 
03859   return NS_OK;
03860 }
03861 
03862 NS_IMETHODIMP nsMsgDBFolder::OnFlagChange(PRUint32 flag)
03863 {
03864   nsresult rv = NS_OK;
03865   nsCOMPtr<nsIMsgDatabase> db;
03866   nsCOMPtr<nsIDBFolderInfo> folderInfo;
03867   rv = GetDBFolderInfoAndDB(getter_AddRefs(folderInfo), getter_AddRefs(db));
03868   if (NS_SUCCEEDED(rv) && folderInfo)
03869   {
03870 #ifdef DEBUG_bienvenu1
03871        nsXPIDLString name;
03872        rv = GetName(getter_Copies(name));
03873        NS_ASSERTION(Compare(name, kLocalizedTrashName) || (mFlags & MSG_FOLDER_FLAG_TRASH), "lost trash flag");
03874 #endif
03875     folderInfo->SetFlags((PRInt32) mFlags);
03876     if (db)
03877       db->Commit(nsMsgDBCommitType::kLargeCommit);
03878 
03879     if (flag & MSG_FOLDER_FLAG_OFFLINE) 
03880     {
03881       PRBool newValue = mFlags & MSG_FOLDER_FLAG_OFFLINE;
03882       rv = NotifyBoolPropertyChanged(kSynchronizeAtom, !newValue, newValue);
03883       NS_ENSURE_SUCCESS(rv,rv);
03884     }
03885     else if (flag & MSG_FOLDER_FLAG_ELIDED) 
03886     {
03887       PRBool newValue = mFlags & MSG_FOLDER_FLAG_ELIDED;
03888       rv = NotifyBoolPropertyChanged(kOpenAtom, newValue, !newValue);
03889       NS_ENSURE_SUCCESS(rv,rv);
03890     }
03891   }
03892   folderInfo = nsnull;
03893   return rv;
03894 }
03895 
03896 NS_IMETHODIMP nsMsgDBFolder::SetFlags(PRUint32 aFlags)
03897 {
03898   if (mFlags != aFlags)
03899   {
03900     mFlags = aFlags;
03901     OnFlagChange(mFlags);
03902   }
03903   return NS_OK;
03904 }
03905 
03906 NS_IMETHODIMP nsMsgDBFolder::GetAllFoldersWithFlag(PRUint32 flag, nsISupportsArray **aResult)
03907 {
03908   NS_ENSURE_ARG_POINTER(aResult);
03909   nsresult rv = CallCreateInstance(NS_SUPPORTSARRAY_CONTRACTID, aResult);
03910   NS_ENSURE_SUCCESS(rv, rv);
03911 
03912   return ListFoldersWithFlag(flag, *aResult);
03913 }
03914 
03915 nsresult nsMsgDBFolder::ListFoldersWithFlag(PRUint32 flag, nsISupportsArray *array)
03916 {
03917   if ((flag & mFlags) == flag) 
03918   {
03919     nsCOMPtr <nsISupports> supports;
03920     QueryInterface(NS_GET_IID(nsISupports), getter_AddRefs(supports));
03921     array->AppendElement(supports);
03922   }
03923 
03924   nsresult rv;
03925   PRUint32 cnt;
03926 
03927   // call GetSubFolders() to ensure that mSubFolders is initialized
03928   nsCOMPtr <nsIEnumerator> enumerator;
03929   rv = GetSubFolders(getter_AddRefs(enumerator));
03930   NS_ENSURE_SUCCESS(rv,rv);
03931 
03932   rv = mSubFolders->Count(&cnt);
03933   if (NS_SUCCEEDED(rv)) 
03934   {
03935     for (PRUint32 i=0; i < cnt; i++)
03936     {
03937       nsCOMPtr<nsIMsgFolder> folder(do_QueryElementAt(mSubFolders, i, &rv));
03938       if (NS_SUCCEEDED(rv) && folder)
03939       {
03940         nsIMsgFolder *msgFolder = folder.get();
03941         nsMsgDBFolder *dbFolder = NS_STATIC_CAST(nsMsgDBFolder *, msgFolder);
03942         dbFolder->ListFoldersWithFlag(flag,array);
03943       }
03944     }
03945   }
03946   return NS_OK;
03947 }
03948 
03949 NS_IMETHODIMP nsMsgDBFolder::GetFoldersWithFlag(PRUint32 flags, PRUint32 resultsize, PRUint32 *numFolders, nsIMsgFolder **result)
03950 {
03951   PRUint32 num = 0;
03952   if ((flags & mFlags) == flags) 
03953   {
03954     if (result && (num < resultsize)) 
03955     {
03956       result[num] = this;
03957       NS_IF_ADDREF(result[num]);
03958     }
03959     num++;
03960   }
03961 
03962   nsresult rv;
03963   PRUint32 cnt;
03964 
03965   // call GetSubFolders() to ensure that mSubFolders is initialized
03966   nsCOMPtr <nsIEnumerator> enumerator;
03967   rv = GetSubFolders(getter_AddRefs(enumerator));
03968   NS_ENSURE_SUCCESS(rv,rv);
03969 
03970   rv = mSubFolders->Count(&cnt);
03971   if (NS_SUCCEEDED(rv)) 
03972   {
03973     for (PRUint32 i=0; i < cnt; i++)
03974     {
03975       nsCOMPtr<nsIMsgFolder> folder(do_QueryElementAt(mSubFolders, i, &rv));
03976       if (NS_SUCCEEDED(rv) && folder)
03977       {
03978         // CAREFUL! if NULL is passed in for result then the caller
03979         // still wants the full count!  Otherwise, the result should be at most the
03980         // number that the caller asked for.
03981         PRUint32 numSubFolders;
03982 
03983         if (!result)
03984         {
03985           folder->GetFoldersWithFlag(flags, 0, &numSubFolders, NULL);
03986           num += numSubFolders;
03987         }
03988         else if (num < resultsize)
03989         {
03990           folder->GetFoldersWithFlag(flags, resultsize - num, &numSubFolders, result+num);
03991           num += numSubFolders;
03992         }
03993         else
03994         {
03995           break;
03996         }
03997       }
03998     }
03999   }
04000   *numFolders = num;
04001   return NS_OK;
04002 }
04003 
04004 NS_IMETHODIMP nsMsgDBFolder::GetExpansionArray(nsISupportsArray *expansionArray)
04005 {
04006   // the application of flags in GetExpansionArray is subtly different
04007   // than in GetFoldersWithFlag
04008 
04009   nsresult rv;
04010   PRUint32 cnt;
04011   rv = mSubFolders->Count(&cnt);
04012   if (NS_FAILED(rv)) return rv;
04013   for (PRUint32 i = 0; i < cnt; i++)
04014   {
04015     nsCOMPtr<nsIMsgFolder> folder(do_QueryElementAt(mSubFolders, i, &rv));
04016     if (NS_SUCCEEDED(rv))
04017     {
04018       PRUint32 cnt2;
04019       rv = expansionArray->Count(&cnt2);
04020       if (NS_SUCCEEDED(rv)) 
04021       {
04022         expansionArray->InsertElementAt(folder, cnt2);
04023         PRUint32 flags;
04024         folder->GetFlags(&flags);
04025         if (!(flags & MSG_FOLDER_FLAG_ELIDED))
04026           folder->GetExpansionArray(expansionArray);
04027       }
04028     }
04029   }
04030 
04031   return NS_OK;
04032 }
04033 
04034 
04035 NS_IMETHODIMP nsMsgDBFolder::GetDeletable(PRBool *deletable)
04036 {
04037   NS_ENSURE_ARG_POINTER(deletable);
04038 
04039   *deletable = PR_FALSE;
04040   return NS_OK;
04041 }
04042 
04043 NS_IMETHODIMP nsMsgDBFolder::GetRequiresCleanup(PRBool *requiredCleanup)
04044 {
04045   NS_ENSURE_ARG_POINTER(requiredCleanup);
04046 
04047   *requiredCleanup = PR_FALSE;
04048   return NS_OK;
04049 }
04050 
04051 NS_IMETHODIMP nsMsgDBFolder::ClearRequiresCleanup()
04052 {
04053   return NS_OK;
04054 }
04055 
04056 
04057 NS_IMETHODIMP nsMsgDBFolder::GetKnowsSearchNntpExtension(PRBool *knowsExtension)
04058 {
04059   NS_ENSURE_ARG_POINTER(knowsExtension);
04060 
04061   *knowsExtension = PR_FALSE;
04062   return NS_OK;
04063 }
04064 
04065 NS_IMETHODIMP nsMsgDBFolder::GetAllowsPosting(PRBool *allowsPosting)
04066 {
04067   NS_ENSURE_ARG_POINTER(allowsPosting);
04068 
04069   *allowsPosting = PR_TRUE;
04070   return NS_OK;
04071 }
04072 
04073 NS_IMETHODIMP nsMsgDBFolder::GetDisplayRecipients(PRBool *displayRecipients)
04074 {
04075   nsresult rv;
04076 
04077   *displayRecipients = PR_FALSE;
04078 
04079   if (mFlags & MSG_FOLDER_FLAG_SENTMAIL && !(mFlags & MSG_FOLDER_FLAG_INBOX))
04080     *displayRecipients = PR_TRUE;
04081   else if (mFlags & MSG_FOLDER_FLAG_QUEUE)
04082     *displayRecipients = PR_TRUE;
04083   else
04084   {
04085     // Only mail folders can be FCC folders
04086     if (mFlags & MSG_FOLDER_FLAG_MAIL || mFlags & MSG_FOLDER_FLAG_IMAPBOX)
04087     {
04088       // There's one FCC folder for sent mail, and one for sent news
04089       nsIMsgFolder *fccFolders[2];
04090       int numFccFolders = 0;
04091       for (int i = 0; i < numFccFolders; i++)
04092       {
04093         PRBool isAncestor;
04094         if (NS_SUCCEEDED(rv = fccFolders[i]->IsAncestorOf(this, &isAncestor)))
04095         {
04096           if (isAncestor)
04097             *displayRecipients = PR_TRUE;
04098         }
04099         NS_RELEASE(fccFolders[i]);
04100       }
04101     }
04102   }
04103   return NS_OK;
04104 }
04105 
04106 
04107 NS_IMETHODIMP nsMsgDBFolder::AcquireSemaphore(nsISupports *semHolder)
04108 {
04109   nsresult rv = NS_OK;
04110 
04111   if (mSemaphoreHolder == NULL)
04112   {
04113     mSemaphoreHolder = semHolder; //Don't AddRef due to ownership issues.
04114   }
04115   else
04116     rv = NS_MSG_FOLDER_BUSY;
04117 
04118   return rv;
04119 
04120 }
04121 
04122 NS_IMETHODIMP nsMsgDBFolder::ReleaseSemaphore(nsISupports *semHolder)
04123 {
04124   if (!mSemaphoreHolder || mSemaphoreHolder == semHolder)
04125   {
04126     mSemaphoreHolder = NULL;
04127   }
04128   return NS_OK;
04129 }
04130 
04131 NS_IMETHODIMP nsMsgDBFolder::TestSemaphore(nsISupports *semHolder, PRBool *result)
04132 {
04133   NS_ENSURE_ARG_POINTER(result);
04134 
04135   *result = (mSemaphoreHolder == semHolder);
04136   return NS_OK;
04137 }
04138 
04139 NS_IMETHODIMP nsMsgDBFolder::GetLocked(PRBool *isLocked)
04140 {
04141   *isLocked =  mSemaphoreHolder != NULL;
04142   return  NS_OK;
04143 }
04144 
04145 
04146 NS_IMETHODIMP nsMsgDBFolder::GetRelativePathName(char **pathName)
04147 {
04148   NS_ENSURE_ARG_POINTER(pathName);
04149 
04150   *pathName = nsnull;
04151   return NS_OK;
04152 }
04153 
04154 
04155 NS_IMETHODIMP nsMsgDBFolder::GetSizeOnDisk(PRUint32 *size)
04156 {
04157   NS_ENSURE_ARG_POINTER(size);
04158 
04159   *size = 0;
04160   return NS_OK;
04161 }
04162 
04163 NS_IMETHODIMP nsMsgDBFolder::SetSizeOnDisk(PRUint32 aSizeOnDisk)
04164 {
04165   NotifyIntPropertyChanged(kFolderSizeAtom, mFolderSize, aSizeOnDisk);
04166   mFolderSize = aSizeOnDisk;
04167   return NS_OK;
04168 }
04169 
04170 NS_IMETHODIMP nsMsgDBFolder::GetUsername(char **userName)
04171 {
04172   NS_ENSURE_ARG_POINTER(userName);
04173   nsresult rv;
04174   nsCOMPtr <nsIMsgIncomingServer> server;
04175 
04176   rv = GetServer(getter_AddRefs(server));
04177   if (NS_FAILED(rv)) return rv;
04178 
04179   if (server)
04180     return server->GetUsername(userName);
04181 
04182   return NS_ERROR_UNEXPECTED;
04183 }
04184 
04185 NS_IMETHODIMP nsMsgDBFolder::GetHostname(char **hostName)
04186 {
04187   NS_ENSURE_ARG_POINTER(hostName);
04188 
04189   nsresult rv;
04190 
04191   nsCOMPtr<nsIMsgIncomingServer> server;
04192   rv = GetServer(getter_AddRefs(server));
04193   if (NS_FAILED(rv)) return rv;
04194 
04195   if (server)
04196     return server->GetHostName(hostName);
04197 
04198   return NS_ERROR_UNEXPECTED;
04199 }
04200 
04201 NS_IMETHODIMP nsMsgDBFolder::GetNewMessages(nsIMsgWindow *, nsIUrlListener * /* aListener */)
04202 {
04203   return NS_ERROR_NOT_IMPLEMENTED;
04204 }
04205 
04206 NS_IMETHODIMP nsMsgDBFolder::GetBiffState(PRUint32 *aBiffState)
04207 {
04208   nsCOMPtr<nsIMsgIncomingServer> server;
04209   nsresult rv = GetServer(getter_AddRefs(server));
04210   if (NS_SUCCEEDED(rv) && server)
04211     rv = server->GetBiffState(aBiffState);
04212   return rv;
04213 }
04214 
04215 NS_IMETHODIMP nsMsgDBFolder::SetBiffState(PRUint32 aBiffState)
04216 {
04217   PRUint32 oldBiffState;
04218 
04219   nsCOMPtr<nsIMsgIncomingServer> server;
04220   nsresult rv = GetServer(getter_AddRefs(server));
04221   if (NS_SUCCEEDED(rv) && server)
04222     rv = server->GetBiffState(&oldBiffState);
04223     // Get the server and notify it and not inbox.
04224   if (oldBiffState != aBiffState)
04225   {
04226     // we don't distinguish between unknown and noMail for servers
04227     if (! (oldBiffState == nsMsgBiffState_Unknown && aBiffState == nsMsgBiffState_NoMail))
04228     {
04229       if (!mIsServer)
04230       {
04231         nsCOMPtr<nsIMsgFolder> folder;
04232         rv = GetRootFolder(getter_AddRefs(folder));
04233         if (NS_SUCCEEDED(rv) && folder)
04234           return folder->SetBiffState(aBiffState);
04235       }
04236 
04237       if (server)
04238         server->SetBiffState(aBiffState);
04239       NotifyIntPropertyChanged(kBiffStateAtom, oldBiffState, aBiffState);
04240     }
04241   }
04242   else if (aBiffState == nsMsgBiffState_NoMail)
04243   {
04244     // even if the old biff state equals the new biff state, it is still possible that we've never
04245     // cleared the number of new messages for this particular folder. This happens when the new mail state
04246     // got cleared by viewing a new message in folder that is different from this one. Biff state is stored per server
04247     //  the num. of new messages is per folder. 
04248     SetNumNewMessages(0);
04249   }
04250   return NS_OK;
04251 }
04252 
04253 NS_IMETHODIMP nsMsgDBFolder::GetNumNewMessages(PRBool deep, PRInt32 *aNumNewMessages)
04254 {
04255   NS_ENSURE_ARG_POINTER(aNumNewMessages);
04256 
04257   PRInt32 numNewMessages = (!deep || ! (mFlags & MSG_FOLDER_FLAG_VIRTUAL))
04258     ? mNumNewBiffMessages : 0;
04259   if (deep)
04260   {
04261     PRUint32 count;
04262     nsresult rv = NS_OK;
04263     rv = mSubFolders->Count(&count);
04264     if (NS_SUCCEEDED(rv))
04265     {
04266       for (PRUint32 i = 0; i < count; i++)
04267       {
04268         nsCOMPtr<nsIMsgFolder> folder(do_QueryElementAt(mSubFolders, i, &rv));
04269         if (NS_SUCCEEDED(rv))
04270         {
04271           PRInt32 num;
04272           folder->GetNumNewMessages(deep, &num);
04273           if (num > 0) // it's legal for counts to be negative if we don't know
04274             numNewMessages += num;
04275         }
04276       }
04277     }
04278   }
04279   *aNumNewMessages = numNewMessages;
04280   return NS_OK;
04281 }
04282 
04283 NS_IMETHODIMP nsMsgDBFolder::SetNumNewMessages(PRInt32 aNumNewMessages)
04284 {
04285   if (aNumNewMessages != mNumNewBiffMessages)
04286   {
04287     PRInt32 oldNumMessages = mNumNewBiffMessages;
04288     mNumNewBiffMessages = aNumNewMessages;
04289 
04290     nsCAutoString oldNumMessagesStr;
04291     oldNumMessagesStr.AppendInt(oldNumMessages);
04292     nsCAutoString newNumMessagesStr;
04293     newNumMessagesStr.AppendInt(aNumNewMessages);
04294     NotifyPropertyChanged(kNumNewBiffMessagesAtom, oldNumMessagesStr.get(), newNumMessagesStr.get());
04295   }
04296 
04297   return NS_OK;
04298 }
04299 
04300 NS_IMETHODIMP nsMsgDBFolder::GetNewMessagesNotificationDescription(PRUnichar * *aDescription)
04301 {
04302   nsresult rv;
04303   nsAutoString description;
04304   nsCOMPtr<nsIMsgIncomingServer> server;
04305   rv = GetServer(getter_AddRefs(server));
04306 
04307   if (NS_SUCCEEDED(rv))
04308   {
04309     if (!(mFlags & MSG_FOLDER_FLAG_INBOX))
04310     {
04311       nsXPIDLString folderName;
04312       rv = GetPrettyName(getter_Copies(folderName));
04313       if (NS_SUCCEEDED(rv) && folderName)
04314         description.Assign(folderName);
04315     }
04316 
04317     // append the server name
04318     nsXPIDLString serverName;
04319     rv = server->GetPrettyName(getter_Copies(serverName));
04320     if (NS_SUCCEEDED(rv)) 
04321     {
04322       // put this test here because we don't want to just put "folder name on"
04323       // in case the above failed
04324       if (!(mFlags & MSG_FOLDER_FLAG_INBOX))
04325         description.AppendLiteral(" on ");
04326       description.Append(serverName);
04327     }
04328   }
04329   *aDescription = ToNewUnicode(description);
04330   return NS_OK;
04331 }
04332 
04333 NS_IMETHODIMP nsMsgDBFolder::GetRootFolder(nsIMsgFolder * *aRootFolder)
04334 {
04335   NS_ENSURE_ARG_POINTER(aRootFolder);
04336 
04337   nsresult rv;
04338   nsCOMPtr<nsIMsgIncomingServer> server;
04339   rv = GetServer(getter_AddRefs(server));
04340   if (NS_FAILED(rv)) return rv;
04341   NS_ASSERTION(server, "server is null");
04342   // if this happens, bail.
04343   if (!server) return NS_ERROR_NULL_POINTER;
04344 
04345   rv = server->GetRootMsgFolder(aRootFolder);
04346   if (!aRootFolder)
04347     return NS_ERROR_NULL_POINTER;
04348   else
04349     return rv;
04350 }
04351 
04352 NS_IMETHODIMP
04353 nsMsgDBFolder::GetPath(nsIFileSpec * *aPath)
04354 {
04355   NS_ENSURE_ARG_POINTER(aPath);
04356   nsresult rv=NS_OK;
04357 
04358   if (!mPath)
04359     rv = parseURI(PR_TRUE);
04360 
04361   *aPath = mPath;
04362   NS_IF_ADDREF(*aPath);
04363 
04364   return rv;
04365 }
04366 
04367 NS_IMETHODIMP
04368 nsMsgDBFolder::SetPath(nsIFileSpec  *aPath)
04369 {
04370   // XXX - make a local copy!
04371   mPath = aPath;
04372 
04373   return NS_OK;
04374 }
04375 
04376 NS_IMETHODIMP
04377 nsMsgDBFolder::SetFilePath(nsILocalFile *aFile)
04378 {
04379   NS_ASSERTION(PR_FALSE, "don't call this until we've converted mPath to an nsIFile");
04380   return NS_ERROR_NOT_IMPLEMENTED;
04381 }
04382 
04383 NS_IMETHODIMP
04384 nsMsgDBFolder::GetFilePath(nsILocalFile * *aFile)
04385 {
04386   NS_ENSURE_ARG_POINTER(aFile);
04387 
04388   nsCOMPtr <nsIFileSpec> fileSpec;
04389   nsresult rv = GetPath(getter_AddRefs(fileSpec));
04390   NS_ENSURE_SUCCESS(rv, rv);
04391 
04392   nsFileSpec spec;
04393   rv = fileSpec->GetFileSpec(&spec);
04394   NS_ENSURE_SUCCESS(rv, rv);
04395 
04396   return NS_FileSpecToIFile(&spec, aFile);
04397 }
04398 
04399 NS_IMETHODIMP
04400 nsMsgDBFolder::MarkMessagesRead(nsISupportsArray *messages, PRBool markRead)
04401 {
04402   PRUint32 count;
04403   nsresult rv;
04404 
04405   rv = messages->Count(&count);
04406   if (NS_FAILED(rv))
04407     return rv;
04408 
04409   for(PRUint32 i = 0; i < count; i++)
04410   {
04411     nsCOMPtr<nsIMsgDBHdr> message = do_QueryElementAt(messages, i, &rv);
04412 
04413     if (message)
04414       rv = message->MarkRead(markRead);
04415 
04416     if (NS_FAILED(rv))
04417       return rv;
04418 
04419   }
04420   return NS_OK;
04421 }
04422 
04423 NS_IMETHODIMP
04424 nsMsgDBFolder::MarkMessagesFlagged(nsISupportsArray *messages, PRBool markFlagged)
04425 {
04426   PRUint32 count;
04427   nsresult rv;
04428 
04429   rv = messages->Count(&count);
04430   if (NS_FAILED(rv))
04431     return rv;
04432 
04433   for(PRUint32 i = 0; i < count; i++)
04434   {
04435     nsCOMPtr<nsIMsgDBHdr> message = do_QueryElementAt(messages, i, &rv);
04436 
04437     if (message)
04438       rv = message->MarkFlagged(markFlagged);
04439 
04440     if (NS_FAILED(rv))
04441       return rv;
04442 
04443   }
04444   return NS_OK;
04445 }
04446 
04447 NS_IMETHODIMP
04448 nsMsgDBFolder::SetLabelForMessages(nsISupportsArray *aMessages, nsMsgLabelValue aLabel)
04449 {
04450   GetDatabase(nsnull);
04451   if (mDatabase)
04452   {
04453   PRUint32 count;
04454   NS_ENSURE_ARG(aMessages);
04455   nsresult rv = aMessages->Count(&count);
04456   NS_ENSURE_SUCCESS(rv, rv);
04457 
04458   for(PRUint32 i = 0; i < count; i++)
04459   {
04460       nsMsgKey msgKey;
04461     nsCOMPtr<nsIMsgDBHdr> message = do_QueryElementAt(aMessages, i, &rv);
04462     NS_ENSURE_SUCCESS(rv, rv);
04463       (void) message->GetMessageKey(&msgKey);
04464       rv = mDatabase->SetLabel(msgKey, aLabel);
04465     NS_ENSURE_SUCCESS(rv, rv);
04466     }
04467   }
04468   return NS_OK;
04469 }
04470 
04471 NS_IMETHODIMP
04472 nsMsgDBFolder::SetJunkScoreForMessages(nsISupportsArray *aMessages, const char *junkScore)
04473 {
04474   nsresult rv = NS_OK;
04475   GetDatabase(nsnull);
04476   if (mDatabase)
04477   {
04478     PRUint32 count;
04479     NS_ENSURE_ARG(aMessages);
04480     nsresult rv = aMessages->Count(&count);
04481     NS_ENSURE_SUCCESS(rv, rv);
04482 
04483     for(PRUint32 i = 0; i < count; i++)
04484     {
04485       nsMsgKey msgKey;
04486       nsCOMPtr<nsIMsgDBHdr> message = do_QueryElementAt(aMessages, i, &rv);
04487       NS_ENSURE_SUCCESS(rv, rv);
04488       (void) message->GetMessageKey(&msgKey);
04489 
04490       mDatabase->SetStringProperty(msgKey, "junkscore", junkScore);
04491       mDatabase->SetStringProperty(msgKey, "junkscoreorigin", /* ### should this be plugin? */"plugin");
04492     }
04493   }
04494   return rv;
04495 }
04496 
04497 NS_IMETHODIMP
04498 nsMsgDBFolder::ApplyRetentionSettings()
04499 {
04500   return ApplyRetentionSettings(PR_TRUE);
04501 }
04502 
04503 nsresult nsMsgDBFolder::ApplyRetentionSettings(PRBool deleteViaFolder)
04504 {
04505   if (mFlags & MSG_FOLDER_FLAG_VIRTUAL) // ignore virtual folders.
04506     return NS_OK;
04507   nsresult rv;
04508   PRBool weOpenedDB = PR_FALSE;
04509   if (!mDatabase)
04510   {
04511     rv = GetDatabase(nsnull);
04512     NS_ENSURE_SUCCESS(rv, rv);
04513     weOpenedDB = PR_TRUE;
04514   }
04515   if (mDatabase)
04516   {
04517     nsCOMPtr<nsIMsgRetentionSettings> retentionSettings;
04518     rv = GetRetentionSettings(getter_AddRefs(retentionSettings));
04519     if (NS_SUCCEEDED(rv))
04520        rv = mDatabase->ApplyRetentionSettings(retentionSettings, deleteViaFolder);
04521     // we don't want applying retention settings to keep the db open, because
04522     // if we try to purge a bunch of folders, that will leave the dbs all open.
04523     // So if we opened the db, close it.
04524     if (weOpenedDB)
04525       CloseDBIfFolderNotOpen();
04526   }
04527   return rv;
04528 }
04529 
04530 
04531 NS_IMETHODIMP
04532 nsMsgDBFolder::DeleteMessages(nsISupportsArray *messages,
04533                               nsIMsgWindow *msgWindow,
04534                               PRBool deleteStorage,
04535                               PRBool isMove,
04536                               nsIMsgCopyServiceListener *listener,
04537                               PRBool allowUndo)
04538 {
04539   return NS_ERROR_NOT_IMPLEMENTED;
04540 }
04541 
04542 NS_IMETHODIMP
04543 nsMsgDBFolder::CopyMessages(nsIMsgFolder* srcFolder,
04544                           nsISupportsArray *messages,
04545                           PRBool isMove,
04546                           nsIMsgWindow *window,
04547                           nsIMsgCopyServiceListener* listener,
04548                           PRBool isFolder,
04549                           PRBool allowUndo)
04550 {
04551   return NS_ERROR_NOT_IMPLEMENTED;
04552 }
04553 
04554 NS_IMETHODIMP
04555 nsMsgDBFolder::CopyFolder(nsIMsgFolder* srcFolder,
04556                         PRBool isMoveFolder,
04557                         nsIMsgWindow *window,
04558                         nsIMsgCopyServiceListener* listener)
04559 {
04560   NS_ASSERTION(PR_FALSE, "should be overridden by child class");
04561   return NS_ERROR_NOT_IMPLEMENTED;
04562 }
04563 
04564 NS_IMETHODIMP
04565 nsMsgDBFolder::CopyFileMessage(nsIFileSpec* fileSpec,
04566                              nsIMsgDBHdr* messageToReplace,
04567                              PRBool isDraftOrTemplate,
04568                              PRUint32 aNewMsgFlags,
04569                              nsIMsgWindow *window,
04570                              nsIMsgCopyServiceListener* listener)
04571 {
04572   return NS_ERROR_NOT_IMPLEMENTED;
04573 }
04574 
04575 NS_IMETHODIMP nsMsgDBFolder::CopyDataToOutputStreamForAppend(nsIInputStream *aInStream,
04576                      PRInt32 aLength, nsIOutputStream *aOutputStream)
04577 {
04578   if (!aInStream)
04579     return NS_OK;
04580 
04581   PRUint32 uiWritten;
04582   return aOutputStream->WriteFrom(aInStream, aLength, &uiWritten);
04583 }
04584 
04585 NS_IMETHODIMP nsMsgDBFolder::CopyDataDone()
04586 {
04587   return NS_OK;
04588 }
04589 
04590 NS_IMETHODIMP nsMsgDBFolder::MatchName(nsString *name, PRBool *matches)
04591 {
04592   NS_ENSURE_ARG_POINTER(matches);
04593 
04594   *matches = mName.Equals(*name, nsCaseInsensitiveStringComparator());
04595   return NS_OK;
04596 }
04597 
04598 nsresult
04599 nsMsgDBFolder::NotifyPropertyChanged(nsIAtom *property,
04600                                    const char *oldValue, const char* newValue)
04601 {
04602   for (PRInt32 i = 0; i < mListeners.Count(); i++)
04603   {
04604     //Folderlisteners aren't refcounted.
04605     nsIFolderListener* listener =(nsIFolderListener*)mListeners.ElementAt(i);
04606     listener->OnItemPropertyChanged(this, property, oldValue, newValue);
04607   }
04608 
04609   //Notify listeners who listen to every folder
04610   nsresult rv;
04611   nsCOMPtr<nsIFolderListener> folderListenerManager =
04612            do_GetService(NS_MSGMAILSESSION_CONTRACTID, &rv);
04613   if (NS_SUCCEEDED(rv))
04614     folderListenerManager->OnItemPropertyChanged(this, property, oldValue, newValue);
04615 
04616   return NS_OK;
04617 
04618 }
04619 
04620 nsresult
04621 nsMsgDBFolder::NotifyUnicharPropertyChanged(nsIAtom *property,
04622                                           const PRUnichar* oldValue,
04623                                           const PRUnichar *newValue)
04624 {
04625   nsresult rv;
04626 
04627   for (PRInt32 i = 0; i < mListeners.Count(); i++) 
04628   {
04629     // folderlisteners aren't refcounted in the array
04630     nsIFolderListener* listener=(nsIFolderListener*)mListeners.ElementAt(i);
04631     listener->OnItemUnicharPropertyChanged(this, property, oldValue, newValue);
04632   }
04633 
04634   // Notify listeners who listen to every folder
04635   nsCOMPtr<nsIFolderListener> folderListenerManager =
04636            do_GetService(NS_MSGMAILSESSION_CONTRACTID, &rv);
04637   if (NS_SUCCEEDED(rv))
04638     rv = folderListenerManager->OnItemUnicharPropertyChanged(this,
04639                                                    property,
04640                                                    oldValue,
04641                                                    newValue);
04642   return NS_OK;
04643 }
04644 
04645 nsresult nsMsgDBFolder::NotifyIntPropertyChanged(nsIAtom *property, PRInt32 oldValue, PRInt32 newValue)
04646 {
04647   //Don't send off count notifications if they are turned off.
04648   if (!mNotifyCountChanges && ((property == kTotalMessagesAtom) ||( property ==  kTotalUnreadMessagesAtom)))
04649     return NS_OK;
04650 
04651   for (PRInt32 i = 0; i < mListeners.Count(); i++)
04652   {
04653     //Folderlisteners aren't refcounted.
04654     nsIFolderListener* listener =(nsIFolderListener*)mListeners.ElementAt(i);
04655     listener->OnItemIntPropertyChanged(this, property, oldValue, newValue);
04656   }
04657 
04658   //Notify listeners who listen to every folder
04659   nsresult rv;
04660   nsCOMPtr<nsIFolderListener> folderListenerManager =
04661            do_GetService(NS_MSGMAILSESSION_CONTRACTID, &rv);
04662   if (NS_SUCCEEDED(rv))
04663     folderListenerManager->OnItemIntPropertyChanged(this, property, oldValue, newValue);
04664 
04665   return NS_OK;
04666 
04667 }
04668 
04669 nsresult
04670 nsMsgDBFolder::NotifyBoolPropertyChanged(nsIAtom* property,
04671                                        PRBool oldValue, PRBool newValue)
04672 {
04673   for (PRInt32 i = 0; i < mListeners.Count(); i++)
04674   {
04675     //Folderlisteners aren't refcounted.
04676     nsIFolderListener* listener =(nsIFolderListener*)mListeners.ElementAt(i);
04677     listener->OnItemBoolPropertyChanged(this, property, oldValue, newValue);
04678   }
04679 
04680   //Notify listeners who listen to every folder
04681   nsresult rv;
04682   nsCOMPtr<nsIFolderListener> folderListenerManager =
04683            do_GetService(NS_MSGMAILSESSION_CONTRACTID, &rv);
04684   if (NS_SUCCEEDED(rv))
04685     folderListenerManager->OnItemBoolPropertyChanged(this, property, oldValue, newValue);
04686 
04687   return NS_OK;
04688 
04689 }
04690 
04691 nsresult
04692 nsMsgDBFolder::NotifyPropertyFlagChanged(nsIMsgDBHdr *item, nsIAtom *property,
04693                                        PRUint32 oldValue, PRUint32 newValue)
04694 {
04695   PRInt32 i;
04696   for(i = 0; i < mListeners.Count(); i++)
04697   {
04698     //Folderlistener's aren't refcounted.
04699     nsIFolderListener *listener = (nsIFolderListener*)mListeners.ElementAt(i);
04700     listener->OnItemPropertyFlagChanged(item, property, oldValue, newValue);
04701   }
04702 
04703   //Notify listeners who listen to every folder
04704   nsresult rv;
04705   nsCOMPtr<nsIFolderListener> folderListenerManager =
04706            do_GetService(NS_MSGMAILSESSION_CONTRACTID, &rv);
04707   if (NS_SUCCEEDED(rv))
04708     folderListenerManager->OnItemPropertyFlagChanged(item, property, oldValue, newValue);
04709 
04710   return NS_OK;
04711 }
04712 
04713 NS_IMETHODIMP nsMsgDBFolder::NotifyItemAdded(nsISupports *aItem)
04714 {
04715   static PRBool notify = PR_TRUE;
04716 
04717   if (!notify)
04718     return NS_OK;
04719 
04720   PRInt32 i;
04721   for(i = 0; i < mListeners.Count(); i++)
04722   {
04723     //Folderlistener's aren't refcounted.
04724     nsIFolderListener *listener = (nsIFolderListener*)mListeners.ElementAt(i);
04725     listener->OnItemAdded(this, aItem);
04726   }
04727 
04728   //Notify listeners who listen to every folder
04729   nsresult rv;
04730   nsCOMPtr<nsIFolderListener> folderListenerManager =
04731            do_GetService(NS_MSGMAILSESSION_CONTRACTID, &rv);
04732   if (NS_SUCCEEDED(rv))
04733     folderListenerManager->OnItemAdded(this, aItem);
04734 
04735   return NS_OK;
04736 
04737 }
04738 
04739 nsresult nsMsgDBFolder::NotifyItemRemoved(nsISupports *item)
04740 {
04741 
04742   PRInt32 i;
04743   for(i = 0; i < mListeners.Count(); i++)
04744   {
04745     //Folderlistener's aren't refcounted.
04746     nsIFolderListener *listener = (nsIFolderListener*)mListeners.ElementAt(i);
04747     listener->OnItemRemoved(this, item);
04748   }
04749   //Notify listeners who listen to every folder
04750   nsresult rv;
04751   nsCOMPtr<nsIFolderListener> folderListenerManager =
04752            do_GetService(NS_MSGMAILSESSION_CONTRACTID, &rv);
04753   if (NS_SUCCEEDED(rv))
04754     folderListenerManager->OnItemRemoved(this, item);
04755 
04756   return NS_OK;
04757 
04758 }
04759 
04760 
04761 nsresult nsMsgDBFolder::NotifyFolderEvent(nsIAtom* aEvent)
04762 {
04763   PRInt32 i;
04764   for(i = 0; i < mListeners.Count(); i++)
04765   {
04766     //Folderlistener's aren't refcounted.
04767     nsIFolderListener *listener = (nsIFolderListener*)mListeners.ElementAt(i);
04768     listener->OnItemEvent(this, aEvent);
04769   }
04770   //Notify listeners who listen to every folder
04771   nsresult rv;
04772   nsCOMPtr<nsIFolderListener> folderListenerManager =
04773            do_GetService(NS_MSGMAILSESSION_CONTRACTID, &rv);
04774   if (NS_SUCCEEDED(rv))
04775     folderListenerManager->OnItemEvent(this, aEvent);
04776 
04777   return NS_OK;
04778 }
04779 
04780 nsresult
04781 nsGetMailFolderSeparator(nsString& result)
04782 {
04783   result.AssignLiteral(".sbd");
04784   return NS_OK;
04785 }
04786 
04787 NS_IMETHODIMP
04788 nsMsgDBFolder::GetFilterList(nsIMsgWindow *aMsgWindow, nsIMsgFilterList **aResult)
04789 {
04790   nsCOMPtr<nsIMsgIncomingServer> server;
04791   nsresult rv = GetServer(getter_AddRefs(server));
04792   NS_ENSURE_SUCCESS(rv, rv);
04793   NS_ENSURE_TRUE(server, NS_ERROR_FAILURE);
04794 
04795   return server->GetFilterList(aMsgWindow, aResult);
04796 }
04797 
04798 NS_IMETHODIMP
04799 nsMsgDBFolder::SetFilterList(nsIMsgFilterList *aFilterList)
04800 {
04801   nsCOMPtr<nsIMsgIncomingServer> server;
04802   nsresult rv = GetServer(getter_AddRefs(server));
04803   NS_ENSURE_SUCCESS(rv, rv);
04804   NS_ENSURE_TRUE(server, NS_ERROR_FAILURE);
04805   
04806   return server->SetFilterList(aFilterList);
04807 }
04808 
04809 /* void enableNotifications (in long notificationType, in boolean enable); */
04810 NS_IMETHODIMP nsMsgDBFolder::EnableNotifications(PRInt32 notificationType, PRBool enable, PRBool dbBatching)
04811 {
04812   if (notificationType == nsIMsgFolder::allMessageCountNotifications)
04813   {
04814     mNotifyCountChanges = enable;
04815 
04816     // start and stop db batching here. This is under the theory
04817     // that any time we want to enable and disable notifications,
04818     // we're probably doing something that should be batched.
04819     nsCOMPtr <nsIMsgDatabase> database;
04820 
04821     if (dbBatching)  //only if we do dbBatching we need to get db
04822       GetMsgDatabase(nsnull, getter_AddRefs(database));
04823 
04824     if (enable)
04825     {
04826       if (database)
04827         database->EndBatch();
04828       UpdateSummaryTotals(PR_TRUE);
04829     }
04830     else if (database)
04831       return database->StartBatch();
04832 
04833     return NS_OK;
04834   }
04835   return NS_ERROR_NOT_IMPLEMENTED;
04836 }
04837 
04838 
04839 NS_IMETHODIMP nsMsgDBFolder::GetMessageHeader(nsMsgKey msgKey, nsIMsgDBHdr **aMsgHdr)
04840 {
04841   nsresult rv = NS_OK;
04842   if (aMsgHdr)
04843   {
04844     nsCOMPtr <nsIMsgDatabase> database;
04845 
04846     rv = GetMsgDatabase(nsnull, getter_AddRefs(database));
04847     if (NS_SUCCEEDED(rv) && database) // did we get a db back?
04848       rv = database->GetMsgHdrForKey(msgKey, aMsgHdr);
04849   }
04850   else
04851     rv = NS_ERROR_NULL_POINTER;
04852 
04853   return rv;
04854 }
04855 
04856 // this gets the deep sub-folders too, e.g., the children of the children
04857 NS_IMETHODIMP nsMsgDBFolder::ListDescendents(nsISupportsArray *descendents)
04858 {
04859   NS_ENSURE_ARG(descendents);
04860   PRUint32 cnt;
04861   nsresult rv = mSubFolders->Count(&cnt);
04862   if (NS_FAILED(rv)) return rv;
04863   for (PRUint32 index = 0; index < cnt; index++)
04864   {
04865     nsresult rv;
04866     nsCOMPtr<nsISupports> supports(do_QueryElementAt(mSubFolders, index));
04867     nsCOMPtr<nsIMsgFolder> child(do_QueryInterface(supports, &rv));
04868 
04869     if (NS_SUCCEEDED(rv))
04870     {
04871       if (!descendents->AppendElement(supports))
04872         rv = NS_ERROR_OUT_OF_MEMORY;
04873       else
04874         rv = child->ListDescendents(descendents);  // recurse
04875     }
04876   }
04877   return rv;
04878 }
04879 
04880 NS_IMETHODIMP nsMsgDBFolder::GetBaseMessageURI(char **baseMessageURI)
04881 {
04882   NS_ENSURE_ARG_POINTER(baseMessageURI);
04883 
04884   if (!mBaseMessageURI)
04885     return NS_ERROR_FAILURE;
04886 
04887   *baseMessageURI = nsCRT::strdup(mBaseMessageURI);
04888   return NS_OK;
04889 }
04890 
04891 NS_IMETHODIMP nsMsgDBFolder::GetUriForMsg(nsIMsgDBHdr *msgHdr, char **aURI)
04892 {
04893   NS_ENSURE_ARG(msgHdr);
04894   NS_ENSURE_ARG(aURI);
04895   nsMsgKey msgKey;
04896   msgHdr->GetMessageKey(&msgKey);
04897   nsCAutoString uri;
04898   uri.Assign(mBaseMessageURI);
04899 
04900   // append a "#" followed by the message key.
04901   uri.Append('#');
04902   uri.AppendInt(msgKey);
04903 
04904   *aURI = ToNewCString(uri);
04905   return NS_OK;
04906 }
04907 
04908 NS_IMETHODIMP nsMsgDBFolder::GenerateMessageURI(nsMsgKey msgKey, char **aURI)
04909 {
04910   NS_ENSURE_ARG_POINTER(aURI);
04911   nsXPIDLCString baseURI;
04912 
04913   nsresult rv = GetBaseMessageURI(getter_Copies(baseURI));
04914   NS_ENSURE_SUCCESS(rv,rv);
04915 
04916   nsCAutoString uri;
04917   uri.Assign(baseURI);
04918 
04919   // append a "#" followed by the message key.
04920   uri.Append('#');
04921   uri.AppendInt(msgKey);
04922 
04923   *aURI = ToNewCString(uri);
04924   if (! *aURI)
04925     return NS_ERROR_OUT_OF_MEMORY;
04926   return NS_OK;
04927 }
04928 
04929 nsresult
04930 nsMsgDBFolder::GetBaseStringBundle(nsIStringBundle **aBundle)
04931 {
04932   nsresult rv=NS_OK;
04933   NS_ENSURE_ARG_POINTER(aBundle);
04934   nsCOMPtr<nsIStringBundleService> bundleService =
04935          do_GetService(NS_STRINGBUNDLE_CONTRACTID, &rv);
04936   nsCOMPtr<nsIStringBundle> bundle;
04937   if (bundleService && NS_SUCCEEDED(rv))
04938     bundleService->CreateBundle("chrome://messenger/locale/messenger.properties",
04939                                  getter_AddRefs(bundle));
04940   *aBundle = bundle;
04941   NS_IF_ADDREF(*aBundle);
04942   return rv;
04943 }
04944 
04945 nsresult //Do not use this routine if you have to call it very often because it creates a new bundle each time
04946 nsMsgDBFolder::GetStringFromBundle(const char *msgName, PRUnichar **aResult)
04947 {
04948   nsresult rv=NS_OK;
04949   NS_ENSURE_ARG_POINTER(aResult);
04950   nsCOMPtr <nsIStringBundle> bundle;
04951   rv = GetBaseStringBundle(getter_AddRefs(bundle));
04952   if (NS_SUCCEEDED(rv) && bundle)
04953     rv=bundle->GetStringFromName(NS_ConvertASCIItoUCS2(msgName).get(), aResult);
04954   return rv;
04955 
04956 }
04957 
04958 nsresult
04959 nsMsgDBFolder::ThrowConfirmationPrompt(nsIMsgWindow *msgWindow, const PRUnichar *confirmString, PRBool *confirmed)
04960 {
04961   nsresult rv=NS_OK;
04962   if (msgWindow)
04963   {
04964     nsCOMPtr <nsIDocShell> docShell;
04965     msgWindow->GetRootDocShell(getter_AddRefs(docShell));
04966     if (docShell)
04967     {
04968       nsCOMPtr<nsIPrompt> dialog(do_GetInterface(docShell));
04969       if (dialog && confirmString)
04970         dialog->Confirm(nsnull, confirmString, confirmed);
04971     }
04972   }
04973   return rv;
04974 }
04975 
04976 NS_IMETHODIMP
04977 nsMsgDBFolder::GetStringWithFolderNameFromBundle(const char *msgName, PRUnichar **aResult)
04978 {
04979   nsCOMPtr <nsIStringBundle> bundle;
04980   nsresult rv = GetBaseStringBundle(getter_AddRefs(bundle));
04981   if (NS_SUCCEEDED(rv) && bundle)
04982   {
04983     nsXPIDLString folderName;
04984     GetName(getter_Copies(folderName));
04985     const PRUnichar *formatStrings[] =
04986     {
04987       folderName,
04988       kLocalizedBrandShortName
04989     };
04990     rv = bundle->FormatStringFromName(NS_ConvertASCIItoUCS2(msgName).get(),
04991                                       formatStrings, 2, aResult);
04992   }
04993   return rv;
04994 }
04995 
04996 NS_IMETHODIMP nsMsgDBFolder::ConfirmFolderDeletionForFilter(nsIMsgWindow *msgWindow, PRBool *confirmed)
04997 {
04998   nsXPIDLString confirmString;
04999   nsresult rv = GetStringWithFolderNameFromBundle("confirmFolderDeletionForFilter", getter_Copies(confirmString));
05000   if (NS_SUCCEEDED(rv) && confirmString)
05001     rv = ThrowConfirmationPrompt(msgWindow, confirmString.get(), confirmed);
05002   return rv;
05003 }
05004 
05005 NS_IMETHODIMP nsMsgDBFolder::ThrowAlertMsg(const char*msgName, nsIMsgWindow *msgWindow)
05006 {
05007   nsXPIDLString alertString;
05008   nsresult rv = GetStringWithFolderNameFromBundle(msgName, getter_Copies(alertString));
05009   if (NS_SUCCEEDED(rv) && alertString && msgWindow)
05010   {
05011     nsCOMPtr <nsIDocShell> docShell;
05012     msgWindow->GetRootDocShell(getter_AddRefs(docShell));
05013     if (docShell)
05014     {
05015       nsCOMPtr<nsIPrompt> dialog(do_GetInterface(docShell));
05016       if (dialog && alertString)
05017         dialog->Alert(nsnull, alertString);
05018     }
05019   }
05020   return rv;
05021 }
05022 
05023 NS_IMETHODIMP nsMsgDBFolder::AlertFilterChanged(nsIMsgWindow *msgWindow)
05024 {  //this is a different alert i.e  alert w/ checkbox.
05025   nsresult rv = NS_OK;
05026   PRBool checkBox=PR_FALSE;
05027   GetWarnFilterChanged(&checkBox);
05028   if (msgWindow && !checkBox)
05029   {
05030     nsCOMPtr <nsIDocShell> docShell;
05031     msgWindow->GetRootDocShell(getter_AddRefs(docShell));
05032     nsXPIDLString alertString;
05033     rv = GetStringFromBundle("alertFilterChanged", getter_Copies(alertString));
05034     nsXPIDLString alertCheckbox;
05035     rv = GetStringFromBundle("alertFilterCheckbox", getter_Copies(alertCheckbox));
05036     if (alertString && alertCheckbox && docShell)
05037     {
05038       nsCOMPtr<nsIPrompt> dialog(do_GetInterface(docShell));
05039       if (dialog)
05040       {
05041         dialog->AlertCheck(nsnull, alertString, alertCheckbox, &checkBox);
05042         SetWarnFilterChanged(checkBox);
05043       }
05044     }
05045   }
05046   return rv;
05047 }
05048 
05049 nsresult
05050 nsMsgDBFolder::GetWarnFilterChanged(PRBool *aVal)
05051 {
05052   NS_ENSURE_ARG(aVal);
05053   nsresult rv;
05054   nsCOMPtr<nsIPrefBranch> prefBranch = do_GetService(NS_PREFSERVICE_CONTRACTID, &rv);
05055   if (NS_SUCCEEDED(rv) && prefBranch)
05056   {
05057     rv = prefBranch->GetBoolPref(PREF_MAIL_WARN_FILTER_CHANGED, aVal);
05058     if (NS_FAILED(rv))
05059     {
05060       *aVal = PR_FALSE;
05061       rv = NS_OK;
05062     }
05063   }
05064   return rv;
05065 }
05066 
05067 nsresult
05068 nsMsgDBFolder::SetWarnFilterChanged(PRBool aVal)
05069 {
05070   nsresult rv=NS_OK;
05071   nsCOMPtr<nsIPrefBranch> prefBranch = do_GetService(NS_PREFSERVICE_CONTRACTID, &rv);
05072   if (NS_SUCCEEDED(rv) && prefBranch)
05073     rv = prefBranch->SetBoolPref(PREF_MAIL_WARN_FILTER_CHANGED, aVal);
05074   return rv;
05075 }
05076 
05077 NS_IMETHODIMP nsMsgDBFolder::NotifyCompactCompleted()
05078 {
05079   NS_ASSERTION(PR_FALSE, "should be overridden by child class");
05080   return NS_ERROR_NOT_IMPLEMENTED;
05081 }
05082 
05083 nsresult nsMsgDBFolder::CloseDBIfFolderNotOpen()
05084 {
05085   nsresult rv;
05086   nsCOMPtr<nsIMsgMailSession> session = 
05087            do_GetService(NS_MSGMAILSESSION_CONTRACTID, &rv); 
05088   if (NS_SUCCEEDED(rv) && session) // don't use NS_ENSURE_SUCCESS here - we need to release semaphore below
05089   {
05090     PRBool folderOpen;
05091     session->IsFolderOpenInWindow(this, &folderOpen);
05092     if (!folderOpen && ! (mFlags & (MSG_FOLDER_FLAG_TRASH | MSG_FOLDER_FLAG_INBOX)))
05093       SetMsgDatabase(nsnull);
05094   }
05095   return rv;
05096 }
05097 
05098 NS_IMETHODIMP nsMsgDBFolder::SetSortOrder(PRInt32 order)
05099 {
05100   NS_ASSERTION(PR_FALSE, "not implemented");
05101   return NS_ERROR_NOT_IMPLEMENTED;
05102 }
05103 
05104 NS_IMETHODIMP nsMsgDBFolder::GetSortOrder(PRInt32 *order)
05105 {
05106   NS_ENSURE_ARG_POINTER(order);
05107 
05108   PRUint32 flags;
05109   nsresult rv = GetFlags(&flags);
05110   NS_ENSURE_SUCCESS(rv,rv);
05111 
05112   if (flags & MSG_FOLDER_FLAG_INBOX)
05113     *order = 0;
05114   else if (flags & MSG_FOLDER_FLAG_QUEUE)
05115     *order = 1;
05116   else if (flags & MSG_FOLDER_FLAG_DRAFTS)
05117     *order = 2;
05118   else if (flags & MSG_FOLDER_FLAG_TEMPLATES)
05119     *order = 3;
05120   else if (flags & MSG_FOLDER_FLAG_SENTMAIL)
05121     *order = 4;
05122   else if (flags & MSG_FOLDER_FLAG_JUNK)
05123     *order = 5;
05124   else if (flags & MSG_FOLDER_FLAG_TRASH)
05125     *order = 6;
05126   else if (flags & MSG_FOLDER_FLAG_VIRTUAL)
05127     *order = 7;
05128   else
05129     *order = 8;
05130 
05131   return NS_OK;
05132 }
05133 
05134 NS_IMETHODIMP nsMsgDBFolder::GetSortKey(PRUint8 **aKey, PRUint32 *aLength)
05135 {
05136   NS_ENSURE_ARG(aKey);
05137   PRInt32 order;
05138   nsresult rv = GetSortOrder(&order);
05139   NS_ENSURE_SUCCESS(rv,rv);
05140   nsAutoString orderString;
05141   orderString.AppendInt(order);
05142 
05143   nsXPIDLString folderName;
05144   rv = GetName(getter_Copies(folderName));
05145   NS_ENSURE_SUCCESS(rv,rv);
05146   orderString.Append(folderName);
05147   return CreateCollationKey(orderString, aKey, aLength);
05148 }
05149 
05150 NS_IMETHODIMP nsMsgDBFolder::GetPersistElided(PRBool *aPersistElided)
05151 {
05152   // by default, we should always persist the open / closed state of folders & servers
05153   *aPersistElided = PR_TRUE;
05154   return NS_OK;
05155 }
05156 
05157 nsresult
05158 nsMsgDBFolder::CreateCollationKey(const nsString &aSource,  PRUint8 **aKey, PRUint32 *aLength)
05159 {
05160   NS_ASSERTION(gCollationKeyGenerator, "gCollationKeyGenerator is null");
05161   if (!gCollationKeyGenerator)
05162     return NS_ERROR_NULL_POINTER;
05163 
05164   return gCollationKeyGenerator->AllocateRawSortKey(nsICollation::kCollationCaseInSensitive, aSource, aKey, aLength);
05165 }
05166 
05167 NS_IMETHODIMP nsMsgDBFolder::CompareSortKeys(nsIMsgFolder *aFolder, PRInt32 *sortOrder)
05168 {
05169   PRUint8 *sortKey1=nsnull;
05170   PRUint8 *sortKey2=nsnull;
05171   PRUint32 sortKey1Length;
05172   PRUint32 sortKey2Length;
05173   nsresult rv = GetSortKey(&sortKey1, &sortKey1Length);
05174   NS_ENSURE_SUCCESS(rv,rv);
05175   aFolder->GetSortKey(&sortKey2, &sortKey2Length);
05176   NS_ENSURE_SUCCESS(rv,rv);
05177 
05178   rv = gCollationKeyGenerator->CompareRawSortKey(sortKey1, sortKey1Length, sortKey2, sortKey2Length, sortOrder);
05179   PR_Free(sortKey1);
05180   PR_Free(sortKey2);
05181   return rv;
05182 }
05183 
05184 NS_IMETHODIMP nsMsgDBFolder::GetInVFEditSearchScope (PRBool *aInVFEditSearchScope)
05185 {
05186   *aInVFEditSearchScope = mInVFEditSearchScope;
05187   return NS_OK;
05188 }
05189 
05190 NS_IMETHODIMP nsMsgDBFolder::SetInVFEditSearchScope (PRBool aInVFEditSearchScope, PRBool aSetOnSubFolders)
05191 {
05192   PRBool oldInVFEditSearchScope = mInVFEditSearchScope;
05193   mInVFEditSearchScope = aInVFEditSearchScope;
05194   NotifyBoolPropertyChanged(kInVFEditSearchScopeAtom, oldInVFEditSearchScope, mInVFEditSearchScope);
05195   return NS_OK;
05196 }
05197 
05198 NS_IMETHODIMP nsMsgDBFolder::FetchMsgPreviewText(nsMsgKey *aKeysToFetch, PRUint32 aNumKeys,
05199                                                  PRBool aLocalOnly, nsIUrlListener *aUrlListener, 
05200                                                  PRBool *aAsyncResults)
05201 {
05202   NS_ENSURE_ARG_POINTER(aKeysToFetch);
05203   NS_ENSURE_ARG_POINTER(aAsyncResults);
05204   return NS_ERROR_NOT_IMPLEMENTED;
05205 }
05206 
05207 NS_IMETHODIMP nsMsgDBFolder::GetMsgTextFromStream(nsIMsgDBHdr *msgHdr, nsIInputStream *stream, 
05208                                                   PRInt32 bytesToRead,
05209                                                   PRInt32 aMaxOutputLen,
05210                                                   PRBool aCompressQuotes,
05211                                                   nsACString &aMsgText) 
05212 {
05213   /*
05214    1. non mime message - the message body starts after the blank line following the headers.
05215    2. mime message, multipart/alternative - we could simply scan for the boundary line, 
05216    advance past its headers, and treat the next few lines as the text.
05217    3. mime message, text/plain - body follows headers
05218    4. multipart/mixed - scan past boundary, treat next part as body.
05219    */
05220   
05221   // If we've got a header charset use it, otherwise look for one in the mime parts.
05222   PRUint32 len;
05223   msgHdr->GetMessageSize(&len);
05224   nsLineBuffer<char> *lineBuffer;
05225 
05226   nsresult rv = NS_InitLineBuffer(&lineBuffer);
05227   NS_ENSURE_SUCCESS(rv, rv);
05228 
05229   nsXPIDLCString strCharset;
05230   msgHdr->GetCharset(getter_Copies(strCharset));
05231   nsAutoString charset (NS_ConvertUTF8toUTF16(strCharset.get()));
05232 
05233   nsCString msgText;
05234   nsCAutoString encoding;
05235   nsCAutoString boundary;
05236   nsCAutoString curLine;
05237   
05238   // might want to use a state var instead of bools.
05239   PRBool inMsgBody = PR_FALSE, msgBodyIsHtml = PR_FALSE, lookingForBoundary = PR_FALSE;
05240   PRBool lookingForCharset = PR_FALSE;
05241   PRBool haveBoundary = PR_FALSE;
05242   PRBool isBase64 = PR_FALSE;
05243   PRBool reachedEndBody = bytesToRead >= len;
05244   PRBool more = PR_TRUE;
05245   while (len > 0 && more)
05246   {
05247     // might be on same line as content-type, so look before
05248     // we read the next line.
05249     if (lookingForBoundary) 
05250     {
05251       // Mail.app doesn't wrap the boundary id in quotes so we need 
05252       // to be sure to handle an unquoted boundary.
05253       PRInt32 boundaryIndex = curLine.Find("boundary=", PR_TRUE /* ignore case*/);
05254       if (boundaryIndex != kNotFound)
05255       {
05256         boundaryIndex += 9;
05257         if (curLine[boundaryIndex] == '\"')
05258           boundaryIndex++;
05259 
05260         PRInt32 endBoundaryIndex = curLine.RFindChar('"');
05261         if (endBoundaryIndex == kNotFound)
05262           endBoundaryIndex = curLine.Length(); // no trailing quote? assume the boundary runs to the end of the line
05263 
05264         // prepend "--" to boundary, and then boundary delimiter, minus the trailing " 
05265         boundary.Assign("--");
05266         boundary.Append(Substring(curLine, boundaryIndex, endBoundaryIndex - boundaryIndex));
05267         haveBoundary = PR_TRUE;
05268         lookingForBoundary = PR_FALSE;
05269       }
05270     }
05271     rv = NS_ReadLine(stream, lineBuffer, curLine, &more);
05272     if (NS_SUCCEEDED(rv))
05273     {
05274       len -= MSG_LINEBREAK_LEN;
05275       len -= curLine.Length();
05276       if (inMsgBody)
05277       {
05278         if (!boundary.IsEmpty() && boundary.Equals(curLine))
05279         {
05280           reachedEndBody = PR_TRUE;
05281           break;
05282         }
05283         msgText.Append(curLine);
05284         if (!isBase64) // don't append a LF for base64 encoded text
05285           msgText.Append(nsCRT::LF); // put a LF back, we'll strip this out later        
05286         
05287         if (msgText.Length() > bytesToRead)
05288           break;
05289         continue;
05290       }
05291       if (haveBoundary)
05292       {
05293         // this line is the boundary; continue and fall into code that looks
05294         // for msg body after headers
05295         if (curLine.Equals(boundary))
05296           haveBoundary = PR_FALSE;
05297         continue;
05298       }
05299       if (curLine.IsEmpty())
05300       {
05301         inMsgBody = PR_TRUE;
05302         continue;
05303       }
05304       if (StringBeginsWith(curLine, NS_LITERAL_CSTRING("Content-Type:"),
05305                            nsCaseInsensitiveCStringComparator()) ||
05306                            lookingForCharset)
05307       {
05308         // look for a charset in the Content-Type header line, we'll take the first one we find.
05309         nsCOMPtr<nsIMIMEHeaderParam> mimehdrpar = do_GetService(NS_MIMEHEADERPARAM_CONTRACTID, &rv);
05310         if (NS_SUCCEEDED(rv) && charset.IsEmpty())
05311           mimehdrpar->GetParameter(curLine, "charset", EmptyCString(), false, nsnull, charset);
05312         // if the Content-Type header is multiline, look for a charset in other lines
05313         if (charset.IsEmpty())
05314           lookingForCharset = PR_TRUE;
05315         else if (lookingForCharset || (curLine[0] != ' ' && curLine[0] != '\t'))
05316           lookingForCharset = PR_FALSE;
05317         if (FindInReadable(NS_LITERAL_CSTRING("text/html"), curLine,
05318                            nsCaseInsensitiveCStringComparator()))
05319           msgBodyIsHtml = PR_TRUE;
05320         else if (FindInReadable(NS_LITERAL_CSTRING("multipart/"), curLine,
05321                                 nsCaseInsensitiveCStringComparator()))
05322           lookingForBoundary = PR_TRUE;
05323       }
05324       else if (StringBeginsWith(curLine, NS_LITERAL_CSTRING("Content-Transfer-Encoding:"),
05325                            nsCaseInsensitiveCStringComparator()))
05326       {
05327         curLine.Right(encoding, curLine.Length() - 27);
05328         if (encoding.EqualsLiteral("base64"))
05329           isBase64 = PR_TRUE;
05330       }
05331     }
05332   }
05333 
05334   // if the snippet is encoded, decode it
05335   if (!encoding.IsEmpty())
05336     decodeMsgSnippet(encoding, !reachedEndBody, msgText);
05337 
05338   // In order to turn our snippet into unicode, we need to convert it from the charset we
05339   // detected earlier.
05340   nsString unicodeMsgBodyStr;
05341   ConvertToUnicode(NS_ConvertUTF16toUTF8(charset).get(), msgText, unicodeMsgBodyStr);
05342 
05343   // now we've got a msg body. If it's html, convert it to plain text.
05344   // Then, set the previewProperty on the msg hdr to the plain text.
05345   if (msgBodyIsHtml)
05346     convertMsgSnippetToPlainText(unicodeMsgBodyStr);
05347 
05348   // step 3, optionally remove quoted text from the snippet
05349   nsString compressedQuotesMsgStr;
05350   if (aCompressQuotes)
05351     compressQuotesInMsgSnippet(unicodeMsgBodyStr, compressedQuotesMsgStr);
05352 
05353   // now convert back to utf-8 which is more convenient for storage
05354   CopyUTF16toUTF8(aCompressQuotes ? compressedQuotesMsgStr : unicodeMsgBodyStr, aMsgText);
05355   
05356   // finally, truncate the string based on aMaxOutputLen
05357   if (aMsgText.Length() > aMaxOutputLen)
05358     aMsgText.Truncate(aMaxOutputLen);
05359   return rv;
05360 }
05361 
05372 void nsMsgDBFolder::decodeMsgSnippet(const nsACString& aEncodingType, PRBool aIsComplete, nsCString& aMsgSnippet)
05373 {
05374   if (!aEncodingType.IsEmpty())
05375   {
05376     if (aEncodingType.EqualsLiteral("base64"))
05377     {
05378       PRInt32 base64Len = aMsgSnippet.Length();
05379       if (aIsComplete)
05380         base64Len -= base64Len % 4;
05381       char *decodedBody = PL_Base64Decode(aMsgSnippet.get(), base64Len, nsnull);
05382       if (decodedBody)
05383         aMsgSnippet.Adopt(decodedBody);
05384 
05385       // base64 encoded message haven't had line endings converted to LFs yet.
05386       aMsgSnippet.ReplaceChar(nsCRT::CR, nsCRT::LF);
05387       
05388     }
05389     else if (aEncodingType.EqualsLiteral("quoted-printable"))
05390     {
05391       // giant hack - decode in place, and truncate string.
05392       MsgStripQuotedPrintable((unsigned char *) aMsgSnippet.get());
05393       aMsgSnippet.Truncate(strlen(aMsgSnippet.get()));
05394     }
05395   }
05396 }
05397 
05404 void nsMsgDBFolder::compressQuotesInMsgSnippet(const nsString& aMsgSnippet, nsAString& aCompressedQuotes)
05405 {
05406   PRUint32 msgBodyStrLen = aMsgSnippet.Length();
05407   PRBool lastLineWasAQuote = PR_FALSE;
05408   PRUint32 offset = 0;
05409   PRUint32 lineFeedPos = 0;
05410   while (offset < msgBodyStrLen)
05411   {
05412     lineFeedPos = aMsgSnippet.FindChar(nsCRT::LF, offset);
05413     if (lineFeedPos != kNotFound)
05414     {
05415       const nsAString& currentLine = Substring(aMsgSnippet, offset, lineFeedPos - offset);
05416       // this catches quoted text ("> "), nested quotes of any level (">> ", ">>> ", ...)
05417       // it also catches empty line quoted text (">"). It might be over agressive and require
05418       // tweaking later.
05419       // Try to strip the citation. If the current line ends with a ':' and the next line 
05420       // looks like a quoted reply (starts with a ">") skip the current line
05421       if (StringBeginsWith(currentLine, NS_LITERAL_STRING(">")) ||
05422           (lineFeedPos + 1 < msgBodyStrLen  && lineFeedPos
05423           && aMsgSnippet[lineFeedPos - 1] == PRUnichar(':')
05424           && aMsgSnippet[lineFeedPos + 1] == PRUnichar('>')))
05425       {
05426         lastLineWasAQuote = PR_TRUE;
05427       }
05428       else if (!currentLine.IsEmpty())
05429       {
05430         if (lastLineWasAQuote)
05431         {
05432           aCompressedQuotes += NS_LITERAL_STRING(" ... ");
05433           lastLineWasAQuote = PR_FALSE;
05434         }
05435 
05436         aCompressedQuotes += currentLine;      
05437         aCompressedQuotes += PRUnichar(' '); // don't forget to substitute a space for the line feed
05438       }
05439 
05440       offset = lineFeedPos + 1;
05441     }
05442     else
05443     {
05444       aCompressedQuotes.Append(Substring(aMsgSnippet, offset, msgBodyStrLen - offset));
05445       break;
05446     }
05447   }
05448 }
05449 
05454 nsresult nsMsgDBFolder::convertMsgSnippetToPlainText(nsAString& aMessageText)
05455 {
05456   nsString bodyText;
05457   nsresult rv = NS_OK;
05458   
05459   // Create a parser
05460   nsCOMPtr<nsIParser> parser = do_CreateInstance(kParserCID, &rv);
05461   NS_ENSURE_SUCCESS(rv, rv);
05462     
05463   // Create the appropriate output sink
05464   nsCOMPtr<nsIContentSink> sink = do_CreateInstance(NS_PLAINTEXTSINK_CONTRACTID,&rv);
05465   NS_ENSURE_SUCCESS(rv, rv);
05466     
05467   nsCOMPtr<nsIHTMLToTextSink> textSink(do_QueryInterface(sink));
05468   NS_ENSURE_TRUE(textSink, NS_ERROR_FAILURE);
05469   PRUint32 flags = nsIDocumentEncoder::OutputLFLineBreak 
05470                    | nsIDocumentEncoder::OutputNoScriptContent
05471                    | nsIDocumentEncoder::OutputNoFramesContent
05472                    | nsIDocumentEncoder::OutputBodyOnly;
05473   textSink->Initialize(&bodyText, flags, 80);
05474     
05475   parser->SetContentSink(sink);
05476   nsCOMPtr<nsIDTD> dtd = do_CreateInstance(kNavDTDCID,&rv);
05477   NS_ENSURE_SUCCESS(rv, rv);
05478 
05479   parser->RegisterDTD(dtd);
05480   rv = parser->Parse(aMessageText, 0, NS_LITERAL_CSTRING("text/html"), PR_FALSE, PR_TRUE);
05481   aMessageText.Assign(bodyText);
05482   return rv;
05483 }
05484 
05485 nsresult nsMsgDBFolder::GetMsgPreviewTextFromStream(nsIMsgDBHdr *msgHdr, nsIInputStream *stream) 
05486 {
05487   nsCString msgBody;
05488   nsresult rv = GetMsgTextFromStream(msgHdr, stream, 2048, 255, PR_TRUE, msgBody);
05489   // replaces all tabs and line returns with a space, then trims off leading and trailing white space
05490   msgBody.CompressWhitespace(PR_TRUE, PR_TRUE);
05491   msgHdr->SetStringProperty("preview", msgBody.get());
05492   return rv;
05493 }
05494 
05495 void nsMsgDBFolder::SetMRUTime()
05496 {
05497   PRUint32 seconds;
05498   PRTime2Seconds(PR_Now(), &seconds);
05499   nsCAutoString nowStr;
05500   nowStr.AppendInt(seconds);
05501   SetStringProperty(MRU_TIME_PROPERTY, nowStr.get());
05502 }
05503 
05504 
05505 NS_IMETHODIMP nsMsgDBFolder::AddKeywordsToMessages(nsISupportsArray *aMessages, const char *aKeywords)
05506 {
05507   nsresult rv = NS_OK;
05508   GetDatabase(nsnull);
05509   if (mDatabase)
05510   {
05511     PRUint32 count;
05512     NS_ENSURE_ARG(aMessages);
05513     nsresult rv = aMessages->Count(&count);
05514     NS_ENSURE_SUCCESS(rv, rv);
05515     nsXPIDLCString keywords;
05516 
05517     for(PRUint32 i = 0; i < count; i++)
05518     {
05519       nsCOMPtr<nsIMsgDBHdr> message = do_QueryElementAt(aMessages, i, &rv);
05520       NS_ENSURE_SUCCESS(rv, rv);
05521 
05522       message->GetStringProperty("keywords", getter_Copies(keywords));
05523       nsCStringArray keywordArray;
05524       keywordArray.ParseString(aKeywords, " ");
05525       for (PRInt32 j = 0; j < keywordArray.Count(); j++)
05526       {
05527         nsACString::const_iterator start, end;
05528         if (!MsgFindKeyword(*(keywordArray[j]), keywords, start, end))
05529         {
05530           if (!keywords.IsEmpty())
05531             keywords.Append(' ');
05532           keywords.Append(keywordArray[j]->get());
05533         }
05534       }
05535       // go through the msg, not the db, to set the string property, because
05536       // in the case of filters running on incoming pop3 mail with quarantining
05537       // turned on, the message key is wrong.
05538       message->SetStringProperty("keywords", keywords);
05539       PRUint32 msgFlags;
05540       message->GetFlags(&msgFlags);
05541       mDatabase->NotifyHdrChangeAll(message, msgFlags, msgFlags, nsnull);
05542     }
05543   }
05544   return rv;
05545 }
05546 
05547 NS_IMETHODIMP nsMsgDBFolder::RemoveKeywordsFromMessages(nsISupportsArray *aMessages, const char *aKeywords)
05548 {
05549   nsresult rv = NS_OK;
05550   GetDatabase(nsnull);
05551   if (mDatabase)
05552   {
05553     PRUint32 count;
05554     NS_ENSURE_ARG(aMessages);
05555     nsresult rv = aMessages->Count(&count);
05556     NS_ENSURE_SUCCESS(rv, rv);
05557     nsXPIDLCString keywords;
05558     // If the tag is also a label, we should remove the label too...
05559 
05560     for(PRUint32 i = 0; i < count; i++)
05561     {
05562       nsMsgKey msgKey;
05563       nsCOMPtr<nsIMsgDBHdr> message = do_QueryElementAt(aMessages, i, &rv);
05564       NS_ENSURE_SUCCESS(rv, rv);
05565       (void) message->GetMessageKey(&msgKey);
05566       rv = message->GetStringProperty("keywords", getter_Copies(keywords));
05567       nsCStringArray keywordArray;
05568       keywordArray.ParseString(aKeywords, " ");
05569       for (PRInt32 j = 0; j < keywordArray.Count(); j++)
05570       {
05571         PRBool keywordIsLabel = (StringBeginsWith(*(keywordArray[j]), NS_LITERAL_CSTRING("$label"))  
05572           && keywordArray[j]->CharAt(6) >= '1' && keywordArray[j]->CharAt(6) <= '5');
05573         if (keywordIsLabel)
05574         {
05575           nsMsgLabelValue labelValue;
05576           message->GetLabel(&labelValue);
05577           // if we're removing the keyword that corresponds to a pre 2.0 label,
05578           // we need to clear the old label attribute on the messsage.
05579           if (labelValue == (nsMsgLabelValue) (keywordArray[j]->CharAt(6) - '0'))
05580             message->SetLabel((nsMsgLabelValue) 0);
05581         }
05582 
05583         nsACString::const_iterator start, end;
05584         nsACString::const_iterator saveStart;
05585         keywords.BeginReading(saveStart);
05586         if (MsgFindKeyword(*(keywordArray[j]), keywords, start, end))
05587         {
05588           keywords.Cut(Distance(saveStart, start), Distance(start, end));
05589           NS_ASSERTION(keywords.IsEmpty() || keywords.CharAt(0) != ' ', "space only keyword");
05590         }
05591       }
05592       mDatabase->SetStringProperty(msgKey, "keywords", keywords);
05593     }
05594   }
05595   return rv;
05596 }
05597 
05598 NS_IMETHODIMP nsMsgDBFolder::GetCustomIdentity(nsIMsgIdentity **aIdentity)
05599 {
05600   NS_ENSURE_ARG_POINTER(aIdentity);
05601   *aIdentity = nsnull;
05602   return NS_OK;
05603 }