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