NS_IMETHODIMP nsFolderCompactState::OnStopRunningUrl(nsIURI *url, nsresult status) { if (m_parsingFolder) { m_parsingFolder = false; if (NS_SUCCEEDED(status)) status = Compact(m_folder, m_compactingOfflineFolders, m_listener, m_window); else if (m_compactAll) CompactNextFolder(); } else if (m_compactAll) // this should be the imap case only { nsCOMPtr <nsIMsgFolder> prevFolder = do_QueryElementAt(m_folderArray, m_folderIndex); if (prevFolder) prevFolder->SetMsgDatabase(nsnull); CompactNextFolder(); } else if (m_listener) { CompactCompleted(status); } return NS_OK; }
nsresult nsOfflineStoreCompactState::FinishCompact() { // All okay time to finish up the compact process nsCOMPtr<nsILocalFile> path; PRUint32 flags; // get leaf name and database name of the folder m_folder->GetFlags(&flags); nsresult rv = m_folder->GetFilePath(getter_AddRefs(path)); nsCString leafName; path->GetNativeLeafName(leafName); if (m_fileStream) { // close down the temp file stream; preparing for deleting the old folder // and its database; then rename the temp folder and database m_fileStream->Flush(); m_fileStream->Close(); m_fileStream = nsnull; } // make sure the new database is valid nsCOMPtr <nsIDBFolderInfo> dbFolderInfo; m_db->GetDBFolderInfo(getter_AddRefs(dbFolderInfo)); if (dbFolderInfo) dbFolderInfo->SetExpungedBytes(0); // this forces the m_folder to update mExpungedBytes from the db folder info. PRUint32 expungedBytes; m_folder->GetExpungedBytes(&expungedBytes); m_folder->UpdateSummaryTotals(true); m_db->SetSummaryValid(true); // remove the old folder path->Remove(false); // rename the copied folder to be the original folder m_file->MoveToNative((nsIFile *) nsnull, leafName); ShowStatusMsg(EmptyString()); m_folder->NotifyCompactCompleted(); if (m_compactAll) rv = CompactNextFolder(); return rv; }
nsresult nsFolderCompactState::FinishCompact() { if (!m_folder) return NS_ERROR_NOT_INITIALIZED; // All okay time to finish up the compact process nsCOMPtr<nsILocalFile> path; nsCOMPtr<nsIDBFolderInfo> folderInfo; // get leaf name and database name of the folder nsresult rv = m_folder->GetFilePath(getter_AddRefs(path)); nsCOMPtr <nsILocalFile> folderPath = do_CreateInstance(NS_LOCAL_FILE_CONTRACTID, &rv); NS_ENSURE_SUCCESS(rv, rv); nsCOMPtr <nsILocalFile> summaryFile; folderPath->InitWithFile(path); // need to make sure we put the .msf file in the same directory // as the original mailbox, so resolve symlinks. folderPath->SetFollowLinks(true); GetSummaryFileLocation(folderPath, getter_AddRefs(summaryFile)); nsCString leafName; summaryFile->GetNativeLeafName(leafName); nsCAutoString dbName(leafName); path->GetNativeLeafName(leafName); // close down the temp file stream; preparing for deleting the old folder // and its database; then rename the temp folder and database m_fileStream->Flush(); m_fileStream->Close(); m_fileStream = nsnull; // make sure the new database is valid. // Close it so we can rename the .msf file. if (m_db) { m_db->ForceClosed(); m_db = nsnull; } nsCOMPtr <nsILocalFile> newSummaryFile; GetSummaryFileLocation(m_file, getter_AddRefs(newSummaryFile)); nsCOMPtr <nsIDBFolderInfo> transferInfo; m_folder->GetDBTransferInfo(getter_AddRefs(transferInfo)); // close down database of the original folder m_folder->ForceDBClosed(); nsCOMPtr<nsIFile> cloneFile; PRInt64 fileSize; m_file->Clone(getter_AddRefs(cloneFile)); cloneFile->GetFileSize(&fileSize); bool tempFileRightSize = (fileSize == m_totalMsgSize); NS_ASSERTION(tempFileRightSize, "temp file not of expected size in compact"); bool folderRenameSucceeded = false; bool msfRenameSucceeded = false; if (tempFileRightSize) { bool summaryFileExists; // remove the old folder and database rv = summaryFile->Remove(false); summaryFile->Exists(&summaryFileExists); if (NS_SUCCEEDED(rv) && !summaryFileExists) { bool folderPathExists; rv = folderPath->Remove(false); folderPath->Exists(&folderPathExists); if (NS_SUCCEEDED(rv) && !folderPathExists) { // rename the copied folder and database to be the original folder and // database rv = m_file->MoveToNative((nsIFile *) nsnull, leafName); NS_ASSERTION(NS_SUCCEEDED(rv), "error renaming compacted folder"); if (NS_SUCCEEDED(rv)) { folderRenameSucceeded = true; rv = newSummaryFile->MoveToNative((nsIFile *) nsnull, dbName); NS_ASSERTION(NS_SUCCEEDED(rv), "error renaming compacted folder's db"); msfRenameSucceeded = NS_SUCCEEDED(rv); } } } NS_ASSERTION(msfRenameSucceeded && folderRenameSucceeded, "rename failed in compact"); } if (!folderRenameSucceeded) m_file->Remove(false); if (!msfRenameSucceeded) newSummaryFile->Remove(false); rv = ReleaseFolderLock(); NS_ASSERTION(NS_SUCCEEDED(rv),"folder lock not released successfully"); if (msfRenameSucceeded && folderRenameSucceeded) { nsCOMPtr<nsIMsgDBService> msgDBService = do_GetService(NS_MSGDB_SERVICE_CONTRACTID, &rv); NS_ENSURE_SUCCESS(rv, rv); rv = msgDBService->OpenFolderDB(m_folder, true, getter_AddRefs(m_db)); m_db->SetSummaryValid(true); m_folder->SetDBTransferInfo(transferInfo); nsCOMPtr<nsIDBFolderInfo> dbFolderInfo; m_db->GetDBFolderInfo(getter_AddRefs(dbFolderInfo)); // since we're transferring info from the old db, we need to reset the expunged bytes, // and set the summary valid again. if(dbFolderInfo) dbFolderInfo->SetExpungedBytes(0); } if (m_db) m_db->Close(true); m_db = nsnull; // Notify that compaction of the folder is completed. nsCOMPtr<nsIMsgFolderNotificationService> notifier(do_GetService(NS_MSGNOTIFICATIONSERVICE_CONTRACTID)); if (notifier) notifier->NotifyItemEvent(m_folder, NS_LITERAL_CSTRING("FolderCompactFinish"), nsnull); m_folder->NotifyCompactCompleted(); if (m_compactAll) rv = CompactNextFolder(); else CompactCompleted(NS_OK); 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; } }
nsresult nsFolderCompactState::FinishCompact() { NS_ENSURE_TRUE(m_folder, NS_ERROR_NOT_INITIALIZED); NS_ENSURE_TRUE(m_file, NS_ERROR_NOT_INITIALIZED); // All okay time to finish up the compact process nsCOMPtr<nsIFile> path; nsCOMPtr<nsIDBFolderInfo> folderInfo; // get leaf name and database name of the folder nsresult rv = m_folder->GetFilePath(getter_AddRefs(path)); nsCOMPtr <nsIFile> folderPath = do_CreateInstance(NS_LOCAL_FILE_CONTRACTID, &rv); NS_ENSURE_SUCCESS(rv, rv); rv = folderPath->InitWithFile(path); NS_ENSURE_SUCCESS(rv, rv); // need to make sure we put the .msf file in the same directory // as the original mailbox, so resolve symlinks. folderPath->SetFollowLinks(true); nsCOMPtr <nsIFile> oldSummaryFile; rv = GetSummaryFileLocation(folderPath, getter_AddRefs(oldSummaryFile)); NS_ENSURE_SUCCESS(rv, rv); nsAutoCString dbName; oldSummaryFile->GetNativeLeafName(dbName); nsAutoCString folderName; path->GetNativeLeafName(folderName); // close down the temp file stream; preparing for deleting the old folder // and its database; then rename the temp folder and database if (m_fileStream) { m_fileStream->Flush(); m_fileStream->Close(); m_fileStream = nullptr; } // make sure the new database is valid. // Close it so we can rename the .msf file. if (m_db) { m_db->ForceClosed(); m_db = nullptr; } nsCOMPtr <nsIFile> newSummaryFile; rv = GetSummaryFileLocation(m_file, getter_AddRefs(newSummaryFile)); NS_ENSURE_SUCCESS(rv, rv); nsCOMPtr <nsIDBFolderInfo> transferInfo; m_folder->GetDBTransferInfo(getter_AddRefs(transferInfo)); // close down database of the original folder m_folder->ForceDBClosed(); nsCOMPtr<nsIFile> cloneFile; int64_t fileSize; rv = m_file->Clone(getter_AddRefs(cloneFile)); if (NS_SUCCEEDED(rv)) rv = cloneFile->GetFileSize(&fileSize); bool tempFileRightSize = (fileSize == m_totalMsgSize); NS_WARN_IF_FALSE(tempFileRightSize, "temp file not of expected size in compact"); bool folderRenameSucceeded = false; bool msfRenameSucceeded = false; if (NS_SUCCEEDED(rv) && tempFileRightSize) { // First we're going to try and move the old summary file out the way. // We don't delete it yet, as we want to keep the files in sync. nsCOMPtr<nsIFile> tempSummaryFile; rv = oldSummaryFile->Clone(getter_AddRefs(tempSummaryFile)); if (NS_SUCCEEDED(rv)) rv = tempSummaryFile->CreateUnique(nsIFile::NORMAL_FILE_TYPE, 0600); nsAutoCString tempSummaryFileName; if (NS_SUCCEEDED(rv)) rv = tempSummaryFile->GetNativeLeafName(tempSummaryFileName); if (NS_SUCCEEDED(rv)) rv = oldSummaryFile->MoveToNative((nsIFile*) nullptr, tempSummaryFileName); NS_WARN_IF_FALSE(NS_SUCCEEDED(rv), "error moving compacted folder's db out of the way"); if (NS_SUCCEEDED(rv)) { // Now we've successfully moved the summary file out the way, try moving // the newly compacted message file over the old one. rv = m_file->MoveToNative((nsIFile *) nullptr, folderName); folderRenameSucceeded = NS_SUCCEEDED(rv); NS_WARN_IF_FALSE(folderRenameSucceeded, "error renaming compacted folder"); if (folderRenameSucceeded) { // That worked, so land the new summary file in the right place. nsCOMPtr<nsIFile> renamedCompactedSummaryFile; newSummaryFile->Clone(getter_AddRefs(renamedCompactedSummaryFile)); if (renamedCompactedSummaryFile) { rv = renamedCompactedSummaryFile->MoveToNative((nsIFile *) nullptr, dbName); msfRenameSucceeded = NS_SUCCEEDED(rv); } NS_WARN_IF_FALSE(msfRenameSucceeded, "error renaming compacted folder's db"); } if (!msfRenameSucceeded) { // Do our best to put the summary file back to where it was rv = tempSummaryFile->MoveToNative((nsIFile*) nullptr, dbName); if (NS_SUCCEEDED(rv)) tempSummaryFile = nullptr; // flagging that a renamed db no longer exists else NS_WARNING("error restoring uncompacted folder's db"); } } // We don't want any temporarily renamed summary file to lie around if (tempSummaryFile) tempSummaryFile->Remove(false); } NS_WARN_IF_FALSE(msfRenameSucceeded, "compact failed"); rv = ReleaseFolderLock(); NS_WARN_IF_FALSE(NS_SUCCEEDED(rv),"folder lock not released successfully"); // Cleanup of nstmp-named compacted files if failure if (!folderRenameSucceeded) { // remove the abandoned compacted version with the wrong name m_file->Remove(false); } if (!msfRenameSucceeded) { // remove the abandoned compacted summary file newSummaryFile->Remove(false); } if (msfRenameSucceeded) { // Transfer local db information from transferInfo nsCOMPtr<nsIMsgDBService> msgDBService = do_GetService(NS_MSGDB_SERVICE_CONTRACTID, &rv); NS_ENSURE_SUCCESS(rv, rv); rv = msgDBService->OpenFolderDB(m_folder, true, getter_AddRefs(m_db)); NS_ENSURE_TRUE(m_db, NS_FAILED(rv) ? rv : NS_ERROR_FAILURE); m_db->SetSummaryValid(true); m_folder->SetDBTransferInfo(transferInfo); // since we're transferring info from the old db, we need to reset the expunged bytes nsCOMPtr<nsIDBFolderInfo> dbFolderInfo; m_db->GetDBFolderInfo(getter_AddRefs(dbFolderInfo)); if(dbFolderInfo) dbFolderInfo->SetExpungedBytes(0); } if (m_db) m_db->Close(true); m_db = nullptr; // Notify that compaction of the folder is completed. nsCOMPtr<nsIMsgFolderNotificationService> notifier(do_GetService(NS_MSGNOTIFICATIONSERVICE_CONTRACTID)); if (notifier) notifier->NotifyItemEvent(m_folder, NS_LITERAL_CSTRING("FolderCompactFinish"), nullptr); m_folder->NotifyCompactCompleted(); if (m_compactAll) rv = CompactNextFolder(); else CompactCompleted(NS_OK); 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<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; }