diff options
Diffstat (limited to 'mailnews/addrbook/src/nsAbLDAPReplicationData.cpp')
-rw-r--r-- | mailnews/addrbook/src/nsAbLDAPReplicationData.cpp | 489 |
1 files changed, 489 insertions, 0 deletions
diff --git a/mailnews/addrbook/src/nsAbLDAPReplicationData.cpp b/mailnews/addrbook/src/nsAbLDAPReplicationData.cpp new file mode 100644 index 000000000..faa8cdd23 --- /dev/null +++ b/mailnews/addrbook/src/nsAbLDAPReplicationData.cpp @@ -0,0 +1,489 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "nsILDAPMessage.h" +#include "nsAbLDAPReplicationData.h" +#include "nsIAbCard.h" +#include "nsAbBaseCID.h" +#include "nsAbUtils.h" +#include "nsAbLDAPReplicationQuery.h" +#include "nsILDAPErrors.h" +#include "nsComponentManagerUtils.h" +#include "nsMsgUtils.h" + +// once bug # 101252 gets fixed, this should be reverted back to be non threadsafe +// implementation is not really thread safe since each object should exist +// independently along with its related independent nsAbLDAPReplicationQuery object. +NS_IMPL_ISUPPORTS(nsAbLDAPProcessReplicationData, nsIAbLDAPProcessReplicationData, nsILDAPMessageListener) + +nsAbLDAPProcessReplicationData::nsAbLDAPProcessReplicationData() : + nsAbLDAPListenerBase(), + mState(kIdle), + mProtocol(-1), + mCount(0), + mDBOpen(false), + mInitialized(false) +{ +} + +nsAbLDAPProcessReplicationData::~nsAbLDAPProcessReplicationData() +{ + /* destructor code */ + if(mDBOpen && mReplicationDB) + mReplicationDB->Close(false); +} + +NS_IMETHODIMP nsAbLDAPProcessReplicationData::Init( + nsIAbLDAPDirectory *aDirectory, + nsILDAPConnection *aConnection, + nsILDAPURL* aURL, + nsIAbLDAPReplicationQuery *aQuery, + nsIWebProgressListener *aProgressListener) +{ + NS_ENSURE_ARG_POINTER(aDirectory); + NS_ENSURE_ARG_POINTER(aConnection); + NS_ENSURE_ARG_POINTER(aURL); + NS_ENSURE_ARG_POINTER(aQuery); + + mDirectory = aDirectory; + mConnection = aConnection; + mDirectoryUrl = aURL; + mQuery = aQuery; + + mListener = aProgressListener; + + nsresult rv = mDirectory->GetAttributeMap(getter_AddRefs(mAttrMap)); + if (NS_FAILED(rv)) { + mQuery = nullptr; + return rv; + } + + rv = mDirectory->GetAuthDn(mLogin); + if (NS_FAILED(rv)) { + mQuery = nullptr; + return rv; + } + + rv = mDirectory->GetSaslMechanism(mSaslMechanism); + if (NS_FAILED(rv)) { + mQuery = nullptr; + return rv; + } + + mInitialized = true; + + return rv; +} + +NS_IMETHODIMP nsAbLDAPProcessReplicationData::GetReplicationState(int32_t *aReplicationState) +{ + NS_ENSURE_ARG_POINTER(aReplicationState); + *aReplicationState = mState; + return NS_OK; +} + +NS_IMETHODIMP nsAbLDAPProcessReplicationData::GetProtocolUsed(int32_t *aProtocolUsed) +{ + NS_ENSURE_ARG_POINTER(aProtocolUsed); + *aProtocolUsed = mProtocol; + return NS_OK; +} + +NS_IMETHODIMP nsAbLDAPProcessReplicationData::OnLDAPMessage(nsILDAPMessage *aMessage) +{ + NS_ENSURE_ARG_POINTER(aMessage); + + if (!mInitialized) + return NS_ERROR_NOT_INITIALIZED; + + int32_t messageType; + nsresult rv = aMessage->GetType(&messageType); + if (NS_FAILED(rv)) { + Done(false); + return rv; + } + + switch (messageType) + { + case nsILDAPMessage::RES_BIND: + rv = OnLDAPMessageBind(aMessage); + if (NS_FAILED(rv)) + rv = Abort(); + break; + case nsILDAPMessage::RES_SEARCH_ENTRY: + rv = OnLDAPSearchEntry(aMessage); + break; + case nsILDAPMessage::RES_SEARCH_RESULT: + rv = OnLDAPSearchResult(aMessage); + break; + default: + // for messageTypes we do not handle return NS_OK to LDAP and move ahead. + rv = NS_OK; + break; + } + + return rv; +} + +NS_IMETHODIMP nsAbLDAPProcessReplicationData::Abort() +{ + if (!mInitialized) + return NS_ERROR_NOT_INITIALIZED; + + nsresult rv = NS_OK; + + if (mState != kIdle && mOperation) { + rv = mOperation->AbandonExt(); + if (NS_SUCCEEDED(rv)) + mState = kIdle; + } + + if (mReplicationDB && mDBOpen) { + // force close since we need to delete the file. + mReplicationDB->ForceClosed(); + mDBOpen = false; + + // delete the unsaved replication file + if (mReplicationFile) { + rv = mReplicationFile->Remove(false); + if (NS_SUCCEEDED(rv) && mDirectory) { + nsAutoCString fileName; + rv = mDirectory->GetReplicationFileName(fileName); + // now put back the backed up replicated file if aborted + if (NS_SUCCEEDED(rv) && mBackupReplicationFile) + rv = mBackupReplicationFile->MoveToNative(nullptr, fileName); + } + } + } + + Done(false); + + return rv; +} + +nsresult nsAbLDAPProcessReplicationData::DoTask() +{ + if (!mInitialized) + return NS_ERROR_NOT_INITIALIZED; + + nsresult rv = OpenABForReplicatedDir(true); + if (NS_FAILED(rv)) + // do not call done here since it is called by OpenABForReplicationDir + return rv; + + mOperation = do_CreateInstance(NS_LDAPOPERATION_CONTRACTID, &rv); + NS_ENSURE_SUCCESS(rv, rv); + + rv = mOperation->Init(mConnection, this, nullptr); + NS_ENSURE_SUCCESS(rv, rv); + + // get the relevant attributes associated with the directory server url + nsAutoCString urlFilter; + rv = mDirectoryUrl->GetFilter(urlFilter); + if (NS_FAILED(rv)) + return rv; + + nsAutoCString dn; + rv = mDirectoryUrl->GetDn(dn); + if (NS_FAILED(rv)) + return rv; + + if (dn.IsEmpty()) + return NS_ERROR_UNEXPECTED; + + int32_t scope; + rv = mDirectoryUrl->GetScope(&scope); + if (NS_FAILED(rv)) + return rv; + + nsAutoCString attributes; + rv = mDirectoryUrl->GetAttributes(attributes); + if (NS_FAILED(rv)) + return rv; + + mState = kReplicatingAll; + + if (mListener && NS_SUCCEEDED(rv)) + // XXX Cast from bool to nsresult + mListener->OnStateChange(nullptr, nullptr, + nsIWebProgressListener::STATE_START, + static_cast<nsresult>(true)); + + return mOperation->SearchExt(dn, scope, urlFilter, attributes, 0, 0); +} + +void nsAbLDAPProcessReplicationData::InitFailed(bool aCancelled) +{ + // Just call Done() which will ensure everything is tidied up nicely. + Done(false); +} + +nsresult nsAbLDAPProcessReplicationData::OnLDAPSearchEntry(nsILDAPMessage *aMessage) +{ + NS_ENSURE_ARG_POINTER(aMessage); + if (!mInitialized) + return NS_ERROR_NOT_INITIALIZED; + // since this runs on the main thread and is single threaded, this will + // take care of entries returned by LDAP Connection thread after Abort. + if (!mReplicationDB || !mDBOpen) + return NS_ERROR_FAILURE; + + nsresult rv = NS_OK; + + // Although we would may naturally create an nsIAbLDAPCard here, we don't + // need to as we are writing this straight to the database, so just create + // the database version instead. + nsCOMPtr<nsIAbCard> newCard(do_CreateInstance(NS_ABMDBCARD_CONTRACTID, + &rv)); + if (NS_FAILED(rv)) { + Abort(); + return rv; + } + + rv = mAttrMap->SetCardPropertiesFromLDAPMessage(aMessage, newCard); + if (NS_FAILED(rv)) + { + NS_WARNING("nsAbLDAPProcessReplicationData::OnLDAPSearchEntry" + "No card properties could be set"); + // if some entries are bogus for us, continue with next one + return NS_OK; + } + + rv = mReplicationDB->CreateNewCardAndAddToDB(newCard, false, nullptr); + if(NS_FAILED(rv)) { + Abort(); + return rv; + } + + // now set the attribute for the DN of the entry in the card in the DB + nsAutoCString authDN; + rv = aMessage->GetDn(authDN); + if(NS_SUCCEEDED(rv) && !authDN.IsEmpty()) + { + newCard->SetPropertyAsAUTF8String("_DN", authDN); + } + + rv = mReplicationDB->EditCard(newCard, false, nullptr); + if(NS_FAILED(rv)) { + Abort(); + return rv; + } + + + mCount ++; + + if (mListener && !(mCount % 10)) // inform the listener every 10 entries + { + mListener->OnProgressChange(nullptr,nullptr,mCount, -1, mCount, -1); + // in case if the LDAP Connection thread is starved and causes problem + // uncomment this one and try. + // PR_Sleep(PR_INTERVAL_NO_WAIT); // give others a chance + } + + return rv; +} + + +nsresult nsAbLDAPProcessReplicationData::OnLDAPSearchResult(nsILDAPMessage *aMessage) +{ +#ifdef DEBUG_rdayal + printf("LDAP Replication : Got Results for Completion"); +#endif + + NS_ENSURE_ARG_POINTER(aMessage); + if(!mInitialized) + return NS_ERROR_NOT_INITIALIZED; + + int32_t errorCode; + nsresult rv = aMessage->GetErrorCode(&errorCode); + + if(NS_SUCCEEDED(rv)) { + // We are done with the LDAP search for all entries. + if(errorCode == nsILDAPErrors::SUCCESS || errorCode == nsILDAPErrors::SIZELIMIT_EXCEEDED) { + Done(true); + if(mReplicationDB && mDBOpen) { + rv = mReplicationDB->Close(true); + NS_ASSERTION(NS_SUCCEEDED(rv), "Replication DB Close on Success failed"); + mDBOpen = false; + // once we have saved the new replication file, delete the backup file + if(mBackupReplicationFile) + { + rv = mBackupReplicationFile->Remove(false); + NS_ASSERTION(NS_SUCCEEDED(rv), "Replication BackupFile Remove on Success failed"); + } + } + return NS_OK; + } + } + + // in case if GetErrorCode returned error or errorCode is not SUCCESS / SIZELIMIT_EXCEEDED + if(mReplicationDB && mDBOpen) { + // if error result is returned close the DB without saving ??? + // should we commit anyway ??? whatever is returned is not lost then !! + rv = mReplicationDB->ForceClosed(); // force close since we need to delete the file. + NS_ASSERTION(NS_SUCCEEDED(rv), "Replication DB ForceClosed on Failure failed"); + mDBOpen = false; + // if error result is returned remove the replicated file + if(mReplicationFile) { + rv = mReplicationFile->Remove(false); + NS_ASSERTION(NS_SUCCEEDED(rv), "Replication File Remove on Failure failed"); + if(NS_SUCCEEDED(rv)) { + // now put back the backed up replicated file + if(mBackupReplicationFile && mDirectory) + { + nsAutoCString fileName; + rv = mDirectory->GetReplicationFileName(fileName); + if (NS_SUCCEEDED(rv) && !fileName.IsEmpty()) + { + rv = mBackupReplicationFile->MoveToNative(nullptr, fileName); + NS_ASSERTION(NS_SUCCEEDED(rv), "Replication Backup File Move back on Failure failed"); + } + } + } + } + Done(false); + } + + return NS_OK; +} + +nsresult nsAbLDAPProcessReplicationData::OpenABForReplicatedDir(bool aCreate) +{ + if (!mInitialized) + return NS_ERROR_NOT_INITIALIZED; + + nsresult rv = mDirectory->GetReplicationFile(getter_AddRefs(mReplicationFile)); + if (NS_FAILED(rv)) + { + Done(false); + return NS_ERROR_FAILURE; + } + + nsCString fileName; + rv = mReplicationFile->GetNativeLeafName(fileName); + if (NS_FAILED(rv)) { + Done(false); + return rv; + } + + // if the AB DB already exists backup existing one, + // in case if the user cancels or Abort put back the backed up file + bool fileExists; + rv = mReplicationFile->Exists(&fileExists); + if(NS_SUCCEEDED(rv) && fileExists) { + // create the backup file object same as the Replication file object. + // we create a backup file here since we need to cleanup the existing file + // for create and then commit so instead of deleting existing cards we just + // clone the existing one for a much better performance - for Download All. + // And also important in case if replication fails we donot lose user's existing + // replicated data for both Download all and Changelog. + nsCOMPtr<nsIFile> clone; + rv = mReplicationFile->Clone(getter_AddRefs(clone)); + if(NS_FAILED(rv)) { + Done(false); + return rv; + } + mBackupReplicationFile = do_QueryInterface(clone, &rv); + if(NS_FAILED(rv)) { + Done(false); + return rv; + } + rv = mBackupReplicationFile->CreateUnique(nsIFile::NORMAL_FILE_TYPE, 0777); + if(NS_FAILED(rv)) { + Done(false); + return rv; + } + nsAutoString backupFileLeafName; + rv = mBackupReplicationFile->GetLeafName(backupFileLeafName); + if(NS_FAILED(rv)) { + Done(false); + return rv; + } + // remove the newly created unique backup file so that move and copy succeeds. + rv = mBackupReplicationFile->Remove(false); + if(NS_FAILED(rv)) { + Done(false); + return rv; + } + + if(aCreate) { + // set backup file to existing replication file for move + mBackupReplicationFile->SetNativeLeafName(fileName); + + rv = mBackupReplicationFile->MoveTo(nullptr, backupFileLeafName); + // set the backup file leaf name now + if (NS_SUCCEEDED(rv)) + mBackupReplicationFile->SetLeafName(backupFileLeafName); + } + else { + // set backup file to existing replication file for copy + mBackupReplicationFile->SetNativeLeafName(fileName); + + // specify the parent here specifically, + // passing nullptr to copy to the same dir actually renames existing file + // instead of making another copy of the existing file. + nsCOMPtr<nsIFile> parent; + rv = mBackupReplicationFile->GetParent(getter_AddRefs(parent)); + if (NS_SUCCEEDED(rv)) + rv = mBackupReplicationFile->CopyTo(parent, backupFileLeafName); + // set the backup file leaf name now + if (NS_SUCCEEDED(rv)) + mBackupReplicationFile->SetLeafName(backupFileLeafName); + } + if(NS_FAILED(rv)) { + Done(false); + return rv; + } + } + + nsCOMPtr<nsIAddrDatabase> addrDBFactory = + do_GetService(NS_ADDRDATABASE_CONTRACTID, &rv); + if(NS_FAILED(rv)) { + if (mBackupReplicationFile) + mBackupReplicationFile->Remove(false); + Done(false); + return rv; + } + + rv = addrDBFactory->Open(mReplicationFile, aCreate, true, getter_AddRefs(mReplicationDB)); + if(NS_FAILED(rv)) { + Done(false); + if (mBackupReplicationFile) + mBackupReplicationFile->Remove(false); + return rv; + } + + mDBOpen = true; // replication DB is now Open + return rv; +} + +void nsAbLDAPProcessReplicationData::Done(bool aSuccess) +{ + if (!mInitialized) + return; + + mState = kReplicationDone; + + if (mQuery) + mQuery->Done(aSuccess); + + if (mListener) + // XXX Cast from bool to nsresult + mListener->OnStateChange(nullptr, nullptr, + nsIWebProgressListener::STATE_STOP, + static_cast<nsresult>(aSuccess)); + + // since this is called when all is done here, either on success, + // failure or abort release the query now. + mQuery = nullptr; +} + +nsresult nsAbLDAPProcessReplicationData::DeleteCard(nsString & aDn) +{ + nsCOMPtr<nsIAbCard> cardToDelete; + mReplicationDB->GetCardFromAttribute(nullptr, "_DN", NS_ConvertUTF16toUTF8(aDn), + false, getter_AddRefs(cardToDelete)); + return mReplicationDB->DeleteCard(cardToDelete, false, nullptr); +} |