summaryrefslogtreecommitdiffstats
path: root/comm/mailnews/db/msgdb/src/nsMailDatabase.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'comm/mailnews/db/msgdb/src/nsMailDatabase.cpp')
-rw-r--r--comm/mailnews/db/msgdb/src/nsMailDatabase.cpp380
1 files changed, 380 insertions, 0 deletions
diff --git a/comm/mailnews/db/msgdb/src/nsMailDatabase.cpp b/comm/mailnews/db/msgdb/src/nsMailDatabase.cpp
new file mode 100644
index 0000000000..c07294785a
--- /dev/null
+++ b/comm/mailnews/db/msgdb/src/nsMailDatabase.cpp
@@ -0,0 +1,380 @@
+/* -*- Mode: C++; tab-width: 2; 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/. */
+
+#include "msgCore.h"
+#include "nsMailDatabase.h"
+#include "nsDBFolderInfo.h"
+#include "nsMsgLocalFolderHdrs.h"
+#include "nsNetUtil.h"
+#include "nsMsgOfflineImapOperation.h"
+#include "nsMsgFolderFlags.h"
+#include "mozilla/Logging.h"
+#include "prprf.h"
+#include "nsMsgUtils.h"
+#include "nsIMsgPluggableStore.h"
+#include "nsSimpleEnumerator.h"
+
+using namespace mozilla;
+
+extern LazyLogModule IMAPOffline; // defined in nsMsgOfflineImapOperation.cpp
+
+// scope for all offine ops table
+const char* kOfflineOpsScope = "ns:msg:db:row:scope:ops:all";
+const char* kOfflineOpsTableKind = "ns:msg:db:table:kind:ops";
+struct mdbOid gAllOfflineOpsTableOID;
+
+nsMailDatabase::nsMailDatabase() : m_reparse(false) {
+ m_mdbAllOfflineOpsTable = nullptr;
+ m_offlineOpsRowScopeToken = 0;
+ m_offlineOpsTableKindToken = 0;
+}
+
+nsMailDatabase::~nsMailDatabase() {}
+
+// caller passes in upgrading==true if they want back a db even if the db is out
+// of date. If so, they'll extract out the interesting info from the db, close
+// it, delete it, and then try to open the db again, prior to reparsing.
+nsresult nsMailDatabase::Open(nsMsgDBService* aDBService, nsIFile* aSummaryFile,
+ bool aCreate, bool aUpgrading) {
+#ifdef DEBUG
+ nsString leafName;
+ aSummaryFile->GetLeafName(leafName);
+ if (!StringEndsWith(leafName, NS_LITERAL_STRING_FROM_CSTRING(SUMMARY_SUFFIX),
+ nsCaseInsensitiveStringComparator))
+ NS_ERROR("non summary file passed into open");
+#endif
+ return nsMsgDatabase::Open(aDBService, aSummaryFile, aCreate, aUpgrading);
+}
+
+NS_IMETHODIMP nsMailDatabase::ForceClosed() {
+ m_mdbAllOfflineOpsTable = nullptr;
+ return nsMsgDatabase::ForceClosed();
+}
+
+// get this on demand so that only db's that have offline ops will
+// create the table.
+nsresult nsMailDatabase::GetAllOfflineOpsTable() {
+ nsresult rv = NS_OK;
+ if (!m_mdbAllOfflineOpsTable)
+ rv = GetTableCreateIfMissing(kOfflineOpsScope, kOfflineOpsTableKind,
+ getter_AddRefs(m_mdbAllOfflineOpsTable),
+ m_offlineOpsRowScopeToken,
+ m_offlineOpsTableKindToken);
+ return rv;
+}
+
+NS_IMETHODIMP nsMailDatabase::DeleteMessages(
+ nsTArray<nsMsgKey> const& nsMsgKeys, nsIDBChangeListener* instigator) {
+ nsresult rv;
+ if (m_folder) {
+ bool isLocked;
+ m_folder->GetLocked(&isLocked);
+ if (isLocked) {
+ NS_ASSERTION(false, "Some other operation is in progress");
+ return NS_MSG_FOLDER_BUSY;
+ }
+ }
+
+ rv = nsMsgDatabase::DeleteMessages(nsMsgKeys, instigator);
+ SetSummaryValid(true);
+ return rv;
+}
+
+NS_IMETHODIMP nsMailDatabase::GetSummaryValid(bool* aResult) {
+ uint32_t version;
+ m_dbFolderInfo->GetVersion(&version);
+ if (GetCurVersion() != version) {
+ *aResult = false;
+ return NS_OK;
+ }
+ if (!m_folder) {
+ // If the folder is not set, we just return without checking the validity
+ // of the summary file. For now, this is an expected condition when the
+ // message database is being opened from a URL in
+ // nsMailboxUrl::GetMsgHdrForKey() which calls
+ // nsMsgDBService::OpenMailDBFromFile() without a folder.
+ // Returning an error here would lead to the deletion of the MSF in the
+ // caller nsMsgDatabase::CheckForErrors().
+ *aResult = true;
+ return NS_OK;
+ }
+
+ // If this is a virtual folder, there is no storage.
+ bool isVirtual = false;
+ m_folder->GetFlag(nsMsgFolderFlags::Virtual, &isVirtual);
+ if (isVirtual) {
+ *aResult = true;
+ return NS_OK;
+ }
+
+ nsCOMPtr<nsIMsgPluggableStore> msgStore;
+ nsresult rv = m_folder->GetMsgStore(getter_AddRefs(msgStore));
+ NS_ENSURE_SUCCESS(rv, rv);
+ return msgStore->IsSummaryFileValid(m_folder, this, aResult);
+}
+
+NS_IMETHODIMP nsMailDatabase::SetSummaryValid(bool aValid) {
+ nsMsgDatabase::SetSummaryValid(aValid);
+
+ if (!m_folder) return NS_ERROR_NULL_POINTER;
+
+ // If this is a virtual folder, there is no storage.
+ bool flag;
+ m_folder->GetFlag(nsMsgFolderFlags::Virtual, &flag);
+ if (flag) return NS_OK;
+
+ nsCOMPtr<nsIMsgPluggableStore> msgStore;
+ nsresult rv = m_folder->GetMsgStore(getter_AddRefs(msgStore));
+ NS_ENSURE_SUCCESS(rv, rv);
+ return msgStore->SetSummaryFileValid(m_folder, this, aValid);
+}
+
+NS_IMETHODIMP nsMailDatabase::RemoveOfflineOp(nsIMsgOfflineImapOperation* op) {
+ nsresult rv = GetAllOfflineOpsTable();
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ if (!op || !m_mdbAllOfflineOpsTable) return NS_ERROR_NULL_POINTER;
+ nsMsgOfflineImapOperation* offlineOp =
+ static_cast<nsMsgOfflineImapOperation*>(
+ op); // closed system, so this is ok
+ nsIMdbRow* row = offlineOp->GetMDBRow();
+ rv = m_mdbAllOfflineOpsTable->CutRow(GetEnv(), row);
+ row->CutAllColumns(GetEnv());
+ return rv;
+}
+
+NS_IMETHODIMP nsMailDatabase::GetOfflineOpForKey(
+ nsMsgKey msgKey, bool create, nsIMsgOfflineImapOperation** offlineOp) {
+ mdb_bool hasOid;
+ mdbOid rowObjectId;
+ nsresult err;
+
+ nsresult rv = GetAllOfflineOpsTable();
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ if (!offlineOp || !m_mdbAllOfflineOpsTable) return NS_ERROR_NULL_POINTER;
+
+ *offlineOp = NULL;
+
+ rowObjectId.mOid_Id = msgKey;
+ rowObjectId.mOid_Scope = m_offlineOpsRowScopeToken;
+ err = m_mdbAllOfflineOpsTable->HasOid(GetEnv(), &rowObjectId, &hasOid);
+ if (NS_SUCCEEDED(err) && m_mdbStore && (hasOid || create)) {
+ nsCOMPtr<nsIMdbRow> offlineOpRow;
+ err = m_mdbStore->GetRow(GetEnv(), &rowObjectId,
+ getter_AddRefs(offlineOpRow));
+
+ if (create) {
+ if (!offlineOpRow) {
+ err = m_mdbStore->NewRowWithOid(GetEnv(), &rowObjectId,
+ getter_AddRefs(offlineOpRow));
+ NS_ENSURE_SUCCESS(err, err);
+ }
+ if (offlineOpRow && !hasOid)
+ m_mdbAllOfflineOpsTable->AddRow(GetEnv(), offlineOpRow);
+ }
+
+ if (NS_SUCCEEDED(err) && offlineOpRow) {
+ NS_IF_ADDREF(*offlineOp =
+ new nsMsgOfflineImapOperation(this, offlineOpRow));
+ (*offlineOp)->SetMessageKey(msgKey);
+ }
+ if (!hasOid && m_dbFolderInfo) {
+ // set initial value for flags so we don't lose them.
+ nsCOMPtr<nsIMsgDBHdr> msgHdr;
+ GetMsgHdrForKey(msgKey, getter_AddRefs(msgHdr));
+ if (msgHdr) {
+ uint32_t flags;
+ msgHdr->GetFlags(&flags);
+ (*offlineOp)->SetNewFlags(flags);
+ }
+ int32_t newFlags;
+ m_dbFolderInfo->OrFlags(nsMsgFolderFlags::OfflineEvents, &newFlags);
+ }
+ }
+
+ return err;
+}
+
+NS_IMETHODIMP nsMailDatabase::ListAllOfflineOpIds(
+ nsTArray<nsMsgKey>& offlineOpIds) {
+ nsresult rv = GetAllOfflineOpsTable();
+ NS_ENSURE_SUCCESS(rv, rv);
+ nsIMdbTableRowCursor* rowCursor;
+
+ if (m_mdbAllOfflineOpsTable) {
+ nsresult err =
+ m_mdbAllOfflineOpsTable->GetTableRowCursor(GetEnv(), -1, &rowCursor);
+ while (NS_SUCCEEDED(err) && rowCursor) {
+ mdbOid outOid;
+ mdb_pos outPos;
+
+ err = rowCursor->NextRowOid(GetEnv(), &outOid, &outPos);
+ // is this right? Mork is returning a 0 id, but that should valid.
+ if (outPos < 0 || outOid.mOid_Id == (mdb_id)-1) break;
+ if (NS_SUCCEEDED(err)) {
+ offlineOpIds.AppendElement(outOid.mOid_Id);
+ if (MOZ_LOG_TEST(IMAPOffline, LogLevel::Info)) {
+ nsCOMPtr<nsIMsgOfflineImapOperation> offlineOp;
+ GetOfflineOpForKey(outOid.mOid_Id, false, getter_AddRefs(offlineOp));
+ if (offlineOp) {
+ nsMsgOfflineImapOperation* logOp =
+ static_cast<nsMsgOfflineImapOperation*>(
+ static_cast<nsIMsgOfflineImapOperation*>(offlineOp.get()));
+ if (logOp) logOp->Log();
+ }
+ }
+ }
+ }
+ // TODO: would it cause a problem to replace this with "rv = err;" ?
+ rv = (NS_SUCCEEDED(err)) ? NS_OK : NS_ERROR_FAILURE;
+ rowCursor->Release();
+ }
+
+ offlineOpIds.Sort();
+ return rv;
+}
+
+NS_IMETHODIMP nsMailDatabase::ListAllOfflineDeletes(
+ nsTArray<nsMsgKey>& offlineDeletes) {
+ nsresult rv = GetAllOfflineOpsTable();
+ NS_ENSURE_SUCCESS(rv, rv);
+ nsIMdbTableRowCursor* rowCursor;
+ if (m_mdbAllOfflineOpsTable) {
+ nsresult err =
+ m_mdbAllOfflineOpsTable->GetTableRowCursor(GetEnv(), -1, &rowCursor);
+ while (NS_SUCCEEDED(err) && rowCursor) {
+ mdbOid outOid;
+ mdb_pos outPos;
+ nsIMdbRow* offlineOpRow;
+
+ err = rowCursor->NextRow(GetEnv(), &offlineOpRow, &outPos);
+ // is this right? Mork is returning a 0 id, but that should valid.
+ if (outPos < 0 || offlineOpRow == nullptr) break;
+ if (NS_SUCCEEDED(err)) {
+ offlineOpRow->GetOid(GetEnv(), &outOid);
+ RefPtr<nsIMsgOfflineImapOperation> offlineOp =
+ new nsMsgOfflineImapOperation(this, offlineOpRow);
+ imapMessageFlagsType newFlags;
+ nsOfflineImapOperationType opType;
+
+ offlineOp->GetOperation(&opType);
+ offlineOp->GetNewFlags(&newFlags);
+ if (opType & nsIMsgOfflineImapOperation::kMsgMoved ||
+ ((opType & nsIMsgOfflineImapOperation::kFlagsChanged) &&
+ (newFlags & nsIMsgOfflineImapOperation::kMsgMarkedDeleted)))
+ offlineDeletes.AppendElement(outOid.mOid_Id);
+
+ offlineOpRow->Release();
+ }
+ }
+ // TODO: would it cause a problem to replace this with "rv = err;" ?
+ rv = (NS_SUCCEEDED(err)) ? NS_OK : NS_ERROR_FAILURE;
+ rowCursor->Release();
+ }
+ return rv;
+}
+
+// This is used to remember that the db is out of sync with the mail folder
+// and needs to be regenerated.
+void nsMailDatabase::SetReparse(bool reparse) { m_reparse = reparse; }
+
+class nsMsgOfflineOpEnumerator : public nsSimpleEnumerator {
+ public:
+ const nsID& DefaultInterface() override {
+ return NS_GET_IID(nsIMsgOfflineImapOperation);
+ }
+
+ // nsISimpleEnumerator methods:
+ NS_DECL_NSISIMPLEENUMERATOR
+
+ explicit nsMsgOfflineOpEnumerator(nsMailDatabase* db);
+
+ protected:
+ ~nsMsgOfflineOpEnumerator() override;
+ nsresult GetRowCursor();
+ nsresult PrefetchNext();
+ nsMailDatabase* mDB;
+ nsIMdbTableRowCursor* mRowCursor;
+ nsCOMPtr<nsIMsgOfflineImapOperation> mResultOp;
+ bool mDone;
+ bool mNextPrefetched;
+};
+
+nsMsgOfflineOpEnumerator::nsMsgOfflineOpEnumerator(nsMailDatabase* db)
+ : mDB(db), mRowCursor(nullptr), mDone(false) {
+ NS_ADDREF(mDB);
+ mNextPrefetched = false;
+}
+
+nsMsgOfflineOpEnumerator::~nsMsgOfflineOpEnumerator() {
+ NS_IF_RELEASE(mRowCursor);
+ NS_RELEASE(mDB);
+}
+
+nsresult nsMsgOfflineOpEnumerator::GetRowCursor() {
+ nsresult rv = NS_OK;
+ mDone = false;
+
+ if (!mDB || !mDB->m_mdbAllOfflineOpsTable) return NS_ERROR_NULL_POINTER;
+
+ rv = mDB->m_mdbAllOfflineOpsTable->GetTableRowCursor(mDB->GetEnv(), -1,
+ &mRowCursor);
+ return rv;
+}
+
+NS_IMETHODIMP nsMsgOfflineOpEnumerator::GetNext(nsISupports** aItem) {
+ NS_ENSURE_ARG_POINTER(aItem);
+
+ nsresult rv = NS_OK;
+ if (!mNextPrefetched) rv = PrefetchNext();
+ if (NS_SUCCEEDED(rv)) {
+ if (mResultOp) {
+ NS_ADDREF(*aItem = mResultOp);
+ mNextPrefetched = false;
+ }
+ }
+ return rv;
+}
+
+nsresult nsMsgOfflineOpEnumerator::PrefetchNext() {
+ nsresult rv = NS_OK;
+ nsIMdbRow* offlineOpRow;
+ mdb_pos rowPos;
+
+ if (!mRowCursor) {
+ rv = GetRowCursor();
+ if (NS_FAILED(rv)) return rv;
+ }
+
+ rv = mRowCursor->NextRow(mDB->GetEnv(), &offlineOpRow, &rowPos);
+ if (!offlineOpRow) {
+ mDone = true;
+ return NS_ERROR_FAILURE;
+ }
+ if (NS_FAILED(rv)) {
+ mDone = true;
+ return rv;
+ }
+
+ nsIMsgOfflineImapOperation* op =
+ new nsMsgOfflineImapOperation(mDB, offlineOpRow);
+ mResultOp = op;
+ if (!op) return NS_ERROR_OUT_OF_MEMORY;
+
+ if (mResultOp) {
+ mNextPrefetched = true;
+ return NS_OK;
+ }
+ return NS_ERROR_FAILURE;
+}
+
+NS_IMETHODIMP nsMsgOfflineOpEnumerator::HasMoreElements(bool* aResult) {
+ NS_ENSURE_ARG_POINTER(aResult);
+
+ if (!mNextPrefetched) PrefetchNext();
+ *aResult = !mDone;
+ return NS_OK;
+}