1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* This Source Code Form is subject to the terms of the Mozilla Public
3  * License, v. 2.0. If a copy of the MPL was not distributed with this
4  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
5 
6 #ifndef _nsMsgDatabase_H_
7 #define _nsMsgDatabase_H_
8 
9 #include "mozilla/Attributes.h"
10 #include "mozilla/MemoryReporting.h"
11 #include "mozilla/Path.h"
12 #include "nsIFile.h"
13 #include "nsIMsgDatabase.h"
14 #include "nsMsgHdr.h"
15 #include "nsString.h"
16 #include "nsIDBChangeAnnouncer.h"
17 #include "nsMsgMessageFlags.h"
18 #include "nsIMsgFolder.h"
19 #include "nsDBFolderInfo.h"
20 #include "nsICollation.h"
21 #include "nsIMimeConverter.h"
22 #include "nsCOMPtr.h"
23 #include "nsCOMArray.h"
24 #include "PLDHashTable.h"
25 #include "nsTArray.h"
26 #include "nsTObserverArray.h"
27 
28 class nsMsgThread;
29 class nsMsgDatabase;
30 class nsIMsgThread;
31 class nsMsgDBEnumerator;
32 class nsMsgDBThreadEnumerator;
33 
34 const int32_t kMsgDBVersion = 1;
35 
36 // Hopefully we're not opening up lots of databases at the same time, however
37 // this will give us a buffer before we need to start reallocating the cache
38 // array.
39 const uint32_t kInitialMsgDBCacheSize = 20;
40 
41 class nsMsgDBService final : public nsIMsgDBService {
42  public:
43   NS_DECL_ISUPPORTS
44   NS_DECL_NSIMSGDBSERVICE
45 
46   nsMsgDBService();
47 
48   void AddToCache(nsMsgDatabase* pMessageDB);
49   void DumpCache();
EnsureCached(nsMsgDatabase * pMessageDB)50   void EnsureCached(nsMsgDatabase* pMessageDB) {
51     if (!m_dbCache.Contains(pMessageDB)) m_dbCache.AppendElement(pMessageDB);
52   }
RemoveFromCache(nsMsgDatabase * pMessageDB)53   void RemoveFromCache(nsMsgDatabase* pMessageDB) {
54     m_dbCache.RemoveElement(pMessageDB);
55   }
56 
57  protected:
58   ~nsMsgDBService();
59   void HookupPendingListeners(nsIMsgDatabase* db, nsIMsgFolder* folder);
60   void FinishDBOpen(nsIMsgFolder* aFolder, nsMsgDatabase* aMsgDB);
61   nsMsgDatabase* FindInCache(nsIFile* dbName);
62 
63   nsCOMArray<nsIMsgFolder> m_foldersPendingListeners;
64   nsCOMArray<nsIDBChangeListener> m_pendingListeners;
65   AutoTArray<nsMsgDatabase*, kInitialMsgDBCacheSize> m_dbCache;
66 };
67 
68 namespace mozilla {
69 namespace mailnews {
70 class MsgDBReporter;
71 }
72 }  // namespace mozilla
73 
74 class nsMsgDatabase : public nsIMsgDatabase {
75  public:
76   friend class nsMsgDBService;
77   friend class nsMsgPropertyEnumerator;  // accesses m_mdbEnv and m_mdbStore
78 
79   NS_DECL_ISUPPORTS
80   NS_DECL_NSIDBCHANGEANNOUNCER
81   NS_DECL_NSIMSGDATABASE
82 
83   /**
84    * Opens a database folder.
85    *
86    * @param aFolderName     The name of the folder to create.
87    * @param aCreate         Whether or not the file should be created.
88    * @param aLeaveInvalidDB Set to true if you do not want the database to be
89    *                        deleted if it is invalid.
90    * @exception NS_ERROR_FILE_TARGET_DOES_NOT_EXIST
91    *                        The file could not be created.
92    * @exception NS_MSG_ERROR_FOLDER_SUMMARY_OUT_OF_DATE
93    *                        The database is present (and was opened), but the
94    *                        summary file is out of date.
95    * @exception NS_MSG_ERROR_FOLDER_SUMMARY_MISSING
96    *                        The database is present (and was opened), but the
97    *                        summary file is missing.
98    */
99   virtual nsresult Open(nsMsgDBService* aDBService, nsIFile* aFolderName,
100                         bool aCreate, bool aLeaveInvalidDB);
101   virtual nsresult IsHeaderRead(nsIMsgDBHdr* hdr, bool* pRead);
102   virtual nsresult MarkHdrReadInDB(nsIMsgDBHdr* msgHdr, bool bRead,
103                                    nsIDBChangeListener* instigator);
104   nsresult OpenInternal(nsMsgDBService* aDBService, nsIFile* aFolderName,
105                         bool aCreate, bool aLeaveInvalidDB, bool sync);
106   nsresult CheckForErrors(nsresult err, bool sync, nsMsgDBService* aDBService,
107                           nsIFile* summaryFile);
108   virtual nsresult OpenMDB(nsIFile* dbfile, bool create, bool sync);
109   virtual nsresult CloseMDB(bool commit);
110   virtual nsresult CreateMsgHdr(nsIMdbRow* hdrRow, nsMsgKey key,
111                                 nsIMsgDBHdr** result);
112   virtual nsresult GetThreadForMsgKey(nsMsgKey msgKey, nsIMsgThread** result);
113   virtual nsresult EnumerateMessagesWithFlag(nsIMsgEnumerator** result,
114                                              uint32_t* pFlag);
115   nsresult GetSearchResultsTable(const nsACString& searchFolderUri,
116                                  bool createIfMissing, nsIMdbTable** table);
117 
118   //////////////////////////////////////////////////////////////////////////////
119   // nsMsgDatabase methods:
120   nsMsgDatabase();
121 
122   nsresult GetMDBFactory(nsIMdbFactory** aMdbFactory);
GetEnv()123   nsIMdbEnv* GetEnv() { return m_mdbEnv; }
GetStore()124   nsIMdbStore* GetStore() { return m_mdbStore; }
125   virtual uint32_t GetCurVersion();
126   nsresult GetCollationKeyGenerator();
127   nsIMimeConverter* GetMimeConverter();
128 
129   nsresult GetTableCreateIfMissing(const char* scope, const char* kind,
130                                    nsIMdbTable** table, mdb_token& scopeToken,
131                                    mdb_token& kindToken);
132 
133   // helper function to fill in nsStrings from hdr row cell contents.
134   nsresult RowCellColumnTonsString(nsIMdbRow* row, mdb_token columnToken,
135                                    nsAString& resultStr);
136   nsresult RowCellColumnToUInt32(nsIMdbRow* row, mdb_token columnToken,
137                                  uint32_t* uint32Result,
138                                  uint32_t defaultValue = 0);
139   nsresult RowCellColumnToUInt32(nsIMdbRow* row, mdb_token columnToken,
140                                  uint32_t& uint32Result,
141                                  uint32_t defaultValue = 0);
142   nsresult RowCellColumnToUInt64(nsIMdbRow* row, mdb_token columnToken,
143                                  uint64_t* uint64Result,
144                                  uint64_t defaultValue = 0);
145   nsresult RowCellColumnToMime2DecodedString(nsIMdbRow* row,
146                                              mdb_token columnToken,
147                                              nsAString& resultStr);
148   nsresult RowCellColumnToCollationKey(nsIMdbRow* row, mdb_token columnToken,
149                                        nsTArray<uint8_t>& result);
150   nsresult RowCellColumnToConstCharPtr(nsIMdbRow* row, mdb_token columnToken,
151                                        const char** ptr);
152   nsresult RowCellColumnToAddressCollationKey(nsIMdbRow* row,
153                                               mdb_token colToken,
154                                               nsTArray<uint8_t>& result);
155 
156   nsresult GetEffectiveCharset(nsIMdbRow* row, nsACString& resultCharset);
157 
158   // these methods take the property name as a string, not a token.
159   // they should be used when the properties aren't accessed a lot
160   nsresult GetProperty(nsIMdbRow* row, const char* propertyName, char** result);
161   nsresult SetProperty(nsIMdbRow* row, const char* propertyName,
162                        const char* propertyVal);
163   nsresult GetPropertyAsNSString(nsIMdbRow* row, const char* propertyName,
164                                  nsAString& result);
165   nsresult SetPropertyFromNSString(nsIMdbRow* row, const char* propertyName,
166                                    const nsAString& propertyVal);
167   nsresult GetUint32Property(nsIMdbRow* row, const char* propertyName,
168                              uint32_t* result, uint32_t defaultValue = 0);
169   nsresult GetUint64Property(nsIMdbRow* row, const char* propertyName,
170                              uint64_t* result, uint64_t defaultValue = 0);
171   nsresult SetUint32Property(nsIMdbRow* row, const char* propertyName,
172                              uint32_t propertyVal);
173   nsresult SetUint64Property(nsIMdbRow* row, const char* propertyName,
174                              uint64_t propertyVal);
175   nsresult GetBooleanProperty(nsIMdbRow* row, const char* propertyName,
176                               bool* result, bool defaultValue = false);
177   nsresult SetBooleanProperty(nsIMdbRow* row, const char* propertyName,
178                               bool propertyVal);
179   // helper function for once we have the token.
180   nsresult SetNSStringPropertyWithToken(nsIMdbRow* row, mdb_token aProperty,
181                                         const nsAString& propertyStr);
182 
183   // helper functions to put values in cells for the passed-in row
184   nsresult UInt32ToRowCellColumn(nsIMdbRow* row, mdb_token columnToken,
185                                  uint32_t value);
186   nsresult CharPtrToRowCellColumn(nsIMdbRow* row, mdb_token columnToken,
187                                   const char* charPtr);
188   nsresult RowCellColumnToCharPtr(nsIMdbRow* row, mdb_token columnToken,
189                                   char** result);
190   nsresult UInt64ToRowCellColumn(nsIMdbRow* row, mdb_token columnToken,
191                                  uint64_t value);
192 
193   // helper functions to copy an nsString to a yarn, int32 to yarn, and vice
194   // versa.
195   static struct mdbYarn* nsStringToYarn(struct mdbYarn* yarn,
196                                         const nsAString& str);
197   static struct mdbYarn* UInt32ToYarn(struct mdbYarn* yarn, uint32_t i);
198   static struct mdbYarn* UInt64ToYarn(struct mdbYarn* yarn, uint64_t i);
199   static void YarnTonsString(struct mdbYarn* yarn, nsAString& str);
200   static void YarnTonsCString(struct mdbYarn* yarn, nsACString& str);
201   static void YarnToUInt32(struct mdbYarn* yarn, uint32_t* i);
202   static void YarnToUInt64(struct mdbYarn* yarn, uint64_t* i);
203 
204 #ifdef DEBUG
205   virtual nsresult DumpContents();
206 #endif
207 
208   friend class nsMsgHdr;     // use this to get access to cached tokens for hdr
209                              // fields
210   friend class nsMsgThread;  // use this to get access to cached tokens for hdr
211                              // fields
212 
213   friend class nsMsgDBEnumerator;
214   friend class nsMsgDBThreadEnumerator;
215 
216  protected:
217   virtual ~nsMsgDatabase();
218 
219   // prefs stuff - in future, we might want to cache the prefs interface
220   nsresult GetBoolPref(const char* prefName, bool* result);
221   nsresult GetIntPref(const char* prefName, int32_t* result);
222   virtual void GetGlobalPrefs();
223   // retrieval methods
224   nsIMsgThread* GetThreadForReference(nsCString& msgID, nsIMsgDBHdr** pMsgHdr);
225   nsIMsgThread* GetThreadForSubject(nsCString& subject);
226   nsIMsgThread* GetThreadForMessageId(nsCString& msgId);
227   nsIMsgThread* GetThreadForThreadId(nsMsgKey threadId);
228   nsMsgHdr* GetMsgHdrForReference(nsCString& reference);
229   nsIMsgDBHdr* GetMsgHdrForSubject(nsCString& subject);
230   // threading interfaces
231   virtual nsresult CreateNewThread(nsMsgKey key, const char* subject,
232                                    nsMsgThread** newThread);
233   virtual bool ThreadBySubjectWithoutRe();
234   virtual bool UseStrictThreading();
235   virtual bool UseCorrectThreading();
236   virtual nsresult ThreadNewHdr(nsMsgHdr* hdr, bool& newThread);
237   virtual nsresult AddNewThread(nsMsgHdr* msgHdr);
238   virtual nsresult AddToThread(nsMsgHdr* newHdr, nsIMsgThread* thread,
239                                nsIMsgDBHdr* pMsgHdr, bool threadInThread);
240 
241   static PRTime gLastUseTime;  // global last use time
242   PRTime m_lastUseTime;        // last use time for this db
243   // inline to make instrumentation as cheap as possible
RememberLastUseTime()244   inline void RememberLastUseTime() { gLastUseTime = m_lastUseTime = PR_Now(); }
245 
246   bool MatchDbName(nsIFile* dbName);  // returns TRUE if they match
247 
248   // Flag handling routines
249   virtual nsresult SetKeyFlag(nsMsgKey key, bool set, nsMsgMessageFlagType flag,
250                               nsIDBChangeListener* instigator = nullptr);
251   virtual nsresult SetMsgHdrFlag(nsIMsgDBHdr* msgHdr, bool set,
252                                  nsMsgMessageFlagType flag,
253                                  nsIDBChangeListener* instigator);
254 
255   virtual bool SetHdrFlag(nsIMsgDBHdr*, bool bSet, nsMsgMessageFlagType flag);
256   virtual bool SetHdrReadFlag(nsIMsgDBHdr*, bool pRead);
257   virtual uint32_t GetStatusFlags(nsIMsgDBHdr* msgHdr,
258                                   nsMsgMessageFlagType origFlags);
259   // helper function which doesn't involve thread object
260 
261   virtual nsresult RemoveHeaderFromDB(nsMsgHdr* msgHdr);
262   virtual nsresult RemoveHeaderFromThread(nsMsgHdr* msgHdr);
263   virtual nsresult AdjustExpungedBytesOnDelete(nsIMsgDBHdr* msgHdr);
264 
265   nsCOMPtr<nsICollation> m_collationKeyGenerator;
266   nsCOMPtr<nsIMimeConverter> m_mimeConverter;
267   nsCOMPtr<nsIMsgRetentionSettings> m_retentionSettings;
268   nsCOMPtr<nsIMsgDownloadSettings> m_downloadSettings;
269 
270   nsresult FindMessagesOlderThan(uint32_t daysToKeepHdrs,
271                                  bool applyToFlaggedMessages,
272                                  nsTArray<RefPtr<nsIMsgDBHdr>>& hdrsToDelete);
273   nsresult FindExcessMessages(uint32_t numHeadersToKeep,
274                               bool applyToFlaggedMessages,
275                               nsTArray<RefPtr<nsIMsgDBHdr>>& hdrsToDelete);
276 
277   // mdb bookkeeping stuff
278   virtual nsresult InitExistingDB();
279   virtual nsresult InitNewDB();
280   virtual nsresult InitMDBInfo();
281 
282   nsCOMPtr<nsIMsgFolder> m_folder;
283   RefPtr<nsDBFolderInfo> m_dbFolderInfo;
284   nsMsgKey m_nextPseudoMsgKey;
285   nsIMdbEnv* m_mdbEnv;  // to be used in all the db calls.
286   nsIMdbStore* m_mdbStore;
287   nsIMdbTable* m_mdbAllMsgHeadersTable;
288   nsIMdbTable* m_mdbAllThreadsTable;
289 
290   // Used for asynchronous db opens. If non-null, we're still opening
291   // the underlying mork database. If null, the db has been completely opened.
292   nsCOMPtr<nsIMdbThumb> m_thumb;
293   // used to remember the args to Open for async open.
294   bool m_create;
295   bool m_leaveInvalidDB;
296 
297   nsCOMPtr<nsIFile> m_dbFile;
298   nsTArray<nsMsgKey> m_newSet;  // new messages since last open.
299   bool m_mdbTokensInitialized;
300   nsTObserverArray<nsCOMPtr<nsIDBChangeListener>> m_ChangeListeners;
301   mdb_token m_hdrRowScopeToken;
302   mdb_token m_threadRowScopeToken;
303   mdb_token m_hdrTableKindToken;
304   mdb_token m_threadTableKindToken;
305   mdb_token m_allThreadsTableKindToken;
306   mdb_token m_subjectColumnToken;
307   mdb_token m_senderColumnToken;
308   mdb_token m_messageIdColumnToken;
309   mdb_token m_referencesColumnToken;
310   mdb_token m_recipientsColumnToken;
311   mdb_token m_dateColumnToken;
312   mdb_token m_messageSizeColumnToken;
313   mdb_token m_flagsColumnToken;
314   mdb_token m_priorityColumnToken;
315   mdb_token m_labelColumnToken;
316   mdb_token m_statusOffsetColumnToken;
317   mdb_token m_numLinesColumnToken;
318   mdb_token m_ccListColumnToken;
319   mdb_token m_bccListColumnToken;
320   mdb_token m_threadFlagsColumnToken;
321   mdb_token m_threadIdColumnToken;
322   mdb_token m_threadChildrenColumnToken;
323   mdb_token m_threadUnreadChildrenColumnToken;
324   mdb_token m_messageThreadIdColumnToken;
325   mdb_token m_threadSubjectColumnToken;
326   mdb_token m_messageCharSetColumnToken;
327   mdb_token m_threadParentColumnToken;
328   mdb_token m_threadRootKeyColumnToken;
329   mdb_token m_threadNewestMsgDateColumnToken;
330   mdb_token m_offlineMsgOffsetColumnToken;
331   mdb_token m_offlineMessageSizeColumnToken;
332 
333   // header caching stuff - MRU headers, keeps them around in memory
334   nsresult AddHdrToCache(nsIMsgDBHdr* hdr, nsMsgKey key);
335   nsresult ClearHdrCache(bool reInit);
336   nsresult RemoveHdrFromCache(nsIMsgDBHdr* hdr, nsMsgKey key);
337   // all headers currently instantiated, doesn't hold refs
338   // these get added when msg hdrs get constructed, and removed when they get
339   // destroyed.
340   nsresult GetHdrFromUseCache(nsMsgKey key, nsIMsgDBHdr** result);
341   nsresult AddHdrToUseCache(nsIMsgDBHdr* hdr, nsMsgKey key);
342   nsresult ClearUseHdrCache();
343   nsresult RemoveHdrFromUseCache(nsIMsgDBHdr* hdr, nsMsgKey key);
344 
345   // not-reference holding array of threads we've handed out.
346   // If a db goes away, it will clean up the outstanding threads.
347   // We use an nsTArray because we don't expect to ever have very many
348   // of these, rarely more than 5.
349   nsTArray<nsMsgThread*> m_threads;
350   // Clear outstanding thread objects
351   void ClearThreads();
352   nsMsgThread* FindExistingThread(nsMsgKey threadId);
353 
354   mdb_pos FindInsertIndexInSortedTable(nsIMdbTable* table, mdb_id idToInsert);
355 
356   void ClearCachedObjects(bool dbGoingAway);
357   void InvalidateEnumerators();
358   // all instantiated headers, but doesn't hold refs.
359   PLDHashTable* m_headersInUse;
360   static PLDHashNumber HashKey(const void* aKey);
361   static bool MatchEntry(const PLDHashEntryHdr* aEntry, const void* aKey);
362   static void MoveEntry(PLDHashTable* aTable, const PLDHashEntryHdr* aFrom,
363                         PLDHashEntryHdr* aTo);
364   static void ClearEntry(PLDHashTable* aTable, PLDHashEntryHdr* aEntry);
365   static PLDHashTableOps gMsgDBHashTableOps;
366   struct MsgHdrHashElement : public PLDHashEntryHdr {
367     nsMsgKey mKey;
368     nsIMsgDBHdr* mHdr;
369   };
370   PLDHashTable* m_cachedHeaders;
371   bool m_bCacheHeaders;
372   nsMsgKey m_cachedThreadId;
373   nsCOMPtr<nsIMsgThread> m_cachedThread;
374   nsCOMPtr<nsIMdbFactory> mMdbFactory;
375 
376   // Message reference hash table
377   static PLDHashTableOps gRefHashTableOps;
378   struct RefHashElement : public PLDHashEntryHdr {
379     const char* mRef;  // Hash entry key, must come first
380     nsMsgKey mThreadId;
381     uint32_t mCount;
382   };
383   PLDHashTable* m_msgReferences;
384   nsresult GetRefFromHash(nsCString& reference, nsMsgKey* threadId);
385   nsresult AddRefToHash(nsCString& reference, nsMsgKey threadId);
386   nsresult AddMsgRefsToHash(nsIMsgDBHdr* msgHdr);
387   nsresult RemoveRefFromHash(nsCString& reference);
388   nsresult RemoveMsgRefsFromHash(nsIMsgDBHdr* msgHdr);
389   nsresult InitRefHash();
390 
391   // The enumerators add themselves to these lists.
392   // If a db goes away - via destruction or ForceClosed() - it needs to
393   // invalidate any outstanding enumerators.
394   nsTArray<nsMsgDBEnumerator*> m_msgEnumerators;
395   nsTArray<nsMsgDBThreadEnumerator*> m_threadEnumerators;
396 
397   // Memory reporter details
398  public:
399   static size_t HeaderHashSizeOf(PLDHashEntryHdr* hdr,
400                                  mozilla::MallocSizeOf aMallocSizeOf,
401                                  void* arg);
402   virtual size_t SizeOfExcludingThis(mozilla::MallocSizeOf aMallocSizeOf) const;
SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf)403   virtual size_t SizeOfIncludingThis(
404       mozilla::MallocSizeOf aMallocSizeOf) const {
405     return aMallocSizeOf(this) + SizeOfExcludingThis(aMallocSizeOf);
406   }
407 
408  private:
409   uint32_t m_cacheSize;
410   RefPtr<mozilla::mailnews::MsgDBReporter> mMemReporter;
411 };
412 
413 class nsMsgRetentionSettings : public nsIMsgRetentionSettings {
414  public:
415   nsMsgRetentionSettings();
416 
417   NS_DECL_ISUPPORTS
418   NS_DECL_NSIMSGRETENTIONSETTINGS
419  protected:
420   virtual ~nsMsgRetentionSettings();
421   nsMsgRetainByPreference m_retainByPreference;
422   uint32_t m_daysToKeepHdrs;
423   uint32_t m_numHeadersToKeep;
424   bool m_useServerDefaults;
425   bool m_cleanupBodiesByDays;
426   uint32_t m_daysToKeepBodies;
427   bool m_applyToFlaggedMessages;
428 };
429 
430 class nsMsgDownloadSettings : public nsIMsgDownloadSettings {
431  public:
432   nsMsgDownloadSettings();
433 
434   NS_DECL_ISUPPORTS
435   NS_DECL_NSIMSGDOWNLOADSETTINGS
436  protected:
437   virtual ~nsMsgDownloadSettings();
438   bool m_useServerDefaults;
439   bool m_downloadUnreadOnly;
440   bool m_downloadByDate;
441   int32_t m_ageLimitOfMsgsToDownload;
442 };
443 
444 #endif
445