summaryrefslogtreecommitdiffstats
path: root/mailnews/addrbook/src/nsAbLDAPReplicationData.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'mailnews/addrbook/src/nsAbLDAPReplicationData.cpp')
-rw-r--r--mailnews/addrbook/src/nsAbLDAPReplicationData.cpp489
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);
+}