Back to index

lightning-sunbird  0.9+nobinonly
nsMsgDatabase.cpp
Go to the documentation of this file.
00001 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
00002 /* ***** BEGIN LICENSE BLOCK *****
00003  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
00004  *
00005  * The contents of this file are subject to the Mozilla Public License Version
00006  * 1.1 (the "License"); you may not use this file except in compliance with
00007  * the License. You may obtain a copy of the License at
00008  * http://www.mozilla.org/MPL/
00009  *
00010  * Software distributed under the License is distributed on an "AS IS" basis,
00011  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
00012  * for the specific language governing rights and limitations under the
00013  * License.
00014  *
00015  * The Original Code is mozilla.org code.
00016  *
00017  * The Initial Developer of the Original Code is
00018  * Netscape Communications Corporation.
00019  * Portions created by the Initial Developer are Copyright (C) 1999
00020  * the Initial Developer. All Rights Reserved.
00021  *
00022  * Contributor(s):
00023  *   Pierre Phaneuf <pp@ludusdesign.com>
00024  *   David Bienvenu <bienvenu@mozilla.org>
00025  *
00026  * Alternatively, the contents of this file may be used under the terms of
00027  * either of the GNU General Public License Version 2 or later (the "GPL"),
00028  * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
00029  * in which case the provisions of the GPL or the LGPL are applicable instead
00030  * of those above. If you wish to allow use of your version of this file only
00031  * under the terms of either the GPL or the LGPL, and not to allow others to
00032  * use your version of this file under the terms of the MPL, indicate your
00033  * decision by deleting the provisions above and replace them with the notice
00034  * and other provisions required by the GPL or the LGPL. If you do not delete
00035  * the provisions above, a recipient may use your version of this file under
00036  * the terms of any one of the MPL, the GPL or the LGPL.
00037  *
00038  * ***** END LICENSE BLOCK ***** */
00039 
00040 // this file implements the nsMsgDatabase interface using the MDB Interface.
00041 
00042 #ifdef XP_MAC
00043 #include <stat.h>
00044 #else
00045 #include <sys/stat.h>
00046 #endif
00047 
00048 #include "nscore.h"
00049 #include "msgCore.h"
00050 #include "nsMsgDatabase.h"
00051 #include "nsDBFolderInfo.h"
00052 #include "nsMsgKeySet.h"
00053 #include "nsIEnumerator.h"
00054 #include "nsMsgThread.h"
00055 #include "nsFileStream.h"
00056 #include "nsDependentSubstring.h"
00057 #include "nsXPIDLString.h"
00058 #include "nsReadableUtils.h"
00059 #include "nsIMsgHeaderParser.h"
00060 #include "nsMsgBaseCID.h"
00061 #include "nsMorkCID.h"
00062 #include "nsIMdbFactoryFactory.h"
00063 #include "prlog.h"
00064 #include "prprf.h"
00065 #include "nsTime.h"
00066 #include "nsIFileSpec.h"
00067 #include "nsLocalFolderSummarySpec.h"
00068 #include "nsMsgDBCID.h"
00069 #include "nsILocale.h"
00070 #include "nsLocaleCID.h"
00071 #include "nsMsgMimeCID.h"
00072 #include "nsILocaleService.h"
00073 #include "nsMsgFolderFlags.h"
00074 #include "nsIMsgAccountManager.h"
00075 #include "nsIMsgFolderCache.h"
00076 #include "nsIMsgFolderCacheElement.h"
00077 #include "MailNewsTypes2.h"
00078 
00079 static NS_DEFINE_CID(kCMorkFactory, NS_MORK_CID);
00080 
00081 #if defined(XP_MAC) && defined(CompareString)
00082        #undef CompareString
00083 #endif
00084 #include "nsICollation.h"
00085 
00086 #include "nsCollationCID.h"
00087 #include "nsIPrefService.h"
00088 #include "nsIPrefBranch.h"
00089 
00090 #if defined(DEBUG_sspitzer_) || defined(DEBUG_seth_)
00091 #define DEBUG_MSGKEYSET 1
00092 #endif
00093 
00094 static NS_DEFINE_CID(kCollationFactoryCID, NS_COLLATIONFACTORY_CID);
00095 
00096 #define MSG_HASH_SIZE 512
00097 
00098 const PRInt32 kMaxHdrsInCache = 512;  // this will be used on discovery, since we don't know total
00099 
00100 // special keys
00101 static const nsMsgKey kAllMsgHdrsTableKey = 1; 
00102 static const nsMsgKey kTableKeyForThreadOne = 0xfffffffe;
00103 static const nsMsgKey kAllThreadsTableKey = 0xfffffffd;
00104 static const nsMsgKey kFirstPseudoKey = 0xfffffff0;
00105 static const nsMsgKey kIdStartOfFake = 0xffffff80;
00106 
00107 NS_IMPL_ISUPPORTS1(nsMsgDBService, nsIMsgDBService)
00108 
00109 nsMsgDBService::nsMsgDBService()
00110 {
00111 }
00112 
00113 
00114 nsMsgDBService::~nsMsgDBService()
00115 {
00116 }
00117 
00118 NS_IMETHODIMP nsMsgDBService::OpenFolderDB(nsIMsgFolder *aFolder, PRBool aCreate, PRBool aLeaveInvalidDB, nsIMsgDatabase **_retval)
00119 {
00120   NS_ENSURE_ARG(aFolder);
00121   nsMsgDatabase *cacheDB = (nsMsgDatabase *) nsMsgDatabase::FindInCache(aFolder);
00122   if (cacheDB)
00123   {
00124     // this db could have ended up in the folder cache w/o an m_folder pointer via
00125     // OpenMailDBFromFileSpec. If so, take this chance to fix the folder.
00126     if (!cacheDB->m_folder)
00127       cacheDB->m_folder = aFolder;
00128     *_retval = cacheDB; // FindInCache already addRefed.
00129     return NS_OK;
00130   }
00131 
00132   nsCOMPtr <nsIMsgIncomingServer> incomingServer;
00133   nsresult rv = aFolder->GetServer(getter_AddRefs(incomingServer));
00134   NS_ENSURE_SUCCESS(rv, rv);
00135   nsXPIDLCString localStoreType;
00136   incomingServer->GetLocalStoreType(getter_Copies(localStoreType));
00137   nsCAutoString dbContractID(NS_MSGDB_CONTRACTID);
00138   dbContractID.Append(localStoreType.get());
00139   nsCOMPtr <nsIMsgDatabase> msgDB = do_CreateInstance(dbContractID.get(), &rv);
00140   NS_ENSURE_SUCCESS(rv, rv);
00141   nsCOMPtr <nsIFileSpec> folderPath;
00142   rv = aFolder->GetPath(getter_AddRefs(folderPath));
00143   NS_ENSURE_SUCCESS(rv, rv);
00144   rv = msgDB->Open(folderPath, aCreate, aLeaveInvalidDB);
00145   if (NS_FAILED(rv) && (rv != NS_MSG_ERROR_FOLDER_SUMMARY_MISSING 
00146     && rv != NS_MSG_ERROR_FOLDER_SUMMARY_OUT_OF_DATE || !aCreate))
00147     return rv;
00148   NS_IF_ADDREF(*_retval = msgDB);
00149   nsMsgDatabase *msgDatabase = NS_STATIC_CAST(nsMsgDatabase *, *_retval);
00150   msgDatabase->m_folder = aFolder;
00151   PRUint32 folderFlags;
00152   aFolder->GetFlags(&folderFlags);
00153 
00154   if (NS_SUCCEEDED(rv) && ! (folderFlags & MSG_FOLDER_FLAG_VIRTUAL))
00155   {
00156     mdb_count numHdrsInTable = 0;
00157 
00158     if (msgDatabase->m_mdbAllMsgHeadersTable)
00159     {
00160       PRInt32 numMessages;
00161       msgDatabase->m_mdbAllMsgHeadersTable->GetCount(msgDatabase->GetEnv(), &numHdrsInTable);
00162       msgDatabase->m_dbFolderInfo->GetNumMessages(&numMessages);
00163       if (numMessages != (PRInt32) numHdrsInTable)
00164         msgDatabase->SyncCounts();
00165     }
00166   }
00167   NS_ENSURE_SUCCESS(rv, rv);
00168   for (PRInt32 listenerIndex = 0; listenerIndex < m_foldersPendingListeners.Count(); listenerIndex++)
00169   {
00170   //  check if we have a pending listener on this db, and if so, add it.
00171     if (m_foldersPendingListeners[listenerIndex] == aFolder)
00172       (*_retval)->AddListener(m_pendingListeners.ObjectAt(listenerIndex));
00173   }
00174   return rv;
00175 }
00176 
00177 // This method is called when the caller is trying to create a db without 
00178 // having a corresponding nsIMsgFolder object.  This happens in a few
00179 // situations, including imap folder discovery, compacting local folders, 
00180 // and copying local folders.
00181 NS_IMETHODIMP nsMsgDBService::OpenMailDBFromFileSpec(nsIFileSpec *aFolderName, PRBool aCreate, PRBool aLeaveInvalidDB, nsIMsgDatabase** pMessageDB)
00182 {
00183   nsFileSpec  folderName;
00184   
00185   if (!aFolderName)
00186     return NS_ERROR_NULL_POINTER;
00187   aFolderName->GetFileSpec(&folderName);
00188   nsLocalFolderSummarySpec summarySpec(folderName);
00189 
00190   nsFileSpec dbPath(summarySpec);
00191   *pMessageDB = (nsMsgDatabase *) nsMsgDatabase::FindInCache(dbPath);
00192   if (*pMessageDB)
00193     return NS_OK;
00194 
00195   nsresult rv;
00196   nsCOMPtr <nsIMsgDatabase> msgDB = do_CreateInstance(NS_MAILBOXDB_CONTRACTID, &rv);
00197   NS_ENSURE_SUCCESS(rv, rv);
00198   rv = msgDB->Open(aFolderName, aCreate, aLeaveInvalidDB);
00199   if (rv == NS_ERROR_FILE_TARGET_DOES_NOT_EXIST)
00200     return rv;
00201   NS_IF_ADDREF(*pMessageDB = msgDB);
00202   if (aCreate && msgDB && rv == NS_MSG_ERROR_FOLDER_SUMMARY_MISSING)
00203     rv = NS_OK;
00204   return rv;
00205 }
00206 
00207 /* void registerPendingListener (in nsIMsgFolder aFolder, in nsIDBChangeListener aListener); */
00208 NS_IMETHODIMP nsMsgDBService::RegisterPendingListener(nsIMsgFolder *aFolder, nsIDBChangeListener *aListener)
00209 {
00210   // need to make sure we don't hold onto these forever. Maybe a shutdown listener?
00211   // if there is a db open on this folder already, we should register the listener.
00212   m_foldersPendingListeners.AppendObject(aFolder);
00213   m_pendingListeners.AppendObject(aListener);
00214   nsCOMPtr <nsIMsgDatabase> openDB;
00215 
00216   openDB = getter_AddRefs((nsIMsgDatabase *) nsMsgDatabase::FindInCache(aFolder));
00217   if (openDB)
00218     openDB->AddListener(aListener);
00219   return NS_OK;
00220 }
00221 
00222 /* void unregisterPendingListener (in nsIDBChangeListener aListener); */
00223 NS_IMETHODIMP nsMsgDBService::UnregisterPendingListener(nsIDBChangeListener *aListener)
00224 {
00225   PRInt32 listenerIndex = m_pendingListeners.IndexOfObject(aListener);
00226   if (listenerIndex != kNotFound)
00227   {
00228     nsCOMPtr <nsIMsgFolder> folder = m_foldersPendingListeners[listenerIndex];
00229     nsCOMPtr <nsIMsgDatabase> msgDB = getter_AddRefs(nsMsgDatabase::FindInCache(folder));
00230     if (msgDB)
00231       msgDB->RemoveListener(aListener);
00232     m_foldersPendingListeners.RemoveObjectAt(listenerIndex);
00233     m_pendingListeners.RemoveObjectAt(listenerIndex);
00234     return NS_OK;
00235   }
00236   return NS_ERROR_FAILURE;
00237 }
00238 
00239 static PRBool gGotGlobalPrefs = PR_FALSE;
00240 static PRBool gThreadWithoutRe = PR_TRUE;
00241 static PRBool gStrictThreading = PR_FALSE;
00242 
00243 void nsMsgDatabase::GetGlobalPrefs()
00244 {
00245   if (!gGotGlobalPrefs)
00246   {
00247     GetBoolPref("mail.thread_without_re", &gThreadWithoutRe);
00248     GetBoolPref("mail.strict_threading", &gStrictThreading);
00249     gGotGlobalPrefs = PR_TRUE;
00250   }
00251 }
00252 
00253 // we never need to call this because we check the use cache first,
00254 // and any hdr in this cache will be in the use cache.
00255 nsresult nsMsgDatabase::GetHdrFromCache(nsMsgKey key, nsIMsgDBHdr* *result)
00256 {
00257   if (!result)
00258     return NS_ERROR_NULL_POINTER;
00259   
00260   nsresult rv = NS_ERROR_FAILURE;
00261   
00262   *result = nsnull;
00263   if (m_bCacheHeaders && m_cachedHeaders)
00264   {
00265     PLDHashEntryHdr *entry;
00266     entry = PL_DHashTableOperate(m_cachedHeaders, (const void *) key, PL_DHASH_LOOKUP);
00267     if (PL_DHASH_ENTRY_IS_BUSY(entry))
00268     {
00269       MsgHdrHashElement* element = NS_REINTERPRET_CAST(MsgHdrHashElement*, entry);
00270       *result = element->mHdr;
00271       // need to do our own add ref because the PL_DHashTable doesn't addref.
00272       if (*result)
00273       {
00274         NS_ADDREF(*result);
00275         rv = NS_OK;
00276       }
00277     }    
00278     
00279   }
00280   return rv;
00281 }
00282 
00283 nsresult nsMsgDatabase::AddHdrToCache(nsIMsgDBHdr *hdr, nsMsgKey key) // do we want key? We could get it from hdr
00284 {
00285   if (m_bCacheHeaders)
00286   {
00287     if (!m_cachedHeaders)
00288       m_cachedHeaders = PL_NewDHashTable(&gMsgDBHashTableOps, (void *) nsnull, sizeof(struct MsgHdrHashElement), m_cacheSize );
00289     if (m_cachedHeaders)
00290     {
00291       if (key == nsMsgKey_None)
00292         hdr->GetMessageKey(&key);
00293       if (m_cachedHeaders->entryCount > m_cacheSize)
00294         ClearHdrCache(PR_TRUE);
00295       PLDHashEntryHdr *entry = PL_DHashTableOperate(m_cachedHeaders, (void *) key, PL_DHASH_ADD);
00296       if (!entry)
00297         return NS_ERROR_OUT_OF_MEMORY; // XXX out of memory
00298       
00299       MsgHdrHashElement* element = NS_REINTERPRET_CAST(MsgHdrHashElement*, entry);
00300       element->mHdr = hdr;  
00301       element->mKey = key;
00302       NS_ADDREF(hdr);     // make the cache hold onto the header
00303       return NS_OK;
00304     }
00305   }
00306   return NS_ERROR_FAILURE;
00307 }
00308 
00309 
00310 /* static */PLDHashOperator PR_CALLBACK nsMsgDatabase::HeaderEnumerator (PLDHashTable *table, PLDHashEntryHdr *hdr,
00311                                PRUint32 number, void *arg)
00312 {
00313 
00314   MsgHdrHashElement* element = NS_REINTERPRET_CAST(MsgHdrHashElement*, hdr);
00315   NS_IF_RELEASE(element->mHdr); 
00316   return PL_DHASH_NEXT;
00317 }
00318 
00319 /* static */PLDHashOperator PR_CALLBACK nsMsgDatabase::ClearHeaderEnumerator (PLDHashTable *table, PLDHashEntryHdr *hdr,
00320                                PRUint32 number, void *arg)
00321 {
00322 
00323   MsgHdrHashElement* element = NS_REINTERPRET_CAST(MsgHdrHashElement*, hdr);
00324   if (element && element->mHdr)
00325   {
00326     nsMsgHdr* msgHdr = NS_STATIC_CAST(nsMsgHdr*, element->mHdr);  // closed system, so this is ok
00327     // clear out m_mdbRow member variable - the db is going away, which means that this member
00328     // variable might very well point to a mork db that is gone.
00329     msgHdr->m_mdbRow = nsnull;
00330   }
00331   return PL_DHASH_NEXT;
00332 }
00333 
00334 
00335 NS_IMETHODIMP nsMsgDatabase::SetMsgHdrCacheSize(PRUint32 aSize)
00336 {
00337   m_cacheSize = aSize;
00338   return NS_OK;
00339 }
00340 
00341 NS_IMETHODIMP nsMsgDatabase::GetMsgHdrCacheSize(PRUint32 *aSize)
00342 {
00343   NS_ENSURE_ARG_POINTER(aSize);
00344   *aSize = m_cacheSize;
00345   return NS_OK;
00346 }
00347 
00348 NS_IMETHODIMP nsMsgDatabase::ClearCachedHdrs()
00349 {
00350   ClearCachedObjects(PR_FALSE);
00351 #ifdef DEBUG_bienvenu1
00352   if (mRefCnt > 1)
00353   {
00354     NS_ASSERTION(PR_FALSE, "");
00355     printf("someone's holding onto db - refs = %ld\n", mRefCnt);
00356   }
00357 #endif
00358   return NS_OK;
00359 }
00360 
00361 void nsMsgDatabase::ClearCachedObjects(PRBool dbGoingAway)
00362 {
00363   ClearHdrCache(PR_FALSE);
00364 #ifdef DEBUG_bienvenu1
00365   if (m_headersInUse && m_headersInUse->entryCount > 0)
00366   {
00367         NS_ASSERTION(PR_FALSE, "leaking headers");
00368     printf("leaking %d headers in %s\n", m_headersInUse->entryCount, (const char *) m_dbName);
00369   }
00370 #endif
00371   // We should only clear the use hdr cache when the db is going away, or we could
00372   // end up with multiple copies of the same logical msg hdr, which will lead to 
00373   // ref-counting problems.
00374   if (dbGoingAway)
00375     ClearUseHdrCache();
00376   m_cachedThread = nsnull;
00377   m_cachedThreadId = nsMsgKey_None;
00378 }
00379 
00380 nsresult nsMsgDatabase::ClearHdrCache(PRBool reInit)
00381 {
00382   if (m_cachedHeaders)
00383   {
00384     // save this away in case we renter this code.
00385     PLDHashTable  *saveCachedHeaders = m_cachedHeaders;
00386     m_cachedHeaders = nsnull;
00387     PL_DHashTableEnumerate(saveCachedHeaders, HeaderEnumerator, nsnull);
00388     
00389     if (reInit)
00390     {
00391       PL_DHashTableFinish(saveCachedHeaders);
00392       PL_DHashTableInit(saveCachedHeaders, &gMsgDBHashTableOps, nsnull, sizeof(struct MsgHdrHashElement), m_cacheSize);
00393       m_cachedHeaders = saveCachedHeaders;
00394       
00395     }
00396     else
00397     {
00398       PL_DHashTableDestroy(saveCachedHeaders);
00399     }
00400   }
00401   return NS_OK;
00402 }
00403 
00404 nsresult nsMsgDatabase::RemoveHdrFromCache(nsIMsgDBHdr *hdr, nsMsgKey key)
00405 {
00406   if (m_cachedHeaders)
00407   {
00408     if (key == nsMsgKey_None)
00409       hdr->GetMessageKey(&key);
00410     
00411     PLDHashEntryHdr *entry = PL_DHashTableOperate(m_cachedHeaders, (const void *) key, PL_DHASH_LOOKUP);
00412     if (PL_DHASH_ENTRY_IS_BUSY(entry))
00413     {
00414       PL_DHashTableOperate(m_cachedHeaders, (void *) key, PL_DHASH_REMOVE);
00415       NS_RELEASE(hdr); // get rid of extra ref the cache was holding.
00416     }
00417     
00418   }
00419   return NS_OK;
00420 }
00421 
00422 
00423 nsresult nsMsgDatabase::GetHdrFromUseCache(nsMsgKey key, nsIMsgDBHdr* *result)
00424 {
00425   if (!result)
00426     return NS_ERROR_NULL_POINTER;
00427   
00428   nsresult rv = NS_ERROR_FAILURE;
00429   
00430   *result = nsnull;
00431   
00432   if (m_headersInUse)
00433   {
00434     PLDHashEntryHdr *entry;
00435     entry = PL_DHashTableOperate(m_headersInUse, (const void *) key, PL_DHASH_LOOKUP);
00436     if (PL_DHASH_ENTRY_IS_BUSY(entry))
00437     {
00438       MsgHdrHashElement* element = NS_REINTERPRET_CAST(MsgHdrHashElement*, entry);
00439       *result = element->mHdr;
00440     }    
00441     if (*result)
00442     {
00443       NS_ADDREF(*result);
00444       rv = NS_OK;
00445     }
00446   }
00447   return rv;
00448 }
00449 
00450 PLDHashTableOps nsMsgDatabase::gMsgDBHashTableOps =
00451 {
00452   PL_DHashAllocTable,
00453   PL_DHashFreeTable,
00454   GetKey,
00455   HashKey,
00456   MatchEntry,
00457   MoveEntry,
00458   ClearEntry,
00459   PL_DHashFinalizeStub,
00460   nsnull
00461 };
00462 
00463 const void* PR_CALLBACK
00464 nsMsgDatabase::GetKey(PLDHashTable* aTable, PLDHashEntryHdr* aEntry)
00465 {
00466   MsgHdrHashElement* hdr = NS_REINTERPRET_CAST(MsgHdrHashElement*, aEntry);
00467   return (const void *) hdr->mKey;
00468 }
00469 
00470 // HashKey is supposed to maximize entropy in the low order bits, and the key
00471 // as is, should do that.
00472 PLDHashNumber PR_CALLBACK
00473 nsMsgDatabase::HashKey(PLDHashTable* aTable, const void* aKey)
00474 {
00475   return PLDHashNumber(NS_PTR_TO_INT32(aKey));
00476 }
00477 
00478 PRBool PR_CALLBACK
00479 nsMsgDatabase::MatchEntry(PLDHashTable* aTable, const PLDHashEntryHdr* aEntry, const void* aKey)
00480 {
00481   const MsgHdrHashElement* hdr = NS_REINTERPRET_CAST(const MsgHdrHashElement*, aEntry);
00482   return aKey == (const void *) hdr->mKey; // ### or get the key from the hdr...
00483 }
00484 
00485 void PR_CALLBACK
00486 nsMsgDatabase::MoveEntry(PLDHashTable* aTable, const PLDHashEntryHdr* aFrom, PLDHashEntryHdr* aTo)
00487 {
00488   const MsgHdrHashElement* from = NS_REINTERPRET_CAST(const MsgHdrHashElement*, aFrom);
00489   MsgHdrHashElement* to = NS_REINTERPRET_CAST(MsgHdrHashElement*, aTo);
00490   // ### eh? Why is this needed? I don't think we have a copy operator?
00491   *to = *from;
00492 }
00493 
00494 void PR_CALLBACK
00495 nsMsgDatabase::ClearEntry(PLDHashTable* aTable, PLDHashEntryHdr* aEntry)
00496 {
00497   MsgHdrHashElement* element = NS_REINTERPRET_CAST(MsgHdrHashElement*, aEntry);
00498   element->mHdr = nsnull; // eh? Need to release this or not?
00499   element->mKey = nsMsgKey_None; // eh?
00500 }
00501 
00502 
00503 nsresult nsMsgDatabase::AddHdrToUseCache(nsIMsgDBHdr *hdr, nsMsgKey key) 
00504 {
00505   if (!m_headersInUse)
00506   {
00507     mdb_count numHdrs = MSG_HASH_SIZE;
00508     if (m_mdbAllMsgHeadersTable)
00509       m_mdbAllMsgHeadersTable->GetCount(GetEnv(), &numHdrs);
00510     m_headersInUse = PL_NewDHashTable(&gMsgDBHashTableOps, (void *) nsnull, sizeof(struct MsgHdrHashElement), PR_MAX(MSG_HASH_SIZE, numHdrs));
00511   }
00512   if (m_headersInUse)
00513   {
00514     if (key == nsMsgKey_None)
00515       hdr->GetMessageKey(&key);
00516     PLDHashEntryHdr *entry = PL_DHashTableOperate(m_headersInUse, (void *) key, PL_DHASH_ADD);
00517     if (!entry)
00518       return NS_ERROR_OUT_OF_MEMORY; // XXX out of memory
00519 
00520     MsgHdrHashElement* element = NS_REINTERPRET_CAST(MsgHdrHashElement*, entry);
00521     element->mHdr = hdr;  
00522     element->mKey = key;
00523     // the hash table won't add ref, we'll do it ourselves
00524     // stand for the addref that CreateMsgHdr normally does.
00525     NS_ADDREF(hdr);
00526     return NS_OK;
00527   }
00528 
00529   return NS_ERROR_OUT_OF_MEMORY;
00530 }
00531 
00532 nsresult nsMsgDatabase::ClearUseHdrCache()
00533 {
00534   if (m_headersInUse)
00535   {
00536     // clear mdb row pointers of any headers still in use, because the
00537     // underlying db is going away.
00538     PL_DHashTableEnumerate(m_headersInUse, ClearHeaderEnumerator, nsnull);
00539     PL_DHashTableDestroy(m_headersInUse);
00540     m_headersInUse = nsnull;
00541   }
00542   return NS_OK;
00543 }
00544 
00545 nsresult nsMsgDatabase::RemoveHdrFromUseCache(nsIMsgDBHdr *hdr, nsMsgKey key)
00546 {
00547   if (m_headersInUse)
00548   {
00549     if (key == nsMsgKey_None)
00550       hdr->GetMessageKey(&key);
00551     
00552     PL_DHashTableOperate(m_headersInUse, (void *) key, PL_DHASH_REMOVE);
00553   }
00554   return NS_OK;
00555 }
00556 
00557 
00558 nsresult
00559 nsMsgDatabase::CreateMsgHdr(nsIMdbRow* hdrRow, nsMsgKey key, nsIMsgDBHdr* *result)
00560 {
00561   nsresult rv = GetHdrFromUseCache(key, result);
00562   if (NS_SUCCEEDED(rv) && *result)
00563   {
00564     hdrRow->Release();
00565     return rv;
00566   }
00567   
00568   nsMsgHdr *msgHdr = new nsMsgHdr(this, hdrRow);
00569   if(!msgHdr)
00570     return NS_ERROR_OUT_OF_MEMORY;
00571   msgHdr->SetMessageKey(key);
00572   // don't need to addref here; GetHdrFromUseCache addrefs.
00573   *result = msgHdr;
00574   
00575   AddHdrToCache(msgHdr, key);  
00576   
00577   return NS_OK;
00578 }
00579 
00580 NS_IMETHODIMP nsMsgDatabase::AddListener(nsIDBChangeListener *listener)
00581 {
00582   if (m_ChangeListeners == nsnull) 
00583   {
00584     NS_NewISupportsArray(getter_AddRefs(m_ChangeListeners));
00585     if (!m_ChangeListeners) 
00586       return NS_ERROR_OUT_OF_MEMORY;
00587   }
00588   // check if this listener is already registered
00589   // if already registered, do nothing (not an error)
00590   else if (m_ChangeListeners->IndexOf(listener) != -1)
00591     return NS_OK;
00592   return m_ChangeListeners->AppendElement(listener);
00593 }
00594 
00595 NS_IMETHODIMP nsMsgDatabase::RemoveListener(nsIDBChangeListener *listener)
00596 {
00597   if (m_ChangeListeners) 
00598     m_ChangeListeners->RemoveElement(listener);
00599   return NS_OK;
00600 }
00601 
00602 // change announcer methods - just broadcast to all listeners.
00603 NS_IMETHODIMP nsMsgDatabase::NotifyHdrChangeAll(nsIMsgDBHdr *aHdrChanged, PRUint32 oldFlags, PRUint32 newFlags,
00604        nsIDBChangeListener *instigator)
00605 {
00606   if (!m_ChangeListeners)
00607     return NS_OK;
00608 
00609   PRUint32 count;
00610   m_ChangeListeners->Count(&count);
00611   for (PRUint32 i = 0; i < count; i++)
00612   {
00613     nsCOMPtr<nsIDBChangeListener> changeListener;
00614     m_ChangeListeners->QueryElementAt(i, NS_GET_IID(nsIDBChangeListener), (void **) getter_AddRefs(changeListener));
00615 
00616     nsresult rv = changeListener->OnHdrChange(aHdrChanged, oldFlags, newFlags, instigator); 
00617     if (NS_FAILED(rv)) 
00618       return rv;
00619   }
00620   return NS_OK;
00621 }
00622 
00623 NS_IMETHODIMP nsMsgDatabase::NotifyReadChanged(nsIDBChangeListener *instigator)
00624 {
00625   if (m_ChangeListeners == nsnull)
00626     return NS_OK;
00627   PRUint32 count;
00628   m_ChangeListeners->Count(&count);
00629   for (PRUint32 i = 0; i < count; i++)
00630   {
00631     nsCOMPtr<nsIDBChangeListener> changeListener;
00632     m_ChangeListeners->QueryElementAt(i, NS_GET_IID(nsIDBChangeListener), (void **) getter_AddRefs(changeListener));
00633 
00634     nsresult rv = changeListener->OnReadChanged(instigator);
00635     if (NS_FAILED(rv))
00636       return rv;
00637   }
00638   return NS_OK;
00639 }
00640 
00641 NS_IMETHODIMP nsMsgDatabase::NotifyJunkScoreChanged(nsIDBChangeListener *instigator)
00642 {
00643   if (m_ChangeListeners == nsnull)
00644     return NS_OK;
00645   PRUint32 count;
00646   m_ChangeListeners->Count(&count);
00647   for (PRUint32 i = 0; i < count; i++)
00648   {
00649     nsCOMPtr<nsIDBChangeListener> changeListener;
00650     m_ChangeListeners->QueryElementAt(i, NS_GET_IID(nsIDBChangeListener), (void **) getter_AddRefs(changeListener));
00651 
00652     nsresult rv = changeListener->OnJunkScoreChanged(instigator);
00653     if (NS_FAILED(rv))
00654       return rv;
00655   }
00656   return NS_OK;
00657 }
00658 
00659 NS_IMETHODIMP nsMsgDatabase::NotifyHdrDeletedAll(nsIMsgDBHdr *aHdrDeleted, nsMsgKey parentKey, PRInt32 flags, 
00660        nsIDBChangeListener *instigator)
00661 {
00662   if (m_ChangeListeners == nsnull)
00663     return NS_OK;
00664   PRUint32 count;
00665   m_ChangeListeners->Count(&count);
00666   for (PRUint32 i = 0; i < count; i++)
00667   {
00668     nsCOMPtr<nsIDBChangeListener> changeListener;
00669     m_ChangeListeners->QueryElementAt(i, NS_GET_IID(nsIDBChangeListener), (void **) getter_AddRefs(changeListener));
00670     nsresult rv = changeListener->OnHdrDeleted(aHdrDeleted, parentKey, flags, instigator); 
00671     if (NS_FAILED(rv)) 
00672       return rv;
00673   }
00674   return NS_OK;
00675 }
00676 
00677 NS_IMETHODIMP nsMsgDatabase::NotifyHdrAddedAll(nsIMsgDBHdr *aHdrAdded, nsMsgKey parentKey, PRInt32 flags, 
00678        nsIDBChangeListener *instigator)
00679 {
00680 #ifdef DEBUG_bienvenu1
00681        printf("notifying add of %ld parent %ld\n", keyAdded, parentKey);
00682 #endif
00683   if (m_ChangeListeners == nsnull) 
00684     return NS_OK;
00685   PRUint32 count;
00686   m_ChangeListeners->Count(&count);
00687   for (PRUint32 i = 0; i < count; i++)
00688   {
00689     nsCOMPtr<nsIDBChangeListener> changeListener;
00690     m_ChangeListeners->QueryElementAt(i, NS_GET_IID(nsIDBChangeListener), (void **) getter_AddRefs(changeListener));
00691 
00692     nsresult rv = changeListener->OnHdrAdded(aHdrAdded, parentKey, flags, instigator); 
00693     if (NS_FAILED(rv))
00694       return rv;
00695   }
00696   return NS_OK;
00697 }
00698 
00699 NS_IMETHODIMP nsMsgDatabase::NotifyParentChangedAll(nsMsgKey keyReparented, nsMsgKey oldParent, nsMsgKey newParent,
00700        nsIDBChangeListener *instigator)
00701 {
00702   if (m_ChangeListeners == nsnull) 
00703     return NS_OK;
00704   PRUint32 count;
00705   m_ChangeListeners->Count(&count);
00706 
00707   if (!count)
00708     return NS_OK;
00709 
00710   for (PRUint32 i = 0; i < count; i++)
00711   {
00712     nsCOMPtr<nsIDBChangeListener> changeListener;
00713     m_ChangeListeners->QueryElementAt(i, NS_GET_IID(nsIDBChangeListener), (void **) getter_AddRefs(changeListener));
00714     nsresult rv = changeListener->OnParentChanged(keyReparented, oldParent, newParent, instigator); 
00715     if (NS_FAILED(rv)) 
00716       return rv;
00717   }
00718   return NS_OK;
00719 }
00720 
00721 
00722 NS_IMETHODIMP nsMsgDatabase::NotifyAnnouncerGoingAway(void)
00723 {
00724   if (m_ChangeListeners == nsnull)
00725     return NS_OK;
00726   // run loop backwards because listeners remove themselves from the list 
00727   // on this notification
00728   PRUint32 count;
00729   m_ChangeListeners->Count(&count);
00730 
00731   if (!count)
00732     return NS_OK;
00733 
00734   for (PRUint32 i = count; i != 0 ; i--)
00735   {
00736     nsCOMPtr<nsIDBChangeListener> changeListener;
00737     m_ChangeListeners->QueryElementAt(i - 1, NS_GET_IID(nsIDBChangeListener), (void **) getter_AddRefs(changeListener));
00738     if (changeListener) {
00739       nsresult rv = changeListener->OnAnnouncerGoingAway(this); 
00740       NS_ENSURE_SUCCESS(rv,rv);
00741     }
00742   }
00743   return NS_OK;
00744 }
00745 
00746 
00747 
00748 nsVoidArray *nsMsgDatabase::m_dbCache = NULL;
00749 
00750 //----------------------------------------------------------------------
00751 // GetDBCache
00752 //----------------------------------------------------------------------
00753 
00754 nsVoidArray/*<nsMsgDatabase>*/*
00755 nsMsgDatabase::GetDBCache()
00756 {
00757   if (!m_dbCache)
00758     m_dbCache = new nsVoidArray();
00759   
00760   return m_dbCache;
00761   
00762 }
00763 
00764 void
00765 nsMsgDatabase::CleanupCache()
00766 {
00767   if (m_dbCache) // clean up memory leak (needed because some destructors
00768                  // have user-visible effects, which they shouldn't)
00769   {
00770     for (PRInt32 i = 0; i < GetDBCache()->Count(); i++)
00771     {
00772       nsMsgDatabase* pMessageDB = NS_STATIC_CAST(nsMsgDatabase*, GetDBCache()->ElementAt(i));
00773       if (pMessageDB)
00774       {
00775         // hold onto the db until we're finished closing it.
00776         pMessageDB->AddRef();
00777         // break cycle with folder -> parse msg state -> db
00778         pMessageDB->m_folder = nsnull;
00779         pMessageDB->ForceClosed();
00780         nsrefcnt refcount = pMessageDB->Release();
00781 
00782         // ForceClosed may have caused the last reference (other than
00783         // this function's) to go away by breaking a cycle
00784         if (refcount != 0)
00785         {
00786           // The destructor may cause the remaining references to be
00787           // released, so stabilize the refcount and then manually
00788           // delete.
00789           ++pMessageDB->mRefCnt;
00790           delete pMessageDB;
00791         }
00792         i--;  // back up array index, since closing removes db from cache.
00793       }
00794     }
00795     NS_ASSERTION(GetNumInCache() == 0, "some msg dbs left open");     // better not be any open db's.
00796     delete m_dbCache;
00797   }
00798   m_dbCache = nsnull; // Need to reset to NULL since it's a
00799   // static global ptr and maybe referenced 
00800   // again in other places.
00801 }
00802 
00803 //----------------------------------------------------------------------
00804 // FindInCache - this addrefs the db it finds.
00805 //----------------------------------------------------------------------
00806 nsMsgDatabase* nsMsgDatabase::FindInCache(nsFileSpec &dbName)
00807 {
00808   for (PRInt32 i = 0; i < GetDBCache()->Count(); i++)
00809   {
00810     nsMsgDatabase* pMessageDB = NS_STATIC_CAST(nsMsgDatabase*, GetDBCache()->ElementAt(i));
00811     if (pMessageDB->MatchDbName(dbName))
00812     {
00813       if (pMessageDB->m_mdbStore)  // don't return db without store
00814       {
00815         NS_ADDREF(pMessageDB);
00816         return pMessageDB;
00817       }
00818     }
00819   }
00820   return nsnull;
00821 }
00822 
00823 //----------------------------------------------------------------------
00824 // FindInCache(nsIMsgFolder) - this addrefs the db it finds.
00825 //----------------------------------------------------------------------
00826 nsIMsgDatabase* nsMsgDatabase::FindInCache(nsIMsgFolder *folder)
00827 {
00828   nsCOMPtr <nsIFileSpec> folderPath;
00829   nsFileSpec  folderName;
00830   nsresult rv = folder->GetPath(getter_AddRefs(folderPath));
00831   NS_ENSURE_SUCCESS(rv, nsnull);
00832   folderPath->GetFileSpec(&folderName);
00833   nsLocalFolderSummarySpec summarySpec(folderName);
00834 
00835   nsFileSpec dbPath(summarySpec);
00836   return (nsIMsgDatabase *) FindInCache(dbPath);
00837 }
00838 
00839 //----------------------------------------------------------------------
00840 // FindInCache
00841 //----------------------------------------------------------------------
00842 int nsMsgDatabase::FindInCache(nsMsgDatabase* pMessageDB)
00843 {
00844   for (PRInt32 i = 0; i < GetDBCache()->Count(); i++)
00845   {
00846     if (GetDBCache()->ElementAt(i) == pMessageDB)
00847       return(i);
00848   }
00849   return(-1);
00850 }
00851 
00852 PRBool nsMsgDatabase::MatchDbName(nsFileSpec &dbName)   // returns PR_TRUE if they match
00853 {
00854   return (m_dbName == dbName); 
00855 }
00856 
00857 //----------------------------------------------------------------------
00858 // RemoveFromCache
00859 //----------------------------------------------------------------------
00860 void nsMsgDatabase::RemoveFromCache(nsMsgDatabase* pMessageDB)
00861 {
00862   int i = FindInCache(pMessageDB);
00863   if (i != -1)
00864     GetDBCache()->RemoveElementAt(i);
00865 }
00866 
00867 
00868 #ifdef DEBUG
00869 void nsMsgDatabase::DumpCache()
00870 {
00871   nsMsgDatabase* pMessageDB = nsnull;
00872   for (PRInt32 i = 0; i < GetDBCache()->Count(); i++)
00873   {
00874     pMessageDB = NS_STATIC_CAST(nsMsgDatabase*, GetDBCache()->ElementAt(i));
00875   }
00876 }
00877 #endif /* DEBUG */
00878 
00879 nsMsgDatabase::nsMsgDatabase()
00880         : m_dbFolderInfo(nsnull), 
00881         m_nextPseudoMsgKey(kFirstPseudoKey),
00882         m_mdbEnv(nsnull), m_mdbStore(nsnull),
00883         m_mdbAllMsgHeadersTable(nsnull), m_mdbAllThreadsTable(nsnull),
00884         m_dbName(""), 
00885         m_mdbTokensInitialized(PR_FALSE), m_ChangeListeners(nsnull),
00886         m_hdrRowScopeToken(0),
00887         m_hdrTableKindToken(0),
00888         m_threadTableKindToken(0),
00889         m_subjectColumnToken(0),
00890         m_senderColumnToken(0),
00891         m_messageIdColumnToken(0),
00892         m_referencesColumnToken(0),
00893         m_recipientsColumnToken(0),
00894         m_dateColumnToken(0),
00895         m_messageSizeColumnToken(0),
00896         m_flagsColumnToken(0),
00897         m_priorityColumnToken(0),
00898         m_labelColumnToken(0),
00899         m_statusOffsetColumnToken(0),
00900         m_numLinesColumnToken(0),
00901         m_ccListColumnToken(0),
00902         m_threadFlagsColumnToken(0),
00903         m_threadIdColumnToken(0),
00904         m_threadChildrenColumnToken(0),
00905         m_threadUnreadChildrenColumnToken(0),
00906         m_messageThreadIdColumnToken(0),
00907         m_threadSubjectColumnToken(0),
00908         m_numReferencesColumnToken(0),
00909         m_messageCharSetColumnToken(0),
00910         m_threadParentColumnToken(0),
00911         m_threadRootKeyColumnToken(0),
00912         m_threadNewestMsgDateColumnToken(0),
00913         m_offlineMsgOffsetColumnToken(0),
00914         m_offlineMessageSizeColumnToken(0),
00915         m_HeaderParser(nsnull),
00916         m_headersInUse(nsnull),
00917         m_cachedHeaders(nsnull),
00918         m_bCacheHeaders(PR_TRUE),
00919         m_cachedThreadId(nsMsgKey_None),
00920         m_cacheSize(kMaxHdrsInCache)
00921 {
00922 }
00923 
00924 nsMsgDatabase::~nsMsgDatabase()
00925 {
00926   //   Close(FALSE); // better have already been closed.
00927   ClearCachedObjects(PR_TRUE);
00928   delete m_cachedHeaders;
00929   delete m_headersInUse;
00930   RemoveFromCache(this);
00931 #ifdef DEBUG_bienvenu1
00932   if (GetNumInCache() != 0)
00933   {
00934     XP_Trace("closing %s\n", m_dbName);
00935     DumpCache();
00936   }
00937 #endif
00938   // if the db folder info refers to the mdb db, we must clear it because
00939   // the reference will be a dangling one soon.
00940   if (m_dbFolderInfo) 
00941     m_dbFolderInfo->ReleaseExternalReferences();
00942 
00943   NotifyAnnouncerGoingAway();
00944   NS_IF_RELEASE(m_dbFolderInfo);
00945   if (m_HeaderParser)
00946   {
00947     NS_RELEASE(m_HeaderParser);
00948     m_HeaderParser = nsnull;
00949   }
00950   if (m_mdbAllMsgHeadersTable)
00951     m_mdbAllMsgHeadersTable->Release();
00952   
00953   if (m_mdbAllThreadsTable)
00954     m_mdbAllThreadsTable->Release();
00955 
00956   if (m_mdbStore)
00957     m_mdbStore->Release();
00958 
00959   if (m_mdbEnv)
00960   {
00961     m_mdbEnv->Release(); //??? is this right?
00962     m_mdbEnv = nsnull;
00963   }
00964   if (m_ChangeListeners) 
00965   {
00966     //better not be any listeners, because we're going away.
00967     PRUint32 count;
00968     m_ChangeListeners->Count(&count);
00969     NS_ASSERTION(count == 0, "shouldn't have any listeners");
00970     m_ChangeListeners = nsnull;
00971   }
00972 }
00973 
00974 NS_IMPL_ADDREF(nsMsgDatabase)
00975 
00976 NS_IMPL_RELEASE(nsMsgDatabase)
00977 
00978 NS_IMETHODIMP nsMsgDatabase::QueryInterface(REFNSIID aIID, void** aResult)
00979 {   
00980   if (aResult == NULL)  
00981     return NS_ERROR_NULL_POINTER;  
00982   
00983   if (aIID.Equals(NS_GET_IID(nsIMsgDatabase)) ||
00984     aIID.Equals(NS_GET_IID(nsIDBChangeAnnouncer)) ||
00985     aIID.Equals(NS_GET_IID(nsISupports))) 
00986   {
00987     *aResult = NS_STATIC_CAST(nsIMsgDatabase*, this);   
00988     NS_ADDREF_THIS();
00989     return NS_OK;
00990   }
00991   return NS_NOINTERFACE;
00992 }   
00993 
00994 /* static */ nsIMdbFactory *nsMsgDatabase::GetMDBFactory()
00995 {
00996   static nsIMdbFactory *gMDBFactory = nsnull;
00997   if (!gMDBFactory)
00998   {
00999     nsCOMPtr <nsIMdbFactoryFactory> factoryfactory = do_CreateInstance(kCMorkFactory);
01000     if (factoryfactory)
01001       factoryfactory->GetMdbFactory(&gMDBFactory);
01002   }
01003   return gMDBFactory;
01004 }
01005 
01006 #if defined(XP_WIN) || defined(XP_OS2)
01007 // this code is stolen from nsFileSpecWin. Since MDB requires a native path, for 
01008 // the time being, we'll just take the Unix/Canonical form and munge it
01009 void nsMsgDatabase::UnixToNative(char*& ioPath)
01010 // This just does string manipulation.  It doesn't check reality, or canonify, or
01011 // anything
01012 //----------------------------------------------------------------------------------------
01013 {
01014   // Allow for relative or absolute.  We can do this in place, because the
01015   // native path is never longer.
01016   
01017   if (!ioPath || !*ioPath)
01018     return;
01019               
01020   char* src = ioPath;
01021   if (*ioPath == '/')
01022   {
01023     // Strip initial slash for an absolute path
01024     src++;
01025   }
01026               
01027   // Convert the vertical slash to a colon
01028   char* cp = src + 1;
01029   
01030   // If it was an absolute path, check for the drive letter
01031   if (*ioPath == '/' && strstr(cp, "|/") == cp)
01032     *cp = ':';
01033   
01034   // Convert '/' to '\'.
01035   while (*++cp)
01036   {
01037     if (*cp == '/')
01038       *cp = '\\';
01039   }
01040   
01041   if (*ioPath == '/') {
01042     for (cp = ioPath; *cp; ++cp)
01043       *cp = *(cp + 1);
01044   }
01045 }
01046 #endif /* XP_WIN || XP_OS2 */
01047 
01048 #ifdef XP_MAC
01049 // this code is stolen from nsFileSpecMac. Since MDB requires a native path, for 
01050 // the time being, we'll just take the Unix/Canonical form and munge it
01051 void nsMsgDatabase::UnixToNative(char*& ioPath)
01052 // This just does string manipulation.  It doesn't check reality, or canonify, or
01053 // anything
01054 //----------------------------------------------------------------------------------------
01055 {
01056   // Relying on the fact that the unix path is always longer than the mac path:
01057   size_t len = strlen(ioPath);
01058   char* result = new char[len + 2]; // ... but allow for the initial colon in a partial name
01059   if (result)
01060   {
01061     char* dst = result;
01062     const char* src = ioPath;
01063     if (*src == '/')               // * full path
01064       src++;
01065     else if (strchr(src, '/'))     // * partial path, and not just a leaf name
01066       *dst++ = ':';
01067     strcpy(dst, src);
01068     
01069     while ( *dst != 0)
01070     {
01071       if (*dst == '/')
01072         *dst++ = ':';
01073       else
01074         *dst++;
01075     }
01076     nsCRT::free(ioPath);
01077     ioPath = result;
01078   }
01079 }
01080 
01081 #endif /* XP_MAC */
01082 
01083 
01084 // caller passes in leaveInvalidDB==PR_TRUE if they want back a db even if the db is out of date.
01085 // If so, they'll extract out the interesting info from the db, close it, delete it, and
01086 // then try to open the db again, prior to reparsing.
01087 NS_IMETHODIMP nsMsgDatabase::Open(nsIFileSpec *aFolderName, PRBool aCreate, PRBool aLeaveInvalidDB)
01088 {
01089   PRBool summaryFileExists;
01090   PRBool newFile = PR_FALSE;
01091   PRBool deleteInvalidDB = PR_FALSE;
01092   if (!aFolderName)
01093     return NS_ERROR_NULL_POINTER;
01094   
01095   nsFileSpec         folderName;
01096   aFolderName->GetFileSpec(&folderName);
01097   nsLocalFolderSummarySpec  summarySpec(folderName);
01098   
01099   nsIDBFolderInfo    *folderInfo = nsnull;
01100   
01101 #if defined(DEBUG_bienvenu)
01102   printf("really opening db in nsImapMailDatabase::Open(%s, %s, %p, %s) -> %s\n",
01103     (const char*)folderName, aCreate ? "TRUE":"FALSE",
01104     this, aLeaveInvalidDB ? "TRUE":"FALSE", (const char*)folderName);
01105 #endif
01106   // if the old summary doesn't exist, we're creating a new one.
01107   if ((!summarySpec.Exists() || !summarySpec.GetFileSize()) && aCreate)
01108     newFile = PR_TRUE;
01109   
01110   // stat file before we open the db, because if we've latered
01111   // any messages, handling latered will change time stamp on
01112   // folder file.
01113   summaryFileExists = summarySpec.Exists()  && summarySpec.GetFileSize() > 0;
01114   
01115   nsresult err = NS_MSG_ERROR_FOLDER_SUMMARY_OUT_OF_DATE;
01116   
01117   err = OpenMDB((const char *) summarySpec, aCreate);
01118   if (err == NS_ERROR_FILE_TARGET_DOES_NOT_EXIST)
01119     return err;
01120   
01121   if (NS_SUCCEEDED(err))
01122   {
01123     GetDBFolderInfo(&folderInfo);
01124     if (folderInfo == NULL)
01125     {
01126       err = NS_MSG_ERROR_FOLDER_SUMMARY_OUT_OF_DATE;
01127     }
01128     else
01129     {
01130       if (!newFile && summaryFileExists)
01131       {
01132         PRBool valid;
01133         GetSummaryValid(&valid);
01134         if (!valid)
01135           err = NS_MSG_ERROR_FOLDER_SUMMARY_OUT_OF_DATE;
01136       }
01137       // compare current version of db versus filed out version info.
01138       PRUint32 version;
01139       folderInfo->GetVersion(&version);
01140       if (GetCurVersion() != version)
01141         err = NS_MSG_ERROR_FOLDER_SUMMARY_OUT_OF_DATE;
01142       NS_RELEASE(folderInfo);
01143     }
01144     if (NS_FAILED(err) && !aLeaveInvalidDB)
01145       deleteInvalidDB = PR_TRUE;
01146   }
01147   else
01148   {
01149     err = NS_MSG_ERROR_FOLDER_SUMMARY_OUT_OF_DATE;
01150     deleteInvalidDB = PR_TRUE;
01151   }
01152 
01153   if (deleteInvalidDB)
01154   {
01155     // this will make the db folder info release its ref to the mail db...
01156     NS_IF_RELEASE(m_dbFolderInfo);
01157     ForceClosed();
01158     if (err == NS_MSG_ERROR_FOLDER_SUMMARY_OUT_OF_DATE)
01159       summarySpec.Delete(PR_FALSE);
01160   }
01161   if (err != NS_OK || newFile)
01162   {
01163     // if we couldn't open file, or we have a blank one, and we're supposed 
01164     // to upgrade, updgrade it.
01165     if (newFile && !aLeaveInvalidDB)      // caller is upgrading, and we have empty summary file,
01166     {                              // leave db around and open so caller can upgrade it.
01167       err = NS_MSG_ERROR_FOLDER_SUMMARY_MISSING;
01168     }
01169     else if (err != NS_OK && err != NS_MSG_ERROR_FOLDER_SUMMARY_OUT_OF_DATE)
01170     {
01171       Close(PR_FALSE);
01172       summarySpec.Delete(PR_FALSE);  // blow away the db if it's corrupt.
01173     }
01174   }
01175   if (err == NS_OK || err == NS_MSG_ERROR_FOLDER_SUMMARY_MISSING)
01176     AddToCache(this);
01177   return (summaryFileExists) ? err : NS_MSG_ERROR_FOLDER_SUMMARY_MISSING;
01178 }
01179 
01180 // Open the MDB database synchronously. If successful, this routine
01181 // will set up the m_mdbStore and m_mdbEnv of the database object 
01182 // so other database calls can work.
01183 nsresult nsMsgDatabase::OpenMDB(const char *dbName, PRBool create)
01184 {
01185   nsresult ret = NS_OK;
01186   nsIMdbFactory *myMDBFactory = GetMDBFactory();
01187   if (myMDBFactory)
01188   {
01189     ret = myMDBFactory->MakeEnv(NULL, &m_mdbEnv);
01190     if (NS_SUCCEEDED(ret))
01191     {
01192       nsIMdbThumb *thumb = nsnull;
01193       struct stat st;
01194       char    *nativeFileName = nsCRT::strdup(dbName);
01195       nsIMdbHeap* dbHeap = 0;
01196       mdb_bool dbFrozen = mdbBool_kFalse; // not readonly, we want modifiable
01197       
01198       if (!nativeFileName)
01199         return NS_ERROR_OUT_OF_MEMORY;
01200       
01201       if (m_mdbEnv)
01202         m_mdbEnv->SetAutoClear(PR_TRUE);
01203       m_dbName = dbName;
01204 #if defined(XP_WIN) || defined(XP_OS2) || defined(XP_MAC)
01205       UnixToNative(nativeFileName);
01206 #endif
01207       if (stat(nativeFileName, &st)) 
01208         ret = NS_MSG_ERROR_FOLDER_SUMMARY_MISSING;
01209       else
01210       {
01211         mdbOpenPolicy inOpenPolicy;
01212         mdb_bool     canOpen;
01213         mdbYarn             outFormatVersion;
01214         
01215         nsIMdbFile* oldFile = 0;
01216         ret = myMDBFactory->OpenOldFile(m_mdbEnv, dbHeap, nativeFileName,
01217           dbFrozen, &oldFile);
01218         if ( oldFile )
01219         {
01220           if ( ret == NS_OK )
01221           {
01222             ret = myMDBFactory->CanOpenFilePort(m_mdbEnv, oldFile, // the file to investigate
01223               &canOpen, &outFormatVersion);
01224             if (ret == 0 && canOpen)
01225             {
01226               inOpenPolicy.mOpenPolicy_ScopePlan.mScopeStringSet_Count = 0;
01227               inOpenPolicy.mOpenPolicy_MinMemory = 0;
01228               inOpenPolicy.mOpenPolicy_MaxLazy = 0;
01229               
01230               ret = myMDBFactory->OpenFileStore(m_mdbEnv, dbHeap,
01231                 oldFile, &inOpenPolicy, &thumb); 
01232             }
01233             else
01234               ret = NS_MSG_ERROR_FOLDER_SUMMARY_OUT_OF_DATE;
01235           }
01236           NS_RELEASE(oldFile); // always release our file ref, store has own
01237         }
01238       }
01239       if (NS_SUCCEEDED(ret) && thumb)
01240       {
01241         mdb_count outTotal;    // total somethings to do in operation
01242         mdb_count outCurrent;  // subportion of total completed so far
01243         mdb_bool outDone = PR_FALSE;      // is operation finished?
01244         mdb_bool outBroken;     // is operation irreparably dead and broken?
01245         do
01246         {
01247           ret = thumb->DoMore(m_mdbEnv, &outTotal, &outCurrent, &outDone, &outBroken);
01248           if (ret != 0)
01249           {// mork isn't really doing NS erorrs yet.
01250             outDone = PR_TRUE;
01251             break;
01252           }
01253         }
01254         while (NS_SUCCEEDED(ret) && !outBroken && !outDone);
01255         //                         m_mdbEnv->ClearErrors(); // ### temporary...
01256         // only 0 is a non-error return.
01257         if (ret == 0 && outDone)
01258         {
01259           ret = myMDBFactory->ThumbToOpenStore(m_mdbEnv, thumb, &m_mdbStore);
01260           if (ret == NS_OK)
01261             ret = (m_mdbStore) ? InitExistingDB() : NS_ERROR_FAILURE;
01262         }
01263 #ifdef DEBUG_bienvenu1
01264         DumpContents();
01265 #endif
01266       }
01267       else if (create)      // ### need error code saying why open file store failed
01268       {
01269         nsIMdbFile* newFile = 0;
01270         ret = myMDBFactory->CreateNewFile(m_mdbEnv, dbHeap, dbName, &newFile);
01271         if (NS_FAILED(ret))
01272           ret = NS_ERROR_FILE_TARGET_DOES_NOT_EXIST;
01273         if ( newFile )
01274         {
01275           if (ret == NS_OK)
01276           {
01277             mdbOpenPolicy inOpenPolicy;
01278             
01279             inOpenPolicy.mOpenPolicy_ScopePlan.mScopeStringSet_Count = 0;
01280             inOpenPolicy.mOpenPolicy_MinMemory = 0;
01281             inOpenPolicy.mOpenPolicy_MaxLazy = 0;
01282             
01283             ret = myMDBFactory->CreateNewFileStore(m_mdbEnv, dbHeap,
01284               newFile, &inOpenPolicy, &m_mdbStore);
01285             if (ret == NS_OK)
01286               ret = (m_mdbStore) ? InitNewDB() : NS_ERROR_FAILURE;
01287           }
01288           NS_RELEASE(newFile); // always release our file ref, store has own
01289         }
01290       }
01291       NS_IF_RELEASE(thumb);
01292       nsCRT::free(nativeFileName);
01293     }
01294   }
01295 #ifdef DEBUG_David_Bienvenu
01296 //  NS_ASSERTION(NS_SUCCEEDED(ret), "failed opening mdb");
01297 #endif
01298   return ret;
01299 }
01300 
01301 nsresult nsMsgDatabase::CloseMDB(PRBool commit)
01302 {
01303   if (commit)
01304     Commit(nsMsgDBCommitType::kSessionCommit);
01305   return(NS_OK);
01306 }
01307 
01308 NS_IMETHODIMP nsMsgDatabase::ForceFolderDBClosed(nsIMsgFolder *aFolder)
01309 {
01310   NS_ENSURE_ARG(aFolder);
01311   nsCOMPtr <nsIFileSpec> folderPath;
01312   nsFileSpec         folderName;
01313 
01314   nsresult rv = aFolder->GetPath(getter_AddRefs(folderPath));
01315   NS_ENSURE_SUCCESS(rv, rv);
01316   folderPath->GetFileSpec(&folderName);
01317   nsLocalFolderSummarySpec  summarySpec(folderName);
01318   
01319   
01320   nsFileSpec dbPath(summarySpec);
01321   
01322   nsIMsgDatabase *mailDB = (nsMsgDatabase *) FindInCache(dbPath);
01323   if (mailDB)
01324   {
01325     mailDB->ForceClosed();
01326    //FindInCache AddRef's
01327     mailDB->Release();
01328   }
01329   return(NS_OK);
01330  }
01331 
01332 
01333 // force the database to close - this'll flush out anybody holding onto
01334 // a database without having a listener!
01335 // This is evil in the com world, but there are times we need to delete the file.
01336 NS_IMETHODIMP nsMsgDatabase::ForceClosed()
01337 {
01338   nsresult    err = NS_OK;
01339   
01340   // make sure someone has a reference so object won't get deleted out from under us.
01341   AddRef();   
01342   NotifyAnnouncerGoingAway();
01343   // make sure dbFolderInfo isn't holding onto mork stuff because mork db is going away
01344   if (m_dbFolderInfo)
01345     m_dbFolderInfo->ReleaseExternalReferences();
01346   NS_IF_RELEASE(m_dbFolderInfo);
01347   
01348   err = CloseMDB(PR_FALSE); // since we're about to delete it, no need to commit.
01349   ClearCachedObjects(PR_TRUE);
01350   if (m_mdbAllMsgHeadersTable)
01351   {
01352     m_mdbAllMsgHeadersTable->Release();
01353     m_mdbAllMsgHeadersTable = nsnull;
01354   }
01355   if (m_mdbAllThreadsTable)
01356   {
01357     m_mdbAllThreadsTable->Release();
01358     m_mdbAllThreadsTable = nsnull;
01359   }
01360   if (m_mdbStore)
01361   {
01362     m_mdbStore->Release();
01363     m_mdbStore = nsnull;
01364   }
01365   if (m_ChangeListeners) 
01366   {
01367     PRUint32 count;
01368     m_ChangeListeners->Count(&count);
01369     // better not be any listeners, because we're going away.
01370     NS_ASSERTION(count == 0, "shouldn't have any listeners left");
01371   }
01372   Release();
01373   return err;
01374 }
01375 
01376 // caller must Release result.
01377 NS_IMETHODIMP nsMsgDatabase::GetDBFolderInfo(nsIDBFolderInfo   **result)
01378 {
01379   *result = m_dbFolderInfo;
01380   if (m_dbFolderInfo)
01381   {
01382     m_dbFolderInfo->AddRef();
01383     return NS_OK;
01384   }
01385   else
01386   {
01387     NS_ASSERTION(PR_FALSE, "db must be corrupt");
01388     return NS_ERROR_NULL_POINTER; // it's an error if we can't get this
01389   }
01390 }
01391 
01392 NS_IMETHODIMP nsMsgDatabase::Commit(nsMsgDBCommit commitType)
01393 {
01394   nsresult    err = NS_OK;
01395   nsIMdbThumb *commitThumb = NULL;
01396   
01397 #ifdef DEBUG_seth
01398   printf("nsMsgDatabase::Commit(%d)\n",commitType);
01399 #endif
01400   
01401   if (commitType == nsMsgDBCommitType::kLargeCommit || commitType == nsMsgDBCommitType::kSessionCommit)
01402   {
01403     mdb_percent outActualWaste = 0;
01404     mdb_bool outShould;
01405     if (m_mdbStore) {
01406       err = m_mdbStore->ShouldCompress(GetEnv(), 30, &outActualWaste, &outShould);
01407       if (NS_SUCCEEDED(err) && outShould)
01408         commitType = nsMsgDBCommitType::kCompressCommit;
01409     }
01410   }
01411   //   commitType = nsMsgDBCommitType::kCompressCommit; // ### until incremental writing works.
01412   
01413   if (m_mdbStore)
01414   {
01415     switch (commitType)
01416     {
01417     case nsMsgDBCommitType::kSmallCommit:
01418       err = m_mdbStore->SmallCommit(GetEnv());
01419       break;
01420     case nsMsgDBCommitType::kLargeCommit:
01421       err = m_mdbStore->LargeCommit(GetEnv(), &commitThumb);
01422       break;
01423     case nsMsgDBCommitType::kSessionCommit:
01424       err = m_mdbStore->SessionCommit(GetEnv(), &commitThumb);
01425       break;
01426     case nsMsgDBCommitType::kCompressCommit:
01427       err = m_mdbStore->CompressCommit(GetEnv(), &commitThumb);
01428       break;
01429     }
01430   }
01431   if (commitThumb)
01432   {
01433     mdb_count outTotal = 0;    // total somethings to do in operation
01434     mdb_count outCurrent = 0;  // subportion of total completed so far
01435     mdb_bool outDone = PR_FALSE;      // is operation finished?
01436     mdb_bool outBroken = PR_FALSE;     // is operation irreparably dead and broken?
01437     while (!outDone && !outBroken && err == NS_OK)
01438     {
01439       err = commitThumb->DoMore(GetEnv(), &outTotal, &outCurrent, &outDone, &outBroken);
01440     }
01441     
01442     NS_IF_RELEASE(commitThumb);
01443   }
01444   // ### do something with error, but clear it now because mork errors out on commits.
01445   if (GetEnv())
01446     GetEnv()->ClearErrors();
01447   
01448   nsresult rv;
01449   nsCOMPtr<nsIMsgAccountManager> accountManager = 
01450     do_GetService(NS_MSGACCOUNTMANAGER_CONTRACTID, &rv);
01451   if (NS_SUCCEEDED(rv) && accountManager)
01452   {
01453     nsCOMPtr<nsIMsgFolderCache> folderCache;
01454     
01455     rv = accountManager->GetFolderCache(getter_AddRefs(folderCache));
01456     if (NS_SUCCEEDED(rv) && folderCache)
01457     {
01458       nsCOMPtr <nsIMsgFolderCacheElement> cacheElement;
01459       rv = folderCache->GetCacheElement(m_dbName, PR_FALSE, getter_AddRefs(cacheElement));
01460       if (NS_SUCCEEDED(rv) && cacheElement && m_dbFolderInfo)
01461       {
01462         PRInt32 totalMessages, unreadMessages, pendingMessages, pendingUnreadMessages;
01463         
01464         m_dbFolderInfo->GetNumMessages(&totalMessages);
01465         m_dbFolderInfo->GetNumUnreadMessages(&unreadMessages);
01466         m_dbFolderInfo->GetImapUnreadPendingMessages(&pendingUnreadMessages);
01467         m_dbFolderInfo->GetImapTotalPendingMessages(&pendingMessages);
01468         cacheElement->SetInt32Property("totalMsgs", totalMessages);
01469         cacheElement->SetInt32Property("totalUnreadMsgs", unreadMessages);
01470         cacheElement->SetInt32Property("pendingMsgs", pendingMessages);
01471         cacheElement->SetInt32Property("pendingUnreadMsgs", pendingUnreadMessages);
01472         folderCache->Commit(PR_FALSE);
01473       }
01474     }
01475   }
01476   
01477   return err;
01478 }
01479 
01480 NS_IMETHODIMP nsMsgDatabase::Close(PRBool forceCommit /* = TRUE */)
01481 {
01482   return CloseMDB(forceCommit);
01483 }
01484 
01485 const char *kMsgHdrsScope = "ns:msg:db:row:scope:msgs:all";    // scope for all headers table
01486 const char *kMsgHdrsTableKind = "ns:msg:db:table:kind:msgs";
01487 const char *kThreadTableKind = "ns:msg:db:table:kind:thread";
01488 const char *kThreadHdrsScope = "ns:msg:db:row:scope:threads:all"; // scope for all threads table
01489 const char *kAllThreadsTableKind = "ns:msg:db:table:kind:allthreads"; // kind for table of all threads
01490 const char *kSubjectColumnName = "subject";
01491 const char *kSenderColumnName = "sender";
01492 const char *kMessageIdColumnName = "message-id";
01493 const char *kReferencesColumnName = "references";
01494 const char *kRecipientsColumnName = "recipients";
01495 const char *kDateColumnName = "date";
01496 const char *kMessageSizeColumnName = "size";
01497 const char *kFlagsColumnName = "flags";
01498 const char *kPriorityColumnName = "priority";
01499 const char *kLabelColumnName = "label";
01500 const char *kStatusOffsetColumnName = "statusOfset";
01501 const char *kNumLinesColumnName = "numLines";
01502 const char *kCCListColumnName = "ccList";
01503 const char *kMessageThreadIdColumnName = "msgThreadId";
01504 const char *kNumReferencesColumnName = "numRefs";
01505 const char *kThreadFlagsColumnName = "threadFlags";
01506 const char *kThreadIdColumnName = "threadId";
01507 const char *kThreadChildrenColumnName = "children";
01508 const char *kThreadUnreadChildrenColumnName = "unreadChildren";
01509 const char *kThreadSubjectColumnName = "threadSubject";
01510 const char *kMessageCharSetColumnName = "msgCharSet";
01511 const char *kThreadParentColumnName = "threadParent";
01512 const char *kThreadRootColumnName = "threadRoot";
01513 const char *kThreadNewestMsgDateColumnName = "threadNewestMsgDate";
01514 const char *kOfflineMsgOffsetColumnName = "msgOffset";
01515 const char *kOfflineMsgSizeColumnName = "offlineMsgSize";
01516 struct mdbOid gAllMsgHdrsTableOID;
01517 struct mdbOid gAllThreadsTableOID;
01518 const char *kFixedBadRefThreadingProp = "fixedBadRefThreading";
01519 
01520 // set up empty tables, dbFolderInfo, etc.
01521 nsresult nsMsgDatabase::InitNewDB()
01522 {
01523   nsresult err = NS_OK;
01524   
01525   err = InitMDBInfo();
01526   if (err == NS_OK)
01527   {
01528     nsDBFolderInfo *dbFolderInfo = new nsDBFolderInfo(this); 
01529     if (dbFolderInfo)
01530     {
01531       NS_ADDREF(dbFolderInfo); 
01532       err = dbFolderInfo->AddToNewMDB();
01533       dbFolderInfo->SetVersion(GetCurVersion());
01534       dbFolderInfo->SetBooleanProperty(kFixedBadRefThreadingProp, PR_TRUE);
01535       nsIMdbStore *store = GetStore();
01536       // create the unique table for the dbFolderInfo.
01537       mdb_err mdberr;
01538       struct mdbOid allMsgHdrsTableOID;
01539       struct mdbOid allThreadsTableOID;
01540       if (!store)
01541         return NS_ERROR_NULL_POINTER;
01542       
01543       allMsgHdrsTableOID.mOid_Scope = m_hdrRowScopeToken;
01544       allMsgHdrsTableOID.mOid_Id = kAllMsgHdrsTableKey;
01545       allThreadsTableOID.mOid_Scope = m_threadRowScopeToken;
01546       allThreadsTableOID.mOid_Id = kAllThreadsTableKey;
01547       
01548       mdberr  = store->NewTableWithOid(GetEnv(), &allMsgHdrsTableOID, m_hdrTableKindToken, 
01549         PR_FALSE, nsnull, &m_mdbAllMsgHeadersTable);
01550       
01551       // error here is not fatal.
01552       store->NewTableWithOid(GetEnv(), &allThreadsTableOID, m_allThreadsTableKindToken, 
01553         PR_FALSE, nsnull, &m_mdbAllThreadsTable);
01554 
01555       m_dbFolderInfo = dbFolderInfo;
01556       
01557     }
01558     else
01559       err = NS_ERROR_OUT_OF_MEMORY;
01560   }
01561   return err;
01562 }
01563 
01564 nsresult nsMsgDatabase::GetTableCreateIfMissing(const char *scope, const char *kind, nsIMdbTable **table, 
01565                                                 mdb_token &scopeToken, mdb_token &kindToken)
01566 {
01567   struct mdbOid tableOID;
01568 
01569   mdb_err err = GetStore()->StringToToken(GetEnv(), scope, &scopeToken); 
01570   err = GetStore()->StringToToken(GetEnv(), kind, &kindToken); 
01571   tableOID.mOid_Scope = scopeToken;
01572   tableOID.mOid_Id = 1;
01573   
01574   nsresult rv = GetStore()->GetTable(GetEnv(), &tableOID, table);
01575   if (rv != NS_OK)
01576     rv = NS_ERROR_FAILURE;
01577   
01578   // create new all all offline ops table, if it doesn't exist.
01579   if (NS_SUCCEEDED(rv) && !*table)
01580   {
01581     nsIMdbStore *store = GetStore();
01582     err = (nsresult) store->NewTable(GetEnv(), scopeToken,kindToken, 
01583                                     PR_FALSE, nsnull, table);
01584     if (err != NS_OK || !*table)
01585       rv = NS_ERROR_FAILURE;
01586   }
01587   NS_ASSERTION(NS_SUCCEEDED(rv), "couldn't create offline ops table");
01588   return rv;
01589 }
01590 
01591 nsresult nsMsgDatabase::InitExistingDB()
01592 {
01593   nsresult err = NS_OK;
01594   
01595   err = InitMDBInfo();
01596   if (err == NS_OK)
01597   {
01598     err = GetStore()->GetTable(GetEnv(), &gAllMsgHdrsTableOID, &m_mdbAllMsgHeadersTable);
01599     if (err == NS_OK)
01600     {
01601       m_dbFolderInfo = new nsDBFolderInfo(this);
01602       if (m_dbFolderInfo)
01603       {
01604         NS_ADDREF(m_dbFolderInfo); 
01605         err = m_dbFolderInfo->InitFromExistingDB();
01606       }
01607     }
01608     else
01609       err = NS_ERROR_FAILURE;
01610     
01611     NS_ASSERTION(NS_SUCCEEDED(err), "failed initing existing db");
01612     NS_ENSURE_SUCCESS(err, err);
01613     // create new all msg hdrs table, if it doesn't exist.
01614     if (NS_SUCCEEDED(err) && !m_mdbAllMsgHeadersTable)
01615     {
01616       struct mdbOid allMsgHdrsTableOID;
01617       allMsgHdrsTableOID.mOid_Scope = m_hdrRowScopeToken;
01618       allMsgHdrsTableOID.mOid_Id = kAllMsgHdrsTableKey;
01619       
01620       mdb_err mdberr  = GetStore()->NewTableWithOid(GetEnv(), &allMsgHdrsTableOID, m_hdrTableKindToken, 
01621         PR_FALSE, nsnull, &m_mdbAllMsgHeadersTable);
01622       if (mdberr != NS_OK || !m_mdbAllMsgHeadersTable)
01623         err = NS_ERROR_FAILURE;
01624     }
01625     struct mdbOid allThreadsTableOID;
01626     allThreadsTableOID.mOid_Scope = m_threadRowScopeToken;
01627     allThreadsTableOID.mOid_Id = kAllThreadsTableKey;
01628     err = GetStore()->GetTable(GetEnv(), &gAllThreadsTableOID, &m_mdbAllThreadsTable);
01629     if (!m_mdbAllThreadsTable)
01630     {
01631       
01632       mdb_err mdberr  = GetStore()->NewTableWithOid(GetEnv(), &allThreadsTableOID, m_allThreadsTableKindToken, 
01633         PR_FALSE, nsnull, &m_mdbAllThreadsTable);
01634       if (mdberr != NS_OK || !m_mdbAllThreadsTable)
01635         err = NS_ERROR_FAILURE;
01636     }
01637   }
01638   if (NS_SUCCEEDED(err) && m_dbFolderInfo)
01639   {
01640     PRBool fixedBadRefThreading;
01641     m_dbFolderInfo->GetBooleanProperty(kFixedBadRefThreadingProp, PR_FALSE, &fixedBadRefThreading);
01642     if (!fixedBadRefThreading)
01643     {
01644       nsCOMPtr <nsISimpleEnumerator> enumerator;
01645       err = EnumerateMessages(getter_AddRefs(enumerator));
01646       if (NS_SUCCEEDED(err) && enumerator)
01647       {
01648         PRBool hasMore;
01649         
01650         while (NS_SUCCEEDED(err = enumerator->HasMoreElements(&hasMore)) && (hasMore == PR_TRUE)) 
01651         {
01652           nsCOMPtr <nsIMsgDBHdr> msgHdr;
01653           err = enumerator->GetNext(getter_AddRefs(msgHdr));
01654           NS_ASSERTION(NS_SUCCEEDED(err), "nsMsgDBEnumerator broken");
01655           if (msgHdr && NS_SUCCEEDED(err))
01656           {
01657             nsXPIDLCString messageId;
01658             nsCAutoString firstReference;
01659             msgHdr->GetMessageId(getter_Copies(messageId));
01660             msgHdr->GetStringReference(0, firstReference);
01661             if (messageId.Equals(firstReference))
01662             {
01663               err = NS_MSG_ERROR_FOLDER_SUMMARY_OUT_OF_DATE;
01664               break;
01665             }
01666           }
01667         }
01668       }
01669             
01670       m_dbFolderInfo->SetBooleanProperty(kFixedBadRefThreadingProp, PR_TRUE);
01671     }
01672     
01673   }
01674   return err;
01675 }
01676 
01677 // initialize the various tokens and tables in our db's env
01678 nsresult nsMsgDatabase::InitMDBInfo()
01679 {
01680   nsresult err = NS_OK;
01681   
01682   if (!m_mdbTokensInitialized && GetStore())
01683   {
01684     m_mdbTokensInitialized = PR_TRUE;
01685     err       = GetStore()->StringToToken(GetEnv(), kMsgHdrsScope, &m_hdrRowScopeToken); 
01686     if (err == NS_OK)
01687     {
01688       GetStore()->StringToToken(GetEnv(),  kSubjectColumnName, &m_subjectColumnToken);
01689       GetStore()->StringToToken(GetEnv(),  kSenderColumnName, &m_senderColumnToken);
01690       GetStore()->StringToToken(GetEnv(),  kMessageIdColumnName, &m_messageIdColumnToken);
01691       // if we just store references as a string, we won't get any savings from the
01692       // fact there's a lot of duplication. So we may want to break them up into
01693       // multiple columns, r1, r2, etc.
01694       GetStore()->StringToToken(GetEnv(),  kReferencesColumnName, &m_referencesColumnToken);
01695       // similarly, recipients could be tokenized properties
01696       GetStore()->StringToToken(GetEnv(),  kRecipientsColumnName, &m_recipientsColumnToken);
01697       GetStore()->StringToToken(GetEnv(),  kDateColumnName, &m_dateColumnToken);
01698       GetStore()->StringToToken(GetEnv(),  kMessageSizeColumnName, &m_messageSizeColumnToken);
01699       GetStore()->StringToToken(GetEnv(),  kFlagsColumnName, &m_flagsColumnToken);
01700       GetStore()->StringToToken(GetEnv(),  kPriorityColumnName, &m_priorityColumnToken);
01701       GetStore()->StringToToken(GetEnv(),  kLabelColumnName, &m_labelColumnToken);
01702       GetStore()->StringToToken(GetEnv(),  kStatusOffsetColumnName, &m_statusOffsetColumnToken);
01703       GetStore()->StringToToken(GetEnv(),  kNumLinesColumnName, &m_numLinesColumnToken);
01704       GetStore()->StringToToken(GetEnv(),  kCCListColumnName, &m_ccListColumnToken);
01705       GetStore()->StringToToken(GetEnv(),  kMessageThreadIdColumnName, &m_messageThreadIdColumnToken);
01706       GetStore()->StringToToken(GetEnv(),  kThreadIdColumnName, &m_threadIdColumnToken);
01707       GetStore()->StringToToken(GetEnv(),  kThreadFlagsColumnName, &m_threadFlagsColumnToken);
01708       GetStore()->StringToToken(GetEnv(),  kThreadNewestMsgDateColumnName, &m_threadNewestMsgDateColumnToken);
01709       GetStore()->StringToToken(GetEnv(),  kThreadChildrenColumnName, &m_threadChildrenColumnToken);
01710       GetStore()->StringToToken(GetEnv(),  kThreadUnreadChildrenColumnName, &m_threadUnreadChildrenColumnToken);
01711       GetStore()->StringToToken(GetEnv(),  kThreadSubjectColumnName, &m_threadSubjectColumnToken);
01712       GetStore()->StringToToken(GetEnv(),  kNumReferencesColumnName, &m_numReferencesColumnToken);
01713       GetStore()->StringToToken(GetEnv(),  kMessageCharSetColumnName, &m_messageCharSetColumnToken);
01714       err = GetStore()->StringToToken(GetEnv(), kMsgHdrsTableKind, &m_hdrTableKindToken); 
01715       if (err == NS_OK)
01716         err = GetStore()->StringToToken(GetEnv(), kThreadTableKind, &m_threadTableKindToken);
01717       err = GetStore()->StringToToken(GetEnv(), kAllThreadsTableKind, &m_allThreadsTableKindToken); 
01718       err     = GetStore()->StringToToken(GetEnv(), kThreadHdrsScope, &m_threadRowScopeToken); 
01719       err     = GetStore()->StringToToken(GetEnv(), kThreadParentColumnName, &m_threadParentColumnToken);
01720       err     = GetStore()->StringToToken(GetEnv(), kThreadRootColumnName, &m_threadRootKeyColumnToken);
01721       err = GetStore()->StringToToken(GetEnv(), kOfflineMsgOffsetColumnName, &m_offlineMsgOffsetColumnToken);
01722       err = GetStore()->StringToToken(GetEnv(), kOfflineMsgSizeColumnName, &m_offlineMessageSizeColumnToken);
01723       
01724       if (err == NS_OK)
01725       {
01726         // The table of all message hdrs will have table id 1.
01727         gAllMsgHdrsTableOID.mOid_Scope = m_hdrRowScopeToken;
01728         gAllMsgHdrsTableOID.mOid_Id = kAllMsgHdrsTableKey;
01729         gAllThreadsTableOID.mOid_Scope = m_threadRowScopeToken;
01730         gAllThreadsTableOID.mOid_Id = kAllThreadsTableKey;
01731         
01732       }
01733     }
01734   }
01735   return err;
01736 }
01737 
01738 // Returns if the db contains this key
01739 NS_IMETHODIMP nsMsgDatabase::ContainsKey(nsMsgKey key, PRBool *containsKey)
01740 {
01741   
01742   nsresult    err = NS_OK;
01743   mdb_bool    hasOid;
01744   mdbOid             rowObjectId;
01745   
01746   if (!containsKey || !m_mdbAllMsgHeadersTable)
01747     return NS_ERROR_NULL_POINTER;
01748   *containsKey = PR_FALSE;
01749   
01750   rowObjectId.mOid_Id = key;
01751   rowObjectId.mOid_Scope = m_hdrRowScopeToken;
01752   err = m_mdbAllMsgHeadersTable->HasOid(GetEnv(), &rowObjectId, &hasOid);
01753   if(NS_SUCCEEDED(err))
01754     *containsKey = hasOid;
01755   
01756   return err;
01757 }
01758 
01759 // get a message header for the given key. Caller must release()!
01760 NS_IMETHODIMP nsMsgDatabase::GetMsgHdrForKey(nsMsgKey key, nsIMsgDBHdr **pmsgHdr)
01761 {
01762   nsresult    err = NS_OK;
01763   mdb_bool    hasOid;
01764   mdbOid             rowObjectId;
01765   
01766 #ifdef DEBUG_bienvenu1
01767   NS_ASSERTION(m_folder, "folder should be set");
01768 #endif
01769   
01770   if (!pmsgHdr || !m_mdbAllMsgHeadersTable)
01771     return NS_ERROR_NULL_POINTER;
01772   
01773   *pmsgHdr = NULL;
01774   err = GetHdrFromUseCache(key, pmsgHdr);
01775   if (NS_SUCCEEDED(err) && *pmsgHdr)
01776     return err;
01777   
01778   rowObjectId.mOid_Id = key;
01779   rowObjectId.mOid_Scope = m_hdrRowScopeToken;
01780   err = m_mdbAllMsgHeadersTable->HasOid(GetEnv(), &rowObjectId, &hasOid);
01781   if (err == NS_OK && m_mdbStore /* && hasOid */)
01782   {
01783     nsIMdbRow *hdrRow;
01784     err = m_mdbStore->GetRow(GetEnv(), &rowObjectId, &hdrRow);
01785     
01786     if (err == NS_OK)
01787     {
01788       if (!hdrRow)
01789       {
01790         err = NS_ERROR_NULL_POINTER;
01791       }
01792       else
01793       {
01794         //                  NS_ASSERTION(hasOid, "we had oid, right?");
01795         err = CreateMsgHdr(hdrRow,  key, pmsgHdr);
01796       }
01797     }
01798   }
01799   
01800   return err;
01801 }
01802 
01803 NS_IMETHODIMP nsMsgDatabase::StartBatch()
01804 {
01805   return NS_OK;
01806 }
01807 
01808 NS_IMETHODIMP nsMsgDatabase::EndBatch()
01809 {
01810   return NS_OK;
01811 }
01812 
01813 NS_IMETHODIMP nsMsgDatabase::DeleteMessage(nsMsgKey key, nsIDBChangeListener *instigator, PRBool commit)
01814 {
01815   nsCOMPtr <nsIMsgDBHdr> msgHdr;
01816   
01817   nsresult rv = GetMsgHdrForKey(key, getter_AddRefs(msgHdr));
01818   if (!msgHdr)
01819     return NS_MSG_MESSAGE_NOT_FOUND;
01820   
01821   rv = DeleteHeader(msgHdr, instigator, commit, PR_TRUE);
01822   return rv;
01823 }
01824 
01825 
01826 NS_IMETHODIMP nsMsgDatabase::DeleteMessages(nsMsgKeyArray* nsMsgKeys, nsIDBChangeListener *instigator)
01827 {
01828   nsresult    err = NS_OK;
01829   
01830   PRUint32 kindex;
01831   for (kindex = 0; kindex < nsMsgKeys->GetSize(); kindex++)
01832   {
01833     nsMsgKey key = nsMsgKeys->ElementAt(kindex);
01834     nsCOMPtr <nsIMsgDBHdr> msgHdr;
01835     
01836     PRBool hasKey;
01837     
01838     if (NS_SUCCEEDED(ContainsKey(key, &hasKey)) && hasKey)
01839     {
01840       err = GetMsgHdrForKey(key, getter_AddRefs(msgHdr));
01841       if (NS_FAILED(err)) 
01842       {
01843         err = NS_MSG_MESSAGE_NOT_FOUND;
01844         break;
01845       }
01846       if (msgHdr)
01847         err = DeleteHeader(msgHdr, instigator, kindex % 300 == 0, PR_TRUE);
01848       if (err != NS_OK)
01849         break;
01850     }
01851   }
01852   Commit(nsMsgDBCommitType::kSmallCommit);
01853   return err;
01854 }
01855 
01856 nsresult nsMsgDatabase::AdjustExpungedBytesOnDelete(nsIMsgDBHdr *msgHdr)
01857 {
01858   PRUint32 size = 0;
01859   (void)msgHdr->GetMessageSize(&size);
01860   return m_dbFolderInfo->ChangeExpungedBytes (size);
01861 }
01862 
01863 NS_IMETHODIMP nsMsgDatabase::DeleteHeader(nsIMsgDBHdr *msg, nsIDBChangeListener *instigator, PRBool commit, PRBool notify)
01864 {
01865   nsMsgHdr* msgHdr = NS_STATIC_CAST(nsMsgHdr*, msg);  // closed system, so this is ok
01866   nsMsgKey key;
01867   (void)msg->GetMessageKey(&key);
01868   // only need to do this for mail - will this speed up news expiration? 
01869   SetHdrFlag(msg, PR_TRUE, MSG_FLAG_EXPUNGED);   // tell mailbox (mail)
01870   
01871   m_newSet.RemoveElement(key);
01872   
01873   if (m_dbFolderInfo != NULL)
01874   {
01875     PRBool isRead;
01876     m_dbFolderInfo->ChangeNumMessages(-1);
01877     IsRead(key, &isRead);
01878     if (!isRead)
01879       m_dbFolderInfo->ChangeNumUnreadMessages(-1);
01880     AdjustExpungedBytesOnDelete(msg);
01881   }    
01882   
01883   PRUint32 flags;
01884   nsMsgKey threadParent;
01885   
01886   //Save off flags and threadparent since they will no longer exist after we remove the header from the db.
01887   if (notify)
01888   {
01889     
01890     (void)msg->GetFlags(&flags);
01891     msg->GetThreadParent(&threadParent);
01892   }
01893   
01894   RemoveHeaderFromThread(msgHdr);
01895   if (notify /* && NS_SUCCEEDED(ret)*/)
01896   {
01897     
01898     NotifyHdrDeletedAll(msg, threadParent, flags, instigator); // tell listeners
01899   }
01900   //   if (!onlyRemoveFromThread)  // to speed up expiration, try this. But really need to do this in RemoveHeaderFromDB
01901   nsresult ret = RemoveHeaderFromDB(msgHdr);
01902   
01903   
01904   if (commit)
01905     Commit(nsMsgDBCommitType::kLargeCommit);                   // ### dmb is this a good time to commit?
01906   return ret;
01907 }
01908 
01909 NS_IMETHODIMP
01910 nsMsgDatabase::UndoDelete(nsIMsgDBHdr *aMsgHdr)
01911 {
01912     if (aMsgHdr)
01913     {
01914         nsMsgHdr* msgHdr = NS_STATIC_CAST(nsMsgHdr*, aMsgHdr);  // closed system, so this is ok
01915         // force deleted flag, so SetHdrFlag won't bail out because  deleted flag isn't set
01916         msgHdr->m_flags |= MSG_FLAG_EXPUNGED; 
01917         SetHdrFlag(msgHdr, PR_FALSE, MSG_FLAG_EXPUNGED); // clear deleted flag in db
01918     }
01919     return NS_OK;
01920 }
01921 
01922 nsresult nsMsgDatabase::RemoveHeaderFromThread(nsMsgHdr *msgHdr)
01923 {
01924   if (!msgHdr)
01925     return NS_ERROR_NULL_POINTER;
01926   nsresult ret = NS_OK;
01927   nsCOMPtr <nsIMsgThread> thread ;
01928   ret = GetThreadContainingMsgHdr(msgHdr, getter_AddRefs(thread));
01929   if (NS_SUCCEEDED(ret) && thread)
01930   {
01931     nsCOMPtr <nsIDBChangeAnnouncer> announcer = do_QueryInterface(this);
01932     ret = thread->RemoveChildHdr(msgHdr, announcer);
01933   }
01934   return ret;
01935 }
01936 
01937 NS_IMETHODIMP nsMsgDatabase::RemoveHeaderMdbRow(nsIMsgDBHdr *msg)
01938 {
01939   NS_ENSURE_ARG_POINTER(msg);
01940   nsMsgHdr* msgHdr = NS_STATIC_CAST(nsMsgHdr*, msg);  // closed system, so this is ok
01941   return RemoveHeaderFromDB(msgHdr);
01942 }
01943 
01944 // This is a lower level routine which doesn't send notifcations or
01945 // update folder info. One use is when a rule fires moving a header
01946 // from one db to another, to remove it from the first db.
01947 
01948 nsresult nsMsgDatabase::RemoveHeaderFromDB(nsMsgHdr *msgHdr)
01949 {
01950   if (!msgHdr)
01951     return NS_ERROR_NULL_POINTER;
01952   nsresult ret = NS_OK;
01953   
01954   RemoveHdrFromCache(msgHdr, nsMsgKey_None);
01955   nsIMdbRow* row = msgHdr->GetMDBRow();
01956   ret = m_mdbAllMsgHeadersTable->CutRow(GetEnv(), row);
01957   row->CutAllColumns(GetEnv());
01958   msgHdr->m_initedValues = 0; // invalidate cached values.
01959   return ret;
01960 }
01961 
01962 nsresult nsMsgDatabase::IsRead(nsMsgKey key, PRBool *pRead)
01963 {
01964   nsCOMPtr <nsIMsgDBHdr> msgHdr;
01965   
01966   nsresult rv = GetMsgHdrForKey(key, getter_AddRefs(msgHdr));
01967   if (NS_FAILED(rv) || !msgHdr) 
01968     return NS_MSG_MESSAGE_NOT_FOUND; // XXX return rv?
01969   rv = IsHeaderRead(msgHdr, pRead);
01970   return rv;
01971 }
01972 
01973 PRUint32      nsMsgDatabase::GetStatusFlags(nsIMsgDBHdr *msgHdr, PRUint32 origFlags)
01974 {
01975   PRUint32    statusFlags = origFlags;
01976   PRBool      isRead = PR_TRUE;
01977   
01978   nsMsgKey key;
01979   (void)msgHdr->GetMessageKey(&key);
01980   if (m_newSet.GetSize() > 0 && m_newSet.GetAt(m_newSet.GetSize() - 1) == key || m_newSet.IndexOfSorted(key) != kNotFound)
01981     statusFlags |= MSG_FLAG_NEW;
01982   else
01983     statusFlags &= ~MSG_FLAG_NEW;
01984   if (IsHeaderRead(msgHdr, &isRead) == NS_OK && isRead)
01985     statusFlags |= MSG_FLAG_READ;
01986   return statusFlags;
01987 }
01988 
01989 nsresult nsMsgDatabase::IsHeaderRead(nsIMsgDBHdr *msgHdr, PRBool *pRead)
01990 {
01991   if (!msgHdr)
01992     return NS_MSG_MESSAGE_NOT_FOUND;
01993   
01994   nsMsgHdr* hdr = NS_STATIC_CAST(nsMsgHdr*, msgHdr);          // closed system, cast ok
01995   // can't call GetFlags, because it will be recursive.
01996   PRUint32 flags;
01997   hdr->GetRawFlags(&flags);
01998   *pRead = (flags & MSG_FLAG_READ) != 0;
01999   return NS_OK;
02000 }
02001 
02002 NS_IMETHODIMP nsMsgDatabase::IsMarked(nsMsgKey key, PRBool *pMarked)
02003 {
02004   nsCOMPtr <nsIMsgDBHdr> msgHdr;
02005 
02006   nsresult rv = GetMsgHdrForKey(key, getter_AddRefs(msgHdr));
02007   if (NS_FAILED(rv))
02008     return NS_MSG_MESSAGE_NOT_FOUND; // XXX return rv?
02009   
02010   PRUint32 flags;
02011   (void)msgHdr->GetFlags(&flags);
02012   *pMarked = (flags & MSG_FLAG_MARKED) == MSG_FLAG_MARKED;
02013   return rv;
02014 }
02015 
02016 NS_IMETHODIMP nsMsgDatabase::IsIgnored(nsMsgKey key, PRBool *pIgnored)
02017 {
02018   PR_ASSERT(pIgnored != NULL);
02019   if (!pIgnored)
02020     return NS_ERROR_NULL_POINTER;
02021   nsCOMPtr <nsIMsgThread> threadHdr;
02022   
02023   nsresult rv = GetThreadForMsgKey(key, getter_AddRefs(threadHdr));
02024   // This should be very surprising, but we leave that up to the caller
02025   // to determine for now.
02026   if (!threadHdr)
02027     return NS_MSG_MESSAGE_NOT_FOUND;
02028   
02029   PRUint32 threadFlags;
02030   threadHdr->GetFlags(&threadFlags);
02031   *pIgnored = (threadFlags & MSG_FLAG_IGNORED) ? PR_TRUE : PR_FALSE;
02032   return rv;
02033 }
02034 
02035 nsresult nsMsgDatabase::HasAttachments(nsMsgKey key, PRBool *pHasThem)
02036 {
02037   NS_ENSURE_ARG_POINTER(pHasThem);
02038 
02039   nsCOMPtr <nsIMsgDBHdr> msgHdr;
02040   
02041   nsresult rv = GetMsgHdrForKey(key, getter_AddRefs(msgHdr));
02042   if (NS_FAILED(rv)) 
02043     return rv;
02044   
02045   PRUint32 flags;
02046   (void)msgHdr->GetFlags(&flags);
02047   *pHasThem = (flags & MSG_FLAG_ATTACHMENT) ? PR_TRUE : PR_FALSE;
02048   return rv;
02049 }
02050 
02051 PRBool nsMsgDatabase::SetHdrReadFlag(nsIMsgDBHdr *msgHdr, PRBool bRead)
02052 {
02053   return SetHdrFlag(msgHdr, bRead, MSG_FLAG_READ);
02054 }
02055 
02056 nsresult nsMsgDatabase::MarkHdrReadInDB(nsIMsgDBHdr *msgHdr, PRBool bRead,
02057                                              nsIDBChangeListener *instigator)
02058 {
02059   nsresult rv;
02060   nsMsgKey key;
02061   PRUint32 oldFlags;
02062   PRBool   hdrInDB;
02063   (void)msgHdr->GetMessageKey(&key);
02064   msgHdr->GetFlags(&oldFlags);
02065   
02066   m_newSet.RemoveElement(key);
02067   (void) ContainsKey(key, &hdrInDB);
02068   if (hdrInDB && m_dbFolderInfo)
02069   {
02070     if (bRead)
02071       m_dbFolderInfo->ChangeNumUnreadMessages(-1);
02072     else
02073       m_dbFolderInfo->ChangeNumUnreadMessages(1);
02074   }
02075   
02076   SetHdrReadFlag(msgHdr, bRead); // this will cause a commit, at least for local mail, so do it after we change
02077   // the folder counts above, so they will get committed too.
02078   PRUint32 flags;
02079   rv = msgHdr->GetFlags(&flags);
02080   flags &= ~MSG_FLAG_NEW;
02081   msgHdr->SetFlags(flags);
02082   if (NS_FAILED(rv)) return rv;
02083   
02084   if (oldFlags == flags)
02085     return NS_OK;
02086   
02087   return NotifyHdrChangeAll(msgHdr, oldFlags, flags, instigator);
02088 }
02089 
02090 NS_IMETHODIMP nsMsgDatabase::MarkRead(nsMsgKey key, PRBool bRead, 
02091                                       nsIDBChangeListener *instigator)
02092 {
02093   nsresult rv;
02094   nsCOMPtr <nsIMsgDBHdr> msgHdr;
02095   
02096   rv = GetMsgHdrForKey(key, getter_AddRefs(msgHdr));
02097   if (NS_FAILED(rv) || !msgHdr) 
02098     return NS_MSG_MESSAGE_NOT_FOUND; // XXX return rv?
02099   
02100   rv = MarkHdrRead(msgHdr, bRead, instigator);
02101   return rv;
02102 }
02103 
02104 NS_IMETHODIMP nsMsgDatabase::MarkReplied(nsMsgKey key, PRBool bReplied, 
02105                                          nsIDBChangeListener *instigator /* = NULL */)
02106 {
02107   return SetKeyFlag(key, bReplied, MSG_FLAG_REPLIED, instigator);
02108 }
02109 
02110 NS_IMETHODIMP nsMsgDatabase::MarkForwarded(nsMsgKey key, PRBool bForwarded, 
02111                                            nsIDBChangeListener *instigator /* = NULL */) 
02112 {
02113   return SetKeyFlag(key, bForwarded, MSG_FLAG_FORWARDED, instigator);
02114 }
02115 
02116 NS_IMETHODIMP nsMsgDatabase::MarkHasAttachments(nsMsgKey key, PRBool bHasAttachments, 
02117                                                 nsIDBChangeListener *instigator)
02118 {
02119   return SetKeyFlag(key, bHasAttachments, MSG_FLAG_ATTACHMENT, instigator);
02120 }
02121 
02122 NS_IMETHODIMP
02123 nsMsgDatabase::MarkThreadRead(nsIMsgThread *thread, nsIDBChangeListener *instigator, nsMsgKeyArray *thoseMarked)
02124 {
02125   if (!thread)
02126     return NS_ERROR_NULL_POINTER;
02127   nsresult rv = NS_OK;
02128   
02129   PRUint32 numChildren;
02130   thread->GetNumChildren(&numChildren);
02131   for (PRUint32 curChildIndex = 0; curChildIndex < numChildren; curChildIndex++)
02132   {
02133     nsCOMPtr <nsIMsgDBHdr> child;
02134     
02135     rv = thread->GetChildAt(curChildIndex, getter_AddRefs(child));
02136     if (NS_SUCCEEDED(rv) && child)
02137     {
02138       PRBool isRead = PR_TRUE;
02139       IsHeaderRead(child, &isRead);
02140       if (!isRead)
02141       {
02142         if (thoseMarked)
02143         {
02144           nsMsgKey key;
02145           if (NS_SUCCEEDED(child->GetMessageKey(&key)))
02146             thoseMarked->Add(key);
02147         }
02148         MarkHdrRead(child, PR_TRUE, instigator);
02149       }
02150     }
02151   }
02152   
02153   return rv;
02154 }
02155 
02156 NS_IMETHODIMP
02157 nsMsgDatabase::MarkThreadIgnored(nsIMsgThread *thread, nsMsgKey threadKey, PRBool bIgnored,
02158                                  nsIDBChangeListener *instigator)
02159 {
02160   NS_ENSURE_ARG(thread);
02161   PRUint32 threadFlags;
02162   thread->GetFlags(&threadFlags);
02163   PRUint32 oldThreadFlags = threadFlags; // not quite right, since we probably want msg hdr flags.
02164   if (bIgnored)
02165   {
02166     threadFlags |= MSG_FLAG_IGNORED;
02167     threadFlags &= ~MSG_FLAG_WATCHED;     // ignore is implicit un-watch
02168   }
02169   else
02170     threadFlags &= ~MSG_FLAG_IGNORED;
02171   thread->SetFlags(threadFlags);
02172   
02173   nsCOMPtr <nsIMsgDBHdr> msg;
02174   nsresult rv = GetMsgHdrForKey(threadKey, getter_AddRefs(msg));
02175   NS_ENSURE_SUCCESS(rv, rv);
02176   return NotifyHdrChangeAll(msg, oldThreadFlags, threadFlags, instigator);
02177 }
02178 
02179 NS_IMETHODIMP
02180 nsMsgDatabase::MarkThreadWatched(nsIMsgThread *thread, nsMsgKey threadKey, PRBool bWatched,
02181                                  nsIDBChangeListener *instigator)
02182 {
02183   NS_ENSURE_ARG(thread);
02184   PRUint32 threadFlags;
02185   thread->GetFlags(&threadFlags);
02186   PRUint32 oldThreadFlags = threadFlags; // not quite right, since we probably want msg hdr flags.
02187   if (bWatched)
02188   {
02189     threadFlags |= MSG_FLAG_WATCHED;
02190     threadFlags &= ~MSG_FLAG_IGNORED;     // watch is implicit un-ignore
02191   }
02192   else
02193     threadFlags &= ~MSG_FLAG_WATCHED;
02194 
02195   nsCOMPtr <nsIMsgDBHdr> msg;
02196   GetMsgHdrForKey(threadKey, getter_AddRefs(msg));
02197 
02198   nsresult rv  = NotifyHdrChangeAll(msg, oldThreadFlags, threadFlags, instigator);
02199   thread->SetFlags(threadFlags);
02200   return rv;
02201 }
02202 
02203 NS_IMETHODIMP nsMsgDatabase::MarkMarked(nsMsgKey key, PRBool mark,
02204                                                                       nsIDBChangeListener *instigator)
02205 {
02206   return SetKeyFlag(key, mark, MSG_FLAG_MARKED, instigator);
02207 }
02208 
02209 NS_IMETHODIMP nsMsgDatabase::MarkOffline(nsMsgKey key, PRBool offline,
02210                                                                       nsIDBChangeListener *instigator)
02211 {
02212   return SetKeyFlag(key, offline, MSG_FLAG_OFFLINE, instigator);
02213 }
02214 
02215 NS_IMETHODIMP nsMsgDatabase::SetStringProperty(nsMsgKey aKey, const char *aProperty, const char *aValue)
02216 {
02217   nsCOMPtr <nsIMsgDBHdr> msgHdr;
02218               
02219   nsresult rv = GetMsgHdrForKey(aKey, getter_AddRefs(msgHdr));
02220   if (NS_FAILED(rv) || !msgHdr) 
02221     return NS_MSG_MESSAGE_NOT_FOUND; // XXX return rv?
02222 
02223   nsXPIDLCString oldValue;
02224   rv = msgHdr->GetStringProperty(aProperty, getter_Copies(oldValue));
02225   NS_ENSURE_SUCCESS(rv,rv);
02226 
02227   // if no change to this string property, bail out
02228   if (!strcmp(aValue, oldValue.get()))
02229     return NS_OK;
02230 
02231   rv = msgHdr->SetStringProperty(aProperty, aValue);
02232   NS_ENSURE_SUCCESS(rv,rv);
02233 
02234   // if this is the junk score property notify, as long as we're not going
02235   // from no value to non junk
02236   if (!strcmp(aProperty, "junkscore") && !(oldValue.IsEmpty() && !strcmp(aValue, "0")))
02237     NotifyJunkScoreChanged(nsnull);
02238 
02239   PRUint32 flags;
02240   (void)msgHdr->GetFlags(&flags);
02241   
02242   return NotifyHdrChangeAll(msgHdr, flags, flags, nsnull);
02243 }
02244 
02245 NS_IMETHODIMP nsMsgDatabase::SetLabel(nsMsgKey key, nsMsgLabelValue label)
02246 {
02247   nsresult rv;
02248   nsCOMPtr <nsIMsgDBHdr> msgHdr;
02249               
02250   rv = GetMsgHdrForKey(key, getter_AddRefs(msgHdr));
02251   if (NS_FAILED(rv) || !msgHdr) 
02252     return NS_MSG_MESSAGE_NOT_FOUND; 
02253   nsMsgLabelValue oldLabel;
02254   msgHdr->GetLabel(&oldLabel);
02255 
02256   msgHdr->SetLabel(label);
02257   // clear old label
02258   if (oldLabel != label)
02259   {
02260     if (oldLabel != 0)
02261       rv = SetKeyFlag(key, PR_FALSE, oldLabel << 25, nsnull);
02262     // set the flag in the x-mozilla-status2 line.
02263     rv = SetKeyFlag(key, PR_TRUE, label << 25, nsnull);
02264   }
02265   return rv;
02266 }
02267 
02268 NS_IMETHODIMP nsMsgDatabase::MarkImapDeleted(nsMsgKey key, PRBool deleted,
02269                                                                       nsIDBChangeListener *instigator)
02270 {
02271   return SetKeyFlag(key, deleted, MSG_FLAG_IMAP_DELETED, instigator);
02272 }
02273 
02274 NS_IMETHODIMP nsMsgDatabase::MarkMDNNeeded(nsMsgKey key, PRBool bNeeded, 
02275                                                         nsIDBChangeListener *instigator /* = NULL */)
02276 {
02277   return SetKeyFlag(key, bNeeded, MSG_FLAG_MDN_REPORT_NEEDED, instigator);
02278 }
02279 
02280 NS_IMETHODIMP nsMsgDatabase::IsMDNNeeded(nsMsgKey key, PRBool *pNeeded)
02281 {
02282   nsCOMPtr <nsIMsgDBHdr> msgHdr;
02283        
02284   nsresult rv = GetMsgHdrForKey(key, getter_AddRefs(msgHdr));
02285   if (NS_FAILED(rv) || !msgHdr)
02286     return NS_MSG_MESSAGE_NOT_FOUND; // XXX return rv?
02287   
02288   PRUint32 flags;
02289   (void)msgHdr->GetFlags(&flags);
02290   *pNeeded = ((flags & MSG_FLAG_MDN_REPORT_NEEDED) == MSG_FLAG_MDN_REPORT_NEEDED);
02291   return rv;
02292 }
02293 
02294 
02295 nsresult nsMsgDatabase::MarkMDNSent(nsMsgKey key, PRBool bSent, 
02296                                                    nsIDBChangeListener *instigator /* = NULL */)
02297 {
02298   return SetKeyFlag(key, bSent, MSG_FLAG_MDN_REPORT_SENT, instigator);
02299 }
02300 
02301 
02302 nsresult nsMsgDatabase::IsMDNSent(nsMsgKey key, PRBool *pSent)
02303 {
02304   nsCOMPtr <nsIMsgDBHdr> msgHdr;
02305   
02306   nsresult rv = GetMsgHdrForKey(key, getter_AddRefs(msgHdr));
02307   if (NS_FAILED(rv) || !msgHdr) 
02308     return NS_MSG_MESSAGE_NOT_FOUND; // XXX return rv?
02309   
02310   PRUint32 flags;
02311   (void)msgHdr->GetFlags(&flags);
02312   *pSent = flags & MSG_FLAG_MDN_REPORT_SENT;
02313   return rv;
02314 }
02315 
02316 
02317 nsresult  nsMsgDatabase::SetKeyFlag(nsMsgKey key, PRBool set, PRUint32 flag,
02318                                      nsIDBChangeListener *instigator)
02319 {
02320   nsresult rv;
02321   nsCOMPtr <nsIMsgDBHdr> msgHdr;
02322               
02323   rv = GetMsgHdrForKey(key, getter_AddRefs(msgHdr));
02324   if (NS_FAILED(rv) || !msgHdr) 
02325     return NS_MSG_MESSAGE_NOT_FOUND; // XXX return rv?
02326   
02327   PRUint32 oldFlags;
02328   msgHdr->GetFlags(&oldFlags);
02329   
02330   SetHdrFlag(msgHdr, set, flag);
02331   
02332   PRUint32 flags;
02333   (void)msgHdr->GetFlags(&flags);
02334   
02335   if (oldFlags == flags)
02336     return NS_OK;
02337 
02338   return NotifyHdrChangeAll(msgHdr, oldFlags, flags, instigator);
02339 }
02340 
02341 nsresult nsMsgDatabase::SetMsgHdrFlag(nsIMsgDBHdr *msgHdr, PRBool set, PRUint32 flag, nsIDBChangeListener *instigator)
02342 {
02343   PRUint32 oldFlags;
02344   msgHdr->GetFlags(&oldFlags);
02345 
02346   SetHdrFlag(msgHdr, set, flag);
02347 
02348   PRUint32 flags;
02349   (void)msgHdr->GetFlags(&flags);
02350 
02351   if (oldFlags == flags)
02352     return NS_OK;
02353 
02354   return NotifyHdrChangeAll(msgHdr, oldFlags, flags, instigator);
02355 }
02356 
02357 // Helper routine - lowest level of flag setting - returns PR_TRUE if flags change,
02358 // PR_FALSE otherwise.
02359 PRBool nsMsgDatabase::SetHdrFlag(nsIMsgDBHdr *msgHdr, PRBool bSet, MsgFlags flag)
02360 {
02361   PRUint32 statusFlags;
02362   (void)msgHdr->GetFlags(&statusFlags);
02363   PRUint32 currentStatusFlags = GetStatusFlags(msgHdr, statusFlags);
02364   PRBool flagAlreadySet = (currentStatusFlags & flag) != 0;
02365   
02366   if ((flagAlreadySet && !bSet) || (!flagAlreadySet && bSet))
02367   {
02368     PRUint32 resultFlags;
02369     if (bSet)
02370     {
02371       msgHdr->OrFlags(flag, &resultFlags);
02372     }
02373     else
02374     {
02375       msgHdr->AndFlags(~flag, &resultFlags);
02376     }
02377     return PR_TRUE;
02378   }
02379   return PR_FALSE;
02380 }
02381 
02382 
02383 NS_IMETHODIMP nsMsgDatabase::MarkHdrRead(nsIMsgDBHdr *msgHdr, PRBool bRead, 
02384                                          nsIDBChangeListener *instigator)
02385 {
02386   nsresult rv = NS_OK;
02387   PRBool      isRead = PR_TRUE;
02388   PRBool        isReadInDB;
02389 
02390   nsMsgDatabase::IsHeaderRead(msgHdr, &isReadInDB);
02391   IsHeaderRead(msgHdr, &isRead);
02392   // if the flag is already correct in the db, don't change it.
02393   // Check msg flags as well as IsHeaderRead in case it's a newsgroup
02394   // and the msghdr flags are out of sync with the newsrc settings.
02395   // (we could override this method for news db's, but it's a trivial fix here.
02396   if (bRead != isRead || isRead != isReadInDB)
02397   {
02398     nsCOMPtr <nsIMsgThread> threadHdr;
02399     nsMsgKey msgKey;
02400     msgHdr->GetMessageKey(&msgKey);
02401     
02402     rv = GetThreadForMsgKey(msgKey, getter_AddRefs(threadHdr));
02403     if (threadHdr)
02404     {
02405       threadHdr->MarkChildRead(bRead);
02406     }
02407     rv = MarkHdrReadInDB(msgHdr, bRead, instigator);
02408   }
02409   return rv;
02410 }
02411 
02412 NS_IMETHODIMP nsMsgDatabase::MarkHdrReplied(nsIMsgDBHdr *msgHdr, PRBool bReplied,
02413                          nsIDBChangeListener *instigator)
02414 {
02415   return SetMsgHdrFlag(msgHdr, bReplied, MSG_FLAG_REPLIED, instigator);
02416 }
02417 
02418 
02419 NS_IMETHODIMP nsMsgDatabase::MarkHdrMarked(nsIMsgDBHdr *msgHdr, PRBool mark,
02420                          nsIDBChangeListener *instigator)
02421 {
02422   return SetMsgHdrFlag(msgHdr, mark, MSG_FLAG_MARKED, instigator);
02423 }
02424 
02425 
02426 NS_IMETHODIMP nsMsgDatabase::MarkAllRead(nsMsgKeyArray *thoseMarked)
02427 {
02428   nsresult           rv;
02429   nsMsgHdr  *pHeader;
02430   
02431   nsCOMPtr <nsISimpleEnumerator> hdrs;
02432   rv = EnumerateMessages(getter_AddRefs(hdrs));
02433   if (NS_FAILED(rv))
02434     return rv;
02435   PRBool hasMore = PR_FALSE;
02436   
02437   while (NS_SUCCEEDED(rv = hdrs->HasMoreElements(&hasMore)) && (hasMore == PR_TRUE)) 
02438   {
02439     rv = hdrs->GetNext((nsISupports**)&pHeader);
02440     NS_ASSERTION(NS_SUCCEEDED(rv), "nsMsgDBEnumerator broken");
02441     if (NS_FAILED(rv)) 
02442       break;
02443 
02444     PRBool isRead;
02445     IsHeaderRead(pHeader, &isRead);
02446 
02447     if (!isRead)
02448     {
02449       if (thoseMarked) 
02450       {
02451         nsMsgKey key;
02452         (void)pHeader->GetMessageKey(&key);
02453         thoseMarked->Add(key);
02454       }
02455       rv = MarkHdrRead(pHeader, PR_TRUE, nsnull);       // ### dmb - blow off error?
02456     }
02457     NS_RELEASE(pHeader);
02458   }
02459   
02460   // force num new to 0.
02461   PRInt32 numUnreadMessages;
02462   
02463   rv = m_dbFolderInfo->GetNumUnreadMessages(&numUnreadMessages);
02464   if (rv == NS_OK)
02465     m_dbFolderInfo->ChangeNumUnreadMessages(-numUnreadMessages);
02466   // caller will Commit the db, so no need to do it here.
02467   return rv;
02468 }
02469 
02470 NS_IMETHODIMP nsMsgDatabase::MarkReadByDate (PRTime startDate, PRTime endDate, nsMsgKeyArray *markedIds)
02471 {
02472   nsresult rv;
02473   nsMsgHdr  *pHeader;
02474   PRInt32 numChanged = 0;
02475   
02476   nsISimpleEnumerator* hdrs;
02477   rv = EnumerateMessages(&hdrs);
02478   if (NS_FAILED(rv)) 
02479     return rv;
02480               
02481   nsTime t_startDate(startDate);
02482   nsTime t_endDate(endDate);
02483               
02484   PRBool hasMore = PR_FALSE;
02485   
02486   while (NS_SUCCEEDED(rv = hdrs->HasMoreElements(&hasMore)) && (hasMore == PR_TRUE)) 
02487   {
02488     rv = hdrs->GetNext((nsISupports**)&pHeader);
02489     NS_ASSERTION(NS_SUCCEEDED(rv), "nsMsgDBEnumerator broken");
02490     if (NS_FAILED(rv)) break;
02491     
02492     PRTime headerDate;
02493     (void)pHeader->GetDate(&headerDate);
02494     nsTime t_headerDate(headerDate);
02495     
02496     if (t_headerDate > t_startDate && t_headerDate <= t_endDate)
02497     {
02498       PRBool isRead;
02499       nsMsgKey key;
02500       (void)pHeader->GetMessageKey(&key);
02501       IsRead(key, &isRead);
02502       if (!isRead)
02503       {
02504         numChanged++;
02505         if (markedIds)
02506           markedIds->Add(key);
02507         rv = MarkHdrRead(pHeader, PR_TRUE, NULL);       // ### dmb - blow off error?
02508       }
02509     }
02510     NS_RELEASE(pHeader);
02511   }
02512   if (numChanged > 0)
02513     Commit(nsMsgDBCommitType::kSmallCommit);
02514   return rv;
02515 }
02516 
02517 
02518 NS_IMETHODIMP nsMsgDatabase::AddToNewList(nsMsgKey key)
02519 {
02520   // we add new keys in increasing order...
02521   if (m_newSet.GetSize() == 0 
02522       || (m_newSet.GetAt(m_newSet.GetSize() - 1) < key))
02523     m_newSet.Add(key);
02524   return NS_OK;
02525 }
02526 
02527 
02528 NS_IMETHODIMP nsMsgDatabase::ClearNewList(PRBool notify /* = FALSE */)
02529 {
02530   nsresult err = NS_OK;
02531   if (notify && m_newSet.GetSize() > 0)   // need to update view
02532   {
02533     nsMsgKeyArray saveNewSet;
02534     saveNewSet.CopyArray(m_newSet);
02535     // clear m_newSet so that the code that's listening to the key change
02536     // doesn't think we have new messages and send notifications all over
02537     // that we have new messages.
02538     m_newSet.RemoveAll();
02539     for (PRUint32 elementIndex = saveNewSet.GetSize() - 1; ; elementIndex--)
02540     {
02541       nsMsgKey lastNewKey = saveNewSet.ElementAt(elementIndex);
02542       nsCOMPtr <nsIMsgDBHdr> msgHdr;
02543       err = GetMsgHdrForKey(lastNewKey, getter_AddRefs(msgHdr));
02544       if (NS_SUCCEEDED(err))
02545       {
02546         PRUint32 flags;
02547         (void)msgHdr->GetFlags(&flags);
02548         
02549         if ((flags | MSG_FLAG_NEW) != flags)
02550           NotifyHdrChangeAll(msgHdr, flags | MSG_FLAG_NEW, flags, nsnull);
02551       }
02552       if (elementIndex == 0)
02553         break;
02554     }
02555   }
02556   return err;
02557 }
02558 
02559 NS_IMETHODIMP nsMsgDatabase::HasNew(PRBool *_retval)
02560 {
02561   if (!_retval) return NS_ERROR_NULL_POINTER;
02562 
02563   *_retval = (m_newSet.GetSize() > 0);
02564   return NS_OK;
02565 }
02566 
02567 NS_IMETHODIMP nsMsgDatabase::GetFirstNew(nsMsgKey *result)
02568 {
02569   PRBool hasnew;
02570   nsresult rv = HasNew(&hasnew);
02571   if (NS_FAILED(rv)) return rv;
02572   *result = (hasnew) ? m_newSet.ElementAt(0) : nsMsgKey_None;
02573   return NS_OK;
02574 }
02575 
02576 
02578 
02579 
02580 class nsMsgDBEnumerator : public nsISimpleEnumerator {
02581 public:
02582     NS_DECL_ISUPPORTS
02583 
02584     // nsISimpleEnumerator methods:
02585     NS_DECL_NSISIMPLEENUMERATOR
02586 
02587     // nsMsgDBEnumerator methods:
02588     typedef nsresult (*nsMsgDBEnumeratorFilter)(nsIMsgDBHdr* hdr, void* closure);
02589 
02590     nsMsgDBEnumerator(nsMsgDatabase* db, nsIMdbTable *table,
02591                       nsMsgDBEnumeratorFilter filter, void* closure);
02592     virtual ~nsMsgDBEnumerator();
02593 
02594 protected:
02595     nsresult                              GetRowCursor();
02596     nsresult                              PrefetchNext();
02597     nsMsgDatabase*              mDB;
02598     nsIMdbTableRowCursor*       mRowCursor;
02599     nsIMsgDBHdr*                 mResultHdr;
02600     PRBool                      mDone;
02601     PRBool                                       mNextPrefetched;
02602     nsMsgDBEnumeratorFilter     mFilter;
02603     nsCOMPtr <nsIMdbTable>      mTable; 
02604     void*                       mClosure;
02605 };
02606 
02607 nsMsgDBEnumerator::nsMsgDBEnumerator(nsMsgDatabase* db,
02608                                      nsIMdbTable *table,
02609                                      nsMsgDBEnumeratorFilter filter, 
02610                                      void* closure)
02611     : mDB(db), mRowCursor(nsnull), mResultHdr(nsnull), mDone(PR_FALSE),
02612       mFilter(filter), mClosure(closure)
02613 {
02614     NS_ADDREF(mDB);
02615     mNextPrefetched = PR_FALSE;
02616     mTable = table;
02617 }
02618 
02619 nsMsgDBEnumerator::~nsMsgDBEnumerator()
02620 {
02621   if (mRowCursor)
02622     mRowCursor->Release();
02623   mTable = nsnull;
02624   NS_IF_RELEASE(mResultHdr);
02625   NS_RELEASE(mDB);
02626 }
02627 
02628 NS_IMPL_ISUPPORTS1(nsMsgDBEnumerator, nsISimpleEnumerator)
02629 
02630 nsresult nsMsgDBEnumerator::GetRowCursor()
02631 {
02632   mDone = PR_FALSE;
02633   
02634   if (!mDB || !mTable)
02635     return NS_ERROR_NULL_POINTER;
02636               
02637   return mTable->GetTableRowCursor(mDB->GetEnv(), -1, &mRowCursor);
02638 }
02639 
02640 NS_IMETHODIMP nsMsgDBEnumerator::GetNext(nsISupports **aItem)
02641 {
02642   if (!aItem)
02643     return NS_ERROR_NULL_POINTER;
02644   nsresult rv=NS_OK;
02645   if (!mNextPrefetched)
02646     rv = PrefetchNext();
02647   if (NS_SUCCEEDED(rv))
02648   {
02649     if (mResultHdr) 
02650     {
02651       *aItem = mResultHdr;
02652       NS_ADDREF(mResultHdr);
02653       mNextPrefetched = PR_FALSE;
02654     }
02655   }
02656   return rv;
02657 }
02658 
02659 nsresult nsMsgDBEnumerator::PrefetchNext()
02660 {
02661   nsresult rv = NS_OK;
02662   nsIMdbRow* hdrRow;
02663   mdb_pos rowPos;
02664   PRUint32 flags;
02665   
02666   if (!mRowCursor)
02667   {
02668     rv = GetRowCursor();
02669     if (NS_FAILED(rv))
02670       return rv;
02671   }
02672   
02673   do
02674   {
02675     NS_IF_RELEASE(mResultHdr);
02676     mResultHdr = nsnull;
02677     rv = mRowCursor->NextRow(mDB->GetEnv(), &hdrRow, &rowPos);
02678     if (!hdrRow)
02679     {
02680       mDone = PR_TRUE;
02681       return NS_ERROR_FAILURE;
02682     }
02683     if (NS_FAILED(rv)) 
02684     {
02685       mDone = PR_TRUE;
02686       return rv;
02687     }
02688     //Get key from row
02689     mdbOid outOid;
02690     nsMsgKey key=0;
02691     if (hdrRow->GetOid(mDB->GetEnv(), &outOid) == NS_OK)
02692       key = outOid.mOid_Id;
02693     
02694     rv = mDB->GetHdrFromUseCache(key, &mResultHdr);
02695     if (NS_SUCCEEDED(rv) && mResultHdr)
02696       hdrRow->Release();
02697     else
02698       rv = mDB->CreateMsgHdr(hdrRow, key, &mResultHdr);
02699     if (NS_FAILED(rv))
02700       return rv;
02701     
02702     if (mResultHdr)
02703       mResultHdr->GetFlags(&flags);
02704     else
02705       flags = 0;
02706   } 
02707   while (mFilter && NS_FAILED(mFilter(mResultHdr, mClosure)) && !(flags & MSG_FLAG_EXPUNGED));
02708   
02709   if (mResultHdr) 
02710   {
02711     mNextPrefetched = PR_TRUE;
02712     return NS_OK;
02713   }
02714   return NS_ERROR_FAILURE;
02715 }
02716 
02717 NS_IMETHODIMP nsMsgDBEnumerator::HasMoreElements(PRBool *aResult)
02718 {
02719   if (!aResult)
02720     return NS_ERROR_NULL_POINTER;
02721   
02722   if (!mNextPrefetched)
02723     PrefetchNext();
02724   *aResult = !mDone;
02725   return NS_OK;
02726 }
02727 
02729 
02730 NS_IMETHODIMP 
02731 nsMsgDatabase::EnumerateMessages(nsISimpleEnumerator* *result)
02732 {
02733     nsMsgDBEnumerator* e = new nsMsgDBEnumerator(this, m_mdbAllMsgHeadersTable, nsnull, nsnull);
02734     if (e == nsnull)
02735         return NS_ERROR_OUT_OF_MEMORY;
02736     NS_ADDREF(e);
02737     *result = e;
02738     return NS_OK;
02739 }
02740 
02741 NS_IMETHODIMP 
02742 nsMsgDatabase::SyncCounts()
02743 {
02744   nsCOMPtr <nsIMsgDBHdr> pHeader;
02745   nsCOMPtr <nsISimpleEnumerator> hdrs;
02746   nsresult rv = EnumerateMessages(getter_AddRefs(hdrs));
02747   if (NS_FAILED(rv))
02748     return rv;
02749   PRBool hasMore = PR_FALSE;
02750   
02751   mdb_count numHdrsInTable = 0;
02752   PRInt32 numUnread = 0;
02753   PRInt32 numHdrs = 0;
02754 
02755   if (m_mdbAllMsgHeadersTable)
02756     m_mdbAllMsgHeadersTable->GetCount(GetEnv(), &numHdrsInTable);
02757   else
02758     return NS_ERROR_NULL_POINTER;
02759   
02760   while (NS_SUCCEEDED(rv = hdrs->HasMoreElements(&hasMore)) && (hasMore == PR_TRUE)) 
02761   {
02762     rv = hdrs->GetNext(getter_AddRefs(pHeader));
02763     NS_ASSERTION(NS_SUCCEEDED(rv), "nsMsgDBEnumerator broken");
02764     if (NS_FAILED(rv)) 
02765       break;
02766     
02767     PRBool isRead;
02768     IsHeaderRead(pHeader, &isRead);
02769     if (!isRead)
02770       numUnread++;
02771     numHdrs++;  
02772   }
02773 
02774   PRInt32 oldTotal, oldUnread;
02775   (void) m_dbFolderInfo->GetNumUnreadMessages(&oldUnread);
02776   (void) m_dbFolderInfo->GetNumMessages(&oldTotal);
02777   if (oldUnread != numUnread)
02778     m_dbFolderInfo->ChangeNumUnreadMessages(numUnread - oldUnread);
02779   if (oldTotal != numHdrs)
02780     m_dbFolderInfo->ChangeNumMessages(numHdrs - oldTotal);
02781   return NS_OK;
02782 }
02783 
02784 
02785 // resulting output array is sorted by key.
02786 NS_IMETHODIMP nsMsgDatabase::ListAllKeys(nsMsgKeyArray &outputKeys)
02787 {
02788   nsresult    err = NS_OK;
02789   nsIMdbTableRowCursor *rowCursor;
02790   if (m_mdbAllMsgHeadersTable)
02791   {
02792     err = m_mdbAllMsgHeadersTable->GetTableRowCursor(GetEnv(), -1, &rowCursor);
02793     while (err == NS_OK && rowCursor)
02794     {
02795       mdbOid outOid;
02796       mdb_pos outPos;
02797       
02798       err = rowCursor->NextRowOid(GetEnv(), &outOid, &outPos);
02799       // is this right? Mork is returning a 0 id, but that should valid.
02800       if (outPos < 0 || outOid.mOid_Id == (mdb_id) -1)  
02801         break;
02802       if (err == NS_OK)
02803         outputKeys.Add(outOid.mOid_Id);
02804     }
02805     rowCursor->Release();
02806   }
02807   outputKeys.QuickSort();
02808   return err;
02809 }
02810 
02811 class nsMsgDBThreadEnumerator : public nsISimpleEnumerator, public nsIDBChangeListener
02812 {
02813 public:
02814     NS_DECL_ISUPPORTS
02815 
02816     // nsISimpleEnumerator methods:
02817     NS_DECL_NSISIMPLEENUMERATOR
02818 
02819     NS_DECL_NSIDBCHANGELISTENER
02820 
02821     // nsMsgDBEnumerator methods:
02822     typedef nsresult (*nsMsgDBThreadEnumeratorFilter)(nsIMsgThread* thread);
02823 
02824     nsMsgDBThreadEnumerator(nsMsgDatabase* db, nsMsgDBThreadEnumeratorFilter filter);
02825     virtual ~nsMsgDBThreadEnumerator();
02826 
02827 protected:
02828     nsresult                              GetTableCursor(void);
02829     nsresult                              PrefetchNext();
02830     nsMsgDatabase*              mDB;
02831     nsCOMPtr <nsIMdbPortTableCursor>  mTableCursor;
02832     nsIMsgThread*                 mResultThread;
02833     PRBool                      mDone;
02834     PRBool                                       mNextPrefetched;
02835     nsMsgDBThreadEnumeratorFilter     mFilter;
02836 };
02837 
02838 nsMsgDBThreadEnumerator::nsMsgDBThreadEnumerator(nsMsgDatabase* db,
02839                                      nsMsgDBThreadEnumeratorFilter filter)
02840     : mDB(db), mTableCursor(nsnull), mResultThread(nsnull), mDone(PR_FALSE),
02841       mFilter(filter)
02842 {
02843     mDB->AddListener(this);
02844     mNextPrefetched = PR_FALSE;
02845 }
02846 
02847 nsMsgDBThreadEnumerator::~nsMsgDBThreadEnumerator()
02848 {
02849   mTableCursor = nsnull;
02850   NS_IF_RELEASE(mResultThread);
02851   if (mDB)
02852     mDB->RemoveListener(this);
02853 }
02854 
02855 NS_IMPL_ISUPPORTS2(nsMsgDBThreadEnumerator, nsISimpleEnumerator, nsIDBChangeListener)
02856 
02857 
02858 /* void onHdrChange (in nsIMsgDBHdr aHdrChanged, in unsigned long aOldFlags, in unsigned long aNewFlags, in nsIDBChangeListener aInstigator); */
02859 NS_IMETHODIMP nsMsgDBThreadEnumerator::OnHdrChange(nsIMsgDBHdr *aHdrChanged, PRUint32 aOldFlags, PRUint32 aNewFlags, nsIDBChangeListener *aInstigator)
02860 {
02861     return NS_OK;
02862 }
02863 
02864 /* void onHdrDeleted (in nsIMsgDBHdr aHdrChanged, in nsMsgKey aParentKey, in long aFlags, in nsIDBChangeListener aInstigator); */
02865 NS_IMETHODIMP nsMsgDBThreadEnumerator::OnHdrDeleted(nsIMsgDBHdr *aHdrChanged, nsMsgKey aParentKey, PRInt32 aFlags, nsIDBChangeListener *aInstigator)
02866 {
02867     return NS_OK;
02868 }
02869 
02870 /* void onHdrAdded (in nsIMsgDBHdr aHdrChanged, in nsMsgKey aParentKey, in long aFlags, in nsIDBChangeListener aInstigator); */
02871 NS_IMETHODIMP nsMsgDBThreadEnumerator::OnHdrAdded(nsIMsgDBHdr *aHdrChanged, nsMsgKey aParentKey, PRInt32 aFlags, nsIDBChangeListener *aInstigator)
02872 {
02873     return NS_OK;
02874 }
02875 
02876 /* void onParentChanged (in nsMsgKey aKeyChanged, in nsMsgKey oldParent, in nsMsgKey newParent, in nsIDBChangeListener aInstigator); */
02877 NS_IMETHODIMP nsMsgDBThreadEnumerator::OnParentChanged(nsMsgKey aKeyChanged, nsMsgKey oldParent, nsMsgKey newParent, nsIDBChangeListener *aInstigator)
02878 {
02879     return NS_OK;
02880 }
02881 
02882 /* void onAnnouncerGoingAway (in nsIDBChangeAnnouncer instigator); */
02883 NS_IMETHODIMP nsMsgDBThreadEnumerator::OnAnnouncerGoingAway(nsIDBChangeAnnouncer *instigator)
02884 {
02885   mTableCursor = nsnull;
02886   NS_IF_RELEASE(mResultThread);
02887   mDB->RemoveListener(this);
02888   mDB = nsnull;
02889   return NS_OK;
02890 }
02891 
02892 /* void onReadChanged (in nsIDBChangeListener aInstigator); */
02893 NS_IMETHODIMP nsMsgDBThreadEnumerator::OnReadChanged(nsIDBChangeListener *aInstigator)
02894 {
02895     return NS_OK;
02896 }
02897 
02898 /* void onJunkScoreChanged (in nsIDBChangeListener aInstigator); */
02899 NS_IMETHODIMP nsMsgDBThreadEnumerator::OnJunkScoreChanged(nsIDBChangeListener *aInstigator)
02900 {
02901     return NS_OK;
02902 }
02903 
02904 nsresult nsMsgDBThreadEnumerator::GetTableCursor(void)
02905 {
02906   nsresult rv = 0;
02907   
02908   if (!mDB || !mDB->m_mdbStore)
02909     return NS_ERROR_NULL_POINTER;
02910               
02911   mDB->m_mdbStore->GetPortTableCursor(mDB->GetEnv(),   mDB->m_hdrRowScopeToken, mDB->m_threadTableKindToken,
02912     getter_AddRefs(mTableCursor));
02913   
02914   if (NS_FAILED(rv)) 
02915     return rv;
02916   return NS_OK;
02917 }
02918 
02919 NS_IMETHODIMP nsMsgDBThreadEnumerator::GetNext(nsISupports **aItem)
02920 {
02921   if (!aItem)
02922     return NS_ERROR_NULL_POINTER;
02923   *aItem = nsnull;
02924   nsresult rv = NS_OK;
02925   if (!mNextPrefetched)
02926     rv = PrefetchNext();
02927   if (NS_SUCCEEDED(rv))
02928   {
02929     if (mResultThread) 
02930     {
02931       *aItem = mResultThread;
02932       NS_ADDREF(mResultThread);
02933       mNextPrefetched = PR_FALSE;
02934     }
02935   }
02936   return rv;
02937 }
02938 
02939 
02940 nsresult nsMsgDBThreadEnumerator::PrefetchNext()
02941 {
02942   nsresult rv;
02943   nsIMdbTable *table = nsnull;
02944 
02945   if (!mDB)
02946     return NS_ERROR_NULL_POINTER;
02947 
02948   if (!mTableCursor)
02949   {
02950     rv = GetTableCursor();
02951     if (NS_FAILED(rv))
02952       return rv;
02953   }
02954   while (PR_TRUE) 
02955   {
02956     NS_IF_RELEASE(mResultThread);
02957     mResultThread = nsnull;
02958     rv = mTableCursor->NextTable(mDB->GetEnv(), &table);
02959     if (!table) 
02960     {
02961       mDone = PR_TRUE;
02962       return NS_ERROR_FAILURE;
02963     }
02964     if (NS_FAILED(rv)) 
02965     {
02966       mDone = PR_TRUE;
02967       return rv;
02968     }
02969     
02970     if (NS_FAILED(rv)) 
02971       return rv;
02972     
02973     mResultThread = new nsMsgThread(mDB, table);
02974     if(mResultThread)
02975     {
02976       PRUint32 numChildren = 0;
02977       NS_ADDREF(mResultThread);
02978       mResultThread->GetNumChildren(&numChildren);
02979       // we've got empty thread; don't tell caller about it.
02980       if (numChildren == 0)
02981         continue;
02982     }
02983     if (mFilter && NS_FAILED(mFilter(mResultThread)))
02984       continue;
02985     else
02986       break;
02987   }
02988   if (mResultThread) 
02989   {
02990     mNextPrefetched = PR_TRUE;
02991     return NS_OK;
02992   }
02993   return NS_ERROR_FAILURE;
02994 }
02995 
02996 NS_IMETHODIMP nsMsgDBThreadEnumerator::HasMoreElements(PRBool *aResult)
02997 {
02998   if (!aResult)
02999     return NS_ERROR_NULL_POINTER;
03000   if (!mNextPrefetched)
03001     PrefetchNext();
03002   *aResult = !mDone;
03003   return NS_OK;
03004 }
03005 
03006 NS_IMETHODIMP 
03007 nsMsgDatabase::EnumerateThreads(nsISimpleEnumerator* *result)
03008 {
03009   nsMsgDBThreadEnumerator* e = new nsMsgDBThreadEnumerator(this, nsnull);
03010   if (e == nsnull)
03011     return NS_ERROR_OUT_OF_MEMORY;
03012   NS_ADDREF(*result = e);
03013   return NS_OK;
03014 }
03015 
03016 // only return headers with a particular flag set
03017 static nsresult
03018 nsMsgFlagSetFilter(nsIMsgDBHdr *msg, void *closure)
03019 {
03020   PRUint32 msgFlags, desiredFlags;
03021   desiredFlags = * (PRUint32 *) closure;
03022   msg->GetFlags(&msgFlags);
03023   return (msgFlags & desiredFlags) ? NS_OK : NS_ERROR_FAILURE;
03024 }
03025 
03026 nsresult  
03027 nsMsgDatabase::EnumerateMessagesWithFlag(nsISimpleEnumerator* *result, PRUint32 *pFlag)
03028 {
03029     nsMsgDBEnumerator* e = new nsMsgDBEnumerator(this, m_mdbAllMsgHeadersTable, nsMsgFlagSetFilter, pFlag);
03030     if (e == nsnull)
03031         return NS_ERROR_OUT_OF_MEMORY;
03032     NS_ADDREF(*result = e);
03033     return NS_OK;
03034 }
03035 
03036 NS_IMETHODIMP nsMsgDatabase::CreateNewHdr(nsMsgKey key, nsIMsgDBHdr **pnewHdr)
03037 {
03038   nsresult    err = NS_OK;
03039   nsIMdbRow          *hdrRow;
03040   struct mdbOid allMsgHdrsTableOID;
03041   
03042   if (!pnewHdr || !m_mdbAllMsgHeadersTable || !m_mdbStore)
03043     return NS_ERROR_NULL_POINTER;
03044   
03045   allMsgHdrsTableOID.mOid_Scope = m_hdrRowScopeToken;
03046   allMsgHdrsTableOID.mOid_Id = key;       // presumes 0 is valid key value
03047   
03048   err = m_mdbStore->GetRow(GetEnv(), &allMsgHdrsTableOID, &hdrRow);
03049   if (!hdrRow)       
03050     err  = m_mdbStore->NewRowWithOid(GetEnv(), &allMsgHdrsTableOID, &hdrRow);
03051   
03052   if (NS_FAILED(err)) 
03053     return err;
03054   err = CreateMsgHdr(hdrRow, key, pnewHdr);
03055   return err;
03056 }
03057 
03058 NS_IMETHODIMP nsMsgDatabase::AddNewHdrToDB(nsIMsgDBHdr *newHdr, PRBool notify)
03059 {
03060   nsMsgHdr* hdr = NS_STATIC_CAST(nsMsgHdr*, newHdr);          // closed system, cast ok
03061   PRBool newThread;
03062   
03063   nsresult err = ThreadNewHdr(hdr, newThread);
03064   // we thread header before we add it to the all headers table
03065   // so that subject threading will work (otherwise, when we try
03066   // to find the first header with the same subject, we get the
03067   // new header!
03068   if (NS_SUCCEEDED(err))
03069   {
03070     nsMsgKey key;
03071     PRUint32 flags;
03072     
03073     newHdr->GetMessageKey(&key);
03074     hdr->GetRawFlags(&flags);
03075     // use raw flags instead of GetFlags, because GetFlags will
03076     // pay attention to what's in m_newSet, and this new hdr isn't
03077     // in m_newSet yet.
03078     if (flags & MSG_FLAG_NEW)
03079     {
03080       PRUint32 newFlags;
03081       newHdr->AndFlags(~MSG_FLAG_NEW, &newFlags);       // make sure not filed out
03082       AddToNewList(key);
03083     }
03084     if (m_dbFolderInfo != NULL)
03085     {
03086       m_dbFolderInfo->ChangeNumMessages(1);
03087       PRBool isRead = PR_TRUE;
03088       IsHeaderRead(newHdr, &isRead);
03089       if (!isRead) 
03090         m_dbFolderInfo->ChangeNumUnreadMessages(1);
03091       m_dbFolderInfo->SetHighWater(key, PR_FALSE);
03092     }
03093     
03094     err = m_mdbAllMsgHeadersTable->AddRow(GetEnv(), hdr->GetMDBRow());
03095     if (notify)
03096     {
03097       nsMsgKey threadParent;
03098       
03099       newHdr->GetThreadParent(&threadParent);
03100       NotifyHdrAddedAll(newHdr, threadParent, flags, NULL);
03101     }
03102   }
03103   NS_ASSERTION(NS_SUCCEEDED(err), "error creating thread");
03104   return err;
03105 }
03106 
03107 NS_IMETHODIMP nsMsgDatabase::CopyHdrFromExistingHdr(nsMsgKey key, nsIMsgDBHdr *existingHdr, PRBool addHdrToDB, nsIMsgDBHdr **newHdr)
03108 {
03109   nsresult    err = NS_OK;
03110   
03111   if (existingHdr)
03112   {
03113     if (key == nsMsgKey_None)
03114       return NS_MSG_MESSAGE_NOT_FOUND;
03115     
03116     nsMsgHdr* sourceMsgHdr = NS_STATIC_CAST(nsMsgHdr*, existingHdr);      // closed system, cast ok
03117     nsMsgHdr *destMsgHdr = nsnull;
03118     CreateNewHdr(key, (nsIMsgDBHdr **) &destMsgHdr);
03119     if (!destMsgHdr)
03120       return NS_MSG_MESSAGE_NOT_FOUND;
03121     
03122     nsIMdbRow *sourceRow = sourceMsgHdr->GetMDBRow() ;
03123     nsIMdbRow *destRow = destMsgHdr->GetMDBRow();
03124     err = destRow->SetRow(GetEnv(), sourceRow);
03125     if (NS_SUCCEEDED(err))
03126     {
03127       if(addHdrToDB)
03128         err = AddNewHdrToDB(destMsgHdr, PR_TRUE);
03129       if (NS_SUCCEEDED(err) && newHdr)
03130         *newHdr = destMsgHdr;
03131     }
03132     
03133   }
03134   return err;
03135 }
03136 
03137 nsresult nsMsgDatabase::RowCellColumnTonsString(nsIMdbRow *hdrRow, mdb_token columnToken, nsAString &resultStr)
03138 {
03139   nsresult    err = NS_OK;
03140   
03141   if (hdrRow) // ### probably should be an error if hdrRow is NULL...
03142   {
03143     struct mdbYarn yarn;
03144     err = hdrRow->AliasCellYarn(GetEnv(), columnToken, &yarn);
03145     if (err == NS_OK)
03146       YarnTonsString(&yarn, resultStr);
03147   }
03148   return err;
03149 }
03150 
03151 // as long as the row still exists, and isn't changed, the returned const char ** will be valid.
03152 // But be very careful using this data - the caller should never return it in turn to another caller.
03153 nsresult nsMsgDatabase::RowCellColumnToConstCharPtr(nsIMdbRow *hdrRow, mdb_token columnToken, const char **ptr)
03154 {
03155   nsresult    err = NS_OK;
03156   
03157   if (hdrRow) // ### probably should be an error if hdrRow is NULL...
03158   {
03159     struct mdbYarn yarn;
03160     err = hdrRow->AliasCellYarn(GetEnv(), columnToken, &yarn);
03161     if (err == NS_OK)
03162       *ptr = (const char*)yarn.mYarn_Buf;
03163   }
03164   return err;
03165 }
03166 
03167 nsIMimeConverter *nsMsgDatabase::GetMimeConverter()
03168 {
03169   if (!m_mimeConverter)
03170   {
03171     // apply mime decode
03172     m_mimeConverter = do_GetService(NS_MIME_CONVERTER_CONTRACTID);
03173   }
03174   return m_mimeConverter;
03175 }
03176 
03177 nsresult nsMsgDatabase::RowCellColumnToMime2DecodedString(nsIMdbRow *row, mdb_token columnToken, PRUnichar* *resultStr)
03178 {
03179   nsresult err = NS_OK;
03180   const char *nakedString = nsnull;
03181   err = RowCellColumnToConstCharPtr(row, columnToken, &nakedString);
03182   if (NS_SUCCEEDED(err) && nakedString && strlen(nakedString))
03183   {
03184     GetMimeConverter();
03185     if (m_mimeConverter) 
03186     {
03187       nsAutoString decodedStr;
03188       char *charSet;
03189       PRBool characterSetOverride;
03190       m_dbFolderInfo->GetCharacterSetOverride(&characterSetOverride);
03191       err = RowCellColumnToCharPtr(row, m_messageCharSetColumnToken, &charSet);
03192       if (NS_FAILED(err) || !*charSet || !PL_strcasecmp(charSet, "us-ascii") ||
03193           characterSetOverride)
03194       {
03195         PR_Free(charSet);
03196         m_dbFolderInfo->GetCharPtrCharacterSet(&charSet);
03197       }
03198 
03199       err = m_mimeConverter->DecodeMimeHeader(nakedString, resultStr, charSet, characterSetOverride);
03200       PR_Free(charSet);
03201     }
03202   }
03203   return err;
03204 }
03205 
03206 nsresult nsMsgDatabase::RowCellColumnToAddressCollationKey(nsIMdbRow *row, mdb_token colToken, PRUint8 **result, PRUint32 *len)
03207 {
03208   const char *cSender;
03209   nsXPIDLCString name;
03210   
03211   nsresult ret = RowCellColumnToConstCharPtr(row, colToken, &cSender);
03212   if (NS_SUCCEEDED(ret))
03213   {
03214     nsIMsgHeaderParser *headerParser = GetHeaderParser();
03215     if (headerParser)
03216     {
03217       // apply mime decode
03218       nsIMimeConverter *converter = GetMimeConverter();
03219       
03220       if (NS_SUCCEEDED(ret) && nsnull != converter) 
03221       {
03222         char *resultStr = nsnull;
03223         char *charset;
03224         PRBool characterSetOverride;
03225         m_dbFolderInfo->GetCharacterSetOverride(&characterSetOverride);
03226         ret = RowCellColumnToCharPtr(row, m_messageCharSetColumnToken, &charset);
03227         if (NS_FAILED(ret) || !*charset || !PL_strcasecmp(charset, "us-ascii") ||
03228             characterSetOverride)
03229         {
03230           PR_Free(charset);
03231           m_dbFolderInfo->GetCharPtrCharacterSet(&charset);
03232         }
03233 
03234         ret = converter->DecodeMimeHeader(cSender, &resultStr,
03235           charset, characterSetOverride);
03236         if (NS_SUCCEEDED(ret) && resultStr)
03237         {
03238           ret = headerParser->ExtractHeaderAddressName ("UTF-8", resultStr, getter_Copies(name));
03239         }
03240         else {
03241           ret = headerParser->ExtractHeaderAddressName ("UTF-8", cSender, getter_Copies(name));
03242         }
03243         PR_FREEIF(resultStr);
03244         PR_FREEIF(charset);
03245       }
03246       
03247     }
03248   }
03249   if (NS_SUCCEEDED(ret))
03250   {
03251     ret = CreateCollationKey(NS_ConvertUTF8toUCS2(name), result, len);
03252   }
03253   
03254   return ret;
03255 }
03256 
03257 nsresult nsMsgDatabase::GetCollationKeyGenerator()
03258 {
03259   nsresult err = NS_OK;
03260   if (!m_collationKeyGenerator)
03261   {
03262     nsCOMPtr <nsILocale> locale; 
03263     nsAutoString localeName; 
03264     
03265     // get a locale service 
03266     nsCOMPtr <nsILocaleService> localeService = do_GetService(NS_LOCALESERVICE_CONTRACTID, &err);
03267     if (NS_SUCCEEDED(err))
03268     {
03269       // do this for a new db if no UI to be provided for locale selection 
03270       err = localeService->GetApplicationLocale(getter_AddRefs(locale)); 
03271       
03272       if (locale)
03273       {
03274         // or generate a locale from a stored locale name ("en_US", "fr_FR") 
03275         //err = localeFactory->NewLocale(&localeName, &locale); 
03276         
03277         nsCOMPtr <nsICollationFactory> f = do_CreateInstance(kCollationFactoryCID, &err);
03278         if (NS_SUCCEEDED(err) && f)
03279         {
03280           // get a collation interface instance 
03281           err = f->CreateCollation(locale, getter_AddRefs(m_collationKeyGenerator));
03282         }
03283       }
03284     }
03285   }
03286   return err;
03287 }
03288 
03289 nsresult nsMsgDatabase::RowCellColumnToCollationKey(nsIMdbRow *row, mdb_token columnToken, PRUint8 **result, PRUint32 *len)
03290 {
03291   const char *nakedString = nsnull;
03292   nsresult err;
03293   
03294   err = RowCellColumnToConstCharPtr(row, columnToken, &nakedString);
03295   if (NS_SUCCEEDED(err))
03296   {
03297     GetMimeConverter();
03298     if (m_mimeConverter) 
03299     {
03300       nsXPIDLCString decodedStr;
03301       char *charSet;
03302       PRBool characterSetOverride;
03303       m_dbFolderInfo->GetCharacterSetOverride(&characterSetOverride);
03304       err = RowCellColumnToCharPtr(row, m_messageCharSetColumnToken, &charSet);
03305       if (NS_FAILED(err) || !*charSet || !PL_strcasecmp(charSet, "us-ascii") ||
03306           characterSetOverride)
03307       {
03308         PR_Free(charSet);
03309         m_dbFolderInfo->GetCharPtrCharacterSet(&charSet);
03310       }
03311       
03312       err = m_mimeConverter->DecodeMimeHeader(nakedString, getter_Copies(decodedStr), charSet, characterSetOverride);
03313       if (NS_SUCCEEDED(err))
03314         err = CreateCollationKey(NS_ConvertUTF8toUCS2(decodedStr), result, len);
03315       PR_Free(charSet);
03316     }
03317   }
03318   return err;
03319 }
03320 
03321 NS_IMETHODIMP 
03322 nsMsgDatabase::CompareCollationKeys(PRUint8 *key1, PRUint32 len1, PRUint8 *key2, PRUint32 len2, PRInt32 *result)
03323 {
03324   nsresult rv = GetCollationKeyGenerator();
03325   NS_ENSURE_SUCCESS(rv,rv);
03326   if (!m_collationKeyGenerator) return NS_ERROR_FAILURE;
03327 
03328   rv = m_collationKeyGenerator->CompareRawSortKey(key1,len1,key2,len2,result);
03329   NS_ENSURE_SUCCESS(rv,rv);
03330   return rv;
03331 }
03332 
03333 NS_IMETHODIMP 
03334 nsMsgDatabase::CreateCollationKey(const nsAString& sourceString, PRUint8 **result, PRUint32 *len)
03335 {
03336   nsresult err = GetCollationKeyGenerator();
03337   NS_ENSURE_SUCCESS(err,err);
03338   if (!m_collationKeyGenerator) return NS_ERROR_FAILURE;
03339 
03340   err = m_collationKeyGenerator->AllocateRawSortKey(nsICollation::kCollationCaseInSensitive, sourceString, result, len);
03341   NS_ENSURE_SUCCESS(err,err);
03342   return err;
03343 }
03344 
03345 nsIMsgHeaderParser *nsMsgDatabase::GetHeaderParser()
03346 {
03347   if (!m_HeaderParser)
03348   {
03349     nsCOMPtr <nsIMsgHeaderParser> parser = do_GetService(NS_MAILNEWS_MIME_HEADER_PARSER_CONTRACTID);
03350     NS_IF_ADDREF(m_HeaderParser = parser);
03351   }
03352   return m_HeaderParser;
03353 }
03354 
03355 
03356 nsresult nsMsgDatabase::RowCellColumnToUInt32(nsIMdbRow *hdrRow, mdb_token columnToken, PRUint32 &uint32Result, PRUint32 defaultValue)
03357 {
03358   return RowCellColumnToUInt32(hdrRow, columnToken, &uint32Result, defaultValue);
03359 }
03360 
03361 nsresult nsMsgDatabase::RowCellColumnToUInt32(nsIMdbRow *hdrRow, mdb_token columnToken, PRUint32 *uint32Result, PRUint32 defaultValue)
03362 {
03363   nsresult    err = NS_OK;
03364   
03365   if (uint32Result)
03366     *uint32Result = defaultValue;
03367   if (hdrRow) // ### probably should be an error if hdrRow is NULL...
03368   {
03369     struct mdbYarn yarn;
03370     err = hdrRow->AliasCellYarn(GetEnv(), columnToken, &yarn);
03371     if (err == NS_OK)
03372       YarnToUInt32(&yarn, uint32Result);
03373   }
03374   return err;
03375 }
03376 
03377 nsresult nsMsgDatabase::UInt32ToRowCellColumn(nsIMdbRow *row, mdb_token columnToken, PRUint32 value)
03378 {
03379   struct mdbYarn yarn;
03380   char yarnBuf[100];
03381   
03382   if (!row)
03383     return NS_ERROR_NULL_POINTER;
03384 
03385   yarn.mYarn_Buf = (void *) yarnBuf;
03386   yarn.mYarn_Size = sizeof(yarnBuf);
03387   yarn.mYarn_Fill = yarn.mYarn_Size;
03388   yarn.mYarn_Form = 0;
03389   yarn.mYarn_Grow = NULL;
03390   return row->AddColumn(GetEnv(),  columnToken, UInt32ToYarn(&yarn, value));
03391 }
03392 
03393 nsresult nsMsgDatabase::CharPtrToRowCellColumn(nsIMdbRow *row, mdb_token columnToken, const char *charPtr)
03394 {
03395   if (!row)
03396     return NS_ERROR_NULL_POINTER;
03397 
03398   struct mdbYarn yarn;
03399   yarn.mYarn_Buf = (void *) charPtr;
03400   yarn.mYarn_Size = PL_strlen((const char *) yarn.mYarn_Buf) + 1;
03401   yarn.mYarn_Fill = yarn.mYarn_Size - 1;
03402   yarn.mYarn_Form = 0;      // what to do with this? we're storing csid in the msg hdr...
03403   
03404   return row->AddColumn(GetEnv(),  columnToken, &yarn);
03405 }
03406 
03407 // caller must PR_FREEIF result
03408 nsresult nsMsgDatabase::RowCellColumnToCharPtr(nsIMdbRow *row, mdb_token columnToken, char **result)
03409 {
03410   nsresult    err = NS_ERROR_NULL_POINTER;
03411   
03412   if (row && result)
03413   {
03414     struct mdbYarn yarn;
03415     err = row->AliasCellYarn(GetEnv(), columnToken, &yarn);
03416     if (err == NS_OK)
03417     {
03418       *result = (char *) PR_CALLOC(yarn.mYarn_Fill + 1);
03419       if (*result)
03420       {
03421         if (yarn.mYarn_Fill > 0)
03422           memcpy(*result, yarn.mYarn_Buf, yarn.mYarn_Fill);
03423         else
03424           **result = 0;
03425       }
03426       else
03427         err = NS_ERROR_OUT_OF_MEMORY;
03428       
03429     }
03430   }
03431   return err;
03432 }
03433 
03434 
03435 
03436 /* static */struct mdbYarn *nsMsgDatabase::nsStringToYarn(struct mdbYarn *yarn, const nsAString &str)
03437 {
03438   yarn->mYarn_Buf = ToNewCString(str);
03439   yarn->mYarn_Size = str.Length() + 1;
03440   yarn->mYarn_Fill = yarn->mYarn_Size - 1;
03441   yarn->mYarn_Form = 0;     // what to do with this? we're storing csid in the msg hdr...
03442   return yarn;
03443 }
03444 
03445 /* static */struct mdbYarn *nsMsgDatabase::UInt32ToYarn(struct mdbYarn *yarn, PRUint32 i)
03446 {
03447   PR_snprintf((char *) yarn->mYarn_Buf, yarn->mYarn_Size, "%lx", i);
03448   yarn->mYarn_Fill = PL_strlen((const char *) yarn->mYarn_Buf);
03449   yarn->mYarn_Form = 0;     // what to do with this? Should be parsed out of the mime2 header?
03450   return yarn;
03451 }
03452 
03453 /* static */void nsMsgDatabase::YarnTonsString(struct mdbYarn *yarn, nsAString &str)
03454 {
03455   const char* buf = (const char*)yarn->mYarn_Buf;
03456   if (buf)
03457     CopyASCIItoUTF16(Substring(buf, buf + yarn->mYarn_Fill), str);
03458   else
03459     str.Truncate();
03460 }
03461 
03462 /* static */void nsMsgDatabase::YarnTonsCString(struct mdbYarn *yarn, nsACString &str)
03463 {
03464     const char* buf = (const char*)yarn->mYarn_Buf;
03465     if (buf)
03466         str.Assign(buf, yarn->mYarn_Fill);
03467     else
03468         str.Truncate();
03469 }
03470 
03471 // WARNING - if yarn is empty, *pResult will not be changed!!!!
03472 // this is so we can leave default values as they were.
03473 /* static */void nsMsgDatabase::YarnToUInt32(struct mdbYarn *yarn, PRUint32 *pResult)
03474 {
03475   PRUint32 result;
03476   char *p = (char *) yarn->mYarn_Buf;
03477   PRInt32 numChars = PR_MIN(8, yarn->mYarn_Fill);
03478   PRInt32 i;
03479   
03480   if (numChars > 0)
03481   {
03482     for (i=0, result = 0; i<numChars; i++, p++)
03483     {
03484       char C = *p;
03485       
03486       PRInt8 unhex = ((C >= '0' && C <= '9') ? C - '0' :
03487       ((C >= 'A' && C <= 'F') ? C - 'A' + 10 :
03488                         ((C >= 'a' && C <= 'f') ? C - 'a' + 10 : -1)));
03489        if (unhex < 0)
03490          break;
03491        result = (result << 4) | unhex;
03492     }
03493     
03494     *pResult = result;
03495   }
03496 }
03497 
03498 nsresult nsMsgDatabase::GetProperty(nsIMdbRow *row, const char *propertyName, char **result)
03499 {
03500   nsresult err = NS_OK;
03501   mdb_token   property_token;
03502   
03503   if (m_mdbStore)
03504     err = m_mdbStore->StringToToken(GetEnv(),  propertyName, &property_token);
03505   else
03506     err = NS_ERROR_NULL_POINTER;
03507   if (err == NS_OK)
03508     err = RowCellColumnToCharPtr(row, property_token, result);
03509   
03510   return err;
03511 }
03512 
03513 nsresult nsMsgDatabase::SetProperty(nsIMdbRow *row, const char *propertyName, const char *propertyVal)
03514 {
03515   nsresult err = NS_OK;
03516   mdb_token   property_token;
03517   
03518   err = m_mdbStore->StringToToken(GetEnv(),  propertyName, &property_token);
03519   if (err == NS_OK)
03520     CharPtrToRowCellColumn(row, property_token, propertyVal);
03521   return err;
03522 }
03523 
03524 nsresult nsMsgDatabase::GetPropertyAsNSString(nsIMdbRow *row, const char *propertyName, nsAString &result)
03525 {
03526   nsresult err = NS_OK;
03527   mdb_token   property_token;
03528   
03529   err = m_mdbStore->StringToToken(GetEnv(),  propertyName, &property_token);
03530   if (err == NS_OK)
03531     err = RowCellColumnTonsString(row, property_token, result);
03532   
03533   return err;
03534 }
03535 
03536 nsresult nsMsgDatabase::SetPropertyFromNSString(nsIMdbRow *row, const char *propertyName, const nsAString &propertyVal)
03537 {
03538   nsresult err = NS_OK;
03539   mdb_token   property_token;
03540   
03541   err = m_mdbStore->StringToToken(GetEnv(),  propertyName, &property_token);
03542   if (err == NS_OK)
03543     return SetNSStringPropertyWithToken(row, property_token, propertyVal);
03544   
03545   return err;
03546 }
03547 
03548 
03549 nsresult nsMsgDatabase::GetUint32Property(nsIMdbRow *row, const char *propertyName, PRUint32 *result, PRUint32 defaultValue)
03550 {
03551   nsresult err = NS_OK;
03552   mdb_token   property_token;
03553   
03554   err = m_mdbStore->StringToToken(GetEnv(),  propertyName, &property_token);
03555   if (err == NS_OK)
03556     err = RowCellColumnToUInt32(row, property_token, result, defaultValue);
03557   
03558   return err;
03559 }
03560 
03561 nsresult nsMsgDatabase::SetUint32Property(nsIMdbRow *row, const char *propertyName, PRUint32 propertyVal)
03562 {
03563   struct mdbYarn yarn;
03564   char int32StrBuf[20];
03565   yarn.mYarn_Buf = int32StrBuf;
03566   yarn.mYarn_Size = sizeof(int32StrBuf);
03567   yarn.mYarn_Fill = sizeof(int32StrBuf);
03568 
03569   if (!row)
03570     return NS_ERROR_NULL_POINTER;
03571 
03572   mdb_token   property_token;
03573   
03574   nsresult err = m_mdbStore->StringToToken(GetEnv(),  propertyName, &property_token);
03575   if (err == NS_OK)
03576   {
03577     UInt32ToYarn(&yarn, propertyVal);
03578     err = row->AddColumn(GetEnv(), property_token, &yarn);
03579   }
03580   return err;
03581 }
03582 
03583 nsresult nsMsgDatabase::SetNSStringPropertyWithToken(nsIMdbRow *row, mdb_token aProperty, const nsAString &propertyStr)
03584 {
03585   NS_ENSURE_ARG(row);
03586   struct mdbYarn yarn;
03587   
03588   yarn.mYarn_Grow = NULL;
03589   nsresult err = row->AddColumn(GetEnv(), aProperty, nsStringToYarn(&yarn, propertyStr));
03590   nsMemory::Free((char *)yarn.mYarn_Buf); // won't need this when we have nsCString
03591   return err;
03592 }
03593 
03594 
03595 PRUint32 nsMsgDatabase::GetCurVersion()
03596 {
03597   return kMsgDBVersion;
03598 }
03599 
03600 NS_IMETHODIMP nsMsgDatabase::SetSummaryValid(PRBool valid /* = PR_TRUE */)
03601 {
03602   // setting the version to 0 ought to make it pretty invalid.
03603   if (!valid)
03604     m_dbFolderInfo->SetVersion(0);
03605   
03606   // for default db (and news), there's no nothing to set to make it it valid
03607   return NS_OK;
03608 }
03609 
03610 
03611 NS_IMETHODIMP nsMsgDatabase::GetSummaryValid(PRBool *aResult)
03612 {
03613   NS_ENSURE_ARG_POINTER(aResult);
03614   *aResult = PR_TRUE;
03615   return NS_OK;
03616 }
03617        
03618 
03619 // protected routines
03620 
03621 // should we thread messages with common subjects that don't start with Re: together?
03622 // I imagine we might have separate preferences for mail and news, so this is a virtual method.
03623 PRBool nsMsgDatabase::ThreadBySubjectWithoutRe()
03624 {
03625   GetGlobalPrefs();
03626   return gThreadWithoutRe;
03627 }
03628 
03629 PRBool nsMsgDatabase::UseStrictThreading()
03630 {
03631   GetGlobalPrefs();
03632   return gStrictThreading;
03633 }
03634 
03635 
03636 nsresult nsMsgDatabase::CreateNewThread(nsMsgKey threadId, const char *subject, nsMsgThread **pnewThread)
03637 {
03638   nsresult    err = NS_OK;
03639   nsIMdbTable        *threadTable;
03640   struct mdbOid threadTableOID;
03641   struct mdbOid allThreadsTableOID;
03642   
03643   if (!pnewThread || !m_mdbStore)
03644     return NS_ERROR_NULL_POINTER;
03645   
03646   threadTableOID.mOid_Scope = m_hdrRowScopeToken;
03647   threadTableOID.mOid_Id = threadId;
03648   
03649   err  = GetStore()->NewTableWithOid(GetEnv(), &threadTableOID, m_threadTableKindToken, 
03650     PR_FALSE, nsnull, &threadTable);
03651   if (NS_FAILED(err)) 
03652     return err;
03653   
03654   allThreadsTableOID.mOid_Scope = m_threadRowScopeToken;
03655   allThreadsTableOID.mOid_Id = threadId;  
03656   
03657   // add a row for this thread in the table of all threads that we'll use
03658   // to do our mapping between subject strings and threads.
03659   nsIMdbRow *threadRow = nsnull;
03660   
03661   err = m_mdbStore->GetRow(GetEnv(), &allThreadsTableOID, &threadRow);
03662   if (!threadRow)    
03663   {
03664     err  = m_mdbStore->NewRowWithOid(GetEnv(), &allThreadsTableOID, &threadRow);
03665     if (NS_SUCCEEDED(err) && threadRow)
03666     {
03667       if (m_mdbAllThreadsTable)
03668         m_mdbAllThreadsTable->AddRow(GetEnv(), threadRow);
03669       err = CharPtrToRowCellColumn(threadRow, m_threadSubjectColumnToken, subject);
03670       threadRow->Release();
03671     }
03672   }
03673   
03674   *pnewThread = new nsMsgThread(this, threadTable);
03675   if (*pnewThread)
03676     (*pnewThread)->SetThreadKey(threadId);
03677   return err;
03678 }
03679 
03680 
03681 nsIMsgThread *nsMsgDatabase::GetThreadForReference(nsCString &msgID, nsIMsgDBHdr **pMsgHdr)
03682 {
03683   nsIMsgDBHdr *msgHdr = nsnull;
03684   GetMsgHdrForMessageID(msgID.get(), &msgHdr);  
03685   nsIMsgThread *thread = NULL;
03686   
03687   if (msgHdr != NULL)
03688   {
03689     nsMsgKey threadId;
03690     if (NS_SUCCEEDED(msgHdr->GetThreadId(&threadId)))
03691     {
03692       // find thread header for header whose message id we matched.
03693       thread = GetThreadForThreadId(threadId);
03694     }
03695     if (pMsgHdr)
03696       *pMsgHdr = msgHdr;
03697     else
03698       msgHdr->Release();
03699   }
03700   return thread;
03701 }
03702 
03703 nsIMsgThread *       nsMsgDatabase::GetThreadForSubject(nsCString &subject)
03704 {
03705   nsIMsgThread *thread = nsnull;
03706   
03707   mdbYarn     subjectYarn;
03708   
03709   subjectYarn.mYarn_Buf = (void*)subject.get();
03710   subjectYarn.mYarn_Fill = PL_strlen(subject.get());
03711   subjectYarn.mYarn_Form = 0;
03712   subjectYarn.mYarn_Size = subjectYarn.mYarn_Fill;
03713   
03714   nsCOMPtr <nsIMdbRow>      threadRow;
03715   mdbOid             outRowId;
03716   if (m_mdbStore)
03717   {
03718     mdb_err result = m_mdbStore->FindRow(GetEnv(), m_threadRowScopeToken,
03719       m_threadSubjectColumnToken, &subjectYarn,  &outRowId, getter_AddRefs(threadRow));
03720     if (NS_SUCCEEDED(result) && threadRow)
03721     {
03722       //Get key from row
03723       mdbOid outOid;
03724       nsMsgKey key = 0;
03725       if (threadRow->GetOid(GetEnv(), &outOid) == NS_OK)
03726         key = outOid.mOid_Id;
03727       // find thread header for header whose message id we matched.
03728       thread = GetThreadForThreadId(key);
03729     }
03730 #ifdef DEBUG_bienvenu1
03731     else
03732     {
03733       nsresult       rv;
03734       nsMsgThread *pThread;
03735       
03736       nsCOMPtr <nsIMdbPortTableCursor> tableCursor;
03737       m_mdbStore->GetPortTableCursor(GetEnv(),   m_hdrRowScopeToken, m_threadTableKindToken,
03738         getter_AddRefs(tableCursor));
03739       
03740       
03741         nsIMdbTable *table;
03742         
03743         while (PR_TRUE) 
03744         {
03745           rv = tableCursor->NextTable(GetEnv(), &table);
03746           if (!table) 
03747             break;
03748           if (NS_FAILED(rv)) 
03749             break;
03750           
03751           pThread = new nsMsgThread(this, table);
03752           if(pThread)
03753           {
03754             // thread object assumes ref for table.
03755             NS_ADDREF(pThread);
03756             nsXPIDLCString curSubject;
03757             (void)pThread->GetSubject(getter_Copies(curSubject));
03758             if (subject.Equals(curSubject))
03759             {
03760               NS_ASSERTION(PR_FALSE, "thread with subject exists, but FindRow didn't find it\n");
03761               break;
03762             }
03763             NS_IF_RELEASE (pThread);
03764           }
03765           else
03766             break;
03767         }
03768       }
03769 #endif
03770   }
03771   return thread;
03772 }
03773 
03774 nsresult nsMsgDatabase::ThreadNewHdr(nsMsgHdr* newHdr, PRBool &newThread)
03775 {
03776   nsresult result=NS_ERROR_UNEXPECTED;
03777   nsCOMPtr <nsIMsgThread> thread;
03778   nsCOMPtr <nsIMsgDBHdr> replyToHdr;
03779   nsMsgKey threadId = nsMsgKey_None, newHdrKey;
03780   
03781   if (!newHdr)
03782     return NS_ERROR_NULL_POINTER;
03783   
03784   newHdr->SetThreadParent(nsMsgKey_None); // if we're undoing, could have a thread parent
03785   PRUint16 numReferences = 0;
03786   PRUint32 newHdrFlags = 0;
03787   
03788   // use raw flags instead of GetFlags, because GetFlags will
03789   // pay attention to what's in m_newSet, and this new hdr isn't
03790   // in m_newSet yet.
03791   newHdr->GetRawFlags(&newHdrFlags);
03792   newHdr->GetNumReferences(&numReferences);
03793   newHdr->GetMessageKey(&newHdrKey);
03794   
03795   // try reference threading first
03796   for (PRInt32 i = numReferences - 1; i >= 0;  i--)
03797   {
03798     nsCAutoString reference;
03799     
03800     newHdr->GetStringReference(i, reference);
03801     // first reference we have hdr for is best top-level hdr.
03802     // but we have to handle case of promoting new header to top-level
03803     // in case the top-level header comes after a reply.
03804     
03805     if (reference.IsEmpty())
03806       break;
03807     
03808     thread = getter_AddRefs(GetThreadForReference(reference, getter_AddRefs(replyToHdr))) ;
03809     if (thread)
03810     {
03811       if (replyToHdr)
03812       {
03813         nsMsgKey replyToKey;
03814         replyToHdr->GetMessageKey(&replyToKey);
03815         // message claims to be a reply to itself - ignore that since it leads to corrupt threading.
03816         if (replyToKey == newHdrKey)
03817         {
03818           // bad references - throw them all away.
03819           newHdr->SetMessageId("");
03820           thread = nsnull;
03821           break;
03822         }
03823       }
03824       thread->GetThreadKey(&threadId);
03825       newHdr->SetThreadId(threadId);
03826       result = AddToThread(newHdr, thread, replyToHdr, PR_TRUE);
03827       break;
03828     }
03829   }
03830   // if user hasn't said "only thread by ref headers", thread by subject
03831   if (!UseStrictThreading())
03832   {
03833     // try subject threading if we couldn't find a reference and the subject starts with Re:
03834     nsXPIDLCString subject;
03835   
03836     newHdr->GetSubject(getter_Copies(subject));
03837     if ((ThreadBySubjectWithoutRe() || (newHdrFlags & MSG_FLAG_HAS_RE)) && (!thread))
03838     {
03839       nsCAutoString cSubject(subject);
03840       thread = getter_AddRefs(GetThreadForSubject(cSubject));
03841       if(thread)
03842       {
03843         thread->GetThreadKey(&threadId);
03844         newHdr->SetThreadId(threadId);
03845         //TRACE("threading based on subject %s\n", (const char *) msgHdr->m_subject);
03846         // if we move this and do subject threading after, ref threading, 
03847         // don't thread within children, since we know it won't work. But for now, pass TRUE.
03848         result = AddToThread(newHdr, thread, nsnull, PR_TRUE);     
03849       }
03850     }
03851   }
03852   
03853   if (!thread)
03854   {
03855     // couldn't find any parent articles - msgHdr is top-level thread, for now
03856     result = AddNewThread(newHdr);
03857     newThread = PR_TRUE;
03858   }
03859   else
03860   {
03861     newThread = PR_FALSE;
03862   }
03863   return result;
03864 }
03865 
03866 nsresult nsMsgDatabase::AddToThread(nsMsgHdr *newHdr, nsIMsgThread *thread, nsIMsgDBHdr *inReplyTo, PRBool threadInThread)
03867 {
03868   // don't worry about real threading yet.
03869   nsCOMPtr <nsIDBChangeAnnouncer> announcer = do_QueryInterface(this);
03870   
03871   return thread->AddChild(newHdr, inReplyTo, threadInThread, announcer);
03872 }
03873 
03874 nsMsgHdr * nsMsgDatabase::GetMsgHdrForReference(nsCString &reference)
03875 {
03876   NS_ASSERTION(PR_FALSE, "not implemented yet.");
03877   return nsnull;
03878 }
03879 
03880 NS_IMETHODIMP nsMsgDatabase::GetMsgHdrForMessageID(const char *msgID, nsIMsgDBHdr **aHdr)
03881 {
03882   NS_ENSURE_ARG_POINTER(aHdr);
03883   nsIMsgDBHdr *msgHdr = nsnull;
03884   nsresult rv = NS_OK;
03885   mdbYarn     messageIdYarn;
03886   
03887   messageIdYarn.mYarn_Buf = (void *) msgID;
03888   messageIdYarn.mYarn_Fill = PL_strlen(msgID);
03889   messageIdYarn.mYarn_Form = 0;
03890   messageIdYarn.mYarn_Size = messageIdYarn.mYarn_Fill;
03891   
03892   nsIMdbRow   *hdrRow;
03893   mdbOid             outRowId;
03894   mdb_err result = GetStore()->FindRow(GetEnv(), m_hdrRowScopeToken,
03895     m_messageIdColumnToken, &messageIdYarn,  &outRowId, 
03896     &hdrRow);
03897   if (NS_SUCCEEDED(result) && hdrRow)
03898   {
03899     //Get key from row
03900     mdbOid outOid;
03901     nsMsgKey key=0;
03902     if (hdrRow->GetOid(GetEnv(), &outOid) == NS_OK)
03903       key = outOid.mOid_Id;
03904     rv = GetHdrFromUseCache(key, &msgHdr);
03905     if (NS_SUCCEEDED(rv) && msgHdr)
03906       hdrRow->Release();
03907     else
03908       rv = CreateMsgHdr(hdrRow, key, &msgHdr);
03909   }
03910   *aHdr = msgHdr; // already addreffed above.
03911   return NS_OK; // it's not an error not to find a msg hdr.
03912 }
03913 
03914 nsIMsgDBHdr *nsMsgDatabase::GetMsgHdrForSubject(nsCString &subject)
03915 {
03916   nsIMsgDBHdr *msgHdr = nsnull;
03917   nsresult rv = NS_OK;
03918   mdbYarn     subjectYarn;
03919   
03920   subjectYarn.mYarn_Buf = (void*)subject.get();
03921   subjectYarn.mYarn_Fill = PL_strlen(subject.get());
03922   subjectYarn.mYarn_Form = 0;
03923   subjectYarn.mYarn_Size = subjectYarn.mYarn_Fill;
03924   
03925   nsIMdbRow   *hdrRow;
03926   mdbOid             outRowId;
03927   mdb_err result = GetStore()->FindRow(GetEnv(), m_hdrRowScopeToken,
03928     m_subjectColumnToken, &subjectYarn,  &outRowId, 
03929     &hdrRow);
03930   if (NS_SUCCEEDED(result) && hdrRow)
03931   {
03932     //Get key from row
03933     mdbOid outOid;
03934     nsMsgKey key=0;
03935     if (hdrRow->GetOid(GetEnv(), &outOid) == NS_OK)
03936       key = outOid.mOid_Id;
03937     rv = GetHdrFromUseCache(key, &msgHdr);
03938     if (NS_SUCCEEDED(rv) && msgHdr)
03939       hdrRow->Release();
03940     else
03941       rv = CreateMsgHdr(hdrRow, key, &msgHdr);
03942   }
03943   return msgHdr;
03944 }
03945 
03946 NS_IMETHODIMP nsMsgDatabase::GetThreadContainingMsgHdr(nsIMsgDBHdr *msgHdr, nsIMsgThread **result)
03947 {
03948   NS_ENSURE_ARG_POINTER(msgHdr);
03949   
03950   if (!result)
03951     return NS_ERROR_NULL_POINTER;
03952   
03953   *result = nsnull;
03954   nsMsgKey threadId = nsMsgKey_None;
03955   (void)msgHdr->GetThreadId(&threadId);
03956   if (threadId != nsMsgKey_None)
03957     *result = GetThreadForThreadId(threadId);
03958   
03959   // if we can't find the thread, try using the msg key as the thread id,
03960   // because the msg hdr might not have the thread id set correctly
03961   if (!*result) 
03962   {
03963     nsMsgKey msgKey;
03964     NS_ASSERTION(PR_FALSE, "this shouldn't happen");
03965     msgHdr->GetMessageKey(&msgKey);
03966     *result = GetThreadForThreadId(msgKey);
03967   }
03968   return (*result) ? NS_OK : NS_ERROR_FAILURE;
03969 }
03970 
03971 
03972 nsresult nsMsgDatabase::GetThreadForMsgKey(nsMsgKey msgKey, nsIMsgThread **aResult)
03973 {
03974   NS_ENSURE_ARG_POINTER(aResult);
03975   
03976   nsCOMPtr <nsIMsgDBHdr> msg;
03977   nsresult rv = GetMsgHdrForKey(msgKey, getter_AddRefs(msg));
03978   
03979   if (NS_SUCCEEDED(rv) && msg)
03980     rv = GetThreadContainingMsgHdr(msg, aResult);
03981   
03982   return rv;
03983 }
03984 
03985 // caller needs to unrefer.
03986 nsIMsgThread *       nsMsgDatabase::GetThreadForThreadId(nsMsgKey threadId)
03987 {
03988   
03989   if (threadId == m_cachedThreadId && m_cachedThread)
03990   {
03991     nsIMsgThread *retThread = m_cachedThread;
03992     NS_ADDREF(retThread);
03993     return retThread;
03994   }
03995   nsMsgThread *pThread = nsnull;
03996   if (m_mdbStore)
03997   {
03998     mdbOid tableId;
03999     tableId.mOid_Id = threadId;
04000     tableId.mOid_Scope = m_hdrRowScopeToken;
04001     
04002     nsIMdbTable *threadTable;
04003     mdb_err res = m_mdbStore->GetTable(GetEnv(), &tableId, &threadTable);
04004     
04005     if (NS_SUCCEEDED(res) && threadTable)
04006     {
04007       pThread = new nsMsgThread(this, threadTable);
04008       if(pThread)
04009       {
04010         NS_ADDREF(pThread);
04011         m_cachedThread = pThread;
04012         m_cachedThreadId = threadId;
04013       }
04014     }
04015   }
04016   return pThread;
04017 }
04018 
04019 // make the passed in header a thread header
04020 nsresult nsMsgDatabase::AddNewThread(nsMsgHdr *msgHdr)
04021 {
04022   
04023   if (!msgHdr)
04024     return NS_ERROR_NULL_POINTER;
04025   
04026   nsMsgThread *threadHdr = nsnull;
04027   
04028   nsXPIDLCString subject;
04029   nsMsgKey threadKey = msgHdr->m_messageKey;
04030   // can't have a thread with key 1 since that's the table id of the all msg hdr table,
04031   // so give it kTableKeyForThreadOne (0xfffffffe).
04032   if (threadKey == 1)
04033     threadKey = kTableKeyForThreadOne;
04034 
04035   nsresult err = msgHdr->GetSubject(getter_Copies(subject));
04036   
04037   err = CreateNewThread(threadKey, subject, &threadHdr);
04038   msgHdr->SetThreadId(threadKey);
04039   if (threadHdr)
04040   {
04041     //        nsCString subject;
04042     
04043     threadHdr->AddRef();
04044     //        err = msgHdr->GetSubject(subject);
04045     //        threadHdr->SetThreadKey(msgHdr->m_messageKey);
04046     //        threadHdr->SetSubject(subject.get());
04047     
04048     // need to add the thread table to the db.
04049     AddToThread(msgHdr, threadHdr, nsnull, PR_FALSE);
04050     
04051     threadHdr->Release();
04052   }
04053   return err;
04054 }
04055 
04056 nsresult nsMsgDatabase::GetBoolPref(const char *prefName, PRBool *result)
04057 {
04058   PRBool prefValue = PR_FALSE;
04059   nsresult rv;
04060   nsCOMPtr<nsIPrefBranch> pPrefBranch(do_GetService(NS_PREFSERVICE_CONTRACTID, &rv));
04061   if (pPrefBranch)
04062   {
04063     rv = pPrefBranch->GetBoolPref(prefName, &prefValue);
04064     *result = prefValue;
04065   }
04066   return rv;
04067 }
04068 
04069 nsresult nsMsgDatabase::GetIntPref(const char *prefName, PRInt32 *result)
04070 {
04071   PRInt32 prefValue = 0;
04072   nsresult rv;
04073   nsCOMPtr<nsIPrefBranch> pPrefBranch(do_GetService(NS_PREFSERVICE_CONTRACTID, &rv));
04074   if (pPrefBranch)
04075   {
04076     rv = pPrefBranch->GetIntPref(prefName, &prefValue);
04077     *result = prefValue;
04078   }
04079   return rv;
04080 }
04081 
04082 
04083 nsresult nsMsgDatabase::ListAllThreads(nsMsgKeyArray *threadIds)
04084 {
04085   nsresult           rv;
04086   nsMsgThread        *pThread;
04087   
04088   nsCOMPtr <nsISimpleEnumerator> threads;
04089   rv = EnumerateThreads(getter_AddRefs(threads));
04090   if (NS_FAILED(rv)) return rv;
04091   PRBool hasMore = PR_FALSE;
04092   
04093   while (NS_SUCCEEDED(rv = threads->HasMoreElements(&hasMore)) && (hasMore == PR_TRUE)) 
04094   {
04095     rv = threads->GetNext((nsISupports**)&pThread);
04096     NS_ENSURE_SUCCESS(rv,rv);
04097     
04098     if (threadIds) {
04099       nsMsgKey key;
04100       (void)pThread->GetThreadKey(&key);
04101       threadIds->Add(key);
04102     }
04103     //        NS_RELEASE(pThread);
04104     pThread = nsnull;
04105   }
04106   return rv;
04107 }
04108 
04109 NS_IMETHODIMP nsMsgDatabase::SetAttributesOnPendingHdr(nsIMsgDBHdr *pendingHdr, const char *property, 
04110                                   const char *propertyVal, PRInt32 flags)
04111 {
04112   return NS_ERROR_NOT_IMPLEMENTED;
04113 }
04114 
04115 NS_IMETHODIMP nsMsgDatabase::GetOfflineOpForKey(nsMsgKey msgKey, PRBool create, nsIMsgOfflineImapOperation **offlineOp)
04116 {
04117   NS_ASSERTION(PR_FALSE, "overridden by nsMailDatabase");
04118   return NS_ERROR_NOT_IMPLEMENTED;
04119 }
04120 
04121 NS_IMETHODIMP  nsMsgDatabase::RemoveOfflineOp(nsIMsgOfflineImapOperation *op)
04122 {
04123   NS_ASSERTION(PR_FALSE, "overridden by nsMailDatabase");
04124   return NS_ERROR_NOT_IMPLEMENTED;
04125 }
04126 
04127 
04128 NS_IMETHODIMP nsMsgDatabase::ListAllOfflineMsgs(nsMsgKeyArray *outputKeys)
04129 {
04130   nsCOMPtr <nsISimpleEnumerator> enumerator;
04131   PRUint32 flag = MSG_FLAG_OFFLINE;
04132   // if we change this routine to return an enumerator that generates the keys
04133   // one by one, we'll need to somehow make a copy of flag for the enumerator
04134   // to own, since the enumerator will persist past the life of flag on the stack.
04135   nsresult rv = EnumerateMessagesWithFlag(getter_AddRefs(enumerator), &flag);
04136   if (NS_SUCCEEDED(rv) && enumerator)
04137   {
04138     PRBool hasMoreElements;
04139     while(NS_SUCCEEDED(enumerator->HasMoreElements(&hasMoreElements)) && hasMoreElements)
04140     {
04141       nsCOMPtr<nsISupports> childSupports;
04142       rv = enumerator->GetNext(getter_AddRefs(childSupports));
04143       if(NS_FAILED(rv))
04144         return rv;
04145       
04146       // clear out db hdr, because it won't be valid when we get rid of the .msf file
04147       nsCOMPtr<nsIMsgDBHdr> dbMessage(do_QueryInterface(childSupports, &rv));
04148       if(NS_SUCCEEDED(rv) && dbMessage)
04149       {
04150         nsMsgKey msgKey;
04151         dbMessage->GetMessageKey(&msgKey);
04152         outputKeys->Add(msgKey);
04153       }
04154     }
04155   }
04156   outputKeys->QuickSort();
04157   
04158   return rv;
04159 }
04160 
04161 NS_IMETHODIMP nsMsgDatabase::EnumerateOfflineOps(nsISimpleEnumerator **enumerator)
04162 {
04163   NS_ASSERTION(PR_FALSE, "overridden by nsMailDatabase");
04164   return NS_ERROR_NOT_IMPLEMENTED;
04165 }
04166 
04167 NS_IMETHODIMP nsMsgDatabase::ListAllOfflineOpIds(nsMsgKeyArray *offlineOpIds)
04168 {
04169   NS_ASSERTION(PR_FALSE, "overridden by nsMailDatabase");
04170   return NS_ERROR_NOT_IMPLEMENTED;
04171 }
04172 
04173 NS_IMETHODIMP nsMsgDatabase::ListAllOfflineDeletes(nsMsgKeyArray *offlineDeletes)
04174 {
04175   nsresult ret = NS_OK;
04176   if (!offlineDeletes)
04177     return NS_ERROR_NULL_POINTER;
04178   
04179   // technically, notimplemented, but no one's putting offline ops in anyway.
04180   return ret;
04181 }
04182 NS_IMETHODIMP nsMsgDatabase::GetHighWaterArticleNum(nsMsgKey *key)
04183 {
04184   if (!m_dbFolderInfo)
04185     return NS_ERROR_NULL_POINTER;
04186   return m_dbFolderInfo->GetHighWater(key);    
04187 }
04188 
04189 NS_IMETHODIMP nsMsgDatabase::GetLowWaterArticleNum(nsMsgKey *key)
04190 {
04191     return NS_ERROR_NOT_IMPLEMENTED;
04192 }
04193 
04194 /* attribute nsMsgKey NextPseudoMsgKey */
04195 
04196 NS_IMETHODIMP nsMsgDatabase::GetNextPseudoMsgKey(nsMsgKey *nextPseudoMsgKey)
04197 {
04198   NS_ENSURE_ARG_POINTER(nextPseudoMsgKey);
04199   *nextPseudoMsgKey = m_nextPseudoMsgKey--;
04200   return NS_OK;
04201 }
04202 
04203 NS_IMETHODIMP nsMsgDatabase::SetNextPseudoMsgKey(nsMsgKey nextPseudoMsgKey)
04204 {
04205   m_nextPseudoMsgKey = nextPseudoMsgKey;
04206   return NS_OK;
04207 }
04208 
04209 NS_IMETHODIMP nsMsgDatabase::GetNextFakeOfflineMsgKey(nsMsgKey *nextFakeOfflineMsgKey)
04210 {
04211   NS_ENSURE_ARG_POINTER(nextFakeOfflineMsgKey);
04212   // iterate over hdrs looking for first non-existant fake offline msg key
04213        nsMsgKey fakeMsgKey = kIdStartOfFake;
04214 
04215   PRBool containsKey;
04216   do
04217   {
04218      ContainsKey(fakeMsgKey, &containsKey);
04219      if (!containsKey)
04220        break;
04221      fakeMsgKey--;
04222   }
04223   while (containsKey);
04224 
04225   *nextFakeOfflineMsgKey = fakeMsgKey;
04226   return NS_OK;
04227 }
04228 
04229 #ifdef DEBUG
04230 nsresult nsMsgDatabase::DumpContents()
04231 {
04232   nsMsgKey key;
04233   PRUint32 i;
04234   
04235   nsMsgKeyArray keys;
04236   nsresult rv = ListAllKeys(keys);
04237   for (i = 0; i < keys.GetSize(); i++) {
04238     key = keys[i];
04239     nsIMsgDBHdr *msg = NULL;
04240     rv = GetMsgHdrForKey(key, &msg);
04241     nsMsgHdr* msgHdr = NS_STATIC_CAST(nsMsgHdr*, msg);      // closed system, cast ok
04242     if (NS_SUCCEEDED(rv))
04243     {
04244       nsXPIDLCString author;
04245       nsXPIDLCString subject;
04246       
04247       msgHdr->GetMessageKey(&key);
04248       msgHdr->GetAuthor(getter_Copies(author));
04249       msgHdr->GetSubject(getter_Copies(subject));
04250       printf("hdr key = %u, author = %s subject = %s\n", key,
04251         ((const char *)author) ? (const char *)author : "",
04252         ((const char*)subject) ? (const char*)subject : "");
04253       NS_RELEASE(msgHdr);
04254     }
04255   }
04256   nsMsgKeyArray threads;
04257   rv = ListAllThreads(&threads);
04258   for ( i = 0; i < threads.GetSize(); i++) 
04259   {
04260     key = threads[i];
04261     printf("thread key = %u\n", key);
04262     //        DumpThread(key);
04263   }
04264   
04265   
04266   return NS_OK;
04267 }
04268 
04269 nsresult nsMsgDatabase::DumpMsgChildren(nsIMsgDBHdr *msgHdr)
04270 {
04271   return NS_OK;
04272 }
04273 
04274 nsresult      nsMsgDatabase::DumpThread(nsMsgKey threadId)
04275 {
04276   nsresult rv = NS_OK;
04277   nsIMsgThread       *thread = nsnull;
04278   
04279   thread = GetThreadForThreadId(threadId);
04280   if (thread)
04281   {
04282     nsISimpleEnumerator *enumerator = nsnull;
04283     
04284     rv = thread->EnumerateMessages(nsMsgKey_None, &enumerator);
04285     if (NS_SUCCEEDED(rv) && enumerator)
04286     {
04287       PRBool hasMore = PR_FALSE;
04288       
04289       while (NS_SUCCEEDED(rv = enumerator->HasMoreElements(&hasMore)) && (hasMore == PR_TRUE)) 
04290       {
04291         nsCOMPtr <nsIMsgDBHdr> pMessage;
04292         rv = enumerator->GetNext(getter_AddRefs(pMessage));
04293         NS_ASSERTION(NS_SUCCEEDED(rv), "nsMsgDBEnumerator broken");
04294         if (NS_FAILED(rv) || !pMessage) 
04295           break;
04296       }
04297       NS_RELEASE(enumerator);
04298       
04299     }
04300   }
04301   return rv;
04302 }
04303 #endif /* DEBUG */
04304 
04305 NS_IMETHODIMP nsMsgDatabase::SetMsgRetentionSettings(nsIMsgRetentionSettings *retentionSettings)
04306 {
04307   m_retentionSettings = retentionSettings;
04308   if (retentionSettings && m_dbFolderInfo)
04309   {
04310     nsresult rv;
04311 
04312     nsMsgRetainByPreference retainByPreference;
04313     PRUint32 daysToKeepHdrs;
04314     PRUint32 numHeadersToKeep;
04315     PRBool keepUnreadMessagesOnly;
04316     PRUint32 daysToKeepBodies;
04317     PRBool cleanupBodiesByDays;
04318     PRBool useServerDefaults;
04319 
04320     rv = retentionSettings->GetRetainByPreference(&retainByPreference);
04321     NS_ENSURE_SUCCESS(rv, rv);
04322     rv = retentionSettings->GetDaysToKeepHdrs(&daysToKeepHdrs);
04323     NS_ENSURE_SUCCESS(rv, rv);
04324     rv = retentionSettings->GetNumHeadersToKeep(&numHeadersToKeep);
04325     NS_ENSURE_SUCCESS(rv, rv);
04326     rv = retentionSettings->GetKeepUnreadMessagesOnly(&keepUnreadMessagesOnly);
04327     NS_ENSURE_SUCCESS(rv, rv);
04328     rv = retentionSettings->GetDaysToKeepBodies(&daysToKeepBodies);
04329     NS_ENSURE_SUCCESS(rv, rv);
04330     (void) retentionSettings->GetCleanupBodiesByDays(&cleanupBodiesByDays);
04331     (void) retentionSettings->GetUseServerDefaults(&useServerDefaults);
04332     // need to write this to the db. We'll just use the dbfolderinfo to write properties.
04333     m_dbFolderInfo->SetUint32Property("retainBy", retainByPreference);
04334     m_dbFolderInfo->SetUint32Property("daysToKeepHdrs", daysToKeepHdrs);
04335     m_dbFolderInfo->SetUint32Property("numHdrsToKeep", numHeadersToKeep);
04336     m_dbFolderInfo->SetUint32Property("daysToKeepBodies", daysToKeepBodies);
04337     m_dbFolderInfo->SetUint32Property("keepUnreadOnly", (keepUnreadMessagesOnly) ? 1 : 0);
04338     m_dbFolderInfo->SetBooleanProperty("cleanupBodies", cleanupBodiesByDays);
04339     m_dbFolderInfo->SetBooleanProperty("useServerDefaults", useServerDefaults);
04340   }
04341   Commit(nsMsgDBCommitType::kLargeCommit);
04342   return NS_OK;
04343 }
04344 
04345 NS_IMETHODIMP nsMsgDatabase::GetMsgRetentionSettings(nsIMsgRetentionSettings **retentionSettings)
04346 {
04347   NS_ENSURE_ARG_POINTER(retentionSettings);
04348   if (!m_retentionSettings)
04349   {
04350     // create a new one, and initialize it from the db.
04351     m_retentionSettings = new nsMsgRetentionSettings;
04352     if (m_retentionSettings && m_dbFolderInfo)
04353     {
04354       nsresult rv;
04355 
04356       nsMsgRetainByPreference retainByPreference;
04357       PRUint32 daysToKeepHdrs = 0;
04358       PRUint32 numHeadersToKeep = 0;
04359       PRUint32 keepUnreadMessagesProp = 0;
04360       PRBool keepUnreadMessagesOnly = PR_FALSE;
04361       PRBool useServerDefaults;
04362       PRUint32 daysToKeepBodies = 0;
04363       PRBool cleanupBodiesByDays = PR_FALSE;
04364 
04365       rv = m_dbFolderInfo->GetUint32Property("retainBy", nsIMsgRetentionSettings::nsMsgRetainAll, &retainByPreference);
04366       m_dbFolderInfo->GetUint32Property("daysToKeepHdrs", 0, &daysToKeepHdrs);
04367       m_dbFolderInfo->GetUint32Property("numHdrsToKeep", 0, &numHeadersToKeep);
04368       m_dbFolderInfo->GetUint32Property("daysToKeepBodies", 0, &daysToKeepBodies);
04369       m_dbFolderInfo->GetUint32Property("keepUnreadOnly", 0, &keepUnreadMessagesProp);
04370       m_dbFolderInfo->GetBooleanProperty("useServerDefaults", PR_TRUE, &useServerDefaults);
04371       m_dbFolderInfo->GetBooleanProperty("cleanupBodies", PR_FALSE, &cleanupBodiesByDays);
04372       keepUnreadMessagesOnly = (keepUnreadMessagesProp == 1);
04373       m_retentionSettings->SetRetainByPreference(retainByPreference);
04374       m_retentionSettings->SetDaysToKeepHdrs(daysToKeepHdrs);
04375       m_retentionSettings->SetNumHeadersToKeep(numHeadersToKeep);
04376       m_retentionSettings->SetKeepUnreadMessagesOnly(keepUnreadMessagesOnly);
04377       m_retentionSettings->SetDaysToKeepBodies(daysToKeepBodies);
04378       m_retentionSettings->SetUseServerDefaults(useServerDefaults);
04379       m_retentionSettings->SetCleanupBodiesByDays(cleanupBodiesByDays);
04380     }
04381   }
04382   *retentionSettings = m_retentionSettings;
04383   NS_IF_ADDREF(*retentionSettings);
04384   return NS_OK;
04385 }
04386 
04387 NS_IMETHODIMP nsMsgDatabase::SetMsgDownloadSettings(nsIMsgDownloadSettings *downloadSettings)
04388 {
04389   m_downloadSettings = downloadSettings;
04390   if (downloadSettings && m_dbFolderInfo)
04391   {
04392     nsresult rv;
04393 
04394     PRBool useServerDefaults;
04395     PRBool downloadByDate;
04396     PRUint32 ageLimitOfMsgsToDownload;
04397     PRBool downloadUnreadOnly;
04398 
04399     rv = downloadSettings->GetUseServerDefaults(&useServerDefaults);
04400     NS_ENSURE_SUCCESS(rv, rv);
04401     rv = downloadSettings->GetDownloadByDate(&downloadByDate);
04402     NS_ENSURE_SUCCESS(rv, rv);
04403     rv = downloadSettings->GetDownloadUnreadOnly(&downloadUnreadOnly);
04404     NS_ENSURE_SUCCESS(rv, rv);
04405     rv = downloadSettings->GetAgeLimitOfMsgsToDownload(&ageLimitOfMsgsToDownload);
04406     NS_ENSURE_SUCCESS(rv, rv);
04407     // need to write this to the db. We'll just use the dbfolderinfo to write properties.
04408     m_dbFolderInfo->SetBooleanProperty("useServerDefaults", useServerDefaults);
04409     m_dbFolderInfo->SetBooleanProperty("downloadByDate", downloadByDate);
04410     m_dbFolderInfo->SetBooleanProperty("downloadUnreadOnly", downloadUnreadOnly);
04411     m_dbFolderInfo->SetUint32Property("ageLimit", ageLimitOfMsgsToDownload);
04412   }
04413   return NS_OK;
04414 }
04415 
04416 NS_IMETHODIMP nsMsgDatabase::GetMsgDownloadSettings(nsIMsgDownloadSettings **downloadSettings)
04417 {
04418   NS_ENSURE_ARG_POINTER(downloadSettings);
04419   if (!m_downloadSettings)
04420   {
04421     // create a new one, and initialize it from the db.
04422     m_downloadSettings = new nsMsgDownloadSettings;
04423     if (m_downloadSettings && m_dbFolderInfo)
04424     {
04425       PRBool useServerDefaults;
04426       PRBool downloadByDate;
04427       PRUint32 ageLimitOfMsgsToDownload;
04428       PRBool downloadUnreadOnly;
04429 
04430       m_dbFolderInfo->GetBooleanProperty("useServerDefaults", PR_TRUE, &useServerDefaults);
04431       m_dbFolderInfo->GetBooleanProperty("downloadByDate", PR_FALSE, &downloadByDate);
04432       m_dbFolderInfo->GetBooleanProperty("downloadUnreadOnly", PR_FALSE, &downloadUnreadOnly);
04433       m_dbFolderInfo->GetUint32Property("ageLimit", 0, &ageLimitOfMsgsToDownload);
04434 
04435       m_downloadSettings->SetUseServerDefaults(useServerDefaults);
04436       m_downloadSettings->SetDownloadByDate(downloadByDate);
04437       m_downloadSettings->SetDownloadUnreadOnly(downloadUnreadOnly);
04438       m_downloadSettings->SetAgeLimitOfMsgsToDownload(ageLimitOfMsgsToDownload);
04439     }
04440   }
04441   *downloadSettings = m_downloadSettings;
04442   NS_IF_ADDREF(*downloadSettings);
04443   return NS_OK;
04444 }
04445 
04446 NS_IMETHODIMP nsMsgDatabase::ApplyRetentionSettings(nsIMsgRetentionSettings *aMsgRetentionSettings,
04447                                                     PRBool aDeleteViaFolder)
04448 {
04449   NS_ENSURE_ARG_POINTER(aMsgRetentionSettings);
04450   nsresult rv = NS_OK;
04451 
04452   nsCOMPtr <nsISupportsArray> msgHdrsToDelete;
04453   if (aDeleteViaFolder)
04454   {
04455     msgHdrsToDelete = do_CreateInstance(NS_SUPPORTSARRAY_CONTRACTID, &rv);
04456     NS_ENSURE_SUCCESS(rv, rv);
04457   }
04458   nsMsgRetainByPreference retainByPreference;
04459   PRUint32 daysToKeepHdrs = 0;
04460   PRUint32 numHeadersToKeep = 0;
04461   PRBool keepUnreadMessagesOnly = PR_FALSE;
04462   aMsgRetentionSettings->GetRetainByPreference(&retainByPreference);
04463   aMsgRetentionSettings->GetKeepUnreadMessagesOnly(&keepUnreadMessagesOnly);
04464 
04465   switch (retainByPreference)
04466   {
04467   case nsIMsgRetentionSettings::nsMsgRetainAll:
04468     if (keepUnreadMessagesOnly && m_mdbAllMsgHeadersTable)
04469     {
04470       mdb_count numHdrs = 0;
04471       m_mdbAllMsgHeadersTable->GetCount(GetEnv(), &numHdrs);
04472       rv = PurgeExcessMessages(numHdrs, PR_TRUE, msgHdrsToDelete);
04473     }
04474     break;
04475   case nsIMsgRetentionSettings::nsMsgRetainByAge:
04476     aMsgRetentionSettings->GetDaysToKeepHdrs(&daysToKeepHdrs);
04477     rv = PurgeMessagesOlderThan(daysToKeepHdrs, keepUnreadMessagesOnly, msgHdrsToDelete);
04478     break;
04479   case nsIMsgRetentionSettings::nsMsgRetainByNumHeaders:
04480     aMsgRetentionSettings->GetNumHeadersToKeep(&numHeadersToKeep);
04481     rv = PurgeExcessMessages(numHeadersToKeep, keepUnreadMessagesOnly, msgHdrsToDelete);
04482     break;
04483   }
04484   if (m_folder)
04485   {
04486     // update the time we attempted to purge this folder
04487     char dateBuf[100];
04488     dateBuf[0] = '\0';
04489     PRExplodedTime exploded;
04490     PR_ExplodeTime(PR_Now(), PR_LocalTimeParameters, &exploded);
04491     PR_FormatTimeUSEnglish(dateBuf, sizeof(dateBuf), "%a %b %d %H:%M:%S %Y", &exploded);
04492     m_folder->SetStringProperty("LastPurgeTime", dateBuf);
04493   }
04494   if (msgHdrsToDelete)
04495   {
04496     PRUint32 count;
04497     msgHdrsToDelete->Count(&count);
04498     if (count > 0)
04499       rv = m_folder->DeleteMessages(msgHdrsToDelete, nsnull, PR_TRUE, PR_FALSE, nsnull, PR_FALSE);
04500   }
04501   return rv;
04502 }
04503 
04504 nsresult nsMsgDatabase::PurgeMessagesOlderThan(PRUint32 daysToKeepHdrs, 
04505                                                PRBool keepUnreadMessagesOnly,
04506                                                nsISupportsArray *hdrsToDelete)
04507 {
04508   nsresult rv = NS_OK;
04509   nsMsgHdr           *pHeader;
04510   nsCOMPtr <nsISimpleEnumerator> hdrs;
04511   rv = EnumerateMessages(getter_AddRefs(hdrs));
04512   nsMsgKeyArray keysToDelete;
04513   
04514   if (NS_FAILED(rv))
04515     return rv;
04516   PRBool hasMore = PR_FALSE;
04517   
04518   PRTime now = PR_Now();
04519   PRTime cutOffDay;
04520   
04521   PRInt64 microSecondsPerSecond, secondsInDays, microSecondsInDay;
04522   
04523   LL_I2L(microSecondsPerSecond, PR_USEC_PER_SEC);
04524   LL_UI2L(secondsInDays, 60 * 60 * 24 * daysToKeepHdrs);
04525   LL_MUL(microSecondsInDay, secondsInDays, microSecondsPerSecond);
04526   
04527   LL_SUB(cutOffDay, now, microSecondsInDay); // = now - term->m_value.u.age * 60 * 60 * 24; 
04528   // so now cutOffDay is the PRTime cut-off point. Any msg with a date less than that will get purged.
04529   while (NS_SUCCEEDED(rv = hdrs->HasMoreElements(&hasMore)) && (hasMore == PR_TRUE)) 
04530   {
04531     PRBool purgeHdr = PR_FALSE;
04532     
04533     rv = hdrs->GetNext((nsISupports**)&pHeader);
04534     NS_ASSERTION(NS_SUCCEEDED(rv), "nsMsgDBEnumerator broken");
04535     if (NS_FAILED(rv)) 
04536       break;
04537     
04538     if (keepUnreadMessagesOnly)
04539     {
04540       PRBool isRead;
04541       IsHeaderRead(pHeader, &isRead);
04542       if (isRead)
04543         purgeHdr = PR_TRUE;
04544       
04545     }
04546     if (!purgeHdr)
04547     {
04548       PRTime date;
04549       pHeader->GetDate(&date);
04550       if (LL_CMP(date, <, cutOffDay))
04551         purgeHdr = PR_TRUE;
04552     }
04553     if (purgeHdr)
04554     {
04555       nsMsgKey msgKey;
04556       pHeader->GetMessageKey(&msgKey);
04557       keysToDelete.Add(msgKey);
04558       if (hdrsToDelete)
04559         hdrsToDelete->AppendElement(pHeader);
04560     }
04561     NS_RELEASE(pHeader);
04562   }
04563   
04564   if (!hdrsToDelete)
04565   {
04566     DeleteMessages(&keysToDelete, nsnull);
04567   
04568     if (keysToDelete.GetSize() > 10)      // compress commit if we deleted more than 10
04569       Commit(nsMsgDBCommitType::kCompressCommit);
04570     else if (keysToDelete.GetSize() > 0)
04571       Commit(nsMsgDBCommitType::kLargeCommit);
04572   }
04573   return rv;
04574 }
04575 
04576 nsresult nsMsgDatabase::PurgeExcessMessages(PRUint32 numHeadersToKeep, PRBool keepUnreadMessagesOnly,
04577                                             nsISupportsArray *hdrsToDelete)
04578 {
04579   nsresult rv = NS_OK;
04580   nsMsgHdr           *pHeader;
04581   nsCOMPtr <nsISimpleEnumerator> hdrs;
04582   rv = EnumerateMessages(getter_AddRefs(hdrs));
04583   if (NS_FAILED(rv))
04584     return rv;
04585   PRBool hasMore = PR_FALSE;
04586   nsMsgKeyArray keysToDelete;
04587   
04588   mdb_count numHdrs = 0;
04589   if (m_mdbAllMsgHeadersTable)
04590     m_mdbAllMsgHeadersTable->GetCount(GetEnv(), &numHdrs);
04591   else
04592     return NS_ERROR_NULL_POINTER;
04593   
04594   while (NS_SUCCEEDED(rv = hdrs->HasMoreElements(&hasMore)) && (hasMore == PR_TRUE)) 
04595   {
04596     PRBool purgeHdr = PR_FALSE;
04597     rv = hdrs->GetNext((nsISupports**)&pHeader);
04598     NS_ASSERTION(NS_SUCCEEDED(rv), "nsMsgDBEnumerator broken");
04599     if (NS_FAILED(rv)) 
04600       break;
04601     
04602     if (keepUnreadMessagesOnly)
04603     {
04604       PRBool isRead;
04605       IsHeaderRead(pHeader, &isRead);
04606       if (isRead)
04607         purgeHdr = PR_TRUE;
04608       
04609     }
04610     // this isn't quite right - we want to prefer unread messages (keep all of those we can)
04611     if (numHdrs > numHeadersToKeep)
04612       purgeHdr = PR_TRUE;
04613     
04614     if (purgeHdr)
04615     {
04616       nsMsgKey msgKey;
04617       pHeader->GetMessageKey(&msgKey);
04618       keysToDelete.Add(msgKey);
04619       numHdrs--;
04620       if (hdrsToDelete)
04621         hdrsToDelete->AppendElement(pHeader);
04622     }
04623     NS_RELEASE(pHeader);
04624   }
04625   
04626   if (!hdrsToDelete)
04627   {
04628     PRInt32 numKeysToDelete = keysToDelete.GetSize();
04629     if (numKeysToDelete > 0)
04630     {
04631       DeleteMessages(&keysToDelete, nsnull);
04632       if (numKeysToDelete > 10)    // compress commit if we deleted more than 10
04633         Commit(nsMsgDBCommitType::kCompressCommit);
04634       else
04635         Commit(nsMsgDBCommitType::kLargeCommit);
04636     }
04637   }
04638   return rv;
04639 }
04640 
04641 NS_IMPL_ISUPPORTS1(nsMsgRetentionSettings, nsIMsgRetentionSettings)
04642 
04643 nsMsgRetentionSettings::nsMsgRetentionSettings()
04644 {
04645 }
04646 
04647 nsMsgRetentionSettings::~nsMsgRetentionSettings()
04648 {
04649 }
04650 
04651 /* attribute unsigned long retainByPreference */
04652 
04653 NS_IMETHODIMP nsMsgRetentionSettings::GetRetainByPreference(nsMsgRetainByPreference *retainByPreference)
04654 {
04655   NS_ENSURE_ARG_POINTER(retainByPreference);
04656   *retainByPreference = m_retainByPreference;
04657   return NS_OK;
04658 }
04659 
04660 NS_IMETHODIMP nsMsgRetentionSettings::SetRetainByPreference(nsMsgRetainByPreference retainByPreference)
04661 {
04662   m_retainByPreference = retainByPreference;
04663   return NS_OK;
04664 }
04665 
04666 /* attribute long daysToKeepHdrs; */
04667 NS_IMETHODIMP nsMsgRetentionSettings::GetDaysToKeepHdrs(PRUint32 *aDaysToKeepHdrs)
04668 {
04669   NS_ENSURE_ARG_POINTER(aDaysToKeepHdrs);
04670   *aDaysToKeepHdrs = m_daysToKeepHdrs;
04671   return NS_OK;
04672 }
04673 
04674 NS_IMETHODIMP nsMsgRetentionSettings::SetDaysToKeepHdrs(PRUint32 aDaysToKeepHdrs)
04675 {
04676   m_daysToKeepHdrs = aDaysToKeepHdrs;
04677   return NS_OK;
04678 }
04679 
04680 /* attribute long numHeadersToKeep; */
04681 NS_IMETHODIMP nsMsgRetentionSettings::GetNumHeadersToKeep(PRUint32 *aNumHeadersToKeep)
04682 {
04683   NS_ENSURE_ARG_POINTER(aNumHeadersToKeep);
04684   *aNumHeadersToKeep = m_numHeadersToKeep;
04685   return NS_OK;
04686 }
04687 NS_IMETHODIMP nsMsgRetentionSettings::SetNumHeadersToKeep(PRUint32 aNumHeadersToKeep)
04688 {
04689   m_numHeadersToKeep = aNumHeadersToKeep;
04690   return NS_OK;
04691 }
04692 /* attribute boolean useServerDefaults; */
04693 NS_IMETHODIMP nsMsgRetentionSettings::GetUseServerDefaults(PRBool *aUseServerDefaults)
04694 {
04695   NS_ENSURE_ARG_POINTER(aUseServerDefaults);
04696   *aUseServerDefaults = m_useServerDefaults;
04697   return NS_OK;
04698 }
04699 NS_IMETHODIMP nsMsgRetentionSettings::SetUseServerDefaults(PRBool aUseServerDefaults)
04700 {
04701   m_useServerDefaults = aUseServerDefaults;
04702   return NS_OK;
04703 }
04704 
04705 /* attribute boolean keepUnreadMessagesOnly; */
04706 NS_IMETHODIMP nsMsgRetentionSettings::GetKeepUnreadMessagesOnly(PRBool *aKeepUnreadMessagesOnly)
04707 {
04708   NS_ENSURE_ARG_POINTER(aKeepUnreadMessagesOnly);
04709   *aKeepUnreadMessagesOnly = m_keepUnreadMessagesOnly;
04710   return NS_OK;
04711 }
04712 NS_IMETHODIMP nsMsgRetentionSettings::SetKeepUnreadMessagesOnly(PRBool aKeepUnreadMessagesOnly)
04713 {
04714   m_keepUnreadMessagesOnly = aKeepUnreadMessagesOnly;
04715   return NS_OK;
04716 }
04717 
04718 /* attribute boolean cleanupBodiesByDays; */
04719 NS_IMETHODIMP nsMsgRetentionSettings::GetCleanupBodiesByDays(PRBool *aCleanupBodiesByDays)
04720 {
04721   NS_ENSURE_ARG_POINTER(aCleanupBodiesByDays);
04722   *aCleanupBodiesByDays = m_cleanupBodiesByDays;
04723   return NS_OK;
04724 }
04725 NS_IMETHODIMP nsMsgRetentionSettings::SetCleanupBodiesByDays(PRBool aCleanupBodiesByDays)
04726 {
04727   m_cleanupBodiesByDays = aCleanupBodiesByDays;
04728   return NS_OK;
04729 }
04730 
04731 
04732 /* attribute long daysToKeepBodies; */
04733 NS_IMETHODIMP nsMsgRetentionSettings::GetDaysToKeepBodies(PRUint32 *aDaysToKeepBodies)
04734 {
04735   NS_ENSURE_ARG_POINTER(aDaysToKeepBodies);
04736   *aDaysToKeepBodies = m_daysToKeepBodies;
04737   return NS_OK;
04738 }
04739 NS_IMETHODIMP nsMsgRetentionSettings::SetDaysToKeepBodies(PRUint32 aDaysToKeepBodies)
04740 {
04741   m_daysToKeepBodies = aDaysToKeepBodies;
04742   return NS_OK;
04743 }
04744 
04745 NS_IMPL_ISUPPORTS1(nsMsgDownloadSettings, nsIMsgDownloadSettings)
04746 
04747 nsMsgDownloadSettings::nsMsgDownloadSettings()
04748 {
04749   m_useServerDefaults = PR_FALSE;
04750   m_downloadUnreadOnly = PR_FALSE;
04751   m_downloadByDate = PR_FALSE;
04752   m_ageLimitOfMsgsToDownload = 0;
04753 }
04754 
04755 nsMsgDownloadSettings::~nsMsgDownloadSettings()
04756 {
04757 }
04758 
04759 /* attribute boolean useServerDefaults; */
04760 NS_IMETHODIMP nsMsgDownloadSettings::GetUseServerDefaults(PRBool *aUseServerDefaults)
04761 {
04762   NS_ENSURE_ARG_POINTER(aUseServerDefaults);
04763   *aUseServerDefaults = m_useServerDefaults;
04764   return NS_OK;
04765 }
04766 NS_IMETHODIMP nsMsgDownloadSettings::SetUseServerDefaults(PRBool aUseServerDefaults)
04767 {
04768   m_useServerDefaults = aUseServerDefaults;
04769   return NS_OK;
04770 }
04771 
04772 
04773 /* attribute boolean keepUnreadMessagesOnly; */
04774 NS_IMETHODIMP nsMsgDownloadSettings::GetDownloadUnreadOnly(PRBool *aDownloadUnreadOnly)
04775 {
04776   NS_ENSURE_ARG_POINTER(aDownloadUnreadOnly);
04777   *aDownloadUnreadOnly = m_downloadUnreadOnly;
04778   return NS_OK;
04779 }
04780 NS_IMETHODIMP nsMsgDownloadSettings::SetDownloadUnreadOnly(PRBool aDownloadUnreadOnly)
04781 {
04782   m_downloadUnreadOnly = aDownloadUnreadOnly;
04783   return NS_OK;
04784 }
04785 
04786 /* attribute boolean keepUnreadMessagesOnly; */
04787 NS_IMETHODIMP nsMsgDownloadSettings::GetDownloadByDate(PRBool *aDownloadByDate)
04788 {
04789   NS_ENSURE_ARG_POINTER(aDownloadByDate);
04790   *aDownloadByDate = m_downloadByDate;
04791   return NS_OK;
04792 }
04793 
04794 NS_IMETHODIMP nsMsgDownloadSettings::SetDownloadByDate(PRBool aDownloadByDate)
04795 {
04796   m_downloadByDate = aDownloadByDate;
04797   return NS_OK;
04798 }
04799 
04800 
04801 /* attribute long ageLimitOfMsgsToDownload; */
04802 NS_IMETHODIMP nsMsgDownloadSettings::GetAgeLimitOfMsgsToDownload(PRUint32 *ageLimitOfMsgsToDownload)
04803 {
04804   NS_ENSURE_ARG_POINTER(ageLimitOfMsgsToDownload);
04805   *ageLimitOfMsgsToDownload = m_ageLimitOfMsgsToDownload;
04806   return NS_OK;
04807 }
04808 NS_IMETHODIMP nsMsgDownloadSettings::SetAgeLimitOfMsgsToDownload(PRUint32 ageLimitOfMsgsToDownload)
04809 {
04810   m_ageLimitOfMsgsToDownload = ageLimitOfMsgsToDownload;
04811   return NS_OK;
04812 }
04813 
04814 NS_IMETHODIMP nsMsgDatabase::GetDefaultViewFlags(nsMsgViewFlagsTypeValue *aDefaultViewFlags)
04815 { 
04816   NS_ENSURE_ARG_POINTER(aDefaultViewFlags);
04817   GetIntPref("mailnews.default_view_flags", aDefaultViewFlags);
04818   if (*aDefaultViewFlags < nsMsgViewFlagsType::kNone ||
04819       *aDefaultViewFlags > (nsMsgViewFlagsType::kThreadedDisplay |
04820                             nsMsgViewFlagsType::kShowIgnored |
04821                             nsMsgViewFlagsType::kUnreadOnly |
04822                             nsMsgViewFlagsType::kExpandAll |
04823                             nsMsgViewFlagsType::kGroupBySort))
04824     *aDefaultViewFlags = nsMsgViewFlagsType::kNone;
04825   return NS_OK;
04826 }
04827 
04828 NS_IMETHODIMP nsMsgDatabase::GetDefaultSortType(nsMsgViewSortTypeValue *aDefaultSortType)
04829 {
04830   NS_ENSURE_ARG_POINTER(aDefaultSortType);
04831   GetIntPref("mailnews.default_sort_type", aDefaultSortType);
04832   if (*aDefaultSortType < nsMsgViewSortType::byDate ||
04833       *aDefaultSortType > nsMsgViewSortType::byAccount)
04834     *aDefaultSortType = nsMsgViewSortType::byDate;
04835   return NS_OK;
04836 }
04837 
04838 NS_IMETHODIMP nsMsgDatabase::GetDefaultSortOrder(nsMsgViewSortOrderValue *aDefaultSortOrder)
04839 {
04840   NS_ENSURE_ARG_POINTER(aDefaultSortOrder);
04841   GetIntPref("mailnews.default_sort_order", aDefaultSortOrder);
04842   if (*aDefaultSortOrder != nsMsgViewSortOrder::descending)
04843     *aDefaultSortOrder = nsMsgViewSortOrder::ascending;
04844   return NS_OK;
04845 }
04846 
04847 NS_IMETHODIMP nsMsgDatabase::ResetHdrCacheSize(PRUint32 aSize)
04848 {
04849   if (m_cacheSize > aSize) 
04850   {  
04851     m_cacheSize = aSize;
04852     ClearHdrCache(PR_FALSE);
04853   }
04854   return NS_OK;
04855 }
04856 
04857 NS_IMETHODIMP nsMsgDatabase::GetFolderStream(nsIOFileStream **aFileStream)
04858 {
04859   return NS_ERROR_NOT_IMPLEMENTED;
04860 }
04861 
04862 NS_IMETHODIMP nsMsgDatabase::SetFolderStream(nsIOFileStream *aFileStream)
04863 {
04864   NS_ASSERTION(0, "Trying to set the folderStream, not implemented");
04865   return NS_ERROR_NOT_IMPLEMENTED;
04866 }
04867 
04871 NS_IMETHODIMP
04872 nsMsgDatabase::GetNewList(PRUint32 *aCount, PRUint32 **aNewKeys)
04873 {
04874     NS_ENSURE_ARG_POINTER(aCount);
04875     NS_ENSURE_ARG_POINTER(aNewKeys);
04876 
04877     *aCount = m_newSet.GetSize();
04878     if (*aCount > 0)
04879     {
04880       *aNewKeys = NS_STATIC_CAST(PRUint32 *, nsMemory::Alloc(*aCount * sizeof(PRUint32)));
04881       if (!*aNewKeys)
04882         return NS_ERROR_OUT_OF_MEMORY;
04883       memcpy(*aNewKeys, m_newSet.GetArray(), *aCount * sizeof(PRUint32));
04884       return NS_OK;
04885     }
04886     // if there were no new messages, signal this by returning a null pointer
04887     //
04888     *aNewKeys = nsnull;
04889     return NS_OK;
04890 }
04891 
04892 nsresult nsMsgDatabase::GetSearchResultsTable(const char *searchFolderUri, PRBool createIfMissing, nsIMdbTable **table)
04893 {
04894   mdb_kind kindToken;
04895   mdb_count numTables;
04896   mdb_bool mustBeUnique;
04897   mdb_err err = m_mdbStore->StringToToken(GetEnv(), searchFolderUri, &kindToken); 
04898   err = m_mdbStore->GetTableKind(GetEnv(), m_hdrRowScopeToken,  kindToken, 
04899                                   &numTables, &mustBeUnique, table);
04900   if ((!*table || NS_FAILED(err)) && createIfMissing)
04901     err = m_mdbStore->NewTable(GetEnv(), m_hdrRowScopeToken, kindToken, PR_TRUE, nsnull, table);
04902 
04903   return *table ? err : NS_ERROR_FAILURE;
04904 }
04905 
04906 NS_IMETHODIMP
04907 nsMsgDatabase::GetCachedHits(const char *aSearchFolderUri, nsISimpleEnumerator **aEnumerator)
04908 {
04909   nsCOMPtr <nsIMdbTable> table;
04910   nsresult err = GetSearchResultsTable(aSearchFolderUri, PR_FALSE, getter_AddRefs(table));
04911   NS_ENSURE_SUCCESS(err, err);
04912   if (!table)
04913     return NS_ERROR_FAILURE;
04914   nsMsgDBEnumerator* e = new nsMsgDBEnumerator(this, table, nsnull, nsnull);
04915   if (e == nsnull)
04916       return NS_ERROR_OUT_OF_MEMORY;
04917   NS_ADDREF(*aEnumerator = e);
04918   return NS_OK;
04919 }
04920 
04921 NS_IMETHODIMP nsMsgDatabase::RefreshCache(const char *aSearchFolderUri, PRUint32 aNumKeys, nsMsgKey *aNewHits, PRUint32 *aNumBadHits, nsMsgKey **aStaleHits)
04922 {
04923   nsCOMPtr <nsIMdbTable> table;
04924   nsresult err = GetSearchResultsTable(aSearchFolderUri, PR_TRUE, getter_AddRefs(table));
04925   NS_ENSURE_SUCCESS(err, err);
04926   // update the table so that it just contains aNewHits.
04927   // And, keep track of the headers in the original table but not in aNewHits, so we
04928   // can put those in aStaleHits.
04929   // both aNewHits and the db table are sorted by uid/key.
04930   // So, start at the beginning of the table and the aNewHits array.
04931   PRUint32 newHitIndex = 0;
04932   PRUint32 tableRowIndex = 0;
04933   
04934   PRUint32 rowCount;
04935   table->GetCount(GetEnv(), &rowCount);
04936   nsMsgKeyArray staleHits;
04937   // should assert that each array is sorted
04938   while (newHitIndex < aNumKeys || tableRowIndex < rowCount)
04939   {
04940     mdbOid oid;
04941     nsMsgKey tableRowKey = nsMsgKey_None;
04942     if (tableRowIndex < rowCount)
04943     {
04944       nsresult ret = table->PosToOid (GetEnv(), tableRowIndex, &oid);
04945       if (NS_FAILED(ret))
04946       {
04947         tableRowIndex++;
04948         continue;
04949       }
04950       tableRowKey = oid.mOid_Id;  // ### TODO need the real key for the 0th key problem.
04951     }
04952     
04953    if (newHitIndex < aNumKeys && aNewHits[newHitIndex] == tableRowKey) 
04954    {
04955       newHitIndex++;
04956       tableRowIndex++;
04957       continue;
04958     }
04959     else if (tableRowIndex >= rowCount || (newHitIndex < aNumKeys && aNewHits[newHitIndex] < tableRowKey))
04960     {
04961       nsCOMPtr <nsIMdbRow> hdrRow;
04962       mdbOid rowObjectId;
04963 
04964       rowObjectId.mOid_Id = aNewHits[newHitIndex];
04965       rowObjectId.mOid_Scope = m_hdrRowScopeToken;
04966       err = m_mdbStore->GetRow(GetEnv(), &rowObjectId, getter_AddRefs(hdrRow));
04967       if (hdrRow)
04968       {
04969         table->AddRow(GetEnv(), hdrRow);
04970         mdb_pos newPos;
04971 #ifdef DEBUG_David_Bienvenu
04972         printf("adding row %lx at pos %lx \n", rowObjectId.mOid_Id, tableRowIndex);
04973 #endif
04974         table->MoveRow(GetEnv(), hdrRow, rowCount, tableRowIndex, &newPos);
04975         rowCount++;
04976         tableRowIndex++;
04977       }
04978       newHitIndex++; 
04979       continue;
04980     }
04981     else if (newHitIndex >= aNumKeys || aNewHits[newHitIndex] > tableRowKey)
04982     {
04983       staleHits.Add(tableRowKey);
04984       table->CutOid(GetEnv(), &oid);
04985       rowCount--;
04986       continue; // don't increment tableRowIndex since we removed that row.
04987     }
04988    }  
04989    *aNumBadHits = staleHits.GetSize();
04990    if (*aNumBadHits)
04991    {
04992      *aStaleHits = NS_STATIC_CAST(PRUint32 *, nsMemory::Alloc(*aNumBadHits * sizeof(PRUint32)));
04993      if (!*aStaleHits)
04994        return NS_ERROR_OUT_OF_MEMORY;
04995      memcpy(*aStaleHits, staleHits.GetArray(), *aNumBadHits * sizeof(PRUint32));
04996    }
04997    else
04998      *aStaleHits = nsnull;
04999 
05000 #ifdef DEBUG_David_Bienvenu
05001   printf("after refreshing cache\n");
05002   // iterate over table and assert that it's in id order
05003   table->GetCount(GetEnv(), &rowCount);
05004   mdbOid oid;
05005   tableRowIndex = 0;
05006   mdb_id prevId = 0;
05007   while (tableRowIndex < rowCount)
05008   {
05009     nsresult ret = table->PosToOid (m_mdbEnv, tableRowIndex++, &oid);
05010     if (tableRowIndex > 1 && oid.mOid_Id <= prevId)
05011     {
05012       NS_ASSERTION(PR_FALSE, "inserting row into cached hits table, not sorted correctly");
05013       printf("key %lx is before or equal %lx \n", prevId, oid.mOid_Id);
05014     }
05015     prevId = oid.mOid_Id;
05016   }
05017 
05018 #endif
05019   Commit(nsMsgDBCommitType::kLargeCommit);
05020   return NS_OK;
05021 }
05022 
05023 // search sorted table
05024 mdb_pos nsMsgDatabase::FindInsertIndexInSortedTable(nsIMdbTable *table, mdb_id idToInsert)
05025 {
05026   mdb_pos searchPos = 0;
05027   PRUint32 rowCount;
05028   table->GetCount(GetEnv(), &rowCount);
05029   mdb_pos hi = rowCount;
05030   mdb_pos lo = 0;
05031   
05032   while (hi > lo)
05033   {
05034     mdbOid outOid;
05035     searchPos = (lo + hi - 1) / 2;
05036     table->PosToOid(GetEnv(), searchPos, &outOid);
05037     if (outOid.mOid_Id == idToInsert)
05038     {
05039       NS_ASSERTION(PR_FALSE, "id shouldn't be in table");
05040       return hi;
05041     }
05042     if (outOid.mOid_Id > idToInsert)
05043       hi = searchPos;
05044     else // if (outOid.mOid_Id <  idToInsert)
05045       lo = searchPos + 1;
05046   }
05047   return hi;
05048 }
05049 NS_IMETHODIMP
05050 nsMsgDatabase::UpdateHdrInCache(const char *aSearchFolderUri, nsIMsgDBHdr *aHdr, PRBool aAdd)
05051 {
05052   nsCOMPtr <nsIMdbTable> table;
05053   nsresult err = GetSearchResultsTable(aSearchFolderUri, PR_TRUE, getter_AddRefs(table));
05054   NS_ENSURE_SUCCESS(err, err);
05055   nsMsgKey key;
05056   aHdr->GetMessageKey(&key);
05057   nsMsgHdr* msgHdr = NS_STATIC_CAST(nsMsgHdr*, aHdr);  // closed system, so this is ok
05058   if (err == NS_OK && m_mdbStore && msgHdr->m_mdbRow)
05059   {
05060     if (!aAdd)
05061     {
05062       table->CutRow(m_mdbEnv, msgHdr->m_mdbRow);
05063 #ifdef DEBUG_David_Bienvenu
05064       printf("removing key %lx \n", key);
05065 #endif
05066     }
05067     else
05068     {
05069       mdbOid rowId;
05070       msgHdr->m_mdbRow->GetOid(m_mdbEnv, &rowId);
05071       mdb_pos insertPos = FindInsertIndexInSortedTable(table, rowId.mOid_Id);
05072       PRUint32 rowCount;
05073       table->GetCount(m_mdbEnv, &rowCount);
05074       table->AddRow(m_mdbEnv, msgHdr->m_mdbRow);
05075       mdb_pos newPos;
05076       table->MoveRow(m_mdbEnv, msgHdr->m_mdbRow, rowCount, insertPos, &newPos);
05077 #ifdef DEBUG_David_Bienvenu
05078       printf("moving row with key %lx to pos %lx \n", key, newPos);
05079       // iterate over table and assert that it's in id order
05080       table->GetCount(GetEnv(), &rowCount);
05081       mdbOid oid;
05082       PRUint32 tableRowIndex = 0;
05083       mdb_id prevId = 0;
05084       while (tableRowIndex < rowCount)
05085       {
05086         nsresult ret = table->PosToOid (m_mdbEnv, tableRowIndex++, &oid);
05087         if (tableRowIndex > 1 && oid.mOid_Id <= prevId)
05088         {
05089           NS_ASSERTION(PR_FALSE, "inserting row into cached hits table, not sorted correctly");
05090           printf("key %lx is before or equal %lx \n", prevId, oid.mOid_Id);
05091         }
05092         prevId = oid.mOid_Id;
05093       }
05094 
05095 #endif
05096     }
05097   }
05098     
05099 //  if (aAdd)
05100  // if we need to add this hdr, we need to insert it in key order.
05101   return NS_OK;
05102 }
05103 NS_IMETHODIMP
05104 nsMsgDatabase::HdrIsInCache(const char* aSearchFolderUri, nsIMsgDBHdr *aHdr, PRBool *aResult)
05105 {
05106   NS_ENSURE_ARG_POINTER(aResult);
05107   nsCOMPtr <nsIMdbTable> table;
05108   nsresult err = GetSearchResultsTable(aSearchFolderUri, PR_TRUE, getter_AddRefs(table));
05109   NS_ENSURE_SUCCESS(err, err);
05110   nsMsgKey key;
05111   aHdr->GetMessageKey(&key);
05112   mdbOid rowObjectId;
05113   rowObjectId.mOid_Id = key;
05114   rowObjectId.mOid_Scope = m_hdrRowScopeToken;
05115   mdb_bool hasOid;
05116   err =  table->HasOid(GetEnv(), &rowObjectId, &hasOid);
05117   *aResult = hasOid;
05118   return err;
05119 }
05120