Back to index

lightning-sunbird  0.9+nobinonly
nsImapOfflineSync.cpp
Go to the documentation of this file.
00001 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
00002 /* ***** BEGIN LICENSE BLOCK *****
00003  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
00004  *
00005  * The contents of this file are subject to the Mozilla Public License Version
00006  * 1.1 (the "License"); you may not use this file except in compliance with
00007  * the License. You may obtain a copy of the License at
00008  * http://www.mozilla.org/MPL/
00009  *
00010  * Software distributed under the License is distributed on an "AS IS" basis,
00011  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
00012  * for the specific language governing rights and limitations under the
00013  * License.
00014  *
00015  * The Original Code is mozilla.org code.
00016  *
00017  * The Initial Developer of the Original Code is
00018  * Netscape Communications Corporation.
00019  * Portions created by the Initial Developer are Copyright (C) 2001
00020  * the Initial Developer. All Rights Reserved.
00021  *
00022  * Contributor(s):
00023  *
00024  * Alternatively, the contents of this file may be used under the terms of
00025  * either of the GNU General Public License Version 2 or later (the "GPL"),
00026  * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
00027  * in which case the provisions of the GPL or the LGPL are applicable instead
00028  * of those above. If you wish to allow use of your version of this file only
00029  * under the terms of either the GPL or the LGPL, and not to allow others to
00030  * use your version of this file under the terms of the MPL, indicate your
00031  * decision by deleting the provisions above and replace them with the notice
00032  * and other provisions required by the GPL or the LGPL. If you do not delete
00033  * the provisions above, a recipient may use your version of this file under
00034  * the terms of any one of the MPL, the GPL or the LGPL.
00035  *
00036  * ***** END LICENSE BLOCK ***** */
00037 
00038 #include "msgCore.h"
00039 #include "netCore.h"
00040 #include "nsImapOfflineSync.h"
00041 #include "nsImapMailFolder.h"
00042 #include "nsMsgFolderFlags.h"
00043 #include "nsXPIDLString.h"
00044 #include "nsIRDFService.h"
00045 #include "nsMsgBaseCID.h"
00046 #include "nsRDFCID.h"
00047 #include "nsIMsgAccountManager.h"
00048 #include "nsINntpIncomingServer.h"
00049 #include "nsIRequestObserver.h"
00050 #include "nsSpecialSystemDirectory.h"
00051 #include "nsIFileStream.h"
00052 #include "nsIMsgCopyService.h"
00053 #include "nsImapProtocol.h"
00054 
00055 static NS_DEFINE_CID(kRDFServiceCID, NS_RDFSERVICE_CID);
00056 
00057 NS_IMPL_ISUPPORTS2(nsImapOfflineSync, nsIUrlListener, nsIMsgCopyServiceListener)
00058 
00059 nsImapOfflineSync::nsImapOfflineSync(nsIMsgWindow *window, nsIUrlListener *listener, nsIMsgFolder *singleFolderOnly)
00060 {
00061   m_singleFolderToUpdate = singleFolderOnly;
00062   m_window = window;
00063   // not the perfect place for this, but I think it will work.
00064   if (m_window)
00065     m_window->SetStopped(PR_FALSE);
00066 
00067   mCurrentPlaybackOpType = nsIMsgOfflineImapOperation::kFlagsChanged;
00068   m_mailboxupdatesStarted = PR_FALSE;
00069   m_mailboxupdatesFinished = PR_FALSE;
00070   m_createdOfflineFolders = PR_FALSE;
00071   m_pseudoOffline = PR_FALSE;
00072   m_KeyIndex = 0;
00073   mCurrentUIDValidity = nsMsgKey_None;
00074   m_listener = listener;
00075 }
00076 
00077 nsImapOfflineSync::~nsImapOfflineSync()
00078 {
00079 }
00080 
00081 void      nsImapOfflineSync::SetWindow(nsIMsgWindow *window)
00082 {
00083   m_window = window;
00084 }
00085 
00086 NS_IMETHODIMP nsImapOfflineSync::OnStartRunningUrl(nsIURI* url)
00087 {
00088     return NS_OK;
00089 }
00090 
00091 NS_IMETHODIMP
00092 nsImapOfflineSync::OnStopRunningUrl(nsIURI* url, nsresult exitCode)
00093 {
00094   nsresult rv = exitCode;
00095 
00096   // where do we make sure this gets cleared when we start running urls?
00097   PRBool stopped = PR_FALSE;
00098   if (m_window)
00099     m_window->GetStopped(&stopped);
00100 
00101   if (m_curTempFile)
00102   {
00103     m_curTempFile->Delete(PR_FALSE);
00104     m_curTempFile = nsnull;
00105   }
00106   if (stopped)
00107   {
00108     if (m_listener)
00109       m_listener->OnStopRunningUrl(url, NS_BINDING_ABORTED);
00110     return NS_OK;
00111   }
00112   nsCOMPtr<nsIImapUrl> imapUrl = do_QueryInterface(url);
00113 
00114   if (imapUrl)
00115     nsImapProtocol::LogImapUrl(NS_SUCCEEDED(rv) ? "offline imap url succeeded " : "offline imap url failed ", imapUrl);
00116   // NS_BINDING_ABORTED is used for the user pressing stop, which
00117   // should cause us to abort the offline process. Other errors
00118   // should allow us to continue.
00119   if (NS_SUCCEEDED(exitCode))
00120     rv = ProcessNextOperation();
00121   // else if it's a non-stop error, and we're doing multiple folders,
00122   // go to the next folder.
00123   else if (!m_singleFolderToUpdate)
00124   {
00125     rv = AdvanceToNextFolder();
00126     if (NS_SUCCEEDED(rv))
00127       rv = ProcessNextOperation();
00128   }
00129 
00130   return rv;
00131 }
00132 
00133 // leaves m_currentServer at the next imap or local mail "server" that
00134 // might have offline events to playback. If no more servers,
00135 // m_currentServer will be left at nsnull.
00136 // Also, sets up m_serverEnumerator to enumerate over the server
00137 nsresult nsImapOfflineSync::AdvanceToNextServer()
00138 {
00139   nsresult rv;
00140 
00141   if (!m_allServers)
00142   {
00143     NS_ASSERTION(!m_currentServer, "this shouldn't be set");
00144     m_currentServer = nsnull;
00145     nsCOMPtr<nsIMsgAccountManager> accountManager = 
00146              do_GetService(NS_MSGACCOUNTMANAGER_CONTRACTID, &rv);
00147     NS_ASSERTION(accountManager && NS_SUCCEEDED(rv), "couldn't get account mgr");
00148     if (!accountManager || NS_FAILED(rv)) return rv;
00149 
00150     rv = accountManager->GetAllServers(getter_AddRefs(m_allServers));
00151     NS_ENSURE_SUCCESS(rv, rv);
00152   }
00153   PRUint32 serverIndex = (m_currentServer) ? m_allServers->IndexOf(m_currentServer) + 1 : 0;
00154   m_currentServer = nsnull;
00155   PRUint32 numServers; 
00156   m_allServers->Count(&numServers);
00157   nsCOMPtr <nsIMsgFolder> rootFolder;
00158 
00159   while (serverIndex < numServers)
00160   {
00161     nsCOMPtr <nsISupports> serverSupports = getter_AddRefs(m_allServers->ElementAt(serverIndex));
00162     serverIndex++;
00163 
00164     nsCOMPtr<nsIMsgIncomingServer> server = do_QueryInterface(serverSupports);
00165     nsCOMPtr <nsINntpIncomingServer> newsServer = do_QueryInterface(server);
00166     if (newsServer) // news servers aren't involved in offline imap
00167       continue;
00168     if (server)
00169     {
00170       m_currentServer = server;
00171       server->GetRootFolder(getter_AddRefs(rootFolder));
00172       if (rootFolder)
00173       {
00174         NS_NewISupportsArray(getter_AddRefs(m_allFolders));
00175         rv = rootFolder->ListDescendents(m_allFolders);
00176         if (NS_SUCCEEDED(rv))
00177           m_allFolders->Enumerate(getter_AddRefs(m_serverEnumerator));
00178         if (NS_SUCCEEDED(rv) && m_serverEnumerator)
00179         {
00180           rv = m_serverEnumerator->First();
00181           if (NS_SUCCEEDED(rv))
00182             break;
00183         }
00184       }
00185     }
00186   }
00187   return rv;
00188 }
00189 
00190 nsresult nsImapOfflineSync::AdvanceToNextFolder()
00191 {
00192   nsresult rv;
00193        // we always start by changing flags
00194   mCurrentPlaybackOpType = nsIMsgOfflineImapOperation::kFlagsChanged;
00195        
00196   if (m_currentFolder)
00197   {
00198     m_currentFolder->SetMsgDatabase(nsnull);
00199     m_currentFolder = nsnull;
00200   }
00201 
00202   if (!m_currentServer)
00203      rv = AdvanceToNextServer();
00204   else
00205     rv = m_serverEnumerator->Next();
00206   if (NS_FAILED(rv))
00207     rv = AdvanceToNextServer();
00208 
00209   if (NS_SUCCEEDED(rv) && m_serverEnumerator)
00210   {
00211     nsCOMPtr <nsISupports> supports;
00212     rv = m_serverEnumerator->CurrentItem(getter_AddRefs(supports));
00213     m_currentFolder = do_QueryInterface(supports);
00214   }
00215   return rv;
00216 }
00217 
00218 void nsImapOfflineSync::AdvanceToFirstIMAPFolder()
00219 {
00220   nsresult rv;
00221   m_currentServer = nsnull;
00222   nsCOMPtr <nsIMsgImapMailFolder> imapFolder;
00223   do
00224   {
00225     rv = AdvanceToNextFolder();
00226     if (m_currentFolder)
00227       imapFolder = do_QueryInterface(m_currentFolder);
00228   }
00229   while (NS_SUCCEEDED(rv) && m_currentFolder && !imapFolder);
00230 }
00231 
00232 void nsImapOfflineSync::ProcessFlagOperation(nsIMsgOfflineImapOperation *op)
00233 {
00234   nsCOMPtr <nsIMsgOfflineImapOperation> currentOp = op;
00235   nsMsgKeyArray matchingFlagKeys;
00236   PRUint32 currentKeyIndex = m_KeyIndex;
00237 
00238   imapMessageFlagsType matchingFlags;
00239   currentOp->GetNewFlags(&matchingFlags);
00240   imapMessageFlagsType flagOperation;
00241   imapMessageFlagsType newFlags;
00242   PRBool flagsMatch = PR_TRUE;     
00243   do
00244   {    // loop for all messsages with the same flags
00245     if (flagsMatch)
00246     {
00247       nsMsgKey curKey;
00248       currentOp->GetMessageKey(&curKey);
00249       matchingFlagKeys.Add(curKey);
00250       currentOp->ClearOperation(nsIMsgOfflineImapOperation::kFlagsChanged);
00251     }
00252     currentOp = nsnull;
00253     if (++currentKeyIndex < m_CurrentKeys.GetSize())
00254       m_currentDB->GetOfflineOpForKey(m_CurrentKeys[currentKeyIndex], PR_FALSE,
00255         getter_AddRefs(currentOp));
00256     if (currentOp)
00257     {
00258       // init the operation in the currentOp, so we don't crunch it if&when we
00259       // call ClearOperation above.
00260       nsOfflineImapOperationType operation;
00261       currentOp->GetOperation(&operation);
00262       currentOp->GetFlagOperation(&flagOperation);
00263       currentOp->GetNewFlags(&newFlags);
00264     }
00265     flagsMatch = (flagOperation & nsIMsgOfflineImapOperation::kFlagsChanged)
00266                   && (newFlags == matchingFlags);
00267   } while (currentOp);
00268        
00269   if (matchingFlagKeys.GetSize() > 0)
00270   {
00271     nsCAutoString uids;
00272     nsImapMailFolder::AllocateUidStringFromKeys(matchingFlagKeys.GetArray(), matchingFlagKeys.GetSize(), uids);
00273     PRUint32 curFolderFlags;
00274     m_currentFolder->GetFlags(&curFolderFlags);
00275 
00276     if (uids.get() && (curFolderFlags & MSG_FOLDER_FLAG_IMAPBOX)) 
00277     {
00278       nsresult rv = NS_OK;
00279       nsCOMPtr <nsIMsgImapMailFolder> imapFolder = do_QueryInterface(m_currentFolder);
00280       nsCOMPtr <nsIURI> uriToSetFlags;
00281       if (imapFolder)
00282       {
00283         rv = imapFolder->SetImapFlags(uids.get(), matchingFlags, getter_AddRefs(uriToSetFlags));
00284         if (NS_SUCCEEDED(rv) && uriToSetFlags)
00285         {
00286           nsCOMPtr <nsIMsgMailNewsUrl> mailnewsUrl = do_QueryInterface(uriToSetFlags);
00287           if (mailnewsUrl)
00288             mailnewsUrl->RegisterListener(this);
00289         }
00290       }
00291     }
00292   }
00293   else
00294     ProcessNextOperation();
00295 }
00296 
00297 void nsImapOfflineSync::ProcessKeywordOperation(nsIMsgOfflineImapOperation *op)
00298 {
00299   nsCOMPtr <nsIMsgOfflineImapOperation> currentOp = op;
00300   nsMsgKeyArray matchingKeywordKeys;
00301   PRUint32 currentKeyIndex = m_KeyIndex;
00302 
00303   nsXPIDLCString keywords;
00304   if (mCurrentPlaybackOpType == nsIMsgOfflineImapOperation::kAddKeywords)
00305     currentOp->GetKeywordsToAdd(getter_Copies(keywords));
00306   else
00307     currentOp->GetKeywordsToRemove(getter_Copies(keywords));
00308   PRBool keywordsMatch = PR_TRUE;  
00309   do
00310   {    // loop for all messsages with the same keywords
00311     if (keywordsMatch)
00312     {
00313       nsMsgKey curKey;
00314       currentOp->GetMessageKey(&curKey);
00315       matchingKeywordKeys.Add(curKey);
00316       currentOp->ClearOperation(mCurrentPlaybackOpType);
00317     }
00318     currentOp = nsnull;
00319     if (++currentKeyIndex < m_CurrentKeys.GetSize())
00320       m_currentDB->GetOfflineOpForKey(m_CurrentKeys[currentKeyIndex], PR_FALSE,
00321         getter_AddRefs(currentOp));
00322     if (currentOp)
00323     {
00324       nsXPIDLCString curOpKeywords;
00325       nsOfflineImapOperationType operation;
00326       currentOp->GetOperation(&operation);
00327       if (mCurrentPlaybackOpType == nsIMsgOfflineImapOperation::kAddKeywords)
00328         currentOp->GetKeywordsToAdd(getter_Copies(curOpKeywords));
00329       else
00330         currentOp->GetKeywordsToRemove(getter_Copies(curOpKeywords));
00331       keywordsMatch = (operation & mCurrentPlaybackOpType)
00332                   && (curOpKeywords.Equals(keywords));
00333     }
00334   } while (currentOp);
00335        
00336   if (matchingKeywordKeys.GetSize() > 0)
00337   {
00338     PRUint32 curFolderFlags;
00339     m_currentFolder->GetFlags(&curFolderFlags);
00340 
00341     if (curFolderFlags & MSG_FOLDER_FLAG_IMAPBOX)
00342     {
00343       nsresult rv = NS_OK;
00344       nsCOMPtr <nsIMsgImapMailFolder> imapFolder = do_QueryInterface(m_currentFolder);
00345       nsCOMPtr <nsIURI> uriToStoreCustomKeywords;
00346       if (imapFolder)
00347       {
00348         rv = imapFolder->StoreCustomKeywords(m_window, 
00349                     (mCurrentPlaybackOpType == nsIMsgOfflineImapOperation::kAddKeywords) ? keywords.get() : nsnull, 
00350                     (mCurrentPlaybackOpType == nsIMsgOfflineImapOperation::kRemoveKeywords) ? keywords.get() : nsnull, 
00351                     matchingKeywordKeys.GetArray(), 
00352                     matchingKeywordKeys.GetSize(), getter_AddRefs(uriToStoreCustomKeywords));
00353         if (NS_SUCCEEDED(rv) && uriToStoreCustomKeywords)
00354         {
00355           nsCOMPtr <nsIMsgMailNewsUrl> mailnewsUrl = do_QueryInterface(uriToStoreCustomKeywords);
00356           if (mailnewsUrl)
00357             mailnewsUrl->RegisterListener(this);
00358         }
00359       }
00360     }
00361   }
00362   else
00363     ProcessNextOperation();
00364 }
00365 
00366 
00367 
00368 void
00369 nsImapOfflineSync::ProcessAppendMsgOperation(nsIMsgOfflineImapOperation *currentOp, PRInt32 opType)
00370 {
00371   nsCOMPtr <nsIMsgDBHdr> mailHdr;
00372   nsMsgKey msgKey;
00373   currentOp->GetMessageKey(&msgKey);
00374   nsresult rv = m_currentDB->GetMsgHdrForKey(msgKey, getter_AddRefs(mailHdr)); 
00375   if (NS_SUCCEEDED(rv) && mailHdr)
00376   {
00377     nsMsgKey messageOffset;
00378     PRUint32 messageSize;
00379     mailHdr->GetMessageOffset(&messageOffset);
00380     mailHdr->GetOfflineMessageSize(&messageSize);
00381     nsCOMPtr <nsIFileSpec>  tempFileSpec;
00382     nsSpecialSystemDirectory tmpFileSpec(nsSpecialSystemDirectory::OS_TemporaryDirectory);
00383     
00384     tmpFileSpec += "nscpmsg.txt";
00385     tmpFileSpec.MakeUnique();
00386     rv = NS_NewFileSpecWithSpec(tmpFileSpec,
00387       getter_AddRefs(tempFileSpec));
00388     if (tempFileSpec)
00389     {
00390       nsCOMPtr <nsIOutputStream> outputStream;
00391       rv = tempFileSpec->GetOutputStream(getter_AddRefs(outputStream));
00392       if (NS_SUCCEEDED(rv) && outputStream)
00393       {
00394         nsXPIDLCString moveDestination;
00395         currentOp->GetDestinationFolderURI(getter_Copies(moveDestination));
00396         nsCOMPtr<nsIRDFService> rdf(do_GetService(kRDFServiceCID, &rv));
00397         nsCOMPtr<nsIRDFResource> res;
00398         if (NS_FAILED(rv)) return ; // ### return error code.
00399         rv = rdf->GetResource(moveDestination, getter_AddRefs(res));
00400         if (NS_SUCCEEDED(rv))
00401         {
00402           nsCOMPtr<nsIMsgFolder> destFolder(do_QueryInterface(res, &rv));
00403           if (NS_SUCCEEDED(rv) && destFolder)
00404           {
00405             nsCOMPtr <nsIInputStream> offlineStoreInputStream;
00406             rv = destFolder->GetOfflineStoreInputStream(getter_AddRefs(offlineStoreInputStream));
00407             if (NS_SUCCEEDED(rv) && offlineStoreInputStream)
00408             {
00409               nsCOMPtr<nsISeekableStream> seekStream = do_QueryInterface(offlineStoreInputStream);
00410               NS_ASSERTION(seekStream, "non seekable stream - can't read from offline msg");
00411               if (seekStream)
00412               {
00413                 rv = seekStream->Seek(PR_SEEK_SET, messageOffset);
00414                 if (NS_SUCCEEDED(rv))
00415                 {
00416                   // now, copy the dest folder offline store msg to the temp file
00417                   PRInt32 inputBufferSize = 10240;
00418                   char *inputBuffer = nsnull;
00419                   
00420                   while (!inputBuffer && (inputBufferSize >= 512))
00421                   {
00422                     inputBuffer = (char *) PR_Malloc(inputBufferSize);
00423                     if (!inputBuffer)
00424                       inputBufferSize /= 2;
00425                   }
00426                   PRInt32 bytesLeft;
00427                   PRUint32 bytesRead, bytesWritten;
00428                   bytesLeft = messageSize;
00429                   rv = NS_OK;
00430                   while (bytesLeft > 0 && NS_SUCCEEDED(rv))
00431                   {
00432                     PRInt32 bytesToRead = PR_MIN(inputBufferSize, bytesLeft);
00433                     rv = offlineStoreInputStream->Read(inputBuffer, bytesToRead, &bytesRead);
00434                     if (NS_SUCCEEDED(rv) && bytesRead > 0)
00435                     {
00436                       rv = outputStream->Write(inputBuffer, bytesRead, &bytesWritten);
00437                       NS_ASSERTION(bytesWritten == bytesRead, "wrote out correct number of bytes");
00438                     }
00439                     else
00440                       break;
00441                     bytesLeft -= bytesRead;
00442                   }
00443                   outputStream->Flush();
00444                   tempFileSpec->CloseStream();
00445                   if (NS_SUCCEEDED(rv))
00446                   {
00447                     m_curTempFile = tempFileSpec;
00448                     nsCOMPtr<nsIMsgCopyService> copyService = do_GetService(NS_MSGCOPYSERVICE_CONTRACTID);
00449                     if (copyService)
00450                       rv = copyService->CopyFileMessage(tempFileSpec, destFolder,
00451                       /* nsIMsgDBHdr* msgToReplace */ nsnull,
00452                       PR_TRUE /* isDraftOrTemplate */,
00453                       0, // new msg flags - are there interesting flags here?
00454                         this,
00455                         m_window);
00456                   }
00457                   else
00458                     tempFileSpec->Delete(PR_FALSE);
00459                 }
00460                 currentOp->ClearOperation(nsIMsgOfflineImapOperation::kAppendDraft);
00461                 m_currentDB->DeleteHeader(mailHdr, nsnull, PR_TRUE, PR_TRUE);
00462               }
00463             }
00464             // want to close in failure case too
00465             tempFileSpec->CloseStream();
00466           }
00467         }
00468       }
00469     }
00470   }
00471 }
00472 
00473 
00474 void nsImapOfflineSync::ProcessMoveOperation(nsIMsgOfflineImapOperation *op)
00475 {
00476   nsMsgKeyArray matchingFlagKeys ;
00477   PRUint32 currentKeyIndex = m_KeyIndex;
00478   nsXPIDLCString moveDestination;
00479   op->GetDestinationFolderURI(getter_Copies(moveDestination));
00480   PRBool moveMatches = PR_TRUE;
00481   nsCOMPtr <nsIMsgOfflineImapOperation> currentOp = op;
00482   do 
00483   {    // loop for all messsages with the same destination
00484     if (moveMatches)
00485     {
00486       nsMsgKey curKey;
00487       currentOp->GetMessageKey(&curKey);
00488       matchingFlagKeys.Add(curKey);
00489       currentOp->ClearOperation(nsIMsgOfflineImapOperation::kMsgMoved);
00490     }
00491     currentOp = nsnull;
00492     
00493     if (++currentKeyIndex < m_CurrentKeys.GetSize())
00494     {
00495       nsXPIDLCString nextDestination;
00496       nsresult rv = m_currentDB->GetOfflineOpForKey(m_CurrentKeys[currentKeyIndex], PR_FALSE, getter_AddRefs(currentOp));
00497       moveMatches = PR_FALSE;
00498       if (NS_SUCCEEDED(rv) && currentOp)
00499       {
00500         nsOfflineImapOperationType opType; 
00501         currentOp->GetOperation(&opType);
00502         if (opType & nsIMsgOfflineImapOperation::kMsgMoved)
00503         {
00504           currentOp->GetDestinationFolderURI(getter_Copies(nextDestination));
00505           moveMatches = nsCRT::strcmp(moveDestination, nextDestination) == 0;
00506         }
00507       }
00508     }
00509   } 
00510   while (currentOp);
00511   
00512   nsresult rv;
00513   
00514   nsCOMPtr<nsIRDFResource> res;
00515   nsCOMPtr<nsIRDFService> rdf(do_GetService(kRDFServiceCID, &rv));
00516   if (NS_FAILED(rv)) return ; // ### return error code.
00517   rv = rdf->GetResource(moveDestination, getter_AddRefs(res));
00518   if (NS_SUCCEEDED(rv))
00519   {
00520     nsCOMPtr<nsIMsgFolder> destFolder(do_QueryInterface(res, &rv));
00521     if (NS_SUCCEEDED(rv) && destFolder)
00522     {
00523       nsCOMPtr <nsIMsgImapMailFolder> imapFolder = do_QueryInterface(m_currentFolder);
00524       if (imapFolder && DestFolderOnSameServer(destFolder))
00525       {
00526         rv = imapFolder->ReplayOfflineMoveCopy(matchingFlagKeys.GetArray(), matchingFlagKeys.GetSize(), PR_TRUE, destFolder,
00527           this, m_window);
00528       }
00529       else
00530       {
00531         nsCOMPtr <nsISupportsArray> messages = do_CreateInstance(NS_SUPPORTSARRAY_CONTRACTID, &rv);
00532         if (messages && NS_SUCCEEDED(rv))
00533         {
00534           NS_NewISupportsArray(getter_AddRefs(messages));
00535           for (PRUint32 keyIndex = 0; keyIndex < matchingFlagKeys.GetSize(); keyIndex++)
00536           {
00537             nsCOMPtr<nsIMsgDBHdr> mailHdr = nsnull;
00538             rv = m_currentFolder->GetMessageHeader(matchingFlagKeys.ElementAt(keyIndex), getter_AddRefs(mailHdr));
00539             if (NS_SUCCEEDED(rv) && mailHdr)
00540             {
00541               PRUint32 msgSize;
00542               // in case of a move, the header has already been deleted,
00543               // so we've really got a fake header. We need to get its flags and
00544               // size from the offline op to have any chance of doing the move.
00545               mailHdr->GetMessageSize(&msgSize);
00546               if (!msgSize)
00547               {
00548                 imapMessageFlagsType newImapFlags;
00549                 PRUint32 msgFlags = 0;
00550                 op->GetMsgSize(&msgSize);
00551                 op->GetNewFlags(&newImapFlags);
00552                 // first three bits are the same
00553                 msgFlags |= (newImapFlags & 0x07);
00554                 if (newImapFlags & kImapMsgForwardedFlag)
00555                   msgFlags |= MSG_FLAG_FORWARDED;
00556                 mailHdr->SetFlags(msgFlags);
00557                 mailHdr->SetMessageSize(msgSize);
00558               }
00559               nsCOMPtr<nsISupports> iSupports;
00560               iSupports = do_QueryInterface(mailHdr);
00561               messages->AppendElement(iSupports);
00562             }
00563           }
00564           nsCOMPtr<nsIMsgCopyService> copyService = do_GetService(NS_MSGCOPYSERVICE_CONTRACTID, &rv);
00565           if (copyService)
00566             copyService->CopyMessages(m_currentFolder, messages, destFolder, PR_TRUE, this, m_window, PR_FALSE);
00567         }
00568       }
00569     }
00570   }
00571 }
00572 
00573 // I'm tempted to make this a method on nsIMsgFolder, but that interface
00574 // is already so huge, and there are only a few places in the code that do this.
00575 // If there end up to be more places that need this, then we can reconsider.
00576 PRBool nsImapOfflineSync::DestFolderOnSameServer(nsIMsgFolder *destFolder)
00577 {
00578   nsCOMPtr <nsIMsgIncomingServer> srcServer;
00579   nsCOMPtr <nsIMsgIncomingServer> dstServer;
00580 
00581   PRBool sameServer = PR_FALSE;
00582   if (NS_SUCCEEDED(m_currentFolder->GetServer(getter_AddRefs(srcServer))) 
00583     && NS_SUCCEEDED(destFolder->GetServer(getter_AddRefs(dstServer))))
00584     dstServer->Equals(srcServer, &sameServer);
00585   return sameServer;
00586 }
00587 
00588 void nsImapOfflineSync::ProcessCopyOperation(nsIMsgOfflineImapOperation *currentOp)
00589 {
00590   nsMsgKeyArray matchingFlagKeys;
00591   PRUint32 currentKeyIndex = m_KeyIndex;
00592   nsXPIDLCString copyDestination;
00593   currentOp->GetCopyDestination(0, getter_Copies(copyDestination));
00594   PRBool copyMatches = PR_TRUE;
00595   
00596   do { // loop for all messsages with the same destination
00597     if (copyMatches)
00598     {
00599       nsMsgKey curKey;
00600       currentOp->GetMessageKey(&curKey);
00601       matchingFlagKeys.Add(curKey);
00602       currentOp->ClearOperation(nsIMsgOfflineImapOperation::kMsgCopy);
00603     }
00604     currentOp = nsnull;
00605     
00606     if (++currentKeyIndex < m_CurrentKeys.GetSize())
00607     {
00608       nsXPIDLCString nextDestination;
00609       nsresult rv = m_currentDB->GetOfflineOpForKey(m_CurrentKeys[currentKeyIndex], PR_FALSE, &currentOp);
00610       copyMatches = PR_FALSE;
00611       if (NS_SUCCEEDED(rv) && currentOp)
00612       {
00613         nsOfflineImapOperationType opType; 
00614         currentOp->GetOperation(&opType);
00615         if (opType & nsIMsgOfflineImapOperation::kMsgCopy)
00616         {
00617           currentOp->GetCopyDestination(0, getter_Copies(nextDestination));
00618           copyMatches = nsCRT::strcmp(copyDestination, nextDestination) == 0;
00619         }
00620       }
00621     }
00622   } 
00623   while (currentOp);
00624        
00625   nsCAutoString uids;
00626 
00627   nsresult rv;
00628 
00629   nsCOMPtr<nsIRDFResource> res;
00630   nsCOMPtr<nsIRDFService> rdf(do_GetService(kRDFServiceCID, &rv));
00631   if (NS_FAILED(rv)) return ; // ### return error code.
00632   rv = rdf->GetResource(copyDestination, getter_AddRefs(res));
00633   if (NS_SUCCEEDED(rv))
00634   {
00635     nsCOMPtr<nsIMsgFolder> destFolder(do_QueryInterface(res, &rv));
00636     if (NS_SUCCEEDED(rv) && destFolder)
00637     {
00638       nsCOMPtr <nsIMsgImapMailFolder> imapFolder = do_QueryInterface(m_currentFolder);
00639       if (imapFolder && DestFolderOnSameServer(destFolder))
00640       {
00641         rv = imapFolder->ReplayOfflineMoveCopy(matchingFlagKeys.GetArray(), matchingFlagKeys.GetSize(), PR_FALSE, destFolder,
00642                        this, m_window);
00643       }
00644       else
00645       {
00646         nsCOMPtr <nsISupportsArray> messages = do_CreateInstance(NS_SUPPORTSARRAY_CONTRACTID, &rv);
00647         if (messages && NS_SUCCEEDED(rv))
00648         {
00649           NS_NewISupportsArray(getter_AddRefs(messages));
00650           for (PRUint32 keyIndex = 0; keyIndex < matchingFlagKeys.GetSize(); keyIndex++)
00651           {
00652             nsCOMPtr<nsIMsgDBHdr> mailHdr = nsnull;
00653             rv = m_currentFolder->GetMessageHeader(matchingFlagKeys.ElementAt(keyIndex), getter_AddRefs(mailHdr));
00654             if (NS_SUCCEEDED(rv) && mailHdr)
00655             {
00656               nsCOMPtr<nsISupports> iSupports;
00657               iSupports = do_QueryInterface(mailHdr);
00658               messages->AppendElement(iSupports);
00659             }
00660           }
00661           nsCOMPtr<nsIMsgCopyService> copyService = do_GetService(NS_MSGCOPYSERVICE_CONTRACTID, &rv);
00662           if (copyService)
00663             copyService->CopyMessages(m_currentFolder, messages, destFolder, PR_FALSE, this, m_window, PR_FALSE);
00664         }
00665       }
00666     }
00667   }
00668 }
00669 
00670 void nsImapOfflineSync::ProcessEmptyTrash(nsIMsgOfflineImapOperation *currentOp)
00671 {
00672   m_currentFolder->EmptyTrash(m_window, this);
00673   // don't need to remove the current op because emptying trash will
00674   // delete the database.
00675   m_currentDB = nsnull;     // empty trash deletes the database?
00676 }
00677 
00678 // returns PR_TRUE if we found a folder to create, PR_FALSE if we're done creating folders.
00679 PRBool nsImapOfflineSync::CreateOfflineFolders()
00680 {
00681   while (m_currentFolder)
00682   {
00683     PRUint32 flags;
00684     m_currentFolder->GetFlags(&flags);
00685     PRBool offlineCreate = (flags & MSG_FOLDER_FLAG_CREATED_OFFLINE) != 0;
00686     if (offlineCreate)
00687     {
00688       if (CreateOfflineFolder(m_currentFolder))
00689         return PR_TRUE;
00690     }
00691     AdvanceToNextFolder();
00692   }
00693   return PR_FALSE;
00694 }
00695 
00696 PRBool nsImapOfflineSync::CreateOfflineFolder(nsIMsgFolder *folder)
00697 {
00698   nsCOMPtr<nsIMsgFolder> parent;
00699   folder->GetParent(getter_AddRefs(parent));
00700 
00701   nsCOMPtr <nsIMsgImapMailFolder> imapFolder = do_QueryInterface(parent);
00702   nsCOMPtr <nsIURI> createFolderURI;
00703    nsXPIDLCString onlineName;
00704   imapFolder->GetOnlineName(getter_Copies(onlineName));
00705 
00706   NS_ConvertASCIItoUCS2 folderName(onlineName);
00707   nsresult rv = imapFolder->PlaybackOfflineFolderCreate(folderName.get(), nsnull,  getter_AddRefs(createFolderURI));
00708   if (createFolderURI && NS_SUCCEEDED(rv))
00709   {
00710     nsCOMPtr <nsIMsgMailNewsUrl> mailnewsUrl = do_QueryInterface(createFolderURI);
00711     if (mailnewsUrl)
00712       mailnewsUrl->RegisterListener(this);
00713   }
00714   return NS_SUCCEEDED(rv) ? PR_TRUE : PR_FALSE;  // this is asynch, we have to return and be called again by the OfflineOpExitFunction
00715 }
00716 
00717 PRInt32 nsImapOfflineSync::GetCurrentUIDValidity()
00718 {
00719   if (m_currentFolder)
00720   {
00721     nsCOMPtr <nsIImapMailFolderSink> imapFolderSink = do_QueryInterface(m_currentFolder);
00722     if (imapFolderSink)
00723       imapFolderSink->GetUidValidity(&mCurrentUIDValidity);
00724   }
00725   return mCurrentUIDValidity; 
00726 }
00727 
00728 // Playing back offline operations is one giant state machine that runs through ProcessNextOperation.
00729 // The first state is creating online any folders created offline (we do this first, so we can play back
00730 // any operations in them in the next pass)
00731 
00732 nsresult nsImapOfflineSync::ProcessNextOperation()
00733 {
00734   nsresult rv = NS_OK;
00735   // find a folder that needs to process operations
00736   nsIMsgFolder       *deletedAllOfflineEventsInFolder = nsnull;
00737   
00738   // if we haven't created offline folders, and we're updating all folders,
00739   // first, find offline folders to create.
00740   if (!m_createdOfflineFolders)
00741   {
00742     if (m_singleFolderToUpdate)
00743     {
00744       if (!m_pseudoOffline)
00745       {
00746         AdvanceToFirstIMAPFolder();
00747         if (CreateOfflineFolders())
00748           return NS_OK;
00749       }       
00750     }
00751     else
00752     {
00753       if (CreateOfflineFolders())
00754         return NS_OK;
00755       m_currentServer = nsnull;
00756       AdvanceToNextFolder();
00757     }
00758     m_createdOfflineFolders = PR_TRUE;
00759   }
00760   // if updating one folder only, restore m_currentFolder to that folder
00761   if (m_singleFolderToUpdate)
00762     m_currentFolder = m_singleFolderToUpdate;
00763   
00764   PRUint32 folderFlags;
00765   nsCOMPtr <nsIDBFolderInfo> folderInfo;
00766   while (m_currentFolder && !m_currentDB)
00767   {
00768     m_currentFolder->GetFlags(&folderFlags);
00769     // need to check if folder has offline events, /* or is configured for offline */
00770     // shouldn't need to check if configured for offline use, since any folder with
00771     // events should have MSG_FOLDER_FLAG_OFFLINEEVENTS set.
00772     if (folderFlags & (MSG_FOLDER_FLAG_OFFLINEEVENTS /* | MSG_FOLDER_FLAG_OFFLINE */))
00773       m_currentFolder->GetDBFolderInfoAndDB(getter_AddRefs(folderInfo), getter_AddRefs(m_currentDB));
00774 
00775     if (m_currentDB)
00776     {
00777       m_CurrentKeys.RemoveAll();
00778       m_KeyIndex = 0;
00779       if ((m_currentDB->ListAllOfflineOpIds(&m_CurrentKeys) != 0) || !m_CurrentKeys.GetSize())
00780       {
00781         m_currentDB = nsnull;
00782         folderInfo = nsnull; // can't hold onto folderInfo longer than db
00783         m_currentFolder->ClearFlag(MSG_FOLDER_FLAG_OFFLINEEVENTS);
00784       }
00785       else
00786       {
00787         // trash any ghost msgs
00788         PRBool deletedGhostMsgs = PR_FALSE;
00789         for (PRUint32 fakeIndex=0; fakeIndex < m_CurrentKeys.GetSize(); fakeIndex++)
00790         {
00791           nsCOMPtr <nsIMsgOfflineImapOperation> currentOp; 
00792           m_currentDB->GetOfflineOpForKey(m_CurrentKeys[fakeIndex], PR_FALSE, getter_AddRefs(currentOp));
00793           if (currentOp)
00794           {
00795             nsOfflineImapOperationType opType; 
00796             currentOp->GetOperation(&opType);
00797             
00798             if (opType == nsIMsgOfflineImapOperation::kMoveResult)
00799             {
00800               nsMsgKey curKey;
00801               currentOp->GetMessageKey(&curKey);
00802               m_currentDB->RemoveOfflineOp(currentOp);
00803               deletedGhostMsgs = PR_TRUE;
00804               
00805               nsCOMPtr <nsIMsgDBHdr> mailHdr;
00806               m_currentDB->DeleteMessage(curKey, nsnull, PR_FALSE);
00807             }
00808           }
00809         }
00810         
00811         if (deletedGhostMsgs)
00812           m_currentFolder->SummaryChanged();
00813         
00814         m_CurrentKeys.RemoveAll();
00815         if ( (m_currentDB->ListAllOfflineOpIds(&m_CurrentKeys) != 0) || !m_CurrentKeys.GetSize() )
00816         {
00817           m_currentDB = nsnull;
00818           if (deletedGhostMsgs)
00819             deletedAllOfflineEventsInFolder = m_currentFolder;
00820         }
00821         else if (folderFlags & MSG_FOLDER_FLAG_IMAPBOX)
00822         {
00823           //                              if (imapFolder->GetHasOfflineEvents())
00824           //                                     NS_ASSERTION(PR_FALSE, "Hardcoded assertion");
00825           
00826           if (!m_pseudoOffline)    // if pseudo offline, falls through to playing ops back.
00827           {
00828             // there are operations to playback so check uid validity
00829             SetCurrentUIDValidity(0);     // force initial invalid state
00830             // do a lite select here and hook ourselves up as a listener.
00831             nsCOMPtr <nsIMsgImapMailFolder> imapFolder = do_QueryInterface(m_currentFolder, &rv);
00832             if (imapFolder)
00833               rv = imapFolder->LiteSelect(this);
00834             return rv;      // this is async, we have to return as be called again by the OnStopRunningUrl
00835           }
00836         }
00837       }
00838     }
00839     
00840     if (!m_currentDB)
00841     {
00842       // only advance if we are doing all folders
00843       if (!m_singleFolderToUpdate)
00844         AdvanceToNextFolder();
00845       else
00846         m_currentFolder = nsnull;  // force update of this folder now.
00847     }
00848     
00849   }
00850   
00851   if (m_currentFolder)
00852     m_currentFolder->GetFlags(&folderFlags);
00853   // do the current operation
00854   if (m_currentDB)
00855   {    
00856     PRBool currentFolderFinished = PR_FALSE;
00857     if (!folderInfo)
00858       m_currentDB->GetDBFolderInfo(getter_AddRefs(folderInfo));
00859     // user canceled the lite select! if GetCurrentUIDValidity() == 0
00860     if (folderInfo && (m_KeyIndex < m_CurrentKeys.GetSize()) && (m_pseudoOffline || (GetCurrentUIDValidity() != 0) || !(folderFlags & MSG_FOLDER_FLAG_IMAPBOX)) )
00861     {
00862       PRInt32 curFolderUidValidity;
00863       folderInfo->GetImapUidValidity(&curFolderUidValidity);
00864       PRBool uidvalidityChanged = (!m_pseudoOffline && folderFlags & MSG_FOLDER_FLAG_IMAPBOX) && (GetCurrentUIDValidity() != curFolderUidValidity);
00865       nsCOMPtr <nsIMsgOfflineImapOperation> currentOp;
00866       if (uidvalidityChanged)
00867         DeleteAllOfflineOpsForCurrentDB();
00868       else
00869         m_currentDB->GetOfflineOpForKey(m_CurrentKeys[m_KeyIndex], PR_FALSE, getter_AddRefs(currentOp));
00870       
00871       if (currentOp)
00872       {
00873         nsOfflineImapOperationType opType; 
00874         
00875         currentOp->GetOperation(&opType);
00876         // loop until we find the next db record that matches the current playback operation
00877         while (currentOp && !(opType & mCurrentPlaybackOpType))
00878         {
00879           currentOp = nsnull;
00880           ++m_KeyIndex;
00881           if (m_KeyIndex < m_CurrentKeys.GetSize())
00882             m_currentDB->GetOfflineOpForKey(m_CurrentKeys[m_KeyIndex], PR_FALSE, getter_AddRefs(currentOp));
00883           if (currentOp)
00884             currentOp->GetOperation(&opType);
00885         }
00886         
00887         // if we did not find a db record that matches the current playback operation,
00888         // then move to the next playback operation and recurse.  
00889         if (!currentOp)
00890         {
00891           // we are done with the current type
00892           if (mCurrentPlaybackOpType == nsIMsgOfflineImapOperation::kFlagsChanged)
00893           {
00894             mCurrentPlaybackOpType = nsIMsgOfflineImapOperation::kAddKeywords;
00895             // recurse to deal with next type of operation
00896             m_KeyIndex = 0;
00897             ProcessNextOperation();
00898           }
00899           else if (mCurrentPlaybackOpType == nsIMsgOfflineImapOperation::kAddKeywords)
00900           {
00901             mCurrentPlaybackOpType = nsIMsgOfflineImapOperation::kRemoveKeywords;
00902             // recurse to deal with next type of operation
00903             m_KeyIndex = 0;
00904             ProcessNextOperation();
00905           }
00906           else if (mCurrentPlaybackOpType == nsIMsgOfflineImapOperation::kRemoveKeywords)
00907           {
00908             mCurrentPlaybackOpType = nsIMsgOfflineImapOperation::kMsgCopy;
00909             // recurse to deal with next type of operation
00910             m_KeyIndex = 0;
00911             ProcessNextOperation();
00912           }
00913           else if (mCurrentPlaybackOpType == nsIMsgOfflineImapOperation::kMsgCopy)
00914           {
00915             mCurrentPlaybackOpType = nsIMsgOfflineImapOperation::kMsgMoved;
00916             // recurse to deal with next type of operation
00917             m_KeyIndex = 0;
00918             ProcessNextOperation();
00919           }
00920           else if (mCurrentPlaybackOpType == nsIMsgOfflineImapOperation::kMsgMoved)
00921           {
00922             mCurrentPlaybackOpType = nsIMsgOfflineImapOperation::kAppendDraft;
00923             // recurse to deal with next type of operation
00924             m_KeyIndex = 0;
00925             ProcessNextOperation();
00926           }
00927           else if (mCurrentPlaybackOpType == nsIMsgOfflineImapOperation::kAppendDraft)
00928           {
00929             mCurrentPlaybackOpType = nsIMsgOfflineImapOperation::kAppendTemplate;
00930             // recurse to deal with next type of operation
00931             m_KeyIndex = 0;
00932             ProcessNextOperation();
00933           }
00934           else if (mCurrentPlaybackOpType == nsIMsgOfflineImapOperation::kAppendTemplate)
00935           {
00936             mCurrentPlaybackOpType = nsIMsgOfflineImapOperation::kDeleteAllMsgs;
00937             m_KeyIndex = 0;
00938             ProcessNextOperation();
00939           }
00940           else
00941           {
00942             DeleteAllOfflineOpsForCurrentDB();
00943             currentFolderFinished = PR_TRUE;
00944           }
00945           
00946         }
00947         else
00948         {
00949           if (mCurrentPlaybackOpType == nsIMsgOfflineImapOperation::kFlagsChanged)
00950             ProcessFlagOperation(currentOp);
00951           else if (mCurrentPlaybackOpType == nsIMsgOfflineImapOperation::kAddKeywords
00952             ||mCurrentPlaybackOpType == nsIMsgOfflineImapOperation::kRemoveKeywords)
00953             ProcessKeywordOperation(currentOp);
00954           else if (mCurrentPlaybackOpType == nsIMsgOfflineImapOperation::kMsgCopy)
00955             ProcessCopyOperation(currentOp);
00956           else if (mCurrentPlaybackOpType == nsIMsgOfflineImapOperation::kMsgMoved)
00957             ProcessMoveOperation(currentOp);
00958           else if (mCurrentPlaybackOpType == nsIMsgOfflineImapOperation::kAppendDraft)
00959             ProcessAppendMsgOperation(currentOp, nsIMsgOfflineImapOperation::kAppendDraft);
00960           else if (mCurrentPlaybackOpType == nsIMsgOfflineImapOperation::kAppendTemplate)
00961             ProcessAppendMsgOperation(currentOp, nsIMsgOfflineImapOperation::kAppendTemplate);
00962           else if (mCurrentPlaybackOpType == nsIMsgOfflineImapOperation::kDeleteAllMsgs)
00963             ProcessEmptyTrash(currentOp);
00964           else
00965             NS_ASSERTION(PR_FALSE, "invalid playback op type");
00966           // currentOp was unreferred by one of the Process functions
00967           // so do not reference it again!
00968           currentOp = nsnull;
00969         }
00970       }
00971       else
00972         currentFolderFinished = PR_TRUE;
00973     }
00974     else
00975       currentFolderFinished = PR_TRUE;
00976     
00977     if (currentFolderFinished)
00978     {
00979       m_currentDB = nsnull;
00980       if (!m_singleFolderToUpdate)
00981       {
00982         AdvanceToNextFolder();
00983         ProcessNextOperation();
00984         return NS_OK;
00985       }
00986       else
00987         m_currentFolder = nsnull;
00988     }
00989   }
00990   
00991   if (!m_currentFolder && !m_mailboxupdatesStarted)
00992   {
00993     m_mailboxupdatesStarted = PR_TRUE;
00994     
00995     // if we are updating more than one folder then we need the iterator
00996     if (!m_singleFolderToUpdate)
00997     {
00998       m_currentServer = nsnull;
00999       AdvanceToNextFolder();
01000     }
01001     if (m_singleFolderToUpdate)
01002     {
01003       m_singleFolderToUpdate->ClearFlag(MSG_FOLDER_FLAG_OFFLINEEVENTS);
01004       m_singleFolderToUpdate->UpdateFolder(m_window);
01005       // do we have to do anything? Old code would do a start update...
01006     }
01007     else
01008     {
01009     }
01010     
01011     //        MSG_FolderIterator *updateFolderIterator = m_singleFolderToUpdate ? (MSG_FolderIterator *) 0 : m_folderIterator;
01012     
01013     
01014     // we are done playing commands back, now queue up the sync with each imap folder
01015     // If we're using the iterator, m_currentFolder will be set correctly
01016     //               nsIMsgFolder * folder = m_singleFolderToUpdate ? m_singleFolderToUpdate : m_currentFolder;
01017     //               while (folder)
01018     //               {            
01019     //                      PRBool loadingFolder = m_workerPane->GetLoadingImapFolder() == folder;
01020     //                      if ((folder->GetType() == FOLDER_IMAPMAIL) && (deletedAllOfflineEventsInFolder == folder || (folder->GetFolderPrefFlags() & MSG_FOLDER_FLAG_OFFLINE)
01021     //                             || loadingFolder) 
01022     //                             && !(folder->GetFolderPrefFlags() & MSG_FOLDER_PREF_IMAPNOSELECT) )
01023     //                      {
01024     //                             PRBool lastChance = ((deletedAllOfflineEventsInFolder == folder) && m_singleFolderToUpdate) || loadingFolder;
01025     // if deletedAllOfflineEventsInFolder == folder and we're only updating one folder, then we need to update newly selected folder
01026     // I think this also means that we're really opening the folder...so we tell StartUpdate that we're loading a folder.
01027     //                             if (!updateFolderIterator || !(imapMail->GetFlags() & MSG_FOLDER_FLAG_INBOX))              // avoid queueing the inbox twice
01028     //                                    imapMail->StartUpdateOfNewlySelectedFolder(m_workerPane, lastChance, queue, nsnsnull, PR_FALSE, PR_FALSE);
01029     //                      }
01030     //                      folder= m_singleFolderToUpdate ? (MSG_FolderInfo *)nsnull : updateFolderIterator->Next();
01031     //       }
01032   }
01033   // if we get here, then I *think* we're done. Not sure, though.
01034 #ifdef DEBUG_bienvenu
01035   printf("done with offline imap sync\n");
01036 #endif
01037   nsCOMPtr <nsIUrlListener> saveListener = m_listener;
01038   m_listener = nsnull;
01039 
01040   if (saveListener)
01041     saveListener->OnStopRunningUrl(nsnull /* don't know url */, rv);
01042   return rv;
01043 }
01044 
01045 
01046 void nsImapOfflineSync::DeleteAllOfflineOpsForCurrentDB()
01047 {
01048   m_KeyIndex = 0;
01049   nsCOMPtr <nsIMsgOfflineImapOperation> currentOp;
01050   m_currentDB->GetOfflineOpForKey(m_CurrentKeys[m_KeyIndex], PR_FALSE, getter_AddRefs(currentOp));
01051   while (currentOp)
01052   {
01053     // NS_ASSERTION(currentOp->GetOperationFlags() == 0);
01054     // delete any ops that have already played back
01055     m_currentDB->RemoveOfflineOp(currentOp);
01056     m_currentDB->Commit(nsMsgDBCommitType::kLargeCommit);
01057     currentOp = nsnull;
01058     
01059     if (++m_KeyIndex < m_CurrentKeys.GetSize())
01060       m_currentDB->GetOfflineOpForKey(m_CurrentKeys[m_KeyIndex], PR_FALSE, getter_AddRefs(currentOp));
01061   }
01062   // turn off MSG_FOLDER_FLAG_OFFLINEEVENTS
01063   if (m_currentFolder)
01064     m_currentFolder->ClearFlag(MSG_FOLDER_FLAG_OFFLINEEVENTS);
01065 }
01066 
01067 nsImapOfflineDownloader::nsImapOfflineDownloader(nsIMsgWindow *aMsgWindow, nsIUrlListener *aListener) : nsImapOfflineSync(aMsgWindow, aListener)
01068 {
01069 }
01070 
01071 nsImapOfflineDownloader::~nsImapOfflineDownloader()
01072 {
01073 }
01074 
01075 nsresult nsImapOfflineDownloader::ProcessNextOperation()
01076 {
01077   nsresult rv = NS_OK;
01078   if (!m_mailboxupdatesStarted)
01079   {
01080     m_mailboxupdatesStarted = PR_TRUE;
01081     // Update the INBOX first so the updates on the remaining
01082     // folders pickup the results of any filter moves.
01083     nsCOMPtr<nsIMsgAccountManager> accountManager = 
01084              do_GetService(NS_MSGACCOUNTMANAGER_CONTRACTID, &rv);
01085     if (NS_FAILED(rv)) return rv;
01086     nsCOMPtr<nsISupportsArray> servers;
01087   
01088     rv = accountManager->GetAllServers(getter_AddRefs(servers));
01089     if (NS_FAILED(rv)) return rv;
01090   }
01091   if (!m_mailboxupdatesFinished)
01092   {
01093     AdvanceToNextServer();
01094     if (m_currentServer)
01095     {
01096       nsCOMPtr <nsIMsgFolder> rootMsgFolder;
01097       m_currentServer->GetRootFolder(getter_AddRefs(rootMsgFolder));
01098       nsCOMPtr<nsIMsgFolder> inbox;
01099       if (rootMsgFolder)
01100       {
01101           PRUint32 numFolders;
01102           rootMsgFolder->GetFoldersWithFlag(MSG_FOLDER_FLAG_INBOX, 1, &numFolders, getter_AddRefs(inbox));
01103           if (inbox)
01104           {
01105             nsCOMPtr <nsIMsgFolder> offlineImapFolder;
01106             nsCOMPtr <nsIMsgImapMailFolder> imapInbox = do_QueryInterface(inbox);
01107             if (imapInbox)
01108             {
01109               rootMsgFolder->GetFoldersWithFlag(MSG_FOLDER_FLAG_OFFLINE, 1, &numFolders, getter_AddRefs(offlineImapFolder));
01110               if (!offlineImapFolder)
01111               {
01112                 // no imap folders configured for offline use - check if the account is set up
01113                 // so that we always download inbox msg bodies for offline use
01114                 nsCOMPtr <nsIImapIncomingServer> imapServer = do_QueryInterface(m_currentServer);
01115                 if (imapServer)
01116                 {
01117                   PRBool downloadBodiesOnGetNewMail = PR_FALSE;
01118                   imapServer->GetDownloadBodiesOnGetNewMail(&downloadBodiesOnGetNewMail);
01119                   if (downloadBodiesOnGetNewMail)
01120                     offlineImapFolder = inbox;
01121                 }
01122               }
01123             }
01124             // if this isn't an imap inbox, or we have an offline imap sub-folder, then update the inbox.
01125             // otherwise, it's an imap inbox for an account with no folders configured for offline use,
01126             // so just advance to the next server.
01127             if (!imapInbox || offlineImapFolder)
01128             {
01129               // here we should check if this a pop3 server/inbox, and the user doesn't want
01130               // to download pop3 mail for offline use.
01131               if (!imapInbox)
01132               {
01133               }
01134               rv = inbox->GetNewMessages(m_window, this);
01135               if (NS_SUCCEEDED(rv))
01136                 return rv; // otherwise, fall through.
01137             }
01138           }
01139       }
01140       return ProcessNextOperation(); // recurse and do next server.
01141     }
01142     else
01143     {
01144       m_allServers = nsnull;
01145       m_mailboxupdatesFinished = PR_TRUE;
01146     }
01147   }
01148   AdvanceToNextFolder();
01149 
01150   while (m_currentFolder)
01151   {
01152     PRUint32 folderFlags;
01153 
01154     m_currentDB = nsnull;
01155     nsCOMPtr <nsIMsgImapMailFolder> imapFolder;
01156     if (m_currentFolder)
01157       imapFolder = do_QueryInterface(m_currentFolder);
01158     m_currentFolder->GetFlags(&folderFlags);
01159     // need to check if folder has offline events, or is configured for offline
01160     if (imapFolder && folderFlags & MSG_FOLDER_FLAG_OFFLINE)
01161     {
01162       rv = m_currentFolder->DownloadAllForOffline(this, m_window);
01163       if (NS_SUCCEEDED(rv) || rv == NS_BINDING_ABORTED)
01164         return rv;
01165       // if this fails and the user didn't cancel/stop, fall through to code that advances to next folder
01166     }
01167     AdvanceToNextFolder();
01168   }
01169   if (m_listener)
01170     m_listener->OnStopRunningUrl(nsnull, NS_OK);
01171   return rv;
01172 }
01173 
01174 
01175 NS_IMETHODIMP nsImapOfflineSync::OnStartCopy()
01176 {
01177     return NS_ERROR_NOT_IMPLEMENTED;
01178 }
01179 
01180 /* void OnProgress (in PRUint32 aProgress, in PRUint32 aProgressMax); */
01181 NS_IMETHODIMP nsImapOfflineSync::OnProgress(PRUint32 aProgress, PRUint32 aProgressMax)
01182 {
01183     return NS_ERROR_NOT_IMPLEMENTED;
01184 }
01185 
01186 /* void SetMessageKey (in PRUint32 aKey); */
01187 NS_IMETHODIMP nsImapOfflineSync::SetMessageKey(PRUint32 aKey)
01188 {
01189     return NS_ERROR_NOT_IMPLEMENTED;
01190 }
01191 
01192 /* [noscript] void GetMessageId (in nsCString aMessageId); */
01193 NS_IMETHODIMP nsImapOfflineSync::GetMessageId(nsCString * aMessageId)
01194 {
01195     return NS_ERROR_NOT_IMPLEMENTED;
01196 }
01197 
01198 /* void OnStopCopy (in nsresult aStatus); */
01199 NS_IMETHODIMP nsImapOfflineSync::OnStopCopy(nsresult aStatus)
01200 {
01201   return OnStopRunningUrl(nsnull, aStatus);
01202 }
01203 
01204 
01205