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 #include "nsMsgDatabaseEnumerators.h"
7 #include "nsMsgDatabase.h"
8 #include "nsIMsgHdr.h"
9 #include "nsMsgBaseCID.h"
10 #include "nsMsgThread.h"
11
12 /*
13 * nsMsgDBEnumerator implementation
14 */
15
nsMsgDBEnumerator(nsMsgDatabase * db,nsIMdbTable * table,nsMsgDBEnumeratorFilter filter,void * closure,bool iterateForwards)16 nsMsgDBEnumerator::nsMsgDBEnumerator(nsMsgDatabase* db, nsIMdbTable* table,
17 nsMsgDBEnumeratorFilter filter,
18 void* closure, bool iterateForwards)
19 : mDB(db),
20 mDone(false),
21 mIterateForwards(iterateForwards),
22 mFilter(filter),
23 mClosure(closure) {
24 mTable = table;
25 mRowPos = 0;
26 mDB->m_msgEnumerators.AppendElement(this);
27 }
28
~nsMsgDBEnumerator()29 nsMsgDBEnumerator::~nsMsgDBEnumerator() { Invalidate(); }
30
Invalidate()31 void nsMsgDBEnumerator::Invalidate() {
32 // Order is important here. If the database is destroyed first, releasing
33 // the cursor will crash (due, I think, to a disconnect between XPCOM and
34 // Mork internal memory management).
35 mRowCursor = nullptr;
36 mTable = nullptr;
37 mResultHdr = nullptr;
38 mDone = true;
39 if (mDB) {
40 mDB->m_msgEnumerators.RemoveElement(this);
41 mDB = nullptr;
42 }
43 }
44
GetRowCursor()45 nsresult nsMsgDBEnumerator::GetRowCursor() {
46 mDone = false;
47
48 if (!mDB || !mTable) return NS_ERROR_NULL_POINTER;
49
50 if (mIterateForwards) {
51 mRowPos = -1;
52 } else {
53 mdb_count numRows;
54 mTable->GetCount(mDB->GetEnv(), &numRows);
55 mRowPos = numRows; // startPos is 0 relative.
56 }
57 return mTable->GetTableRowCursor(mDB->GetEnv(), mRowPos,
58 getter_AddRefs(mRowCursor));
59 }
60
GetNext(nsIMsgDBHdr ** aItem)61 NS_IMETHODIMP nsMsgDBEnumerator::GetNext(nsIMsgDBHdr** aItem) {
62 if (!aItem) return NS_ERROR_NULL_POINTER;
63 *aItem = nullptr;
64
65 // If we've already got one ready, return it.
66 if (mResultHdr) {
67 mResultHdr.forget(aItem);
68 return NS_OK;
69 }
70
71 // Bail out if enumerator has been invalidated.
72 if (!mDB) {
73 return NS_ERROR_FAILURE;
74 }
75
76 nsresult rv = InternalGetNext(aItem);
77 NS_ENSURE_SUCCESS(rv, rv);
78 return *aItem ? NS_OK : NS_ERROR_FAILURE;
79 }
80
InternalGetNext(nsIMsgDBHdr ** nextHdr)81 nsresult nsMsgDBEnumerator::InternalGetNext(nsIMsgDBHdr** nextHdr) {
82 nsresult rv;
83
84 *nextHdr = nullptr;
85
86 if (!mRowCursor) {
87 rv = GetRowCursor();
88 NS_ENSURE_SUCCESS(rv, rv);
89 }
90
91 while (true) {
92 nsIMdbRow* hdrRow;
93 if (mIterateForwards) {
94 rv = mRowCursor->NextRow(mDB->GetEnv(), &hdrRow, &mRowPos);
95 } else {
96 rv = mRowCursor->PrevRow(mDB->GetEnv(), &hdrRow, &mRowPos);
97 }
98 NS_ENSURE_SUCCESS(rv, rv);
99 if (!hdrRow) {
100 // No more rows, so we're done.
101 *nextHdr = nullptr;
102 return NS_OK;
103 }
104
105 // Get key from row
106 mdbOid outOid;
107 nsMsgKey key = nsMsgKey_None;
108 rv = hdrRow->GetOid(mDB->GetEnv(), &outOid);
109 NS_ENSURE_SUCCESS(rv, rv);
110 key = outOid.mOid_Id;
111
112 nsCOMPtr<nsIMsgDBHdr> hdr;
113 rv = mDB->CreateMsgHdr(hdrRow, key, getter_AddRefs(hdr));
114 NS_ENSURE_SUCCESS(rv, rv);
115
116 // Ignore expunged messages.
117 uint32_t flags;
118 hdr->GetFlags(&flags);
119 if (flags & nsMsgMessageFlags::Expunged) {
120 continue;
121 }
122
123 // Ignore anything which doesn't pass the filter func (if there is one).
124 if (mFilter && NS_FAILED(mFilter(hdr, mClosure))) {
125 continue;
126 }
127
128 // If we get this far, we've found it.
129 hdr.forget(nextHdr);
130 return NS_OK;
131 }
132 }
133
HasMoreElements(bool * aResult)134 NS_IMETHODIMP nsMsgDBEnumerator::HasMoreElements(bool* aResult) {
135 if (!aResult) return NS_ERROR_NULL_POINTER;
136
137 if (!mResultHdr) {
138 // Bail out if enumerator has been invalidated.
139 if (!mDB) {
140 return NS_ERROR_FAILURE;
141 }
142
143 nsresult rv = InternalGetNext(getter_AddRefs(mResultHdr));
144 NS_ENSURE_SUCCESS(rv, rv);
145 if (!mResultHdr) {
146 mDone = true;
147 }
148 }
149
150 *aResult = !mDone;
151 return NS_OK;
152 }
153
154 /*
155 * nsMsgFilteredDBEnumerator implementation
156 */
157
nsMsgFilteredDBEnumerator(nsMsgDatabase * db,nsIMdbTable * table,bool reverse)158 nsMsgFilteredDBEnumerator::nsMsgFilteredDBEnumerator(nsMsgDatabase* db,
159 nsIMdbTable* table,
160 bool reverse)
161 : nsMsgDBEnumerator(db, table, nullptr, nullptr, !reverse) {}
162
~nsMsgFilteredDBEnumerator()163 nsMsgFilteredDBEnumerator::~nsMsgFilteredDBEnumerator() {}
164
165 /**
166 * Create the search session for the enumerator,
167 * add the scope term for "folder" to the search session, and add the search
168 * terms in the array to the search session.
169 */
InitSearchSession(const nsTArray<RefPtr<nsIMsgSearchTerm>> & searchTerms,nsIMsgFolder * folder)170 nsresult nsMsgFilteredDBEnumerator::InitSearchSession(
171 const nsTArray<RefPtr<nsIMsgSearchTerm>>& searchTerms,
172 nsIMsgFolder* folder) {
173 nsresult rv;
174 m_searchSession = do_CreateInstance(NS_MSGSEARCHSESSION_CONTRACTID, &rv);
175 NS_ENSURE_SUCCESS(rv, rv);
176
177 m_searchSession->AddScopeTerm(nsMsgSearchScope::offlineMail, folder);
178 for (auto searchTerm : searchTerms) {
179 m_searchSession->AppendTerm(searchTerm);
180 }
181 return NS_OK;
182 }
183
InternalGetNext(nsIMsgDBHdr ** nextHdr)184 nsresult nsMsgFilteredDBEnumerator::InternalGetNext(nsIMsgDBHdr** nextHdr) {
185 nsCOMPtr<nsIMsgDBHdr> hdr;
186 while (true) {
187 nsresult rv = nsMsgDBEnumerator::InternalGetNext(getter_AddRefs(hdr));
188 NS_ENSURE_SUCCESS(rv, rv);
189 if (!hdr) {
190 break; // No more.
191 }
192 bool matches;
193 rv = m_searchSession->MatchHdr(hdr, mDB, &matches);
194 NS_ENSURE_SUCCESS(rv, rv);
195 if (matches) {
196 break; // Found one!
197 }
198 }
199 hdr.forget(nextHdr);
200 return NS_OK;
201 }
202
203 /*
204 * nsMsgDBThreadEnumerator implementation
205 */
206
nsMsgDBThreadEnumerator(nsMsgDatabase * db,nsMsgDBThreadEnumeratorFilter filter)207 nsMsgDBThreadEnumerator::nsMsgDBThreadEnumerator(
208 nsMsgDatabase* db, nsMsgDBThreadEnumeratorFilter filter)
209 : mDB(db),
210 mTableCursor(nullptr),
211 mResultThread(nullptr),
212 mDone(false),
213 mFilter(filter) {
214 mDB->m_threadEnumerators.AppendElement(this);
215 mNextPrefetched = false;
216 }
217
~nsMsgDBThreadEnumerator()218 nsMsgDBThreadEnumerator::~nsMsgDBThreadEnumerator() { Invalidate(); }
219
Invalidate()220 void nsMsgDBThreadEnumerator::Invalidate() {
221 // Order is important here. If the database is destroyed first, releasing
222 // the cursor will crash (due, I think, to a disconnect between XPCOM and
223 // Mork internal memory management).
224 mTableCursor = nullptr;
225 mResultThread = nullptr;
226 mDone = true;
227 if (mDB) {
228 mDB->m_threadEnumerators.RemoveElement(this);
229 mDB = nullptr;
230 }
231 }
232
GetTableCursor(void)233 nsresult nsMsgDBThreadEnumerator::GetTableCursor(void) {
234 nsresult rv = NS_OK;
235
236 // DB might have disappeared.
237 if (!mDB || !mDB->m_mdbStore) return NS_ERROR_NULL_POINTER;
238 if (NS_FAILED(rv)) return rv;
239 return NS_OK;
240 }
241
HasMoreElements(bool * aResult)242 NS_IMETHODIMP nsMsgDBThreadEnumerator::HasMoreElements(bool* aResult) {
243 NS_ENSURE_ARG_POINTER(aResult);
244
245 if (!mNextPrefetched) {
246 PrefetchNext();
247 }
248 *aResult = !mDone;
249 return NS_OK;
250 }
251
GetNext(nsIMsgThread ** aItem)252 NS_IMETHODIMP nsMsgDBThreadEnumerator::GetNext(nsIMsgThread** aItem) {
253 NS_ENSURE_ARG_POINTER(aItem);
254
255 *aItem = nullptr;
256 nsresult rv = NS_OK;
257 if (!mNextPrefetched) rv = PrefetchNext();
258 if (NS_SUCCEEDED(rv)) {
259 if (mResultThread) {
260 NS_ADDREF(*aItem = mResultThread);
261 mNextPrefetched = false;
262 }
263 }
264 return rv;
265 }
266
PrefetchNext()267 nsresult nsMsgDBThreadEnumerator::PrefetchNext() {
268 nsresult rv;
269
270 // DB might have disappeared.
271 if (!mDB || !mDB->m_mdbStore) {
272 return NS_ERROR_NULL_POINTER;
273 }
274
275 if (!mTableCursor) {
276 rv = mDB->m_mdbStore->GetPortTableCursor(
277 mDB->GetEnv(), mDB->m_hdrRowScopeToken, mDB->m_threadTableKindToken,
278 getter_AddRefs(mTableCursor));
279 NS_ENSURE_SUCCESS(rv, rv);
280 }
281
282 nsCOMPtr<nsIMdbTable> table;
283 while (true) {
284 mResultThread = nullptr;
285 rv = mTableCursor->NextTable(mDB->GetEnv(), getter_AddRefs(table));
286 if (!table) {
287 mDone = true;
288 return NS_ERROR_FAILURE;
289 }
290 if (NS_FAILED(rv)) {
291 mDone = true;
292 return rv;
293 }
294
295 mdbOid tableId;
296 table->GetOid(mDB->GetEnv(), &tableId);
297
298 mResultThread = mDB->FindExistingThread(tableId.mOid_Id);
299 if (!mResultThread) mResultThread = new nsMsgThread(mDB, table);
300
301 if (mResultThread) {
302 uint32_t numChildren = 0;
303 mResultThread->GetNumChildren(&numChildren);
304 // we've got empty thread; don't tell caller about it.
305 if (numChildren == 0) continue;
306 }
307 if (mFilter && NS_FAILED(mFilter(mResultThread)))
308 continue;
309 else
310 break;
311 }
312 if (mResultThread) {
313 mNextPrefetched = true;
314 return NS_OK;
315 }
316 return NS_ERROR_FAILURE;
317 }
318