change folder compaction to use base class offine store compaction code, 15865 r=naving, sr=mscott

This commit is contained in:
bienvenu%netscape.com 2001-01-18 15:37:52 +00:00
parent 0cddd31ff7
commit f7d03cb909

View File

@ -82,6 +82,7 @@
#include "nsIInterfaceRequestor.h"
#include "nsIPop3URL.h"
#include "nsIMsgMailSession.h"
#include "nsIMsgFolderCompactor.h"
static NS_DEFINE_CID(kRDFServiceCID, NS_RDFSERVICE_CID);
@ -100,278 +101,6 @@ extern char* ReadPopData(const char *hostname, const char* username, nsIFileSpec
extern void SavePopData(char *data, nsIFileSpec* maildirectory);
extern void net_pop3_delete_if_in_server(char *data, char *uidl, PRBool *changed);
extern void KillPopData(char* data);
//static void net_pop3_free_state(Pop3UidlHost* host);
//////////////////////////////////////////////////////////////////////////////
// nsFolderCompactState
//////////////////////////////////////////////////////////////////////////////
class nsFolderCompactState : public nsIStreamListener
{
public:
NS_DECL_ISUPPORTS
NS_DECL_NSISTREAMOBSERVER
NS_DECL_NSISTREAMLISTENER
nsFolderCompactState(void);
virtual ~nsFolderCompactState(void);
nsresult FinishCompact();
nsresult GetMessage(nsIMessage **message);
char *m_baseMessageUri; // base message uri
nsCString m_messageUri; // current message uri being copy
nsCOMPtr<nsIMsgFolder> m_folder; // current folder being compact
nsCOMPtr<nsIMsgDatabase> m_db; // new database for the compact folder
nsFileSpec m_fileSpec; // new mailbox for the compact folder
nsOutputFileStream *m_fileStream; // output file stream for writing
nsMsgKeyArray m_keyArray; // all message keys need to be copied over
PRInt32 m_size; // size of the message key array
PRInt32 m_curIndex; // index of the current copied message key in key array
nsMsgKey m_newKey; // new message key for the copied message
char m_dataBuffer[FOUR_K + 1]; // temp data buffer for copying message
nsresult m_status; // the status of the copying operation
nsIMsgMessageService* m_messageService; // message service for copying
// message
};
NS_IMPL_ISUPPORTS2(nsFolderCompactState, nsIStreamObserver, nsIStreamListener)
nsFolderCompactState::nsFolderCompactState()
{
NS_INIT_ISUPPORTS();
m_baseMessageUri = nsnull;
m_fileStream = nsnull;
m_size = 0;
m_curIndex = -1;
m_status = NS_OK;
m_messageService = nsnull;
}
nsFolderCompactState::~nsFolderCompactState()
{
if (m_fileStream)
{
m_fileStream->close();
delete m_fileStream;
m_fileStream = nsnull;
}
if (m_messageService)
{
ReleaseMessageServiceFromURI(m_baseMessageUri, m_messageService);
m_messageService = nsnull;
}
if (m_baseMessageUri)
{
nsCRT::free(m_baseMessageUri);
m_baseMessageUri = nsnull;
}
if (NS_FAILED(m_status))
{
// if for some reason we failed remove the temp folder and database
if (m_db)
m_db->ForceClosed();
nsLocalFolderSummarySpec summarySpec(m_fileSpec);
m_fileSpec.Delete(PR_FALSE);
summarySpec.Delete(PR_FALSE);
}
}
nsresult
nsFolderCompactState::FinishCompact()
{
// All okay time to finish up the compact process
nsresult rv = NS_OK;
nsCOMPtr<nsIFileSpec> pathSpec;
nsCOMPtr<nsIFolder> parent;
nsCOMPtr<nsIMsgFolder> parentFolder;
nsCOMPtr<nsIDBFolderInfo> folderInfo;
nsFileSpec fileSpec;
PRUint32 flags;
// get leaf name and database name of the folder
m_folder->GetFlags(&flags);
rv = m_folder->GetPath(getter_AddRefs(pathSpec));
pathSpec->GetFileSpec(&fileSpec);
nsLocalFolderSummarySpec summarySpec(fileSpec);
nsXPIDLCString idlName;
nsString dbName;
pathSpec->GetLeafName(getter_Copies(idlName));
summarySpec.GetLeafName(dbName);
// 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();
delete m_fileStream;
m_fileStream = nsnull;
// make sure the new database is valid
m_db->SetSummaryValid(PR_TRUE);
m_db->Commit(nsMsgDBCommitType::kLargeCommit);
m_db->ForceClosed();
m_db = null_nsCOMPtr();
nsLocalFolderSummarySpec newSummarySpec(m_fileSpec);
// close down database of the original folder and remove the folder node
// and all it's message node from the tree
m_folder->ForceDBClosed();
m_folder->GetParent(getter_AddRefs(parent));
parentFolder = do_QueryInterface(parent, &rv);
m_folder->SetParent(nsnull);
parentFolder->PropagateDelete(m_folder, PR_FALSE);
// remove the old folder and database
fileSpec.Delete(PR_FALSE);
summarySpec.Delete(PR_FALSE);
// rename the copied folder and database to be the original folder and
// database
m_fileSpec.Rename((const char*) idlName);
newSummarySpec.Rename(dbName);
// add the node back the tree
nsCOMPtr<nsIMsgFolder> child;
nsAutoString folderName; folderName.AssignWithConversion((const char*) idlName);
rv = parentFolder->AddSubfolder(&folderName, getter_AddRefs(child));
if (child)
{
child->SetFlags(flags);
nsCOMPtr<nsISupports> childSupports = do_QueryInterface(child);
nsCOMPtr<nsISupports> parentSupports = do_QueryInterface(parentFolder);
if (childSupports && parentSupports)
{
parentFolder->NotifyItemAdded(parentSupports, childSupports,
"folderView");
}
}
return rv;
}
nsresult
nsFolderCompactState::GetMessage(nsIMessage **message)
{
nsresult rv = NS_ERROR_NULL_POINTER;
if (!message) return rv;
NS_WITH_SERVICE(nsIRDFService, rdfService, kRDFServiceCID, &rv);
if(NS_SUCCEEDED(rv))
{
nsCOMPtr<nsIRDFResource> msgResource;
if(NS_SUCCEEDED(rdfService->GetResource(m_messageUri,
getter_AddRefs(msgResource))))
rv = msgResource->QueryInterface(NS_GET_IID(nsIMessage),
(void**)message);
}
return rv;
}
NS_IMETHODIMP
nsFolderCompactState::OnStartRequest(nsIChannel *channel, nsISupports *ctxt)
{
nsresult rv = NS_ERROR_FAILURE;
NS_ASSERTION(m_fileStream, "Fatal, null m_fileStream...\n");
if (m_fileStream)
{
// record the new message key for the message
m_fileStream->flush();
m_newKey = m_fileStream->tell();
rv = NS_OK;
}
return rv;
}
NS_IMETHODIMP
nsFolderCompactState::OnStopRequest(nsIChannel *channel, nsISupports *ctxt,
nsresult status,
const PRUnichar *errorMsg)
{
nsresult rv = status;
nsCOMPtr<nsIMessage> message;
nsCOMPtr<nsIDBMessage> dbMessage;
nsCOMPtr<nsIURI> uri;
nsCOMPtr<nsIMsgDBHdr> msgHdr;
nsCOMPtr<nsIMsgDBHdr> newMsgHdr;
if (NS_FAILED(rv)) goto done;
uri = do_QueryInterface(ctxt, &rv);
if (NS_FAILED(rv)) goto done;
rv = GetMessage(getter_AddRefs(message));
if (NS_FAILED(rv)) goto done;
dbMessage = do_QueryInterface(message, &rv);
if (NS_FAILED(rv)) goto done;
rv = dbMessage->GetMsgDBHdr(getter_AddRefs(msgHdr));
if (NS_FAILED(rv)) goto done;
// okay done with the current message; copying the existing message header
// to the new database
m_db->CopyHdrFromExistingHdr(m_newKey, msgHdr, PR_TRUE,
getter_AddRefs(newMsgHdr));
m_db->Commit(nsMsgDBCommitType::kLargeCommit);
// advance to next message
m_curIndex ++;
if (m_curIndex >= m_size)
{
msgHdr = nsnull;;
newMsgHdr = nsnull;
// no more to copy finish it up
FinishCompact();
Release(); // kill self
}
else
{
m_messageUri.SetLength(0); // clear the previous message uri
rv = nsBuildLocalMessageURI(m_baseMessageUri, m_keyArray[m_curIndex],
m_messageUri);
if (NS_FAILED(rv)) goto done;
rv = m_messageService->CopyMessage(m_messageUri, this, PR_FALSE, nsnull,
/* ### should get msg window! */ nsnull, nsnull);
}
done:
if (NS_FAILED(rv)) {
m_status = rv; // set the status to rv so the destructor can remove the
// temp folder and database
Release(); // kill self
return rv;
}
return rv;
}
NS_IMETHODIMP
nsFolderCompactState::OnDataAvailable(nsIChannel *channel, nsISupports *ctxt,
nsIInputStream *inStr,
PRUint32 sourceOffset, PRUint32 count)
{
nsresult rv = NS_ERROR_FAILURE;
if (!m_fileStream || !inStr) return rv;
PRUint32 maxReadCount, readCount, writeCount;
rv = NS_OK;
while (NS_SUCCEEDED(rv) && (PRInt32) count > 0)
{
maxReadCount = count > FOUR_K ? FOUR_K : count;
rv = inStr->Read(m_dataBuffer, maxReadCount, &readCount);
if (NS_SUCCEEDED(rv))
{
writeCount = m_fileStream->write(m_dataBuffer, readCount);
count -= readCount;
NS_ASSERTION (writeCount == readCount,
"Oops, write fail, folder can be corrupted!\n");
}
}
return rv;
}
//////////////////////////////////////////////////////////////////////////////
// nsLocal
/////////////////////////////////////////////////////////////////////////////
@ -1071,102 +800,43 @@ nsMsgLocalMailFolder::CreateSubfolder(const PRUnichar *folderName, nsIMsgWindow
return rv;
}
// **** jefft -- needs to provide nsIMsgWindow for the compact status
// update; come back later
NS_IMETHODIMP nsMsgLocalMailFolder::Compact(nsIUrlListener *aListener)
{
// **** jefft -- needs to provide nsIMsgWindow for the compact status
// update; come back later
nsresult rv = NS_ERROR_FAILURE;
nsCOMPtr<nsIMsgDatabase> db;
nsCOMPtr<nsIDBFolderInfo> folderInfo;
PRUint32 expungedBytes = 0;
nsCOMPtr<nsIMsgDatabase> mailDBFactory;
nsresult folderOpen = NS_OK;
nsCOMPtr<nsIFileSpec> pathSpec;
nsCOMPtr<nsIFileSpec> newPathSpec;
rv = GetExpungedBytes(&expungedBytes);
// check if we need to compact the folder
if (expungedBytes > 0)
nsresult rv;
nsCOMPtr <nsIMsgFolderCompactor> folderCompactor = do_CreateInstance(NS_MSGLOCALFOLDERCOMPACTOR_CONTRACTID, &rv);
if (NS_SUCCEEDED(rv) && folderCompactor)
{
nsFolderCompactState *compactState = new nsFolderCompactState();
if (!compactState) return NS_ERROR_OUT_OF_MEMORY;
rv = QueryInterface(NS_GET_IID(nsIMsgFolder), (void **)
getter_AddRefs(compactState->m_folder));
if (NS_FAILED(rv)) goto done;
compactState->m_baseMessageUri = nsCRT::strdup(mBaseMessageURI);
if (!compactState->m_baseMessageUri) {
rv = NS_ERROR_OUT_OF_MEMORY; goto done;
}
nsresult rv = NS_ERROR_FAILURE;
nsCOMPtr<nsIMsgDatabase> db;
nsCOMPtr<nsIDBFolderInfo> folderInfo;
PRUint32 expungedBytes = 0;
nsCOMPtr<nsIMsgDatabase> mailDBFactory;
nsresult folderOpen = NS_OK;
nsCOMPtr<nsIFileSpec> pathSpec;
rv = GetMsgDatabase(nsnull, getter_AddRefs(db));
if (NS_FAILED(rv)) goto done;
rv = GetExpungedBytes(&expungedBytes);
db ->ListAllKeys(compactState->m_keyArray);
compactState->m_size = compactState->m_keyArray.GetSize();
compactState->m_curIndex = 0;
rv = GetPath(getter_AddRefs(pathSpec));
if (NS_FAILED(rv)) goto done;
pathSpec->GetFileSpec(&compactState->m_fileSpec);
compactState->m_fileSpec.SetLeafName("nstmp");
compactState->m_fileStream = new
nsOutputFileStream(compactState->m_fileSpec);
if (!compactState->m_fileStream) {
rv = NS_ERROR_OUT_OF_MEMORY; goto done;
}
rv = NS_NewFileSpecWithSpec(compactState->m_fileSpec,
getter_AddRefs(newPathSpec));
rv = nsComponentManager::CreateInstance(kCMailDB, nsnull,
NS_GET_IID(nsIMsgDatabase),
getter_AddRefs(mailDBFactory));
if (NS_FAILED(rv)) goto done;
folderOpen = mailDBFactory->Open(newPathSpec, PR_TRUE,
PR_FALSE,
getter_AddRefs(compactState->m_db));
if(!NS_SUCCEEDED(folderOpen) &&
folderOpen == NS_MSG_ERROR_FOLDER_SUMMARY_OUT_OF_DATE ||
folderOpen == NS_MSG_ERROR_FOLDER_SUMMARY_MISSING )
// check if we need to compact the folder
if (expungedBytes > 0)
{
// if it's out of date then reopen with upgrade.
rv = mailDBFactory->Open(newPathSpec,
PR_TRUE, PR_TRUE,
getter_AddRefs(compactState->m_db));
rv = GetMsgDatabase(nsnull, getter_AddRefs(db));
if (NS_FAILED(rv)) goto done;
}
rv = GetMessageServiceFromURI(mBaseMessageURI,
&compactState->m_messageService);
done:
if (NS_SUCCEEDED(rv))
{
if (compactState->m_size > 0)
rv = GetPath(getter_AddRefs(pathSpec));
if (NS_SUCCEEDED(rv))
{
compactState->AddRef();
rv = nsBuildLocalMessageURI(mBaseMessageURI,
compactState->m_keyArray[0],
compactState->m_messageUri);
rv = folderCompactor->Init(this, mBaseMessageURI, db, pathSpec);
if (NS_SUCCEEDED(rv))
rv = compactState->m_messageService->CopyMessage(
compactState->m_messageUri, compactState, PR_FALSE, nsnull,
/* ### should get msg window! */ nsnull, nsnull);
}
else
{ // no messages to copy with
compactState->FinishCompact();
delete compactState;
rv = folderCompactor->StartCompacting();
}
}
if (NS_FAILED(rv))
{
compactState->m_status = rv;
delete compactState;
}
return rv;
}
done:
return rv;
}