/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #ifndef COMM_MAILNEWS_DB_MSGDB_SRC_NSMSGDATABASE_H_ #define COMM_MAILNEWS_DB_MSGDB_SRC_NSMSGDATABASE_H_ #include "mozilla/MemoryReporting.h" #include "nsIFile.h" #include "nsIMsgDatabase.h" #include "nsMsgHdr.h" #include "nsIDBChangeAnnouncer.h" #include "nsMsgMessageFlags.h" #include "nsIMsgFolder.h" #include "nsDBFolderInfo.h" #include "mozilla/intl/Collator.h" #include "nsIMimeConverter.h" #include "nsCOMPtr.h" #include "nsCOMArray.h" #include "PLDHashTable.h" #include "nsTArray.h" #include "nsTHashMap.h" #include "nsTObserverArray.h" #include "prtime.h" using mozilla::intl::Collator; class nsMsgThread; class nsMsgDatabase; class nsIMsgOfflineOpsDatabase; class nsIMsgThread; class nsMsgDBEnumerator; class nsMsgDBThreadEnumerator; const int32_t kMsgDBVersion = 1; // Hopefully we're not opening up lots of databases at the same time, however // this will give us a buffer before we need to start reallocating the cache // array. const uint32_t kInitialMsgDBCacheSize = 20; class nsMsgDBService final : public nsIMsgDBService { public: NS_DECL_ISUPPORTS NS_DECL_NSIMSGDBSERVICE nsMsgDBService(); void AddToCache(nsMsgDatabase* pMessageDB); void DumpCache(); void EnsureCached(nsMsgDatabase* pMessageDB) { if (!m_dbCache.Contains(pMessageDB)) m_dbCache.AppendElement(pMessageDB); } void RemoveFromCache(nsMsgDatabase* pMessageDB) { m_dbCache.RemoveElement(pMessageDB); } protected: ~nsMsgDBService(); void HookupPendingListeners(nsIMsgDatabase* db, nsIMsgFolder* folder); void FinishDBOpen(nsIMsgFolder* aFolder, nsMsgDatabase* aMsgDB); nsMsgDatabase* FindInCache(nsIFile* dbName); nsCOMArray m_foldersPendingListeners; nsCOMArray m_pendingListeners; AutoTArray m_dbCache; }; namespace mozilla { namespace mailnews { class MsgDBReporter; } } // namespace mozilla class nsMsgDatabase : public nsIMsgOfflineOpsDatabase { public: friend class nsMsgDBService; friend class nsMsgPropertyEnumerator; // accesses m_mdbEnv and m_mdbStore NS_DECL_ISUPPORTS NS_DECL_NSIDBCHANGEANNOUNCER NS_DECL_NSIMSGDATABASE NS_DECL_NSIMSGOFFLINEOPSDATABASE /** * Opens a database folder. * * @param aFolderName The name of the folder to create. * @param aCreate Whether or not the file should be created. * @param aLeaveInvalidDB Set to true if you do not want the database to be * deleted if it is invalid. * @exception NS_ERROR_FILE_NOT_FOUND * The file could not be created. * @exception NS_MSG_ERROR_FOLDER_SUMMARY_OUT_OF_DATE * The database is present (and was opened), but the * summary file is out of date. * @exception NS_MSG_ERROR_FOLDER_SUMMARY_MISSING * The database is present (and was opened), but the * summary file is missing. */ virtual nsresult Open(nsMsgDBService* aDBService, nsIFile* aFolderName, bool aCreate, bool aLeaveInvalidDB); virtual nsresult IsHeaderRead(nsIMsgDBHdr* hdr, bool* pRead); virtual nsresult MarkHdrRead(nsIMsgDBHdr* msgHdr, bool bRead, nsIDBChangeListener* instigator); virtual nsresult MarkHdrReadInDB(nsIMsgDBHdr* msgHdr, bool bRead, nsIDBChangeListener* instigator); nsresult OpenInternal(nsMsgDBService* aDBService, nsIFile* summaryFile, bool aCreate, bool aLeaveInvalidDB, bool sync); nsresult CheckForErrors(nsresult err, bool sync, nsMsgDBService* aDBService, nsIFile* summaryFile); virtual nsresult OpenMDB(nsIFile* dbfile, bool create, bool sync); virtual nsresult CloseMDB(bool commit); virtual nsresult CreateMsgHdr(nsIMdbRow* hdrRow, nsMsgKey key, nsIMsgDBHdr** result); virtual nsresult GetThreadForMsgKey(nsMsgKey msgKey, nsIMsgThread** result); virtual nsresult EnumerateMessagesWithFlag(nsIMsgEnumerator** result, uint32_t* pFlag); nsresult GetSearchResultsTable(const nsACString& searchFolderUri, bool createIfMissing, nsIMdbTable** table); ////////////////////////////////////////////////////////////////////////////// // nsMsgDatabase methods: nsMsgDatabase(); nsresult GetMDBFactory(nsIMdbFactory** aMdbFactory); nsIMdbEnv* GetEnv() { return m_mdbEnv; } nsIMdbStore* GetStore() { return m_mdbStore; } virtual uint32_t GetCurVersion(); nsresult GetCollationKeyGenerator(); nsIMimeConverter* GetMimeConverter(); nsresult GetTableCreateIfMissing(const char* scope, const char* kind, nsIMdbTable** table, mdb_token& scopeToken, mdb_token& kindToken); // helper function to fill in nsStrings from hdr row cell contents. nsresult RowCellColumnTonsString(nsIMdbRow* row, mdb_token columnToken, nsAString& resultStr); nsresult RowCellColumnToUInt32(nsIMdbRow* row, mdb_token columnToken, uint32_t* uint32Result, uint32_t defaultValue = 0); nsresult RowCellColumnToUInt32(nsIMdbRow* row, mdb_token columnToken, uint32_t& uint32Result, uint32_t defaultValue = 0); nsresult RowCellColumnToUInt64(nsIMdbRow* row, mdb_token columnToken, uint64_t* uint64Result, uint64_t defaultValue = 0); nsresult RowCellColumnToMime2DecodedString(nsIMdbRow* row, mdb_token columnToken, nsAString& resultStr); nsresult RowCellColumnToCollationKey(nsIMdbRow* row, mdb_token columnToken, nsTArray& result); nsresult RowCellColumnToConstCharPtr(nsIMdbRow* row, mdb_token columnToken, const char** ptr); nsresult RowCellColumnToAddressCollationKey(nsIMdbRow* row, mdb_token colToken, nsTArray& result); virtual nsresult GetEffectiveCharset(nsIMdbRow* row, nsACString& resultCharset); // these methods take the property name as a string, not a token. // they should be used when the properties aren't accessed a lot nsresult GetProperty(nsIMdbRow* row, const char* propertyName, char** result); nsresult SetProperty(nsIMdbRow* row, const char* propertyName, const char* propertyVal); nsresult GetPropertyAsNSString(nsIMdbRow* row, const char* propertyName, nsAString& result); nsresult SetPropertyFromNSString(nsIMdbRow* row, const char* propertyName, const nsAString& propertyVal); nsresult GetUint32Property(nsIMdbRow* row, const char* propertyName, uint32_t* result, uint32_t defaultValue = 0); nsresult GetUint64Property(nsIMdbRow* row, const char* propertyName, uint64_t* result, uint64_t defaultValue = 0); nsresult SetUint32Property(nsIMdbRow* row, const char* propertyName, uint32_t propertyVal); nsresult SetUint64Property(nsIMdbRow* row, const char* propertyName, uint64_t propertyVal); nsresult GetBooleanProperty(nsIMdbRow* row, const char* propertyName, bool* result, bool defaultValue = false); nsresult SetBooleanProperty(nsIMdbRow* row, const char* propertyName, bool propertyVal); // helper function for once we have the token. nsresult SetNSStringPropertyWithToken(nsIMdbRow* row, mdb_token aProperty, const nsAString& propertyStr); // helper functions to put values in cells for the passed-in row nsresult UInt32ToRowCellColumn(nsIMdbRow* row, mdb_token columnToken, uint32_t value); nsresult CharPtrToRowCellColumn(nsIMdbRow* row, mdb_token columnToken, const char* charPtr); nsresult RowCellColumnToCharPtr(nsIMdbRow* row, mdb_token columnToken, char** result); nsresult UInt64ToRowCellColumn(nsIMdbRow* row, mdb_token columnToken, uint64_t value); // helper functions to copy an nsString to a yarn, int32 to yarn, and vice // versa. static struct mdbYarn* nsStringToYarn(struct mdbYarn* yarn, const nsAString& str); static struct mdbYarn* UInt32ToYarn(struct mdbYarn* yarn, uint32_t i); static struct mdbYarn* UInt64ToYarn(struct mdbYarn* yarn, uint64_t i); static void YarnTonsString(struct mdbYarn* yarn, nsAString& str); static void YarnTonsCString(struct mdbYarn* yarn, nsACString& str); static void YarnToUInt32(struct mdbYarn* yarn, uint32_t* i); static void YarnToUInt64(struct mdbYarn* yarn, uint64_t* i); #ifdef DEBUG virtual nsresult DumpContents(); #endif friend class nsMsgHdr; // use this to get access to cached tokens for hdr // fields friend class nsMsgThread; // use this to get access to cached tokens for hdr // fields friend class nsMsgDBEnumerator; friend class nsMsgDBThreadEnumerator; protected: virtual ~nsMsgDatabase(); // prefs stuff - in future, we might want to cache the prefs interface virtual void GetGlobalPrefs(); // retrieval methods nsIMsgThread* GetThreadForReference(nsCString& msgID, nsIMsgDBHdr** pMsgHdr); nsIMsgThread* GetThreadForSubject(nsCString& subject); nsIMsgThread* GetThreadForMessageId(nsCString& msgId); nsIMsgThread* GetThreadForThreadId(nsMsgKey threadId); nsIMsgDBHdr* GetMsgHdrForSubject(nsCString& subject); // threading interfaces virtual nsresult CreateNewThread(nsMsgKey key, const char* subject, nsMsgThread** newThread); virtual bool ThreadBySubjectWithoutRe(); virtual bool UseStrictThreading(); virtual bool UseCorrectThreading(); virtual nsresult ThreadNewHdr(nsMsgHdr* hdr, bool& newThread); virtual nsresult AddNewThread(nsMsgHdr* msgHdr); virtual nsresult AddToThread(nsMsgHdr* newHdr, nsIMsgThread* thread, nsIMsgDBHdr* inReplyTo, bool threadInThread); static PRTime gLastUseTime; // global last use time PRTime m_lastUseTime; // last use time for this db // inline to make instrumentation as cheap as possible inline void RememberLastUseTime() { gLastUseTime = m_lastUseTime = PR_Now(); } bool MatchDbName(nsIFile* dbFile); // returns TRUE if they match // Flag handling routines virtual nsresult SetKeyFlag(nsMsgKey key, bool set, nsMsgMessageFlagType flag, nsIDBChangeListener* instigator = nullptr); virtual bool SetHdrFlag(nsIMsgDBHdr*, bool bSet, nsMsgMessageFlagType flag); virtual uint32_t GetStatusFlags(nsIMsgDBHdr* msgHdr, nsMsgMessageFlagType origFlags); // helper function which doesn't involve thread object virtual nsresult RemoveHeaderFromDB(nsMsgHdr* msgHdr); virtual nsresult RemoveHeaderFromThread(nsMsgHdr* msgHdr); virtual nsresult AdjustExpungedBytesOnDelete(nsIMsgDBHdr* msgHdr); mozilla::UniquePtr m_collationKeyGenerator = nullptr; nsCOMPtr m_mimeConverter; nsCOMPtr m_retentionSettings; nsCOMPtr m_downloadSettings; nsresult FindMessagesOlderThan(uint32_t daysToKeepHdrs, bool applyToFlaggedMessages, nsTArray>& hdrsToDelete); nsresult FindExcessMessages(uint32_t numHeadersToKeep, bool applyToFlaggedMessages, nsTArray>& hdrsToDelete); nsMsgKey FindMsgKeyForUID(uint32_t uid); // mdb bookkeeping stuff virtual nsresult InitExistingDB(); virtual nsresult InitNewDB(); virtual nsresult InitMDBInfo(); nsCOMPtr m_folder; RefPtr m_dbFolderInfo; nsMsgKey m_nextPseudoMsgKey; nsIMdbEnv* m_mdbEnv; // to be used in all the db calls. nsIMdbStore* m_mdbStore; nsIMdbTable* m_mdbAllMsgHeadersTable; nsIMdbTable* m_mdbAllThreadsTable; nsTHashMap> m_mdbSearchResultsTables; // Used for asynchronous db opens. If non-null, we're still opening // the underlying mork database. If null, the db has been completely opened. nsCOMPtr m_thumb; // used to remember the args to Open for async open. bool m_create; bool m_leaveInvalidDB; nsCOMPtr m_dbFile; nsTArray m_newSet; // new messages since last open. bool m_mdbTokensInitialized; nsTObserverArray> m_ChangeListeners; mdb_token m_hdrRowScopeToken; mdb_token m_threadRowScopeToken; mdb_token m_hdrTableKindToken; mdb_token m_threadTableKindToken; mdb_token m_allThreadsTableKindToken; mdb_token m_subjectColumnToken; mdb_token m_senderColumnToken; mdb_token m_messageIdColumnToken; mdb_token m_referencesColumnToken; mdb_token m_recipientsColumnToken; mdb_token m_dateColumnToken; mdb_token m_messageSizeColumnToken; mdb_token m_flagsColumnToken; mdb_token m_priorityColumnToken; mdb_token m_labelColumnToken; mdb_token m_numLinesColumnToken; mdb_token m_ccListColumnToken; mdb_token m_bccListColumnToken; mdb_token m_threadFlagsColumnToken; mdb_token m_threadIdColumnToken; mdb_token m_threadChildrenColumnToken; mdb_token m_threadUnreadChildrenColumnToken; mdb_token m_messageThreadIdColumnToken; mdb_token m_threadSubjectColumnToken; mdb_token m_messageCharSetColumnToken; mdb_token m_threadParentColumnToken; mdb_token m_threadRootKeyColumnToken; mdb_token m_threadNewestMsgDateColumnToken; mdb_token m_offlineMsgOffsetColumnToken; mdb_token m_offlineMessageSizeColumnToken; mdb_token m_uidOnServerColumnToken; // header caching stuff - MRU headers, keeps them around in memory nsresult AddHdrToCache(nsIMsgDBHdr* hdr, nsMsgKey key); nsresult ClearHdrCache(bool reInit); nsresult RemoveHdrFromCache(nsIMsgDBHdr* hdr, nsMsgKey key); // all headers currently instantiated, doesn't hold refs // these get added when msg hdrs get constructed, and removed when they get // destroyed. nsresult GetHdrFromUseCache(nsMsgKey key, nsIMsgDBHdr** result); nsresult AddHdrToUseCache(nsIMsgDBHdr* hdr, nsMsgKey key); nsresult ClearUseHdrCache(); nsresult RemoveHdrFromUseCache(nsIMsgDBHdr* hdr, nsMsgKey key); // not-reference holding array of threads we've handed out. // If a db goes away, it will clean up the outstanding threads. // We use an nsTArray because we don't expect to ever have very many // of these, rarely more than 5. nsTArray m_threads; // Clear outstanding thread objects void ClearThreads(); nsMsgThread* FindExistingThread(nsMsgKey threadId); mdb_pos FindInsertIndexInSortedTable(nsIMdbTable* table, mdb_id idToInsert); void ClearCachedObjects(bool dbGoingAway); void InvalidateEnumerators(); // all instantiated headers, but doesn't hold refs. PLDHashTable* m_headersInUse; static PLDHashNumber HashKey(const void* aKey); static bool MatchEntry(const PLDHashEntryHdr* aEntry, const void* aKey); static void MoveEntry(PLDHashTable* aTable, const PLDHashEntryHdr* aFrom, PLDHashEntryHdr* aTo); static void ClearEntry(PLDHashTable* aTable, PLDHashEntryHdr* aEntry); static PLDHashTableOps gMsgDBHashTableOps; struct MsgHdrHashElement : public PLDHashEntryHdr { nsMsgKey mKey; nsIMsgDBHdr* mHdr; }; PLDHashTable* m_cachedHeaders; bool m_bCacheHeaders; nsMsgKey m_cachedThreadId; nsCOMPtr m_cachedThread; nsCOMPtr mMdbFactory; // Message reference hash table static PLDHashTableOps gRefHashTableOps; struct RefHashElement : public PLDHashEntryHdr { const char* mRef; // Hash entry key, must come first nsMsgKey mThreadId; uint32_t mCount; }; PLDHashTable* m_msgReferences; nsresult GetRefFromHash(nsCString& reference, nsMsgKey* threadId); nsresult AddRefToHash(nsCString& reference, nsMsgKey threadId); nsresult AddMsgRefsToHash(nsIMsgDBHdr* msgHdr); nsresult RemoveRefFromHash(nsCString& reference); nsresult RemoveMsgRefsFromHash(nsIMsgDBHdr* msgHdr); nsresult InitRefHash(); // The enumerators add themselves to these lists. // If a db goes away - via destruction or ForceClosed() - it needs to // invalidate any outstanding enumerators. nsTArray m_msgEnumerators; nsTArray m_threadEnumerators; // Memory reporter details public: static size_t HeaderHashSizeOf(PLDHashEntryHdr* hdr, mozilla::MallocSizeOf aMallocSizeOf, void* arg); virtual size_t SizeOfExcludingThis(mozilla::MallocSizeOf aMallocSizeOf) const; virtual size_t SizeOfIncludingThis( mozilla::MallocSizeOf aMallocSizeOf) const { return aMallocSizeOf(this) + SizeOfExcludingThis(aMallocSizeOf); } private: uint32_t m_cacheSize; RefPtr mMemReporter; }; class nsMsgRetentionSettings : public nsIMsgRetentionSettings { public: nsMsgRetentionSettings(); NS_DECL_ISUPPORTS NS_DECL_NSIMSGRETENTIONSETTINGS protected: virtual ~nsMsgRetentionSettings(); nsMsgRetainByPreference m_retainByPreference; uint32_t m_daysToKeepHdrs; uint32_t m_numHeadersToKeep; bool m_useServerDefaults; bool m_applyToFlaggedMessages; }; class nsMsgDownloadSettings : public nsIMsgDownloadSettings { public: nsMsgDownloadSettings(); NS_DECL_ISUPPORTS NS_DECL_NSIMSGDOWNLOADSETTINGS protected: virtual ~nsMsgDownloadSettings(); bool m_useServerDefaults; bool m_downloadUnreadOnly; bool m_downloadByDate; int32_t m_ageLimitOfMsgsToDownload; }; #endif // COMM_MAILNEWS_DB_MSGDB_SRC_NSMSGDATABASE_H_