NS_IMETHODIMP nsFolderCompactState::OnStopRequest(nsIRequest *request, nsISupports *ctxt, nsresult status) { nsCOMPtr<nsIMsgDBHdr> msgHdr; if (NS_FAILED(status)) { m_status = status; // set the m_status to status so the destructor can remove the // temp folder and database CleanupTempFilesAfterError(); m_folder->NotifyCompactCompleted(); ReleaseFolderLock(); m_folder->ThrowAlertMsg("compactFolderWriteFailed", m_window); } else { EndCopy(nullptr, status); if (m_curIndex >= m_size) { msgHdr = nullptr; // no more to copy finish it up FinishCompact(); } else { // in case we're not getting an error, we still need to pretend we did get an error, // because the compact did not successfully complete. m_folder->NotifyCompactCompleted(); CleanupTempFilesAfterError(); ReleaseFolderLock(); } } Release(); // kill self return status; }
nsFolderCompactState::~nsFolderCompactState() { CloseOutputStream(); if (NS_FAILED(m_status)) { CleanupTempFilesAfterError(); // if for some reason we failed remove the temp folder and database } }
nsresult nsFolderCompactState::Init(nsIMsgFolder *folder, const char *baseMsgUri, nsIMsgDatabase *db, nsIFile *path, nsIMsgWindow *aMsgWindow) { nsresult rv; m_folder = folder; m_baseMessageUri = baseMsgUri; m_file = do_CreateInstance(NS_LOCAL_FILE_CONTRACTID, &rv); NS_ENSURE_SUCCESS(rv, rv); m_file->InitWithFile(path); // need to make sure the temp file goes in the same real directory // as the original file, so resolve sym links. m_file->SetFollowLinks(true); m_file->SetNativeLeafName(NS_LITERAL_CSTRING("nstmp")); rv = m_file->CreateUnique(nsIFile::NORMAL_FILE_TYPE, 00600); //make sure we are not crunching existing nstmp file NS_ENSURE_SUCCESS(rv, rv); m_window = aMsgWindow; m_keyArray = new nsMsgKeyArray; m_size = 0; m_totalMsgSize = 0; rv = InitDB(db); if (NS_FAILED(rv)) { CleanupTempFilesAfterError(); return rv; } m_curIndex = 0; rv = MsgNewBufferedFileOutputStream(getter_AddRefs(m_fileStream), m_file, -1, 00600); if (NS_FAILED(rv)) m_folder->ThrowAlertMsg("compactFolderWriteFailed", m_window); else rv = GetMessageServiceFromURI(nsDependentCString(baseMsgUri), getter_AddRefs(m_messageService)); if (NS_FAILED(rv)) { m_status = rv; } return rv; }
NS_IMETHODIMP nsFolderCompactState::Compact(nsIMsgFolder *folder, bool aOfflineStore, nsIUrlListener *aListener, nsIMsgWindow *aMsgWindow) { NS_ENSURE_ARG_POINTER(folder); m_listener = aListener; if (!m_compactingOfflineFolders && !aOfflineStore) { nsCOMPtr <nsIMsgImapMailFolder> imapFolder = do_QueryInterface(folder); if (imapFolder) return imapFolder->Expunge(this, aMsgWindow); } m_window = aMsgWindow; nsresult rv; nsCOMPtr<nsIMsgDatabase> db; nsCOMPtr<nsIDBFolderInfo> folderInfo; nsCOMPtr<nsIMsgDatabase> mailDBFactory; nsCOMPtr<nsILocalFile> path; nsCString baseMessageURI; nsCOMPtr <nsIMsgLocalMailFolder> localFolder = do_QueryInterface(folder, &rv); if (NS_SUCCEEDED(rv) && localFolder) { rv=localFolder->GetDatabaseWOReparse(getter_AddRefs(db)); if (NS_FAILED(rv) || !db) { if (rv == NS_MSG_ERROR_FOLDER_SUMMARY_MISSING || rv == NS_MSG_ERROR_FOLDER_SUMMARY_OUT_OF_DATE) { m_folder = folder; //will be used to compact m_parsingFolder = true; rv = localFolder->ParseFolder(m_window, this); } return rv; } else { bool valid; rv = db->GetSummaryValid(&valid); if (!valid) //we are probably parsing the folder because we selected it. { folder->NotifyCompactCompleted(); if (m_compactAll) return CompactNextFolder(); else return NS_OK; } } } else { rv = folder->GetMsgDatabase(getter_AddRefs(db)); NS_ENSURE_SUCCESS(rv, rv); } rv = folder->GetFilePath(getter_AddRefs(path)); NS_ENSURE_SUCCESS(rv, rv); rv = folder->GetBaseMessageURI(baseMessageURI); NS_ENSURE_SUCCESS(rv, rv); rv = Init(folder, baseMessageURI.get(), db, path, m_window); NS_ENSURE_SUCCESS(rv, rv); bool isLocked; m_folder->GetLocked(&isLocked); if(!isLocked) { nsCOMPtr <nsISupports> supports = do_QueryInterface(static_cast<nsIMsgFolderCompactor*>(this)); m_folder->AcquireSemaphore(supports); return StartCompacting(); } else { m_folder->NotifyCompactCompleted(); m_folder->ThrowAlertMsg("compactFolderDeniedLock", m_window); CleanupTempFilesAfterError(); if (m_compactAll) return CompactNextFolder(); else return NS_OK; } }
NS_IMETHODIMP nsFolderCompactState::Compact(nsIMsgFolder *folder, bool aOfflineStore, nsIUrlListener *aListener, nsIMsgWindow *aMsgWindow) { NS_ENSURE_ARG_POINTER(folder); m_listener = aListener; if (!m_compactingOfflineFolders && !aOfflineStore) { nsCOMPtr <nsIMsgImapMailFolder> imapFolder = do_QueryInterface(folder); if (imapFolder) return imapFolder->Expunge(this, aMsgWindow); } m_window = aMsgWindow; nsresult rv; nsCOMPtr<nsIMsgDatabase> db; nsCOMPtr<nsIFile> path; nsCString baseMessageURI; nsCOMPtr <nsIMsgLocalMailFolder> localFolder = do_QueryInterface(folder, &rv); if (NS_SUCCEEDED(rv) && localFolder) { rv=localFolder->GetDatabaseWOReparse(getter_AddRefs(db)); if (NS_FAILED(rv) || !db) { if (rv == NS_MSG_ERROR_FOLDER_SUMMARY_MISSING || rv == NS_MSG_ERROR_FOLDER_SUMMARY_OUT_OF_DATE) { m_folder = folder; //will be used to compact m_parsingFolder = true; rv = localFolder->ParseFolder(m_window, this); } return rv; } else { bool valid; rv = db->GetSummaryValid(&valid); if (!valid) //we are probably parsing the folder because we selected it. { folder->NotifyCompactCompleted(); if (m_compactAll) return CompactNextFolder(); else return NS_OK; } } } else { rv = folder->GetMsgDatabase(getter_AddRefs(db)); NS_ENSURE_SUCCESS(rv, rv); } rv = folder->GetFilePath(getter_AddRefs(path)); NS_ENSURE_SUCCESS(rv, rv); do { bool exists = false; rv = path->Exists(&exists); if (!exists) { // No need to compact if the local file does not exist. // Can happen e.g. on IMAP when the folder is not marked for offline use. break; } int64_t expunged = 0; folder->GetExpungedBytes(&expunged); if (expunged == 0) { // No need to compact if nothing would be expunged. break; } int64_t diskSize; rv = folder->GetSizeOnDisk(&diskSize); NS_ENSURE_SUCCESS(rv, rv); int64_t diskFree; rv = path->GetDiskSpaceAvailable(&diskFree); if (NS_FAILED(rv)) { // If GetDiskSpaceAvailable() failed, better bail out fast. if (rv != NS_ERROR_NOT_IMPLEMENTED) return rv; // Some platforms do not have GetDiskSpaceAvailable implemented. // In that case skip the preventive free space analysis and let it // fail in compact later if space actually wasn't available. } else { // Let's try to not even start compact if there is really low free space. // It may still fail later as we do not know how big exactly the folder DB will // end up being. // The DB already doesn't contain references to messages that are already deleted. // So theoretically it shouldn't shrink with compact. But in practice, // the automatic shrinking of the DB may still have not yet happened. // So we cap the final size at 1KB per message. db->Commit(nsMsgDBCommitType::kCompressCommit); int64_t dbSize; rv = db->GetDatabaseSize(&dbSize); NS_ENSURE_SUCCESS(rv, rv); int32_t totalMsgs; rv = folder->GetTotalMessages(false, &totalMsgs); NS_ENSURE_SUCCESS(rv, rv); int64_t expectedDBSize = std::min<int64_t>(dbSize, ((int64_t)totalMsgs) * 1024); if (diskFree < diskSize - expunged + expectedDBSize) { if (!m_alreadyWarnedDiskSpace) { folder->ThrowAlertMsg("compactFolderInsufficientSpace", m_window); m_alreadyWarnedDiskSpace = true; } break; } } rv = folder->GetBaseMessageURI(baseMessageURI); NS_ENSURE_SUCCESS(rv, rv); rv = Init(folder, baseMessageURI.get(), db, path, m_window); NS_ENSURE_SUCCESS(rv, rv); bool isLocked = true; m_folder->GetLocked(&isLocked); if (isLocked) { CleanupTempFilesAfterError(); m_folder->ThrowAlertMsg("compactFolderDeniedLock", m_window); break; } // If we got here start the real compacting. nsCOMPtr<nsISupports> supports = do_QueryInterface(static_cast<nsIMsgFolderCompactor*>(this)); m_folder->AcquireSemaphore(supports); m_totalExpungedBytes += expunged; return StartCompacting(); } while(false); // block for easy skipping the compaction using 'break' folder->NotifyCompactCompleted(); if (m_compactAll) return CompactNextFolder(); else return NS_OK; }