1 /* -*- Mode: C++; tab-width: 2; 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 "msgCore.h"
7 #include "netCore.h"
8 #include "nsNetUtil.h"
9 #include "nsImapOfflineSync.h"
10 #include "nsImapMailFolder.h"
11 #include "nsMsgFolderFlags.h"
12 #include "nsMsgMessageFlags.h"
13 #include "nsMsgBaseCID.h"
14 #include "nsIMsgMailNewsUrl.h"
15 #include "nsIMsgAccountManager.h"
16 #include "nsINntpIncomingServer.h"
17 #include "nsDirectoryServiceDefs.h"
18 #include "nsISeekableStream.h"
19 #include "nsIMsgCopyService.h"
20 #include "nsImapProtocol.h"
21 #include "nsMsgUtils.h"
22 #include "nsIAutoSyncManager.h"
23 #include "mozilla/Unused.h"
24
NS_IMPL_ISUPPORTS(nsImapOfflineSync,nsIUrlListener,nsIMsgCopyServiceListener,nsIDBChangeListener)25 NS_IMPL_ISUPPORTS(nsImapOfflineSync, nsIUrlListener, nsIMsgCopyServiceListener,
26 nsIDBChangeListener)
27
28 nsImapOfflineSync::nsImapOfflineSync(nsIMsgWindow* window,
29 nsIUrlListener* listener,
30 nsIMsgFolder* singleFolderOnly,
31 bool isPseudoOffline) {
32 m_singleFolderToUpdate = singleFolderOnly;
33 m_window = window;
34 // not the perfect place for this, but I think it will work.
35 if (m_window) m_window->SetStopped(false);
36
37 mCurrentPlaybackOpType = nsIMsgOfflineImapOperation::kFlagsChanged;
38 m_mailboxupdatesStarted = false;
39 m_mailboxupdatesFinished = false;
40 m_createdOfflineFolders = false;
41 m_pseudoOffline = isPseudoOffline;
42 m_KeyIndex = 0;
43 mCurrentUIDValidity = nsMsgKey_None;
44 m_listener = listener;
45 }
46
~nsImapOfflineSync()47 nsImapOfflineSync::~nsImapOfflineSync() {}
48
SetWindow(nsIMsgWindow * window)49 void nsImapOfflineSync::SetWindow(nsIMsgWindow* window) { m_window = window; }
50
OnStartRunningUrl(nsIURI * url)51 NS_IMETHODIMP nsImapOfflineSync::OnStartRunningUrl(nsIURI* url) {
52 return NS_OK;
53 }
54
55 NS_IMETHODIMP
OnStopRunningUrl(nsIURI * url,nsresult exitCode)56 nsImapOfflineSync::OnStopRunningUrl(nsIURI* url, nsresult exitCode) {
57 nsresult rv = exitCode;
58
59 // where do we make sure this gets cleared when we start running urls?
60 bool stopped = false;
61 if (m_window) m_window->GetStopped(&stopped);
62
63 if (m_curTempFile) {
64 m_curTempFile->Remove(false);
65 m_curTempFile = nullptr;
66 }
67 // NS_BINDING_ABORTED is used for the user pressing stop, which
68 // should cause us to abort the offline process. Other errors
69 // should allow us to continue.
70 if (stopped) {
71 if (m_listener) m_listener->OnStopRunningUrl(url, NS_BINDING_ABORTED);
72 return NS_OK;
73 }
74 nsCOMPtr<nsIImapUrl> imapUrl = do_QueryInterface(url);
75
76 if (imapUrl)
77 nsImapProtocol::LogImapUrl(NS_SUCCEEDED(rv) ? "offline imap url succeeded "
78 : "offline imap url failed ",
79 imapUrl);
80
81 // If we succeeded, or it was an imap move/copy that timed out, clear the
82 // operation.
83 bool moveCopy =
84 mCurrentPlaybackOpType == nsIMsgOfflineImapOperation::kMsgCopy ||
85 mCurrentPlaybackOpType == nsIMsgOfflineImapOperation::kMsgMoved;
86 if (NS_SUCCEEDED(exitCode) || exitCode == NS_MSG_ERROR_IMAP_COMMAND_FAILED ||
87 (moveCopy && exitCode == NS_ERROR_NET_TIMEOUT)) {
88 ClearCurrentOps();
89 rv = ProcessNextOperation();
90 }
91 // else if it's a non-stop error, and we're doing multiple folders,
92 // go to the next folder.
93 else if (!m_singleFolderToUpdate) {
94 if (AdvanceToNextFolder())
95 rv = ProcessNextOperation();
96 else if (m_listener)
97 m_listener->OnStopRunningUrl(url, rv);
98 }
99
100 return rv;
101 }
102
103 /**
104 * Leaves m_currentServer at the next imap or local mail "server" that
105 * might have offline events to playback, and m_folderQueue holding
106 * a (reversed) list of all the folders to consider for that server.
107 * If no more servers, m_currentServer will be left at nullptr and the
108 * function returns false.
109 */
AdvanceToNextServer()110 bool nsImapOfflineSync::AdvanceToNextServer() {
111 nsresult rv = NS_OK;
112
113 if (m_allServers.IsEmpty()) {
114 NS_ASSERTION(!m_currentServer, "this shouldn't be set");
115 m_currentServer = nullptr;
116 nsCOMPtr<nsIMsgAccountManager> accountManager =
117 do_GetService(NS_MSGACCOUNTMANAGER_CONTRACTID, &rv);
118 NS_ASSERTION(accountManager && NS_SUCCEEDED(rv),
119 "couldn't get account mgr");
120 if (!accountManager || NS_FAILED(rv)) return false;
121
122 rv = accountManager->GetAllServers(m_allServers);
123 NS_ENSURE_SUCCESS(rv, false);
124 }
125 size_t serverIndex = 0;
126 if (m_currentServer) {
127 serverIndex = m_allServers.IndexOf(m_currentServer);
128 if (serverIndex == m_allServers.NoIndex) {
129 serverIndex = 0;
130 } else {
131 // Move to the next server
132 ++serverIndex;
133 }
134 }
135 m_currentServer = nullptr;
136 nsCOMPtr<nsIMsgFolder> rootFolder;
137
138 while (serverIndex < m_allServers.Length()) {
139 nsCOMPtr<nsIMsgIncomingServer> server(m_allServers[serverIndex]);
140 serverIndex++;
141
142 nsCOMPtr<nsINntpIncomingServer> newsServer = do_QueryInterface(server);
143 if (newsServer) // news servers aren't involved in offline imap
144 continue;
145
146 if (server) {
147 m_currentServer = server;
148 server->GetRootFolder(getter_AddRefs(rootFolder));
149 if (rootFolder) {
150 rv = rootFolder->GetDescendants(m_folderQueue);
151 if (NS_SUCCEEDED(rv)) {
152 if (!m_folderQueue.IsEmpty()) {
153 // We'll be popping folders off the end as they are processed.
154 m_folderQueue.Reverse();
155 return true;
156 }
157 }
158 }
159 }
160 }
161 return false;
162 }
163
164 /**
165 * Sets m_currentFolder to the next folder to process.
166 *
167 * @return True if next folder to process was found, otherwise false.
168 */
AdvanceToNextFolder()169 bool nsImapOfflineSync::AdvanceToNextFolder() {
170 // we always start by changing flags
171 mCurrentPlaybackOpType = nsIMsgOfflineImapOperation::kFlagsChanged;
172
173 if (m_currentFolder) {
174 m_currentFolder->SetMsgDatabase(nullptr);
175 m_currentFolder = nullptr;
176 }
177
178 bool hasMore = false;
179 if (m_currentServer) {
180 hasMore = !m_folderQueue.IsEmpty();
181 }
182 if (!hasMore) {
183 hasMore = AdvanceToNextServer();
184 }
185 if (hasMore) {
186 m_currentFolder = m_folderQueue.PopLastElement();
187 }
188 ClearDB();
189 return m_currentFolder;
190 }
191
AdvanceToFirstIMAPFolder()192 void nsImapOfflineSync::AdvanceToFirstIMAPFolder() {
193 m_currentServer = nullptr;
194 nsCOMPtr<nsIMsgImapMailFolder> imapFolder;
195 while (!imapFolder && AdvanceToNextFolder()) {
196 imapFolder = do_QueryInterface(m_currentFolder);
197 }
198 }
199
ProcessFlagOperation(nsIMsgOfflineImapOperation * op)200 void nsImapOfflineSync::ProcessFlagOperation(nsIMsgOfflineImapOperation* op) {
201 nsCOMPtr<nsIMsgOfflineImapOperation> currentOp = op;
202 nsTArray<nsMsgKey> matchingFlagKeys;
203 uint32_t currentKeyIndex = m_KeyIndex;
204
205 imapMessageFlagsType matchingFlags;
206 currentOp->GetNewFlags(&matchingFlags);
207 bool flagsMatch = true;
208 do { // loop for all messages with the same flags
209 if (flagsMatch) {
210 nsMsgKey curKey;
211 currentOp->GetMessageKey(&curKey);
212 matchingFlagKeys.AppendElement(curKey);
213 currentOp->SetPlayingBack(true);
214 m_currentOpsToClear.AppendObject(currentOp);
215 }
216 currentOp = nullptr;
217 imapMessageFlagsType newFlags = kNoImapMsgFlag;
218 imapMessageFlagsType flagOperation = kNoImapMsgFlag;
219 if (++currentKeyIndex < m_CurrentKeys.Length())
220 m_currentDB->GetOfflineOpForKey(m_CurrentKeys[currentKeyIndex], false,
221 getter_AddRefs(currentOp));
222 if (currentOp) {
223 currentOp->GetFlagOperation(&flagOperation);
224 currentOp->GetNewFlags(&newFlags);
225 }
226 flagsMatch = (flagOperation & nsIMsgOfflineImapOperation::kFlagsChanged) &&
227 (newFlags == matchingFlags);
228 } while (currentOp);
229
230 if (!matchingFlagKeys.IsEmpty()) {
231 nsAutoCString uids;
232 nsImapMailFolder::AllocateUidStringFromKeys(matchingFlagKeys, uids);
233 uint32_t curFolderFlags;
234 m_currentFolder->GetFlags(&curFolderFlags);
235
236 if (uids.get() && (curFolderFlags & nsMsgFolderFlags::ImapBox)) {
237 nsresult rv = NS_OK;
238 nsCOMPtr<nsIMsgImapMailFolder> imapFolder =
239 do_QueryInterface(m_currentFolder);
240 nsCOMPtr<nsIURI> uriToSetFlags;
241 if (imapFolder) {
242 rv = imapFolder->SetImapFlags(uids.get(), matchingFlags,
243 getter_AddRefs(uriToSetFlags));
244 if (NS_SUCCEEDED(rv) && uriToSetFlags) {
245 nsCOMPtr<nsIMsgMailNewsUrl> mailnewsUrl =
246 do_QueryInterface(uriToSetFlags);
247 if (mailnewsUrl) mailnewsUrl->RegisterListener(this);
248 }
249 }
250 }
251 } else
252 ProcessNextOperation();
253 }
254
ProcessKeywordOperation(nsIMsgOfflineImapOperation * op)255 void nsImapOfflineSync::ProcessKeywordOperation(
256 nsIMsgOfflineImapOperation* op) {
257 nsCOMPtr<nsIMsgOfflineImapOperation> currentOp = op;
258 nsTArray<nsMsgKey> matchingKeywordKeys;
259 uint32_t currentKeyIndex = m_KeyIndex;
260
261 nsAutoCString keywords;
262 if (mCurrentPlaybackOpType == nsIMsgOfflineImapOperation::kAddKeywords)
263 currentOp->GetKeywordsToAdd(getter_Copies(keywords));
264 else
265 currentOp->GetKeywordsToRemove(getter_Copies(keywords));
266 bool keywordsMatch = true;
267 do { // loop for all messages with the same keywords
268 if (keywordsMatch) {
269 nsMsgKey curKey;
270 currentOp->GetMessageKey(&curKey);
271 matchingKeywordKeys.AppendElement(curKey);
272 currentOp->SetPlayingBack(true);
273 m_currentOpsToClear.AppendObject(currentOp);
274 }
275 currentOp = nullptr;
276 if (++currentKeyIndex < m_CurrentKeys.Length())
277 m_currentDB->GetOfflineOpForKey(m_CurrentKeys[currentKeyIndex], false,
278 getter_AddRefs(currentOp));
279 if (currentOp) {
280 nsAutoCString curOpKeywords;
281 nsOfflineImapOperationType operation;
282 currentOp->GetOperation(&operation);
283 if (mCurrentPlaybackOpType == nsIMsgOfflineImapOperation::kAddKeywords)
284 currentOp->GetKeywordsToAdd(getter_Copies(curOpKeywords));
285 else
286 currentOp->GetKeywordsToRemove(getter_Copies(curOpKeywords));
287 keywordsMatch = (operation & mCurrentPlaybackOpType) &&
288 (curOpKeywords.Equals(keywords));
289 }
290 } while (currentOp);
291
292 if (!matchingKeywordKeys.IsEmpty()) {
293 uint32_t curFolderFlags;
294 m_currentFolder->GetFlags(&curFolderFlags);
295
296 if (curFolderFlags & nsMsgFolderFlags::ImapBox) {
297 nsresult rv = NS_OK;
298 nsCOMPtr<nsIMsgImapMailFolder> imapFolder =
299 do_QueryInterface(m_currentFolder);
300 nsCOMPtr<nsIURI> uriToStoreCustomKeywords;
301 if (imapFolder) {
302 rv = imapFolder->StoreCustomKeywords(
303 m_window,
304 (mCurrentPlaybackOpType == nsIMsgOfflineImapOperation::kAddKeywords)
305 ? keywords
306 : EmptyCString(),
307 (mCurrentPlaybackOpType ==
308 nsIMsgOfflineImapOperation::kRemoveKeywords)
309 ? keywords
310 : EmptyCString(),
311 matchingKeywordKeys, getter_AddRefs(uriToStoreCustomKeywords));
312 if (NS_SUCCEEDED(rv) && uriToStoreCustomKeywords) {
313 nsCOMPtr<nsIMsgMailNewsUrl> mailnewsUrl =
314 do_QueryInterface(uriToStoreCustomKeywords);
315 if (mailnewsUrl) mailnewsUrl->RegisterListener(this);
316 }
317 }
318 }
319 } else
320 ProcessNextOperation();
321 }
322
323 // XXX This should not be void but return an error to indicate which low
324 // level routine failed.
ProcessAppendMsgOperation(nsIMsgOfflineImapOperation * currentOp,int32_t opType)325 void nsImapOfflineSync::ProcessAppendMsgOperation(
326 nsIMsgOfflineImapOperation* currentOp, int32_t opType) {
327 nsMsgKey msgKey;
328 currentOp->GetMessageKey(&msgKey);
329 nsCOMPtr<nsIMsgDBHdr> mailHdr;
330 nsresult rv = m_currentDB->GetMsgHdrForKey(msgKey, getter_AddRefs(mailHdr));
331 if (NS_FAILED(rv) || !mailHdr) {
332 m_currentDB->RemoveOfflineOp(currentOp);
333 ProcessNextOperation();
334 return;
335 }
336
337 uint64_t messageOffset;
338 uint32_t messageSize;
339 mailHdr->GetMessageOffset(&messageOffset);
340 mailHdr->GetOfflineMessageSize(&messageSize);
341 nsCOMPtr<nsIFile> tmpFile;
342
343 if (NS_WARN_IF(NS_FAILED(GetSpecialDirectoryWithFileName(
344 NS_OS_TEMP_DIR, "nscpmsg.txt", getter_AddRefs(tmpFile)))))
345 return;
346
347 if (NS_WARN_IF(
348 NS_FAILED(tmpFile->CreateUnique(nsIFile::NORMAL_FILE_TYPE, 00600))))
349 return;
350
351 nsCOMPtr<nsIOutputStream> outputStream;
352 rv = MsgNewBufferedFileOutputStream(getter_AddRefs(outputStream), tmpFile,
353 PR_WRONLY | PR_CREATE_FILE, 00600);
354 if (NS_WARN_IF(NS_FAILED(rv) || !outputStream)) return;
355
356 // We break out of the loop to get to the clean-up code.
357 bool setPlayingBack = false;
358 do {
359 nsCString moveDestination;
360 currentOp->GetDestinationFolderURI(moveDestination);
361
362 nsCOMPtr<nsIMsgFolder> destFolder;
363 rv = GetOrCreateFolder(moveDestination, getter_AddRefs(destFolder));
364 if (NS_WARN_IF(NS_FAILED(rv))) break;
365
366 nsCOMPtr<nsIInputStream> offlineStoreInputStream;
367 bool reusable;
368 rv = destFolder->GetMsgInputStream(mailHdr, &reusable,
369 getter_AddRefs(offlineStoreInputStream));
370 if (NS_WARN_IF((NS_FAILED(rv) || !offlineStoreInputStream))) break;
371
372 nsCOMPtr<nsISeekableStream> seekStream =
373 do_QueryInterface(offlineStoreInputStream);
374 MOZ_ASSERT(seekStream, "non seekable stream - can't read from offline msg");
375 if (!seekStream) break;
376
377 // From this point onwards, we need to set "playing back".
378 setPlayingBack = true;
379
380 rv = seekStream->Seek(PR_SEEK_SET, messageOffset);
381 if (NS_WARN_IF(NS_FAILED(rv))) break;
382
383 // Copy the dest folder offline store msg to the temp file.
384 int32_t inputBufferSize = FILE_IO_BUFFER_SIZE;
385 char* inputBuffer = (char*)PR_Malloc(inputBufferSize);
386 int32_t bytesLeft;
387 uint32_t bytesRead, bytesWritten;
388
389 bytesLeft = messageSize;
390 rv = inputBuffer ? NS_OK : NS_ERROR_OUT_OF_MEMORY;
391 while (bytesLeft > 0 && NS_SUCCEEDED(rv)) {
392 int32_t bytesToRead = std::min(inputBufferSize, bytesLeft);
393 rv = offlineStoreInputStream->Read(inputBuffer, bytesToRead, &bytesRead);
394 if (NS_WARN_IF(NS_FAILED(rv)) || bytesRead == 0) break;
395 rv = outputStream->Write(inputBuffer, bytesRead, &bytesWritten);
396 if (NS_WARN_IF(NS_FAILED(rv))) break;
397 MOZ_ASSERT(bytesWritten == bytesRead,
398 "wrote out incorrect number of bytes");
399 bytesLeft -= bytesRead;
400 }
401 PR_FREEIF(inputBuffer);
402
403 // rv could have an error from Read/Write.
404 nsresult rv2 = outputStream->Close();
405 if (NS_FAILED(rv2)) {
406 NS_WARNING("ouputStream->Close() failed");
407 }
408 outputStream = nullptr; // Don't try to close it again below.
409
410 // rv: Read/Write, rv2: Close
411 if (NS_FAILED(rv) || NS_FAILED(rv2)) {
412 // This Remove() will fail under Windows if the output stream
413 // fails to close above.
414 mozilla::Unused << NS_WARN_IF(NS_FAILED(tmpFile->Remove(false)));
415 break;
416 }
417
418 nsCOMPtr<nsIFile> cloneTmpFile;
419 // clone the tmp file to defeat nsIFile's stat/size caching.
420 tmpFile->Clone(getter_AddRefs(cloneTmpFile));
421 m_curTempFile = cloneTmpFile;
422 nsCOMPtr<nsIMsgCopyService> copyService =
423 do_GetService(NS_MSGCOPYSERVICE_CONTRACTID);
424
425 // CopyFileMessage returns error async to this->OnStopCopy
426 // if copyService is null, let's crash here and now.
427 rv = copyService->CopyFileMessage(cloneTmpFile, destFolder,
428 nullptr, // nsIMsgDBHdr* msgToReplace
429 true, // isDraftOrTemplate
430 0, // new msg flags
431 EmptyCString(), this, m_window);
432 MOZ_ASSERT(NS_SUCCEEDED(rv),
433 "CopyFileMessage() failed. Fatal. Error in call setup?");
434 } while (false);
435
436 if (setPlayingBack) {
437 currentOp->SetPlayingBack(true);
438 m_currentOpsToClear.AppendObject(currentOp);
439 m_currentDB->DeleteHeader(mailHdr, nullptr, true, true);
440 }
441
442 // Close the output stream if it's not already closed.
443 if (outputStream)
444 mozilla::Unused << NS_WARN_IF(NS_FAILED(outputStream->Close()));
445 }
446
ClearCurrentOps()447 void nsImapOfflineSync::ClearCurrentOps() {
448 int32_t opCount = m_currentOpsToClear.Count();
449 for (int32_t i = opCount - 1; i >= 0; i--) {
450 m_currentOpsToClear[i]->SetPlayingBack(false);
451 m_currentOpsToClear[i]->ClearOperation(mCurrentPlaybackOpType);
452 m_currentOpsToClear.RemoveObjectAt(i);
453 }
454 }
455
ProcessMoveOperation(nsIMsgOfflineImapOperation * op)456 void nsImapOfflineSync::ProcessMoveOperation(nsIMsgOfflineImapOperation* op) {
457 nsTArray<nsMsgKey> matchingFlagKeys;
458 uint32_t currentKeyIndex = m_KeyIndex;
459 nsCString moveDestination;
460 op->GetDestinationFolderURI(moveDestination);
461 bool moveMatches = true;
462 nsCOMPtr<nsIMsgOfflineImapOperation> currentOp = op;
463 do { // loop for all messages with the same destination
464 if (moveMatches) {
465 nsMsgKey curKey;
466 currentOp->GetMessageKey(&curKey);
467 matchingFlagKeys.AppendElement(curKey);
468 currentOp->SetPlayingBack(true);
469 m_currentOpsToClear.AppendObject(currentOp);
470 }
471 currentOp = nullptr;
472
473 if (++currentKeyIndex < m_CurrentKeys.Length()) {
474 nsCString nextDestination;
475 nsresult rv = m_currentDB->GetOfflineOpForKey(
476 m_CurrentKeys[currentKeyIndex], false, getter_AddRefs(currentOp));
477 moveMatches = false;
478 if (NS_SUCCEEDED(rv) && currentOp) {
479 nsOfflineImapOperationType opType;
480 currentOp->GetOperation(&opType);
481 if (opType & nsIMsgOfflineImapOperation::kMsgMoved) {
482 currentOp->GetDestinationFolderURI(nextDestination);
483 moveMatches = moveDestination.Equals(nextDestination);
484 }
485 }
486 }
487 } while (currentOp);
488
489 nsCOMPtr<nsIMsgFolder> destFolder;
490 FindFolder(moveDestination, getter_AddRefs(destFolder));
491 // if the dest folder doesn't really exist, these operations are
492 // going to fail, so clear them out and move on.
493 if (!destFolder) {
494 NS_WARNING("trying to playing back move to non-existent folder");
495 ClearCurrentOps();
496 ProcessNextOperation();
497 return;
498 }
499 nsCOMPtr<nsIMsgImapMailFolder> imapFolder =
500 do_QueryInterface(m_currentFolder);
501 if (imapFolder && DestFolderOnSameServer(destFolder)) {
502 imapFolder->ReplayOfflineMoveCopy(matchingFlagKeys, true, destFolder, this,
503 m_window);
504 } else {
505 nsresult rv;
506 nsTArray<RefPtr<nsIMsgDBHdr>> messages;
507 for (uint32_t keyIndex = 0; keyIndex < matchingFlagKeys.Length();
508 keyIndex++) {
509 nsCOMPtr<nsIMsgDBHdr> mailHdr = nullptr;
510 rv = m_currentFolder->GetMessageHeader(
511 matchingFlagKeys.ElementAt(keyIndex), getter_AddRefs(mailHdr));
512 if (NS_SUCCEEDED(rv) && mailHdr) {
513 uint32_t msgSize;
514 // in case of a move, the header has already been deleted,
515 // so we've really got a fake header. We need to get its flags and
516 // size from the offline op to have any chance of doing the move.
517 mailHdr->GetMessageSize(&msgSize);
518 if (!msgSize) {
519 imapMessageFlagsType newImapFlags;
520 uint32_t msgFlags = 0;
521 op->GetMsgSize(&msgSize);
522 op->GetNewFlags(&newImapFlags);
523 // first three bits are the same
524 msgFlags |= (newImapFlags & 0x07);
525 if (newImapFlags & kImapMsgForwardedFlag)
526 msgFlags |= nsMsgMessageFlags::Forwarded;
527 mailHdr->SetFlags(msgFlags);
528 mailHdr->SetMessageSize(msgSize);
529 }
530 messages.AppendElement(mailHdr);
531 }
532 }
533 nsCOMPtr<nsIMsgCopyService> copyService =
534 do_GetService(NS_MSGCOPYSERVICE_CONTRACTID, &rv);
535 if (copyService) {
536 copyService->CopyMessages(m_currentFolder, messages, destFolder, true,
537 this, m_window, false);
538 }
539 }
540 }
541
542 // I'm tempted to make this a method on nsIMsgFolder, but that interface
543 // is already so huge, and there are only a few places in the code that do this.
544 // If there end up to be more places that need this, then we can reconsider.
DestFolderOnSameServer(nsIMsgFolder * destFolder)545 bool nsImapOfflineSync::DestFolderOnSameServer(nsIMsgFolder* destFolder) {
546 nsCOMPtr<nsIMsgIncomingServer> srcServer;
547 nsCOMPtr<nsIMsgIncomingServer> dstServer;
548
549 bool sameServer = false;
550 if (NS_SUCCEEDED(m_currentFolder->GetServer(getter_AddRefs(srcServer))) &&
551 NS_SUCCEEDED(destFolder->GetServer(getter_AddRefs(dstServer))))
552 dstServer->Equals(srcServer, &sameServer);
553 return sameServer;
554 }
555
ProcessCopyOperation(nsIMsgOfflineImapOperation * aCurrentOp)556 void nsImapOfflineSync::ProcessCopyOperation(
557 nsIMsgOfflineImapOperation* aCurrentOp) {
558 nsCOMPtr<nsIMsgOfflineImapOperation> currentOp = aCurrentOp;
559
560 nsTArray<nsMsgKey> matchingFlagKeys;
561 uint32_t currentKeyIndex = m_KeyIndex;
562 nsCString copyDestination;
563 currentOp->GetCopyDestination(0, getter_Copies(copyDestination));
564 bool copyMatches = true;
565 nsresult rv;
566
567 do { // loop for all messages with the same destination
568 if (copyMatches) {
569 nsMsgKey curKey;
570 currentOp->GetMessageKey(&curKey);
571 matchingFlagKeys.AppendElement(curKey);
572 currentOp->SetPlayingBack(true);
573 m_currentOpsToClear.AppendObject(currentOp);
574 }
575 currentOp = nullptr;
576
577 if (++currentKeyIndex < m_CurrentKeys.Length()) {
578 nsCString nextDestination;
579 rv = m_currentDB->GetOfflineOpForKey(m_CurrentKeys[currentKeyIndex],
580 false, getter_AddRefs(currentOp));
581 copyMatches = false;
582 if (NS_SUCCEEDED(rv) && currentOp) {
583 nsOfflineImapOperationType opType;
584 currentOp->GetOperation(&opType);
585 if (opType & nsIMsgOfflineImapOperation::kMsgCopy) {
586 currentOp->GetCopyDestination(0, getter_Copies(nextDestination));
587 copyMatches = copyDestination.Equals(nextDestination);
588 }
589 }
590 }
591 } while (currentOp);
592
593 nsAutoCString uids;
594 nsCOMPtr<nsIMsgFolder> destFolder;
595 FindFolder(copyDestination, getter_AddRefs(destFolder));
596 // if the dest folder doesn't really exist, these operations are
597 // going to fail, so clear them out and move on.
598 if (!destFolder) {
599 NS_ERROR("trying to playing back copy to non-existent folder");
600 ClearCurrentOps();
601 ProcessNextOperation();
602 return;
603 }
604 nsCOMPtr<nsIMsgImapMailFolder> imapFolder =
605 do_QueryInterface(m_currentFolder);
606 if (imapFolder && DestFolderOnSameServer(destFolder)) {
607 rv = imapFolder->ReplayOfflineMoveCopy(matchingFlagKeys, false, destFolder,
608 this, m_window);
609 } else {
610 nsTArray<RefPtr<nsIMsgDBHdr>> messages;
611 for (uint32_t keyIndex = 0; keyIndex < matchingFlagKeys.Length();
612 keyIndex++) {
613 nsCOMPtr<nsIMsgDBHdr> mailHdr = nullptr;
614 rv = m_currentFolder->GetMessageHeader(
615 matchingFlagKeys.ElementAt(keyIndex), getter_AddRefs(mailHdr));
616 if (NS_SUCCEEDED(rv) && mailHdr) messages.AppendElement(mailHdr);
617 }
618 nsCOMPtr<nsIMsgCopyService> copyService =
619 do_GetService(NS_MSGCOPYSERVICE_CONTRACTID, &rv);
620 if (copyService)
621 copyService->CopyMessages(m_currentFolder, messages, destFolder, false,
622 this, m_window, false);
623 }
624 }
625
ProcessEmptyTrash()626 void nsImapOfflineSync::ProcessEmptyTrash() {
627 m_currentFolder->EmptyTrash(m_window, this);
628 ClearDB(); // EmptyTrash closes and deletes the trash db.
629 }
630
631 // returns true if we found a folder to create, false if we're done creating
632 // folders.
CreateOfflineFolders()633 bool nsImapOfflineSync::CreateOfflineFolders() {
634 while (m_currentFolder) {
635 uint32_t flags;
636 m_currentFolder->GetFlags(&flags);
637 bool offlineCreate = (flags & nsMsgFolderFlags::CreatedOffline) != 0;
638 if (offlineCreate) {
639 if (CreateOfflineFolder(m_currentFolder)) return true;
640 }
641 AdvanceToNextFolder();
642 }
643 return false;
644 }
645
CreateOfflineFolder(nsIMsgFolder * folder)646 bool nsImapOfflineSync::CreateOfflineFolder(nsIMsgFolder* folder) {
647 nsCOMPtr<nsIMsgFolder> parent;
648 folder->GetParent(getter_AddRefs(parent));
649
650 nsCOMPtr<nsIMsgImapMailFolder> imapFolder = do_QueryInterface(parent);
651 nsCOMPtr<nsIURI> createFolderURI;
652 nsCString onlineName;
653 imapFolder->GetOnlineName(onlineName);
654
655 NS_ConvertASCIItoUTF16 folderName(onlineName);
656 nsresult rv = imapFolder->PlaybackOfflineFolderCreate(
657 folderName, nullptr, getter_AddRefs(createFolderURI));
658 if (createFolderURI && NS_SUCCEEDED(rv)) {
659 nsCOMPtr<nsIMsgMailNewsUrl> mailnewsUrl =
660 do_QueryInterface(createFolderURI);
661 if (mailnewsUrl) mailnewsUrl->RegisterListener(this);
662 }
663 return NS_SUCCEEDED(rv) ? true
664 : false; // this is asynch, we have to return and be
665 // called again by the OfflineOpExitFunction
666 }
667
GetCurrentUIDValidity()668 int32_t nsImapOfflineSync::GetCurrentUIDValidity() {
669 if (m_currentFolder) {
670 nsCOMPtr<nsIImapMailFolderSink> imapFolderSink =
671 do_QueryInterface(m_currentFolder);
672 if (imapFolderSink) imapFolderSink->GetUidValidity(&mCurrentUIDValidity);
673 }
674 return mCurrentUIDValidity;
675 }
676
677 /**
678 * Playing back offline operations is one giant state machine that runs through
679 * ProcessNextOperation.
680 * The first state is creating online any folders created offline (we do this
681 * first, so we can play back any operations in them in the next pass)
682 */
ProcessNextOperation()683 nsresult nsImapOfflineSync::ProcessNextOperation() {
684 nsresult rv = NS_OK;
685
686 // if we haven't created offline folders, and we're updating all folders,
687 // first, find offline folders to create.
688 if (!m_createdOfflineFolders) {
689 if (m_singleFolderToUpdate) {
690 if (!m_pseudoOffline) {
691 AdvanceToFirstIMAPFolder();
692 if (CreateOfflineFolders()) return NS_OK;
693 }
694 } else {
695 if (CreateOfflineFolders()) return NS_OK;
696 m_currentServer = nullptr;
697 AdvanceToNextFolder();
698 }
699 m_createdOfflineFolders = true;
700 }
701 // if updating one folder only, restore m_currentFolder to that folder
702 if (m_singleFolderToUpdate) m_currentFolder = m_singleFolderToUpdate;
703
704 uint32_t folderFlags;
705 nsCOMPtr<nsIDBFolderInfo> folderInfo;
706 while (m_currentFolder && !m_currentDB) {
707 m_currentFolder->GetFlags(&folderFlags);
708 // need to check if folder has offline events, /* or is configured for
709 // offline */ shouldn't need to check if configured for offline use, since
710 // any folder with events should have nsMsgFolderFlags::OfflineEvents set.
711 if (folderFlags &
712 (nsMsgFolderFlags::OfflineEvents /* | nsMsgFolderFlags::Offline */)) {
713 m_currentFolder->GetDBFolderInfoAndDB(getter_AddRefs(folderInfo),
714 getter_AddRefs(m_currentDB));
715 if (m_currentDB) m_currentDB->AddListener(this);
716 }
717
718 if (m_currentDB) {
719 m_CurrentKeys.Clear();
720 m_KeyIndex = 0;
721 if (NS_FAILED(m_currentDB->ListAllOfflineOpIds(&m_CurrentKeys)) ||
722 m_CurrentKeys.IsEmpty()) {
723 ClearDB();
724 folderInfo = nullptr; // can't hold onto folderInfo longer than db
725 m_currentFolder->ClearFlag(nsMsgFolderFlags::OfflineEvents);
726 } else {
727 // trash any ghost msgs
728 bool deletedGhostMsgs = false;
729 for (uint32_t fakeIndex = 0; fakeIndex < m_CurrentKeys.Length();
730 fakeIndex++) {
731 nsCOMPtr<nsIMsgOfflineImapOperation> currentOp;
732 m_currentDB->GetOfflineOpForKey(m_CurrentKeys[fakeIndex], false,
733 getter_AddRefs(currentOp));
734 if (currentOp) {
735 nsOfflineImapOperationType opType;
736 currentOp->GetOperation(&opType);
737
738 if (opType == nsIMsgOfflineImapOperation::kMoveResult) {
739 nsMsgKey curKey;
740 currentOp->GetMessageKey(&curKey);
741 m_currentDB->RemoveOfflineOp(currentOp);
742 deletedGhostMsgs = true;
743
744 // Remember the pseudo headers before we delete them,
745 // and when we download new headers, tell listeners about the
746 // message key change between the pseudo headers and the real
747 // downloaded headers. Note that we're not currently sending
748 // a msgsDeleted notification for these headers, but the
749 // db listeners are notified about the deletion.
750 // for imap folders, we should adjust the pending counts, because
751 // we have a header that we know about, but don't have in the db.
752 nsCOMPtr<nsIMsgImapMailFolder> imapFolder =
753 do_QueryInterface(m_currentFolder);
754 if (imapFolder) {
755 bool hdrIsRead;
756 m_currentDB->IsRead(curKey, &hdrIsRead);
757 imapFolder->ChangePendingTotal(1);
758 if (!hdrIsRead) imapFolder->ChangePendingUnread(1);
759 imapFolder->AddMoveResultPseudoKey(curKey);
760 }
761 m_currentDB->DeleteMessage(curKey, nullptr, false);
762 }
763 }
764 }
765
766 if (deletedGhostMsgs) m_currentFolder->SummaryChanged();
767
768 m_CurrentKeys.Clear();
769 if (NS_FAILED(m_currentDB->ListAllOfflineOpIds(&m_CurrentKeys)) ||
770 m_CurrentKeys.IsEmpty()) {
771 ClearDB();
772 } else if (folderFlags & nsMsgFolderFlags::ImapBox) {
773 // if pseudo offline, falls through to playing ops back.
774 if (!m_pseudoOffline) {
775 // there are operations to playback so check uid validity
776 SetCurrentUIDValidity(0); // force initial invalid state
777 // do a lite select here and hook ourselves up as a listener.
778 nsCOMPtr<nsIMsgImapMailFolder> imapFolder =
779 do_QueryInterface(m_currentFolder, &rv);
780 if (imapFolder) rv = imapFolder->LiteSelect(this, m_window);
781 // this is async, we will be called again by OnStopRunningUrl.
782 return rv;
783 }
784 }
785 }
786 }
787
788 if (!m_currentDB) {
789 // only advance if we are doing all folders
790 if (!m_singleFolderToUpdate)
791 AdvanceToNextFolder();
792 else
793 m_currentFolder = nullptr; // force update of this folder now.
794 }
795 }
796
797 if (m_currentFolder) m_currentFolder->GetFlags(&folderFlags);
798 // do the current operation
799 if (m_currentDB) {
800 bool currentFolderFinished = false;
801 if (!folderInfo) m_currentDB->GetDBFolderInfo(getter_AddRefs(folderInfo));
802 // user canceled the lite select! if GetCurrentUIDValidity() == 0
803 if (folderInfo && (m_KeyIndex < m_CurrentKeys.Length()) &&
804 (m_pseudoOffline || (GetCurrentUIDValidity() != 0) ||
805 !(folderFlags & nsMsgFolderFlags::ImapBox))) {
806 int32_t curFolderUidValidity;
807 folderInfo->GetImapUidValidity(&curFolderUidValidity);
808 bool uidvalidityChanged =
809 (!m_pseudoOffline && folderFlags & nsMsgFolderFlags::ImapBox) &&
810 (GetCurrentUIDValidity() != curFolderUidValidity);
811 nsCOMPtr<nsIMsgOfflineImapOperation> currentOp;
812 if (uidvalidityChanged)
813 DeleteAllOfflineOpsForCurrentDB();
814 else
815 m_currentDB->GetOfflineOpForKey(m_CurrentKeys[m_KeyIndex], false,
816 getter_AddRefs(currentOp));
817
818 if (currentOp) {
819 nsOfflineImapOperationType opType;
820 currentOp->GetOperation(&opType);
821 // loop until we find the next db record that matches the current
822 // playback operation
823 while (currentOp && !(opType & mCurrentPlaybackOpType)) {
824 // remove operations with no type.
825 if (!opType) m_currentDB->RemoveOfflineOp(currentOp);
826 currentOp = nullptr;
827 ++m_KeyIndex;
828 if (m_KeyIndex < m_CurrentKeys.Length())
829 m_currentDB->GetOfflineOpForKey(m_CurrentKeys[m_KeyIndex], false,
830 getter_AddRefs(currentOp));
831 if (currentOp) currentOp->GetOperation(&opType);
832 }
833 // if we did not find a db record that matches the current playback
834 // operation, then move to the next playback operation and recurse.
835 if (!currentOp) {
836 // we are done with the current type
837 if (mCurrentPlaybackOpType ==
838 nsIMsgOfflineImapOperation::kFlagsChanged) {
839 mCurrentPlaybackOpType = nsIMsgOfflineImapOperation::kAddKeywords;
840 // recurse to deal with next type of operation
841 m_KeyIndex = 0;
842 ProcessNextOperation();
843 } else if (mCurrentPlaybackOpType ==
844 nsIMsgOfflineImapOperation::kAddKeywords) {
845 mCurrentPlaybackOpType =
846 nsIMsgOfflineImapOperation::kRemoveKeywords;
847 // recurse to deal with next type of operation
848 m_KeyIndex = 0;
849 ProcessNextOperation();
850 } else if (mCurrentPlaybackOpType ==
851 nsIMsgOfflineImapOperation::kRemoveKeywords) {
852 mCurrentPlaybackOpType = nsIMsgOfflineImapOperation::kMsgCopy;
853 // recurse to deal with next type of operation
854 m_KeyIndex = 0;
855 ProcessNextOperation();
856 } else if (mCurrentPlaybackOpType ==
857 nsIMsgOfflineImapOperation::kMsgCopy) {
858 mCurrentPlaybackOpType = nsIMsgOfflineImapOperation::kMsgMoved;
859 // recurse to deal with next type of operation
860 m_KeyIndex = 0;
861 ProcessNextOperation();
862 } else if (mCurrentPlaybackOpType ==
863 nsIMsgOfflineImapOperation::kMsgMoved) {
864 mCurrentPlaybackOpType = nsIMsgOfflineImapOperation::kAppendDraft;
865 // recurse to deal with next type of operation
866 m_KeyIndex = 0;
867 ProcessNextOperation();
868 } else if (mCurrentPlaybackOpType ==
869 nsIMsgOfflineImapOperation::kAppendDraft) {
870 mCurrentPlaybackOpType =
871 nsIMsgOfflineImapOperation::kAppendTemplate;
872 // recurse to deal with next type of operation
873 m_KeyIndex = 0;
874 ProcessNextOperation();
875 } else if (mCurrentPlaybackOpType ==
876 nsIMsgOfflineImapOperation::kAppendTemplate) {
877 mCurrentPlaybackOpType = nsIMsgOfflineImapOperation::kDeleteAllMsgs;
878 m_KeyIndex = 0;
879 ProcessNextOperation();
880 } else {
881 DeleteAllOfflineOpsForCurrentDB();
882 currentFolderFinished = true;
883 }
884
885 } else {
886 if (mCurrentPlaybackOpType ==
887 nsIMsgOfflineImapOperation::kFlagsChanged)
888 ProcessFlagOperation(currentOp);
889 else if (mCurrentPlaybackOpType ==
890 nsIMsgOfflineImapOperation::kAddKeywords ||
891 mCurrentPlaybackOpType ==
892 nsIMsgOfflineImapOperation::kRemoveKeywords)
893 ProcessKeywordOperation(currentOp);
894 else if (mCurrentPlaybackOpType ==
895 nsIMsgOfflineImapOperation::kMsgCopy)
896 ProcessCopyOperation(currentOp);
897 else if (mCurrentPlaybackOpType ==
898 nsIMsgOfflineImapOperation::kMsgMoved)
899 ProcessMoveOperation(currentOp);
900 else if (mCurrentPlaybackOpType ==
901 nsIMsgOfflineImapOperation::kAppendDraft)
902 ProcessAppendMsgOperation(currentOp,
903 nsIMsgOfflineImapOperation::kAppendDraft);
904 else if (mCurrentPlaybackOpType ==
905 nsIMsgOfflineImapOperation::kAppendTemplate)
906 ProcessAppendMsgOperation(
907 currentOp, nsIMsgOfflineImapOperation::kAppendTemplate);
908 else if (mCurrentPlaybackOpType ==
909 nsIMsgOfflineImapOperation::kDeleteAllMsgs) {
910 // empty trash is going to delete the db, so we'd better release the
911 // reference to the offline operation first.
912 currentOp = nullptr;
913 ProcessEmptyTrash();
914 } else
915 NS_WARNING("invalid playback op type");
916 }
917 } else
918 currentFolderFinished = true;
919 } else
920 currentFolderFinished = true;
921
922 if (currentFolderFinished) {
923 ClearDB();
924 if (!m_singleFolderToUpdate) {
925 AdvanceToNextFolder();
926 ProcessNextOperation();
927 return NS_OK;
928 }
929 m_currentFolder = nullptr;
930 }
931 }
932
933 if (!m_currentFolder && !m_mailboxupdatesStarted) {
934 m_mailboxupdatesStarted = true;
935
936 // if we are updating more than one folder then we need the iterator
937 if (!m_singleFolderToUpdate) {
938 m_currentServer = nullptr;
939 AdvanceToNextFolder();
940 }
941 if (m_singleFolderToUpdate) {
942 m_singleFolderToUpdate->ClearFlag(nsMsgFolderFlags::OfflineEvents);
943 m_singleFolderToUpdate->UpdateFolder(m_window);
944 }
945 }
946 // if we get here, then I *think* we're done. Not sure, though.
947 #ifdef DEBUG_bienvenu
948 printf("done with offline imap sync\n");
949 #endif
950 nsCOMPtr<nsIUrlListener> saveListener = m_listener;
951 m_listener = nullptr;
952
953 if (saveListener)
954 saveListener->OnStopRunningUrl(nullptr /* don't know url */, rv);
955 return rv;
956 }
957
DeleteAllOfflineOpsForCurrentDB()958 void nsImapOfflineSync::DeleteAllOfflineOpsForCurrentDB() {
959 m_KeyIndex = 0;
960 nsCOMPtr<nsIMsgOfflineImapOperation> currentOp;
961 m_currentDB->GetOfflineOpForKey(m_CurrentKeys[m_KeyIndex], false,
962 getter_AddRefs(currentOp));
963 while (currentOp) {
964 // NS_ASSERTION(currentOp->GetOperationFlags() == 0);
965 // delete any ops that have already played back
966 m_currentDB->RemoveOfflineOp(currentOp);
967 currentOp = nullptr;
968
969 if (++m_KeyIndex < m_CurrentKeys.Length())
970 m_currentDB->GetOfflineOpForKey(m_CurrentKeys[m_KeyIndex], false,
971 getter_AddRefs(currentOp));
972 }
973 m_currentDB->Commit(nsMsgDBCommitType::kLargeCommit);
974 // turn off nsMsgFolderFlags::OfflineEvents
975 if (m_currentFolder)
976 m_currentFolder->ClearFlag(nsMsgFolderFlags::OfflineEvents);
977 }
978
nsImapOfflineDownloader(nsIMsgWindow * aMsgWindow,nsIUrlListener * aListener)979 nsImapOfflineDownloader::nsImapOfflineDownloader(nsIMsgWindow* aMsgWindow,
980 nsIUrlListener* aListener)
981 : nsImapOfflineSync(aMsgWindow, aListener) {
982 // pause auto-sync service
983 nsresult rv;
984 nsCOMPtr<nsIAutoSyncManager> autoSyncMgr =
985 do_GetService(NS_AUTOSYNCMANAGER_CONTRACTID, &rv);
986 if (NS_SUCCEEDED(rv)) autoSyncMgr->Pause();
987 }
988
~nsImapOfflineDownloader()989 nsImapOfflineDownloader::~nsImapOfflineDownloader() {}
990
ProcessNextOperation()991 nsresult nsImapOfflineDownloader::ProcessNextOperation() {
992 nsresult rv = NS_OK;
993 m_mailboxupdatesStarted = true;
994
995 if (!m_mailboxupdatesFinished) {
996 if (AdvanceToNextServer()) {
997 nsCOMPtr<nsIMsgFolder> rootMsgFolder;
998 m_currentServer->GetRootFolder(getter_AddRefs(rootMsgFolder));
999 nsCOMPtr<nsIMsgFolder> inbox;
1000 if (rootMsgFolder) {
1001 // Update the INBOX first so the updates on the remaining
1002 // folders pickup the results of any filter moves.
1003 rootMsgFolder->GetFolderWithFlags(nsMsgFolderFlags::Inbox,
1004 getter_AddRefs(inbox));
1005 if (inbox) {
1006 nsCOMPtr<nsIMsgFolder> offlineImapFolder;
1007 nsCOMPtr<nsIMsgImapMailFolder> imapInbox = do_QueryInterface(inbox);
1008 if (imapInbox) {
1009 rootMsgFolder->GetFolderWithFlags(
1010 nsMsgFolderFlags::Offline, getter_AddRefs(offlineImapFolder));
1011 if (!offlineImapFolder) {
1012 // no imap folders configured for offline use - check if the
1013 // account is set up so that we always download inbox msg bodies
1014 // for offline use
1015 nsCOMPtr<nsIImapIncomingServer> imapServer =
1016 do_QueryInterface(m_currentServer);
1017 if (imapServer) {
1018 bool downloadBodiesOnGetNewMail = false;
1019 imapServer->GetDownloadBodiesOnGetNewMail(
1020 &downloadBodiesOnGetNewMail);
1021 if (downloadBodiesOnGetNewMail) offlineImapFolder = inbox;
1022 }
1023 }
1024 }
1025 // if this isn't an imap inbox, or we have an offline imap sub-folder,
1026 // then update the inbox. otherwise, it's an imap inbox for an account
1027 // with no folders configured for offline use, so just advance to the
1028 // next server.
1029 if (!imapInbox || offlineImapFolder) {
1030 // here we should check if this a pop3 server/inbox, and the user
1031 // doesn't want to download pop3 mail for offline use.
1032 if (!imapInbox) {
1033 }
1034 rv = inbox->GetNewMessages(m_window, this);
1035 if (NS_SUCCEEDED(rv)) return rv; // otherwise, fall through.
1036 }
1037 }
1038 }
1039 return ProcessNextOperation(); // recurse and do next server.
1040 }
1041 m_allServers.Clear();
1042 m_mailboxupdatesFinished = true;
1043 }
1044
1045 while (AdvanceToNextFolder()) {
1046 uint32_t folderFlags;
1047
1048 ClearDB();
1049 nsCOMPtr<nsIMsgImapMailFolder> imapFolder;
1050 if (m_currentFolder) imapFolder = do_QueryInterface(m_currentFolder);
1051 m_currentFolder->GetFlags(&folderFlags);
1052 // need to check if folder has offline events, or is configured for offline
1053 if (imapFolder && folderFlags & nsMsgFolderFlags::Offline &&
1054 !(folderFlags & nsMsgFolderFlags::Virtual)) {
1055 rv = m_currentFolder->DownloadAllForOffline(this, m_window);
1056 if (NS_SUCCEEDED(rv) || rv == NS_BINDING_ABORTED) return rv;
1057 // if this fails and the user didn't cancel/stop, fall through to code
1058 // that advances to next folder
1059 }
1060 }
1061 if (m_listener) m_listener->OnStopRunningUrl(nullptr, NS_OK);
1062 return rv;
1063 }
1064
OnStartCopy()1065 NS_IMETHODIMP nsImapOfflineSync::OnStartCopy() {
1066 return NS_ERROR_NOT_IMPLEMENTED;
1067 }
1068
1069 /* void OnProgress (in uint32_t aProgress, in uint32_t aProgressMax); */
OnProgress(uint32_t aProgress,uint32_t aProgressMax)1070 NS_IMETHODIMP nsImapOfflineSync::OnProgress(uint32_t aProgress,
1071 uint32_t aProgressMax) {
1072 return NS_ERROR_NOT_IMPLEMENTED;
1073 }
1074
1075 /* void SetMessageKey (in uint32_t aKey); */
SetMessageKey(uint32_t aKey)1076 NS_IMETHODIMP nsImapOfflineSync::SetMessageKey(uint32_t aKey) {
1077 return NS_ERROR_NOT_IMPLEMENTED;
1078 }
1079
1080 /* [noscript] void GetMessageId (in nsCString aMessageId); */
GetMessageId(nsACString & messageId)1081 NS_IMETHODIMP nsImapOfflineSync::GetMessageId(nsACString& messageId) {
1082 return NS_ERROR_NOT_IMPLEMENTED;
1083 }
1084
1085 /* void OnStopCopy (in nsresult aStatus); */
OnStopCopy(nsresult aStatus)1086 NS_IMETHODIMP nsImapOfflineSync::OnStopCopy(nsresult aStatus) {
1087 return OnStopRunningUrl(nullptr, aStatus);
1088 }
1089
ClearDB()1090 void nsImapOfflineSync::ClearDB() {
1091 m_currentOpsToClear.Clear();
1092 if (m_currentDB) m_currentDB->RemoveListener(this);
1093 m_currentDB = nullptr;
1094 }
1095
1096 NS_IMETHODIMP
OnHdrPropertyChanged(nsIMsgDBHdr * aHdrToChange,bool aPreChange,uint32_t * aStatus,nsIDBChangeListener * aInstigator)1097 nsImapOfflineSync::OnHdrPropertyChanged(nsIMsgDBHdr* aHdrToChange,
1098 bool aPreChange, uint32_t* aStatus,
1099 nsIDBChangeListener* aInstigator) {
1100 return NS_OK;
1101 }
1102
1103 NS_IMETHODIMP
OnHdrFlagsChanged(nsIMsgDBHdr * aHdrChanged,uint32_t aOldFlags,uint32_t aNewFlags,nsIDBChangeListener * aInstigator)1104 nsImapOfflineSync::OnHdrFlagsChanged(nsIMsgDBHdr* aHdrChanged,
1105 uint32_t aOldFlags, uint32_t aNewFlags,
1106 nsIDBChangeListener* aInstigator) {
1107 return NS_OK;
1108 }
1109
1110 NS_IMETHODIMP
OnHdrDeleted(nsIMsgDBHdr * aHdrChanged,nsMsgKey aParentKey,int32_t aFlags,nsIDBChangeListener * aInstigator)1111 nsImapOfflineSync::OnHdrDeleted(nsIMsgDBHdr* aHdrChanged, nsMsgKey aParentKey,
1112 int32_t aFlags,
1113 nsIDBChangeListener* aInstigator) {
1114 return NS_OK;
1115 }
1116
1117 NS_IMETHODIMP
OnHdrAdded(nsIMsgDBHdr * aHdrAdded,nsMsgKey aParentKey,int32_t aFlags,nsIDBChangeListener * aInstigator)1118 nsImapOfflineSync::OnHdrAdded(nsIMsgDBHdr* aHdrAdded, nsMsgKey aParentKey,
1119 int32_t aFlags,
1120 nsIDBChangeListener* aInstigator) {
1121 return NS_OK;
1122 }
1123
1124 /* void OnParentChanged (in nsMsgKey aKeyChanged, in nsMsgKey oldParent, in
1125 * nsMsgKey newParent, in nsIDBChangeListener aInstigator); */
1126 NS_IMETHODIMP
OnParentChanged(nsMsgKey aKeyChanged,nsMsgKey oldParent,nsMsgKey newParent,nsIDBChangeListener * aInstigator)1127 nsImapOfflineSync::OnParentChanged(nsMsgKey aKeyChanged, nsMsgKey oldParent,
1128 nsMsgKey newParent,
1129 nsIDBChangeListener* aInstigator) {
1130 return NS_OK;
1131 }
1132
1133 /* void OnAnnouncerGoingAway (in nsIDBChangeAnnouncer instigator); */
1134 NS_IMETHODIMP
OnAnnouncerGoingAway(nsIDBChangeAnnouncer * instigator)1135 nsImapOfflineSync::OnAnnouncerGoingAway(nsIDBChangeAnnouncer* instigator) {
1136 ClearDB();
1137 return NS_OK;
1138 }
1139
OnEvent(nsIMsgDatabase * aDB,const char * aEvent)1140 NS_IMETHODIMP nsImapOfflineSync::OnEvent(nsIMsgDatabase* aDB,
1141 const char* aEvent) {
1142 return NS_OK;
1143 }
1144
1145 /* void OnReadChanged (in nsIDBChangeListener instigator); */
1146 NS_IMETHODIMP
OnReadChanged(nsIDBChangeListener * instigator)1147 nsImapOfflineSync::OnReadChanged(nsIDBChangeListener* instigator) {
1148 return NS_OK;
1149 }
1150
1151 /* void OnJunkScoreChanged (in nsIDBChangeListener instigator); */
1152 NS_IMETHODIMP
OnJunkScoreChanged(nsIDBChangeListener * instigator)1153 nsImapOfflineSync::OnJunkScoreChanged(nsIDBChangeListener* instigator) {
1154 return NS_OK;
1155 }
1156