/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- * * The contents of this file are subject to the Netscape Public * License Version 1.1 (the "License"); you may not use this file * except in compliance with the License. You may obtain a copy of * the License at http://www.mozilla.org/NPL/ * * Software distributed under the License is distributed on an "AS * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or * implied. See the License for the specific language governing * rights and limitations under the License. * * The Original Code is mozilla.org code. * * The Initial Developer of the Original Code is Netscape * Communications Corporation. Portions created by Netscape are * Copyright (C) 1998 Netscape Communications Corporation. All * Rights Reserved. * * Contributor(s): */ /* offline imap download stream */ #include "msg.h" #include "imapoff.h" #include "maildb.h" #include "msgfinfo.h" #include "mailhdr.h" #include "msgpane.h" #include "msgprefs.h" #include "msgurlq.h" #include "xpgetstr.h" #include "prefapi.h" #include "imap.h" #include "grpinfo.h" #include "msgdbapi.h" #include "msgstrob.h" extern "C" { extern int MK_MSG_CONFIRM_CONTINUE_IMAP_SYNC; extern int MK_MSG_HTML_IMAP_NO_CACHED_BODY; } // extern "C" // lame forward to kill mac warning void OffOnlineSynchExitFunction (URL_Struct *URL_s, int status, MWContext *window_id); // when we get here, we're done, give the other deferred tasks a chance now void OffOnlineSynchExitFunction (URL_Struct *URL_s, int /*status*/, MWContext *) { OfflineImapGoOnlineState *goState = URL_s->msg_pane->GetGoOnlineState(); if (goState) { delete goState; URL_s->msg_pane->SetGoOnlineState(NULL); } // kick off any other on/off urls (like news synching) if (URL_s->msg_pane && URL_s->msg_pane->GetURLChain()) URL_s->msg_pane->GetURLChain()->GetNextURL(); } // lame forward to kill mac warning void OfflineOpExitFunction (URL_Struct *URL_s, int status, MWContext *window_id); void OfflineOpExitFunction (URL_Struct *URL_s, int status, MWContext *window_id) { OfflineImapGoOnlineState *goState = URL_s->msg_pane->GetGoOnlineState(); if (goState) { #ifdef DEBUG_bienvenu XP_ASSERT(status != MK_INTERRUPTED); // unless user interrupted, don't want to see this. #endif if (status != MK_INTERRUPTED && ((status >= 0) || FE_Confirm(URL_s->msg_pane->GetContext(), XP_GetString(MK_MSG_CONFIRM_CONTINUE_IMAP_SYNC) ))) goState->ProcessNextOperation(); else { // This function will clear the pane of any information about // folder loads and send a MSG_PaneNotifyFolderLoaded is necessary if (goState->ProcessingStaleFolderUpdate()) ImapFolderSelectCompleteExitFunction(URL_s, status, window_id); delete goState; URL_s->msg_pane->SetGoOnlineState(NULL); } } } void ReportLiteSelectUIDVALIDITY(MSG_Pane *receivingPane, uint32 UIDVALIDITY) { OfflineImapGoOnlineState *goState = receivingPane ? receivingPane->GetGoOnlineState() : (OfflineImapGoOnlineState *)NULL; if (goState) goState->SetCurrentUIDValidity(UIDVALIDITY); } OfflineImapGoOnlineState::OfflineImapGoOnlineState(MSG_Pane *workerPane, MSG_IMAPFolderInfoMail *singleFolderOnly) { workerPane->SetGoOnlineState(this); workerPane->GetMaster()->SetPlayingBackOfflineOps(TRUE); m_workerPane = workerPane; m_mailboxupdatesStarted = FALSE; mCurrentPlaybackOpType = kFlagsChanged; m_pseudoOffline = FALSE; m_createdOfflineFolders = FALSE; m_singleFolderToUpdate = singleFolderOnly; // start with the first imap mailbox if (!m_singleFolderToUpdate) { m_folderIterator = new MSG_FolderIterator(workerPane->GetMaster()->GetImapMailFolderTree()); m_currentFolder = (MSG_FolderInfoMail *) m_folderIterator->First(); if (m_currentFolder && (m_currentFolder->GetFlags() & MSG_FOLDER_FLAG_IMAP_SERVER)) AdvanceToNextFolder(); } else m_currentFolder = m_singleFolderToUpdate; m_urlQueue = NULL; m_downloadOfflineImapState = NULL; } OfflineImapGoOnlineState::~OfflineImapGoOnlineState() { if (m_currentDB) m_currentDB->Close(); m_workerPane->GetMaster()->SetPlayingBackOfflineOps(FALSE); delete m_folderIterator; delete m_downloadOfflineImapState; } MSG_UrlQueue *OfflineImapGoOnlineState::GetUrlQueue(XP_Bool *alreadyRunning) { MSG_UrlQueue *queue = MSG_UrlQueue::FindQueue (m_workerPane); *alreadyRunning = queue != NULL; if (!queue) // should this be a MSG_ImapLoadFolderUrlQueue? I think so. queue = new MSG_ImapLoadFolderUrlQueue(m_workerPane); else XP_Trace("we're using another queue for playing back an online operation!\n"); queue->AddInterruptCallback(MSG_UrlQueue::HandleFolderLoadInterrupt); return queue; } void OfflineImapGoOnlineState::AdvanceToNextFolder() { // we always start by changing flags mCurrentPlaybackOpType = kFlagsChanged; m_currentFolder = (MSG_FolderInfoMail *) m_folderIterator->Next(); if (m_currentFolder && (m_currentFolder->GetFlags() & MSG_FOLDER_FLAG_IMAP_SERVER)) AdvanceToNextFolder(); if (!m_currentFolder && (m_folderIterator->m_masterParent == m_workerPane->GetMaster()->GetImapMailFolderTree())) { delete m_folderIterator; m_folderIterator = new MSG_FolderIterator(m_workerPane->GetMaster()->GetLocalMailFolderTree()); m_currentFolder = (MSG_FolderInfoMail *) m_folderIterator->First(); } } void OfflineImapGoOnlineState::ProcessFlagOperation(DBOfflineImapOperation *currentOp) { IDArray matchingFlagKeys; int currentKeyIndex = m_KeyIndex; imapMessageFlagsType matchingFlags = currentOp->GetNewMessageFlags(); do { // loop for all messsages with the same flags matchingFlagKeys.Add(currentOp->GetMessageKey()); currentOp->ClearImapFlagOperation(); delete currentOp; currentOp = NULL; if (++currentKeyIndex < m_CurrentKeys.GetSize()) currentOp = m_currentDB->GetOfflineOpForKey(m_CurrentKeys[currentKeyIndex], FALSE); } while (currentOp && (currentOp->GetOperationFlags() & kFlagsChanged) && (currentOp->GetNewMessageFlags() == matchingFlags) ); if (currentOp) { delete currentOp; currentOp = NULL; } char *uids = MSG_IMAPFolderInfoMail::AllocateImapUidString(matchingFlagKeys); if (uids && (m_currentFolder->GetFlags() & MSG_FOLDER_FLAG_IMAPBOX)) { char *url = CreateImapSetMessageFlagsUrl(((MSG_IMAPFolderInfoMail *) m_currentFolder)->GetHostName(), ((MSG_IMAPFolderInfoMail *) m_currentFolder)->GetOnlineName(), ((MSG_IMAPFolderInfoMail *) m_currentFolder)->GetOnlineHierarchySeparator(), uids, matchingFlags, TRUE); if (url) { XP_Bool alreadyRunningQueue; MSG_UrlQueue *queue = GetUrlQueue(&alreadyRunningQueue); if (queue) { // should we insert this at 0, or add? I think we want to run offline events // before any new events...but this is just a lite select queue->AddUrl(url, OfflineOpExitFunction); if (!alreadyRunningQueue) queue->GetNextUrl(); } XP_FREE(url); } } FREEIF(uids); } /* static */ void OfflineImapGoOnlineState::PostAppendMsgExitFunction(URL_Struct *URL_s, int status, MWContext *window_id) { AppendMsgOfflineImapState *appendMsgState = (AppendMsgOfflineImapState *) URL_s->fe_data; // Append Msg specific if (status >= 0) { appendMsgState->RemoveMsgFile(); appendMsgState->RemoveHdrFromDB(); } URL_s->fe_data = 0; delete appendMsgState; OfflineOpExitFunction(URL_s, status, window_id); } void OfflineImapGoOnlineState::ProcessAppendMsgOperation(DBOfflineImapOperation *currentOp, int32 opType) { MailMessageHdr *mailHdr = m_currentDB->GetMailHdrForKey(currentOp->GetMessageKey()); if (mailHdr) { char *msg_file_name = WH_TempName (xpFileToPost, "nsmail"); if (msg_file_name) { XP_File msg_file = XP_FileOpen(msg_file_name, xpFileToPost, XP_FILE_WRITE_BIN); if (msg_file) { mailHdr->WriteOfflineMessage(msg_file, m_currentDB->GetDB()); XP_FileClose(msg_file); XPStringObj moveDestination; currentOp->GetMoveDestination(m_currentDB->GetDB(), moveDestination); MSG_IMAPFolderInfoMail *currentIMAPFolder = m_currentFolder->GetIMAPFolderInfoMail(); MSG_IMAPFolderInfoMail *mailFolderInfo = currentIMAPFolder ? m_workerPane->GetMaster()->FindImapMailFolder(currentIMAPFolder->GetHostName(), moveDestination, NULL, FALSE) : m_workerPane->GetMaster()->FindImapMailFolder(moveDestination); char *urlString = CreateImapAppendMessageFromFileUrl( mailFolderInfo->GetHostName(), mailFolderInfo->GetOnlineName(), mailFolderInfo->GetOnlineHierarchySeparator(), opType == kAppendDraft); if (urlString) { URL_Struct *url = NET_CreateURLStruct(urlString, NET_NORMAL_RELOAD); if (url) { url->post_data = XP_STRDUP(msg_file_name); url->post_data_size = XP_STRLEN(msg_file_name); url->post_data_is_file = TRUE; url->method = URL_POST_METHOD; url->fe_data = (void *) new AppendMsgOfflineImapState( mailFolderInfo, currentOp->GetMessageKey(), msg_file_name); url->internal_url = TRUE; url->msg_pane = m_workerPane; m_workerPane->GetContext()->imapURLPane = m_workerPane; MSG_UrlQueue::AddUrlToPane (url, PostAppendMsgExitFunction, m_workerPane, TRUE); currentOp->ClearAppendMsgOperation(m_currentDB->GetDB(), opType); } XP_FREEIF(urlString); } } XP_FREEIF(msg_file_name); } delete mailHdr; } delete currentOp; } void OfflineImapGoOnlineState::ProcessMoveOperation(DBOfflineImapOperation *currentOp) { IDArray *matchingFlagKeys = new IDArray ; int currentKeyIndex = m_KeyIndex; XPStringObj moveDestination; currentOp->GetMoveDestination(m_currentDB->GetDB(), moveDestination); XP_Bool moveMatches = TRUE; do { // loop for all messsages with the same destination if (moveMatches) { matchingFlagKeys->Add(currentOp->GetMessageKey()); currentOp->ClearMoveOperation(m_currentDB->GetDB()); } delete currentOp; currentOp = NULL; if (++currentKeyIndex < m_CurrentKeys.GetSize()) { XPStringObj nextDestination; currentOp = m_currentDB->GetOfflineOpForKey(m_CurrentKeys[currentKeyIndex], FALSE); if (currentOp && (currentOp->GetOperationFlags() & kMsgMoved) ) { currentOp->GetMoveDestination(m_currentDB->GetDB(), nextDestination); moveMatches = XP_STRCMP(moveDestination, nextDestination) == 0; } else moveMatches = FALSE; } } while (currentOp); XP_Bool alreadyRunningQueue; MSG_UrlQueue *queue = GetUrlQueue(&alreadyRunningQueue); MSG_IMAPFolderInfoMail *currentIMAPFolder = m_currentFolder->GetIMAPFolderInfoMail(); MSG_IMAPFolderInfoMail *destFolder = (currentIMAPFolder) ? m_workerPane->GetMaster()->FindImapMailFolder(currentIMAPFolder->GetHostName(), moveDestination, NULL, FALSE) : 0; m_currentFolder->StartAsyncCopyMessagesInto( destFolder, m_workerPane, m_currentDB, matchingFlagKeys, matchingFlagKeys->GetSize(), m_workerPane->GetContext(), queue, TRUE); if (!alreadyRunningQueue) queue->GetNextUrl(); } void OfflineImapGoOnlineState::ProcessCopyOperation(DBOfflineImapOperation *currentOp) { IDArray *matchingFlagKeys = new IDArray ; int currentKeyIndex = m_KeyIndex; XPStringObj copyDestination; currentOp->GetIndexedCopyDestination(m_currentDB->GetDB(), 0,copyDestination); XP_Bool copyMatches = TRUE; do { // loop for all messsages with the same destination if (copyMatches) { matchingFlagKeys->Add(currentOp->GetMessageKey()); currentOp->ClearFirstCopyOperation(m_currentDB->GetDB()); } delete currentOp; currentOp = NULL; if (++currentKeyIndex < m_CurrentKeys.GetSize()) { XPStringObj nextDestination; currentOp = m_currentDB->GetOfflineOpForKey(m_CurrentKeys[currentKeyIndex], FALSE); if (currentOp && (currentOp->GetOperationFlags() & kMsgCopy) ) { currentOp->GetIndexedCopyDestination(m_currentDB->GetDB(), 0,copyDestination); copyMatches = XP_STRCMP(copyDestination, nextDestination) == 0; } else copyMatches = FALSE; } } while (currentOp); XP_Bool alreadyRunningQueue; MSG_UrlQueue *queue = GetUrlQueue(&alreadyRunningQueue); MSG_IMAPFolderInfoMail *currentIMAPFolder = m_currentFolder->GetIMAPFolderInfoMail(); MSG_IMAPFolderInfoMail *destFolder = (currentIMAPFolder) ? m_workerPane->GetMaster()->FindImapMailFolder(currentIMAPFolder->GetHostName(), copyDestination, NULL, FALSE) : 0; m_currentFolder->StartAsyncCopyMessagesInto(destFolder, m_workerPane, m_currentDB, matchingFlagKeys, matchingFlagKeys->GetSize(), m_workerPane->GetContext(), queue, FALSE); if (!alreadyRunningQueue) queue->GetNextUrl(); } void OfflineImapGoOnlineState::ProcessEmptyTrash(DBOfflineImapOperation *currentOp) { XP_Bool alreadyRunningQueue; MSG_UrlQueue *queue = GetUrlQueue(&alreadyRunningQueue); delete currentOp; MSG_IMAPFolderInfoMail *currentIMAPFolder = m_currentFolder->GetIMAPFolderInfoMail(); char *trashUrl = CreateImapDeleteAllMessagesUrl(currentIMAPFolder->GetHostName(), currentIMAPFolder->GetOnlineName(), currentIMAPFolder->GetOnlineHierarchySeparator()); // we're not going to delete sub-folders, since that prompts the user, a no-no while synchronizing. if (trashUrl) { queue->AddUrl(trashUrl, OfflineOpExitFunction); if (!alreadyRunningQueue) queue->GetNextUrl(); m_currentDB->DeleteOfflineOp(currentOp->GetMessageKey()); m_currentDB = NULL; // empty trash deletes the database? } } // returns TRUE if we found a folder to create, FALSE if we're done creating folders. XP_Bool OfflineImapGoOnlineState::CreateOfflineFolders() { while (m_currentFolder) { int32 prefFlags = m_currentFolder->GetFolderPrefFlags(); XP_Bool offlineCreate = prefFlags & MSG_FOLDER_PREF_CREATED_OFFLINE; if (offlineCreate) { if (CreateOfflineFolder(m_currentFolder)) return TRUE; } AdvanceToNextFolder(); } return FALSE; } XP_Bool OfflineImapGoOnlineState::CreateOfflineFolder(MSG_FolderInfo *folder) { MSG_IMAPFolderInfoMail *imapFolder = folder->GetIMAPFolderInfoMail(); char *url = CreateImapMailboxCreateUrl(imapFolder->GetHostName(), imapFolder->GetOnlineName(), imapFolder->GetOnlineHierarchySeparator()); if (url) { XP_Bool alreadyRunningQueue; MSG_UrlQueue *queue = GetUrlQueue(&alreadyRunningQueue); if (queue) { // should we insert this at 0, or add? I think we want to run offline events // before any new events...but this is just a lite select queue->AddUrl(url, OfflineOpExitFunction); if (!alreadyRunningQueue) queue->GetNextUrl(); return TRUE; // this is asynch, we have to return and be called again by the OfflineOpExitFunction } } return FALSE; } // Playing back offline operations is one giant state machine that runs through ProcessNextOperation. // The first state is creating online any folders created offline (we do this first, so we can play back // any operations in them in the next pass) void OfflineImapGoOnlineState::ProcessNextOperation() { char *url; // find a folder that needs to process operations MSG_FolderInfo *deletedAllOfflineEventsInFolder = NULL; // if we haven't created offline folders, and we're updating all folders, // first, find offline folders to create. if (!m_createdOfflineFolders) { if (m_singleFolderToUpdate) { int32 prefFlags = m_singleFolderToUpdate->GetFolderPrefFlags(); XP_Bool offlineCreate = prefFlags & MSG_FOLDER_PREF_CREATED_OFFLINE; m_createdOfflineFolders = TRUE; if (offlineCreate && CreateOfflineFolder(m_singleFolderToUpdate)) return; } else { if (CreateOfflineFolders()) return; delete m_folderIterator; m_folderIterator = new MSG_FolderIterator(m_workerPane->GetMaster()->GetImapMailFolderTree()); m_currentFolder = (MSG_FolderInfoMail *) m_folderIterator->First(); if (m_currentFolder && (m_currentFolder->GetFlags() & MSG_FOLDER_FLAG_IMAP_SERVER)) AdvanceToNextFolder(); } m_createdOfflineFolders = TRUE; } while (m_currentFolder && !m_currentDB) { int32 prefFlags = m_currentFolder->GetFolderPrefFlags(); // need to check if folder has offline events, or is configured for offline if (prefFlags & (MSG_FOLDER_PREF_OFFLINEEVENTS | MSG_FOLDER_PREF_OFFLINE)) { if (m_currentFolder->GetFlags() & MSG_FOLDER_FLAG_IMAPBOX) { XP_Bool wasCreated=FALSE; ImapMailDB::Open(m_currentFolder->GetPathname(), FALSE, &m_currentDB, m_currentFolder->GetMaster(), &wasCreated); } else MailDB::Open (m_currentFolder->GetPathname(), FALSE, &m_currentDB, FALSE); } if (m_currentDB) { FE_Progress(m_workerPane->GetContext(), m_currentFolder->GetName()); m_CurrentKeys.RemoveAll(); m_KeyIndex = 0; if ((m_currentDB->ListAllOfflineOpIds(m_CurrentKeys) != 0) || !m_CurrentKeys.GetSize()) { m_currentDB->Close(); m_currentDB = NULL; } else { // trash any ghost msgs XP_Bool deletedGhostMsgs = FALSE; for (int fakeIndex=0; fakeIndex < m_CurrentKeys.GetSize(); fakeIndex++) { DBOfflineImapOperation *currentOp = m_currentDB->GetOfflineOpForKey(m_CurrentKeys[fakeIndex], FALSE); if (currentOp && (currentOp->GetOperationFlags() & kMoveResult)) { m_currentDB->DeleteOfflineOp(currentOp->GetMessageKey()); deletedGhostMsgs = TRUE; MailMessageHdr *mailHdr = m_currentDB->GetMailHdrForKey(currentOp->GetMessageKey()); if (mailHdr) { m_currentDB->DeleteMessage(mailHdr->GetMessageKey(), NULL, FALSE); delete mailHdr; } delete currentOp; } } if (deletedGhostMsgs) m_currentFolder->SummaryChanged(); m_CurrentKeys.RemoveAll(); if ( (m_currentDB->ListAllOfflineOpIds(m_CurrentKeys) != 0) || !m_CurrentKeys.GetSize() ) { m_currentDB->Close(); m_currentDB = NULL; if (deletedGhostMsgs) deletedAllOfflineEventsInFolder = m_currentFolder; } else if (m_currentFolder->GetFlags() & MSG_FOLDER_FLAG_IMAPBOX) { MSG_IMAPFolderInfoMail *imapFolder = m_currentFolder->GetIMAPFolderInfoMail(); // if (imapFolder->GetHasOfflineEvents()) // XP_ASSERT(FALSE); if (!m_pseudoOffline) // if pseudo offline, falls through to playing ops back. { // there are operations to playback so check uid validity SetCurrentUIDValidity(0); // force initial invalid state url = CreateImapMailboxLITESelectUrl(imapFolder->GetHostName(), imapFolder->GetOnlineName(), imapFolder->GetOnlineHierarchySeparator()); if (url) { XP_Bool alreadyRunningQueue; MSG_UrlQueue *queue = GetUrlQueue(&alreadyRunningQueue); if (queue) { // should we insert this at 0, or add? I think we want to run offline events // before any new events...but this is just a lite select queue->AddUrl(url, OfflineOpExitFunction); if (!alreadyRunningQueue) queue->GetNextUrl(); return; // this is asynch, we have to return as be called again by the OfflineOpExitFunction } } } } } } if (!m_currentDB) { // only advance if we are doing all folders if (!m_singleFolderToUpdate) AdvanceToNextFolder(); else m_currentFolder = NULL; // force update of this folder now. } } // do the current operation if (m_currentDB) { XP_Bool currentFolderFinished = FALSE; // user canceled the lite select! if GetCurrentUIDValidity() == 0 if ((m_KeyIndex < m_CurrentKeys.GetSize()) && (m_pseudoOffline || (GetCurrentUIDValidity() != 0) || !(m_currentFolder->GetFlags() & MSG_FOLDER_FLAG_IMAPBOX)) ) { XP_Bool uidvalidityChanged = (!m_pseudoOffline && m_currentFolder->GetFlags() & MSG_FOLDER_FLAG_IMAPBOX) && (GetCurrentUIDValidity() != m_currentDB->m_dbFolderInfo->GetImapUidValidity()); DBOfflineImapOperation *currentOp = NULL; if (uidvalidityChanged) DeleteAllOfflineOpsForCurrentDB(); else currentOp = m_currentDB->GetOfflineOpForKey(m_CurrentKeys[m_KeyIndex], FALSE); if (currentOp) { // loop until we find the next db record that matches the current playback operation while (currentOp && !(currentOp->GetOperationFlags() & mCurrentPlaybackOpType)) { delete currentOp; currentOp = NULL; if (++m_KeyIndex < m_CurrentKeys.GetSize()) currentOp = m_currentDB->GetOfflineOpForKey(m_CurrentKeys[m_KeyIndex], FALSE); } // if we did not find a db record that matches the current playback operation, // then move to the next playback operation and recurse. if (!currentOp) { // we are done with the current type if (mCurrentPlaybackOpType == kFlagsChanged) { mCurrentPlaybackOpType = kMsgCopy; // recurse to deal with next type of operation m_KeyIndex = 0; ProcessNextOperation(); } else if (mCurrentPlaybackOpType == kMsgCopy) { mCurrentPlaybackOpType = kMsgMoved; // recurse to deal with next type of operation m_KeyIndex = 0; ProcessNextOperation(); } else if (mCurrentPlaybackOpType == kMsgMoved) { mCurrentPlaybackOpType = kAppendDraft; // recurse to deal with next type of operation m_KeyIndex = 0; ProcessNextOperation(); } else if (mCurrentPlaybackOpType == kAppendDraft) { mCurrentPlaybackOpType = kAppendTemplate; // recurse to deal with next type of operation m_KeyIndex = 0; ProcessNextOperation(); } else if (mCurrentPlaybackOpType == kAppendTemplate) { mCurrentPlaybackOpType = kDeleteAllMsgs; m_KeyIndex = 0; ProcessNextOperation(); } else { DeleteAllOfflineOpsForCurrentDB(); currentFolderFinished = TRUE; } } else { if (mCurrentPlaybackOpType == kFlagsChanged) ProcessFlagOperation(currentOp); else if (mCurrentPlaybackOpType == kMsgCopy) ProcessCopyOperation(currentOp); else if (mCurrentPlaybackOpType == kMsgMoved) ProcessMoveOperation(currentOp); else if (mCurrentPlaybackOpType == kAppendDraft) ProcessAppendMsgOperation(currentOp, kAppendDraft); else if (mCurrentPlaybackOpType == kAppendTemplate) ProcessAppendMsgOperation(currentOp, kAppendTemplate); else if (mCurrentPlaybackOpType == kDeleteAllMsgs) ProcessEmptyTrash(currentOp); else XP_ASSERT(FALSE); // currentOp was RemoveReferencered by one of the Process functions // so do not reference it again! currentOp = NULL; } } else currentFolderFinished = TRUE; } else currentFolderFinished = TRUE; if (currentFolderFinished) { m_currentDB->Close(); m_currentDB = NULL; if (!m_singleFolderToUpdate) { AdvanceToNextFolder(); ProcessNextOperation(); return; } else m_currentFolder = NULL; } } if (!m_currentFolder && !m_mailboxupdatesStarted) { m_mailboxupdatesStarted = TRUE; FE_Progress(m_workerPane->GetContext(), ""); // if we are updating more than one folder then we need the iterator MSG_FolderIterator *updateFolderIterator = m_singleFolderToUpdate ? (MSG_FolderIterator *)NULL : new MSG_FolderIterator(m_workerPane->GetMaster()->GetImapMailFolderTree()); MSG_UrlQueue *queue = NULL; if ((updateFolderIterator || m_singleFolderToUpdate)) { if (updateFolderIterator) { // this means that we are updating all of the folders. Update the INBOX first so the updates on the remaining // folders pickup the results of any filter moves. MSG_FolderInfo *inboxFolder; if (!m_pseudoOffline && m_workerPane->GetMaster()->GetImapMailFolderTree()->GetFoldersWithFlag(MSG_FOLDER_FLAG_INBOX, &inboxFolder, 1) ) ((MSG_IMAPFolderInfoMail *) inboxFolder)->StartUpdateOfNewlySelectedFolder(m_workerPane, FALSE, queue, NULL, FALSE, FALSE); } // we are done playing commands back, now queue up the sync with each imap folder MSG_FolderInfo* folder = m_singleFolderToUpdate ? m_singleFolderToUpdate : updateFolderIterator->First(); while (folder) { XP_Bool loadingFolder = m_workerPane->GetLoadingImapFolder() == folder; if ((folder->GetType() == FOLDER_IMAPMAIL) && (deletedAllOfflineEventsInFolder == folder || (folder->GetFolderPrefFlags() & MSG_FOLDER_PREF_OFFLINE) || loadingFolder) && !(folder->GetFolderPrefFlags() & MSG_FOLDER_PREF_IMAPNOSELECT) ) { MSG_IMAPFolderInfoMail *imapMail = (MSG_IMAPFolderInfoMail *) folder; XP_Bool lastChance = ((deletedAllOfflineEventsInFolder == folder) && m_singleFolderToUpdate) || loadingFolder; // if deletedAllOfflineEventsInFolder == folder and we're only updating one folder, then we need to update newly selected folder // I think this also means that we're really opening the folder...so we tell StartUpdate that we're loading a folder. if (!updateFolderIterator || !(imapMail->GetFlags() & MSG_FOLDER_FLAG_INBOX)) // avoid queueing the inbox twice imapMail->StartUpdateOfNewlySelectedFolder(m_workerPane, lastChance, queue, NULL, FALSE, FALSE); } folder= m_singleFolderToUpdate ? (MSG_FolderInfo *)NULL : updateFolderIterator->Next(); } MSG_UrlQueue::AddUrlToPane(kImapOnOffSynchCompleteURL, OffOnlineSynchExitFunction, m_workerPane); } } } void OfflineImapGoOnlineState::DeleteAllOfflineOpsForCurrentDB() { m_KeyIndex = 0; DBOfflineImapOperation *currentOp = m_currentDB->GetOfflineOpForKey(m_CurrentKeys[m_KeyIndex], FALSE); while (currentOp) { XP_ASSERT(currentOp->GetOperationFlags() == 0); // delete any ops that have already played back m_currentDB->DeleteOfflineOp(currentOp->GetMessageKey()); delete currentOp; currentOp = NULL; if (++m_KeyIndex < m_CurrentKeys.GetSize()) currentOp = m_currentDB->GetOfflineOpForKey(m_CurrentKeys[m_KeyIndex], FALSE); } MSG_FolderInfo *folderInfo = m_currentDB->GetFolderInfo(); // turn off MSG_FOLDER_PREF_OFFLINEEVENTS if (folderInfo) { folderInfo->SetFolderPrefFlags(folderInfo->GetFolderPrefFlags() & ~MSG_FOLDER_PREF_OFFLINEEVENTS); if (folderInfo->GetType() == FOLDER_IMAPMAIL) { MSG_IMAPFolderInfoMail *imapFolder = (MSG_IMAPFolderInfoMail *) folderInfo; imapFolder->SetHasOfflineEvents(FALSE); // where should we clear this flag? At the end of the process events url? } } } OfflineImapState::OfflineImapState(MSG_IMAPFolderInfoMail *folder) { XP_ASSERT(folder); m_maildb = NULL; m_msgHdr = NULL; SetFolderInfo(folder); } void OfflineImapState::SetFolderInfo(MSG_IMAPFolderInfoMail *folder) { XP_Bool wasCreated=FALSE; if (m_folderInfo != folder) { if (m_maildb) m_maildb->Close(); ImapMailDB::Open(folder->GetPathname(), TRUE, &m_maildb, folder->GetMaster(), &wasCreated); } m_folderInfo = folder; } OfflineImapState::~OfflineImapState() { delete m_msgHdr; if (m_maildb) m_maildb->Close(); } DownloadOfflineImapState::DownloadOfflineImapState(MSG_IMAPFolderInfoMail *folder, NET_StreamClass *displayStream) : OfflineImapState(folder), m_displayStream(displayStream) { m_offlineWriteFailure = FALSE; m_dbWriteDocument = NULL; m_deleteOnComplete = TRUE; } DownloadOfflineImapState::~DownloadOfflineImapState() { MSG_OfflineMsgDocumentHandle_Destroy(m_dbWriteDocument); } //static void DownloadOfflineImapState::CreateOfflineImapStream(NET_StreamClass *theStream, MSG_IMAPFolderInfoMail *folder, NET_StreamClass *displayStream, DownloadOfflineImapState *closureData) { XP_Bool deleteOnComplete = (closureData == NULL); if (!closureData) { closureData = new DownloadOfflineImapState(folder, displayStream); } else { closureData->m_displayStream = displayStream; // memory leak? closureData->SetFolderInfo(folder); } theStream->data_object = closureData; theStream->is_write_ready = DownloadOfflineImapState::OfflineImapStreamWriteReady; theStream->put_block = DownloadOfflineImapState::OfflineImapStreamWrite; theStream->complete = DownloadOfflineImapState::OfflineImapStreamComplete; theStream->abort = DownloadOfflineImapState::OfflineImapStreamAbort; } //static void DownloadOfflineImapState::OfflineImapStreamComplete(NET_StreamClass *stream) { DownloadOfflineImapState *state = (DownloadOfflineImapState *) stream->data_object; if (state) { state->CompleteStream(); if (state->m_dbWriteDocument) MSG_OfflineMsgDocumentHandle_Complete(state->m_dbWriteDocument); if (state->m_deleteOnComplete) delete state; } } //static void DownloadOfflineImapState::OfflineImapStreamAbort (NET_StreamClass *stream, int status) { DownloadOfflineImapState *state = (DownloadOfflineImapState *) stream->data_object; if (state) { state->AbortStream(status); delete state; } } //static int DownloadOfflineImapState::OfflineImapStreamWrite (NET_StreamClass *stream, const char *block, int32 messageKey) { DownloadOfflineImapState *state = (DownloadOfflineImapState *) stream->data_object; if (state) return state->WriteStream(block, messageKey); else return XP_STRLEN(block); } int DownloadOfflineImapState::WriteStream(const char *block, uint32 messageKey) { int returnLength = 0; int blockLength = XP_STRLEN(block); if (m_maildb) { if (!m_msgHdr || (m_msgHdr->GetMessageKey() != messageKey)) { if (m_msgHdr) { // this should not happen but be paranoid m_msgHdr->PurgeOfflineMessage(m_maildb->GetDB()); delete m_msgHdr; } m_msgHdr = m_maildb->GetMailHdrForKey(messageKey); if (!m_dbWriteDocument) m_dbWriteDocument = MSG_OfflineMsgDocumentHandle_Create(m_maildb->GetDB(), m_msgHdr->GetHandle()); else MSG_OfflineMsgDocumentHandle_SetMsgHeaderHandle(m_dbWriteDocument, m_msgHdr->GetHandle(), m_maildb->GetDB()); } if (m_msgHdr && m_dbWriteDocument && !m_offlineWriteFailure) { // int32 bytesAdded = m_msgHdr->AddToOfflineMessage(block, blockLength, m_maildb->GetDB()); int32 bytesAdded = MSG_OfflineMsgDocumentHandle_AddToOfflineMessage(m_dbWriteDocument, block, blockLength); m_offlineWriteFailure = bytesAdded != blockLength; } } if (m_maildb) returnLength += blockLength; if (m_displayStream) (*m_displayStream->put_block) (m_displayStream, block, XP_STRLEN(block)); return returnLength; } void DownloadOfflineImapState::CompleteStream() { if (m_maildb && m_msgHdr) { m_maildb->MarkOffline(m_msgHdr->GetMessageKey(), TRUE, NULL); delete m_msgHdr; m_msgHdr = NULL; } if ( m_displayStream) (*m_displayStream->complete) (m_displayStream); } void DownloadOfflineImapState::AbortStream(int /*status*/) { if (m_maildb && m_msgHdr) { m_msgHdr->PurgeOfflineMessage(m_maildb->GetDB()); delete m_msgHdr; m_msgHdr = NULL; } if ( m_displayStream) (*m_displayStream->abort) (m_displayStream, -1); } DisplayOfflineImapState::DisplayOfflineImapState(MSG_IMAPFolderInfoMail *folder, MessageKey key) : OfflineImapState(folder) { m_bytesDisplayedSoFar = 0; m_wasUnread = FALSE; if (m_maildb) { m_msgHdr = m_maildb->GetMailHdrForKey(key); if (m_msgHdr) m_wasUnread = !((m_msgHdr->GetFlags() & kIsRead) != 0); } } DisplayOfflineImapState::~DisplayOfflineImapState() { } uint32 DisplayOfflineImapState::GetMsgSize() { uint32 size = 0; if (m_msgHdr) size = m_msgHdr->GetOfflineMessageLength(m_maildb->GetDB()); return size; } int DisplayOfflineImapState::ProcessDisplay(char *socketBuffer, uint32 read_size) { uint32 bytesDisplayed = 0; if (m_msgHdr) { int32 offlineLength = m_msgHdr->GetOfflineMessageLength(m_maildb->GetDB()); if (0 == offlineLength) { if (0 == m_bytesDisplayedSoFar) { // annoy the user about going online const char *htmlAnnoyance = XP_GetString(MK_MSG_HTML_IMAP_NO_CACHED_BODY); bytesDisplayed = XP_STRLEN( htmlAnnoyance ) < read_size ? XP_STRLEN( htmlAnnoyance ) + 1 : read_size; XP_MEMCPY(socketBuffer, htmlAnnoyance, bytesDisplayed); m_bytesDisplayedSoFar = bytesDisplayed; } // else bytesDisplayed == 0, ProcessDisplay is not called again } else { uint32 bytesLeft = offlineLength - m_bytesDisplayedSoFar; uint32 bytesToDisplay = (read_size <= bytesLeft) ? read_size : bytesLeft; if (bytesToDisplay) { bytesDisplayed += m_msgHdr->ReadFromOfflineMessage(socketBuffer, bytesToDisplay, m_bytesDisplayedSoFar, m_maildb->GetDB()); m_bytesDisplayedSoFar += bytesDisplayed; } else if (NET_IsOffline() && m_wasUnread) { // we are done displaying this message. Save an operation to mark it read IDArray readIds; readIds.Add(m_msgHdr->GetMessageKey()); m_folderInfo->StoreImapFlags(NULL, kImapMsgSeenFlag, TRUE, readIds); } } } return bytesDisplayed; } AppendMsgOfflineImapState::AppendMsgOfflineImapState(MSG_IMAPFolderInfoMail *folder, MessageKey key, const char *msg_file_name) : OfflineImapState(folder) { m_msg_file_name = XP_STRDUP(msg_file_name); if (m_maildb) { m_msgHdr = m_maildb->GetMailHdrForKey(key); } } AppendMsgOfflineImapState::~AppendMsgOfflineImapState() { XP_FREEIF(m_msg_file_name); } void AppendMsgOfflineImapState::RemoveHdrFromDB() { if (m_maildb && m_msgHdr) { MessageKey doomedKey = m_msgHdr->GetMessageKey(); delete m_msgHdr; m_msgHdr = 0; m_maildb->DeleteMessage(doomedKey); } } void AppendMsgOfflineImapState::RemoveMsgFile() { if (m_msg_file_name) { XP_FileRemove(m_msg_file_name, xpFileToPost); XP_FREEIF(m_msg_file_name); } } /* member functions for offlinedb records */ DBOfflineImapOperation::DBOfflineImapOperation() { m_offlineIMAPOperationHandle = NULL; } DBOfflineImapOperation::~DBOfflineImapOperation() { if (m_offlineIMAPOperationHandle) MSG_OfflineIMAPOperationHandle_RemoveReference(m_offlineIMAPOperationHandle); } void DBOfflineImapOperation::SetMessageKey(MessageKey messageKey) { MSG_OfflineIMAPOperationHandle_SetMessageKey(GetHandle(), m_dbHandle, messageKey); } // the flags we start with void DBOfflineImapOperation::SetInitialImapFlags(imapMessageFlagsType flags) { MSG_OfflineIMAPOperationHandle_SetInitialImapFlags(GetHandle(), flags, m_dbHandle); } void DBOfflineImapOperation::SetImapFlagOperation(imapMessageFlagsType flags) { MSG_OfflineIMAPOperationHandle_SetImapFlagOperation(GetHandle(), flags, m_dbHandle); } void DBOfflineImapOperation::ClearImapFlagOperation() { MSG_OfflineIMAPOperationHandle_ClearImapFlagOperation(GetHandle(), m_dbHandle); } uint32 DBOfflineImapOperation::GetOperationFlags() { return MSG_OfflineIMAPOperationHandle_GetOperationFlags(GetHandle(), m_dbHandle); } imapMessageFlagsType DBOfflineImapOperation::GetNewMessageFlags() { return MSG_OfflineIMAPOperationHandle_GetNewMessageFlags(GetHandle(), m_dbHandle); } void DBOfflineImapOperation::SetAppendMsgOperation(const char *destinationBox, int32 opType) { MSG_OfflineIMAPOperationHandle_SetAppendMsgOperation(GetHandle(), m_dbHandle, destinationBox, opType); } void DBOfflineImapOperation::ClearAppendMsgOperation(MSG_DBHandle dbHandle, int32 opType) { MSG_OfflineIMAPOperationHandle_ClearAppendMsgOperation(GetHandle(), dbHandle, opType); } void DBOfflineImapOperation::SetMessageMoveOperation(const char *destinationBox) { MSG_OfflineIMAPOperationHandle_SetMessageMoveOperation(GetHandle(), m_dbHandle, destinationBox); } void DBOfflineImapOperation::ClearMoveOperation(MSG_DBHandle dbHandle) { MSG_OfflineIMAPOperationHandle_ClearMoveOperation(GetHandle(), dbHandle); } void DBOfflineImapOperation::AddMessageCopyOperation(const char *destinationBox) { MSG_OfflineIMAPOperationHandle_AddMessageCopyOperation(GetHandle(), m_dbHandle, destinationBox); } void DBOfflineImapOperation::ClearFirstCopyOperation(MSG_DBHandle dbHandle) { MSG_OfflineIMAPOperationHandle_ClearFirstCopyOperation(GetHandle(), m_dbHandle); } void DBOfflineImapOperation::SetDeleteAllMsgs() { MSG_OfflineIMAPOperationHandle_SetDeleteAllMsgs(GetHandle(), m_dbHandle); } void DBOfflineImapOperation::ClearDeleteAllMsgs() { MSG_OfflineIMAPOperationHandle_ClearDeleteAllMsgs(GetHandle(), m_dbHandle); } void DBOfflineImapOperation::SetSourceMailbox(const char *sourceMailbox, MessageId sourceKey) { MSG_OfflineIMAPOperationHandle_SetSourceMailbox(GetHandle(), m_dbHandle, sourceMailbox, sourceKey); } // This is the key used in the database, which is almost always the same // as the m_messageKey, except for the first message in a mailbox, // which has a m_messageKey of 0, but a non-zero ID in the database. MessageKey DBOfflineImapOperation::GetMessageKey() { return MSG_OfflineIMAPOperationHandle_GetMessageKey(GetHandle(), m_dbHandle); } XP_Bool DBOfflineImapOperation::GetMoveDestination(MSG_DBHandle dbHandle, XPStringObj &boxName) { char *boxNameStr; XP_Bool ret = MSG_OfflineIMAPOperationHandle_GetMoveDestination(GetHandle(), dbHandle, &boxNameStr); boxName.SetStrPtr(boxNameStr); return ret; } uint32 DBOfflineImapOperation::GetNumberOfCopyOps() { return MSG_OfflineIMAPOperationHandle_GetNumberOfCopyOps(GetHandle(), m_dbHandle); } XP_Bool DBOfflineImapOperation::GetIndexedCopyDestination(MSG_DBHandle dbHandle, uint32 index, XPStringObj &boxName) { char *boxNameStr; XP_Bool ret = MSG_OfflineIMAPOperationHandle_GetIndexedCopyDestination(GetHandle(), dbHandle, index, &boxNameStr); if (ret) boxName.SetStrPtr(boxNameStr); return ret; } XP_Bool DBOfflineImapOperation::GetSourceInfo(XPStringObj &sourceBoxName, MessageId &sourceKey) { sourceKey = MSG_OfflineIMAPOperationHandle_GetSourceMessageKey(GetHandle(), m_dbHandle); return GetMoveDestination(m_dbHandle, sourceBoxName); }