summaryrefslogtreecommitdiffstats
path: root/comm/mailnews/db/msgdb/src/nsMsgDatabaseEnumerators.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'comm/mailnews/db/msgdb/src/nsMsgDatabaseEnumerators.cpp')
-rw-r--r--comm/mailnews/db/msgdb/src/nsMsgDatabaseEnumerators.cpp317
1 files changed, 317 insertions, 0 deletions
diff --git a/comm/mailnews/db/msgdb/src/nsMsgDatabaseEnumerators.cpp b/comm/mailnews/db/msgdb/src/nsMsgDatabaseEnumerators.cpp
new file mode 100644
index 0000000000..a64ef46f57
--- /dev/null
+++ b/comm/mailnews/db/msgdb/src/nsMsgDatabaseEnumerators.cpp
@@ -0,0 +1,317 @@
+/* -*- 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/. */
+
+#include "nsMsgDatabaseEnumerators.h"
+#include "nsMsgDatabase.h"
+#include "nsIMsgHdr.h"
+#include "nsMsgThread.h"
+
+/*
+ * nsMsgDBEnumerator implementation
+ */
+
+nsMsgDBEnumerator::nsMsgDBEnumerator(nsMsgDatabase* db, nsIMdbTable* table,
+ nsMsgDBEnumeratorFilter filter,
+ void* closure, bool iterateForwards)
+ : mDB(db),
+ mDone(false),
+ mIterateForwards(iterateForwards),
+ mFilter(filter),
+ mClosure(closure) {
+ mTable = table;
+ mRowPos = 0;
+ mDB->m_msgEnumerators.AppendElement(this);
+}
+
+nsMsgDBEnumerator::~nsMsgDBEnumerator() { Invalidate(); }
+
+void nsMsgDBEnumerator::Invalidate() {
+ // Order is important here. If the database is destroyed first, releasing
+ // the cursor will crash (due, I think, to a disconnect between XPCOM and
+ // Mork internal memory management).
+ mRowCursor = nullptr;
+ mTable = nullptr;
+ mResultHdr = nullptr;
+ mDone = true;
+ if (mDB) {
+ mDB->m_msgEnumerators.RemoveElement(this);
+ mDB = nullptr;
+ }
+}
+
+nsresult nsMsgDBEnumerator::GetRowCursor() {
+ mDone = false;
+
+ if (!mDB || !mTable) return NS_ERROR_NULL_POINTER;
+
+ if (mIterateForwards) {
+ mRowPos = -1;
+ } else {
+ mdb_count numRows;
+ mTable->GetCount(mDB->GetEnv(), &numRows);
+ mRowPos = numRows; // startPos is 0 relative.
+ }
+ return mTable->GetTableRowCursor(mDB->GetEnv(), mRowPos,
+ getter_AddRefs(mRowCursor));
+}
+
+NS_IMETHODIMP nsMsgDBEnumerator::GetNext(nsIMsgDBHdr** aItem) {
+ if (!aItem) return NS_ERROR_NULL_POINTER;
+ *aItem = nullptr;
+
+ // If we've already got one ready, return it.
+ if (mResultHdr) {
+ mResultHdr.forget(aItem);
+ return NS_OK;
+ }
+
+ // Bail out if enumerator has been invalidated.
+ if (!mDB) {
+ return NS_ERROR_FAILURE;
+ }
+
+ nsresult rv = InternalGetNext(aItem);
+ NS_ENSURE_SUCCESS(rv, rv);
+ return *aItem ? NS_OK : NS_ERROR_FAILURE;
+}
+
+nsresult nsMsgDBEnumerator::InternalGetNext(nsIMsgDBHdr** nextHdr) {
+ nsresult rv;
+
+ *nextHdr = nullptr;
+
+ if (!mRowCursor) {
+ rv = GetRowCursor();
+ NS_ENSURE_SUCCESS(rv, rv);
+ }
+
+ while (true) {
+ nsIMdbRow* hdrRow;
+ if (mIterateForwards) {
+ rv = mRowCursor->NextRow(mDB->GetEnv(), &hdrRow, &mRowPos);
+ } else {
+ rv = mRowCursor->PrevRow(mDB->GetEnv(), &hdrRow, &mRowPos);
+ }
+ NS_ENSURE_SUCCESS(rv, rv);
+ if (!hdrRow) {
+ // No more rows, so we're done.
+ *nextHdr = nullptr;
+ return NS_OK;
+ }
+
+ // Get key from row
+ mdbOid outOid;
+ nsMsgKey key = nsMsgKey_None;
+ rv = hdrRow->GetOid(mDB->GetEnv(), &outOid);
+ NS_ENSURE_SUCCESS(rv, rv);
+ key = outOid.mOid_Id;
+
+ nsCOMPtr<nsIMsgDBHdr> hdr;
+ rv = mDB->CreateMsgHdr(hdrRow, key, getter_AddRefs(hdr));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ // Ignore expunged messages.
+ uint32_t flags;
+ hdr->GetFlags(&flags);
+ if (flags & nsMsgMessageFlags::Expunged) {
+ continue;
+ }
+
+ // Ignore anything which doesn't pass the filter func (if there is one).
+ if (mFilter && NS_FAILED(mFilter(hdr, mClosure))) {
+ continue;
+ }
+
+ // If we get this far, we've found it.
+ hdr.forget(nextHdr);
+ return NS_OK;
+ }
+}
+
+NS_IMETHODIMP nsMsgDBEnumerator::HasMoreElements(bool* aResult) {
+ if (!aResult) return NS_ERROR_NULL_POINTER;
+
+ if (!mResultHdr) {
+ // Bail out if enumerator has been invalidated.
+ if (!mDB) {
+ return NS_ERROR_FAILURE;
+ }
+
+ nsresult rv = InternalGetNext(getter_AddRefs(mResultHdr));
+ NS_ENSURE_SUCCESS(rv, rv);
+ if (!mResultHdr) {
+ mDone = true;
+ }
+ }
+
+ *aResult = !mDone;
+ return NS_OK;
+}
+
+/*
+ * nsMsgFilteredDBEnumerator implementation
+ */
+
+nsMsgFilteredDBEnumerator::nsMsgFilteredDBEnumerator(nsMsgDatabase* db,
+ nsIMdbTable* table,
+ bool reverse)
+ : nsMsgDBEnumerator(db, table, nullptr, nullptr, !reverse) {}
+
+nsMsgFilteredDBEnumerator::~nsMsgFilteredDBEnumerator() {}
+
+/**
+ * Create the search session for the enumerator,
+ * add the scope term for "folder" to the search session, and add the search
+ * terms in the array to the search session.
+ */
+nsresult nsMsgFilteredDBEnumerator::InitSearchSession(
+ const nsTArray<RefPtr<nsIMsgSearchTerm>>& searchTerms,
+ nsIMsgFolder* folder) {
+ nsresult rv;
+ m_searchSession =
+ do_CreateInstance("@mozilla.org/messenger/searchSession;1", &rv);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ m_searchSession->AddScopeTerm(nsMsgSearchScope::offlineMail, folder);
+ for (auto searchTerm : searchTerms) {
+ m_searchSession->AppendTerm(searchTerm);
+ }
+ return NS_OK;
+}
+
+nsresult nsMsgFilteredDBEnumerator::InternalGetNext(nsIMsgDBHdr** nextHdr) {
+ nsCOMPtr<nsIMsgDBHdr> hdr;
+ while (true) {
+ nsresult rv = nsMsgDBEnumerator::InternalGetNext(getter_AddRefs(hdr));
+ NS_ENSURE_SUCCESS(rv, rv);
+ if (!hdr) {
+ break; // No more.
+ }
+ bool matches;
+ rv = m_searchSession->MatchHdr(hdr, mDB, &matches);
+ NS_ENSURE_SUCCESS(rv, rv);
+ if (matches) {
+ break; // Found one!
+ }
+ }
+ hdr.forget(nextHdr);
+ return NS_OK;
+}
+
+/*
+ * nsMsgDBThreadEnumerator implementation
+ */
+
+nsMsgDBThreadEnumerator::nsMsgDBThreadEnumerator(
+ nsMsgDatabase* db, nsMsgDBThreadEnumeratorFilter filter)
+ : mDB(db),
+ mTableCursor(nullptr),
+ mResultThread(nullptr),
+ mDone(false),
+ mFilter(filter) {
+ mDB->m_threadEnumerators.AppendElement(this);
+ mNextPrefetched = false;
+}
+
+nsMsgDBThreadEnumerator::~nsMsgDBThreadEnumerator() { Invalidate(); }
+
+void nsMsgDBThreadEnumerator::Invalidate() {
+ // Order is important here. If the database is destroyed first, releasing
+ // the cursor will crash (due, I think, to a disconnect between XPCOM and
+ // Mork internal memory management).
+ mTableCursor = nullptr;
+ mResultThread = nullptr;
+ mDone = true;
+ if (mDB) {
+ mDB->m_threadEnumerators.RemoveElement(this);
+ mDB = nullptr;
+ }
+}
+
+nsresult nsMsgDBThreadEnumerator::GetTableCursor(void) {
+ nsresult rv = NS_OK;
+
+ // DB might have disappeared.
+ if (!mDB || !mDB->m_mdbStore) return NS_ERROR_NULL_POINTER;
+ if (NS_FAILED(rv)) return rv;
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsMsgDBThreadEnumerator::HasMoreElements(bool* aResult) {
+ NS_ENSURE_ARG_POINTER(aResult);
+
+ if (!mNextPrefetched) {
+ PrefetchNext();
+ }
+ *aResult = !mDone;
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsMsgDBThreadEnumerator::GetNext(nsIMsgThread** aItem) {
+ NS_ENSURE_ARG_POINTER(aItem);
+
+ *aItem = nullptr;
+ nsresult rv = NS_OK;
+ if (!mNextPrefetched) rv = PrefetchNext();
+ if (NS_SUCCEEDED(rv)) {
+ if (mResultThread) {
+ NS_ADDREF(*aItem = mResultThread);
+ mNextPrefetched = false;
+ }
+ }
+ return rv;
+}
+
+nsresult nsMsgDBThreadEnumerator::PrefetchNext() {
+ nsresult rv;
+
+ // DB might have disappeared.
+ if (!mDB || !mDB->m_mdbStore) {
+ return NS_ERROR_NULL_POINTER;
+ }
+
+ if (!mTableCursor) {
+ rv = mDB->m_mdbStore->GetPortTableCursor(
+ mDB->GetEnv(), mDB->m_hdrRowScopeToken, mDB->m_threadTableKindToken,
+ getter_AddRefs(mTableCursor));
+ NS_ENSURE_SUCCESS(rv, rv);
+ }
+
+ nsCOMPtr<nsIMdbTable> table;
+ while (true) {
+ mResultThread = nullptr;
+ rv = mTableCursor->NextTable(mDB->GetEnv(), getter_AddRefs(table));
+ if (!table) {
+ mDone = true;
+ return NS_ERROR_FAILURE;
+ }
+ if (NS_FAILED(rv)) {
+ mDone = true;
+ return rv;
+ }
+
+ mdbOid tableId;
+ table->GetOid(mDB->GetEnv(), &tableId);
+
+ mResultThread = mDB->FindExistingThread(tableId.mOid_Id);
+ if (!mResultThread) mResultThread = new nsMsgThread(mDB, table);
+
+ if (mResultThread) {
+ uint32_t numChildren = 0;
+ mResultThread->GetNumChildren(&numChildren);
+ // we've got empty thread; don't tell caller about it.
+ if (numChildren == 0) continue;
+ }
+ if (mFilter && NS_FAILED(mFilter(mResultThread)))
+ continue;
+ else
+ break;
+ }
+ if (mResultThread) {
+ mNextPrefetched = true;
+ return NS_OK;
+ }
+ return NS_ERROR_FAILURE;
+}