summaryrefslogtreecommitdiffstats
path: root/mailnews/addrbook/src/nsAbLDAPDirectoryQuery.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'mailnews/addrbook/src/nsAbLDAPDirectoryQuery.cpp')
-rw-r--r--mailnews/addrbook/src/nsAbLDAPDirectoryQuery.cpp610
1 files changed, 610 insertions, 0 deletions
diff --git a/mailnews/addrbook/src/nsAbLDAPDirectoryQuery.cpp b/mailnews/addrbook/src/nsAbLDAPDirectoryQuery.cpp
new file mode 100644
index 000000000..9b22c796c
--- /dev/null
+++ b/mailnews/addrbook/src/nsAbLDAPDirectoryQuery.cpp
@@ -0,0 +1,610 @@
+/* -*- 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 "nsAbLDAPDirectoryQuery.h"
+#include "nsAbBoolExprToLDAPFilter.h"
+#include "nsILDAPMessage.h"
+#include "nsILDAPErrors.h"
+#include "nsILDAPOperation.h"
+#include "nsIAbLDAPAttributeMap.h"
+#include "nsIAbLDAPCard.h"
+#include "nsAbUtils.h"
+#include "nsAbBaseCID.h"
+#include "nsStringGlue.h"
+#include "prprf.h"
+#include "nsServiceManagerUtils.h"
+#include "nsComponentManagerUtils.h"
+#include "nsCategoryManagerUtils.h"
+#include "nsAbLDAPDirectory.h"
+#include "nsAbLDAPListenerBase.h"
+#include "nsXPCOMCIDInternal.h"
+
+using namespace mozilla;
+
+// nsAbLDAPListenerBase inherits nsILDAPMessageListener
+class nsAbQueryLDAPMessageListener : public nsAbLDAPListenerBase
+{
+public:
+ NS_DECL_THREADSAFE_ISUPPORTS
+
+ // Note that the directoryUrl is the details of the ldap directory
+ // without any search params or return attributes specified. The searchUrl
+ // therefore has the search params and return attributes specified.
+ // nsAbQueryLDAPMessageListener(nsIAbDirectoryQuery* directoryQuery,
+ nsAbQueryLDAPMessageListener(nsIAbDirectoryQueryResultListener* resultListener,
+ nsILDAPURL* directoryUrl,
+ nsILDAPURL* searchUrl,
+ nsILDAPConnection* connection,
+ nsIAbDirectoryQueryArguments* queryArguments,
+ nsIMutableArray* serverSearchControls,
+ nsIMutableArray* clientSearchControls,
+ const nsACString &login,
+ const nsACString &mechanism,
+ const int32_t resultLimit = -1,
+ const int32_t timeOut = 0);
+
+ // nsILDAPMessageListener
+ NS_IMETHOD OnLDAPMessage(nsILDAPMessage *aMessage) override;
+
+protected:
+ virtual ~nsAbQueryLDAPMessageListener ();
+ nsresult OnLDAPMessageSearchEntry(nsILDAPMessage *aMessage);
+ nsresult OnLDAPMessageSearchResult(nsILDAPMessage *aMessage);
+
+ friend class nsAbLDAPDirectoryQuery;
+
+ nsresult Cancel();
+ virtual nsresult DoTask() override;
+ virtual void InitFailed(bool aCancelled = false) override;
+
+ nsCOMPtr<nsILDAPURL> mSearchUrl;
+ nsIAbDirectoryQueryResultListener *mResultListener;
+ int32_t mContextID;
+ nsCOMPtr<nsIAbDirectoryQueryArguments> mQueryArguments;
+ int32_t mResultLimit;
+
+ bool mFinished;
+ bool mCanceled;
+ bool mWaitingForPrevQueryToFinish;
+
+ nsCOMPtr<nsIMutableArray> mServerSearchControls;
+ nsCOMPtr<nsIMutableArray> mClientSearchControls;
+};
+
+
+NS_IMPL_ISUPPORTS(nsAbQueryLDAPMessageListener, nsILDAPMessageListener)
+
+nsAbQueryLDAPMessageListener::nsAbQueryLDAPMessageListener(
+ nsIAbDirectoryQueryResultListener *resultListener,
+ nsILDAPURL* directoryUrl,
+ nsILDAPURL* searchUrl,
+ nsILDAPConnection* connection,
+ nsIAbDirectoryQueryArguments* queryArguments,
+ nsIMutableArray* serverSearchControls,
+ nsIMutableArray* clientSearchControls,
+ const nsACString &login,
+ const nsACString &mechanism,
+ const int32_t resultLimit,
+ const int32_t timeOut) :
+ nsAbLDAPListenerBase(directoryUrl, connection, login, timeOut),
+ mSearchUrl(searchUrl),
+ mResultListener(resultListener),
+ mQueryArguments(queryArguments),
+ mResultLimit(resultLimit),
+ mFinished(false),
+ mCanceled(false),
+ mWaitingForPrevQueryToFinish(false),
+ mServerSearchControls(serverSearchControls),
+ mClientSearchControls(clientSearchControls)
+{
+ mSaslMechanism.Assign(mechanism);
+}
+
+nsAbQueryLDAPMessageListener::~nsAbQueryLDAPMessageListener ()
+{
+}
+
+nsresult nsAbQueryLDAPMessageListener::Cancel ()
+{
+ nsresult rv = Initiate();
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ MutexAutoLock lock(mLock);
+
+ if (mFinished || mCanceled)
+ return NS_OK;
+
+ mCanceled = true;
+ if (!mFinished)
+ mWaitingForPrevQueryToFinish = true;
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsAbQueryLDAPMessageListener::OnLDAPMessage(nsILDAPMessage *aMessage)
+{
+ nsresult rv = Initiate();
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ int32_t messageType;
+ rv = aMessage->GetType(&messageType);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ bool cancelOperation = false;
+
+ // Enter lock
+ {
+ MutexAutoLock lock (mLock);
+
+ if (mFinished)
+ return NS_OK;
+
+ if (messageType == nsILDAPMessage::RES_SEARCH_RESULT)
+ mFinished = true;
+ else if (mCanceled)
+ {
+ mFinished = true;
+ cancelOperation = true;
+ }
+ }
+ // Leave lock
+
+ if (!mResultListener)
+ return NS_ERROR_NULL_POINTER;
+
+ if (!cancelOperation)
+ {
+ switch (messageType)
+ {
+ case nsILDAPMessage::RES_BIND:
+ rv = OnLDAPMessageBind(aMessage);
+ if (NS_FAILED(rv))
+ // We know the bind failed and hence the message has an error, so we
+ // can just call SearchResult with the message and that'll sort it out
+ // for us.
+ rv = OnLDAPMessageSearchResult(aMessage);
+ break;
+ case nsILDAPMessage::RES_SEARCH_ENTRY:
+ if (!mFinished && !mWaitingForPrevQueryToFinish)
+ rv = OnLDAPMessageSearchEntry(aMessage);
+ break;
+ case nsILDAPMessage::RES_SEARCH_RESULT:
+ mWaitingForPrevQueryToFinish = false;
+ rv = OnLDAPMessageSearchResult(aMessage);
+ NS_ENSURE_SUCCESS(rv, rv);
+ break;
+ default:
+ break;
+ }
+ }
+ else
+ {
+ if (mOperation)
+ rv = mOperation->AbandonExt();
+
+ rv = mResultListener->OnQueryResult(
+ nsIAbDirectoryQueryResultListener::queryResultStopped, 0);
+
+ // reset because we might re-use this listener...except don't do this
+ // until the search is done, so we'll ignore results from a previous
+ // search.
+ if (messageType == nsILDAPMessage::RES_SEARCH_RESULT)
+ mCanceled = mFinished = false;
+ }
+
+ return rv;
+}
+
+nsresult nsAbQueryLDAPMessageListener::DoTask()
+{
+ nsresult rv;
+ mCanceled = mFinished = false;
+
+ mOperation = do_CreateInstance(NS_LDAPOPERATION_CONTRACTID, &rv);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ rv = mOperation->Init(mConnection, this, nullptr);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsAutoCString dn;
+ rv = mSearchUrl->GetDn(dn);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ int32_t scope;
+ rv = mSearchUrl->GetScope(&scope);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsAutoCString filter;
+ rv = mSearchUrl->GetFilter(filter);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsAutoCString attributes;
+ rv = mSearchUrl->GetAttributes(attributes);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ rv = mOperation->SetServerControls(mServerSearchControls);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ rv = mOperation->SetClientControls(mClientSearchControls);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ return mOperation->SearchExt(dn, scope, filter, attributes, mTimeOut,
+ mResultLimit);
+}
+
+void nsAbQueryLDAPMessageListener::InitFailed(bool aCancelled)
+{
+ if (!mResultListener)
+ return;
+
+ // In the !aCancelled case we know there was an error, but we won't be
+ // able to translate it, so just return an error code of zero.
+ mResultListener->OnQueryResult(
+ aCancelled ? nsIAbDirectoryQueryResultListener::queryResultStopped :
+ nsIAbDirectoryQueryResultListener::queryResultError, 0);
+}
+
+nsresult nsAbQueryLDAPMessageListener::OnLDAPMessageSearchEntry(nsILDAPMessage *aMessage)
+{
+ nsresult rv;
+
+ if (!mResultListener)
+ return NS_ERROR_NULL_POINTER;
+
+ // the map for translating between LDAP attrs <-> addrbook fields
+ nsCOMPtr<nsISupports> iSupportsMap;
+ rv = mQueryArguments->GetTypeSpecificArg(getter_AddRefs(iSupportsMap));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsCOMPtr<nsIAbLDAPAttributeMap> map = do_QueryInterface(iSupportsMap, &rv);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsCOMPtr<nsIAbCard> card = do_CreateInstance(NS_ABLDAPCARD_CONTRACTID, &rv);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ rv = map->SetCardPropertiesFromLDAPMessage(aMessage, card);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsCOMPtr<nsIAbLDAPCard> ldapCard = do_QueryInterface(card, &rv);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ rv = ldapCard->SetMetaProperties(aMessage);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ return mResultListener->OnQueryFoundCard(card);
+}
+
+nsresult nsAbQueryLDAPMessageListener::OnLDAPMessageSearchResult(nsILDAPMessage *aMessage)
+{
+ int32_t errorCode;
+ nsresult rv = aMessage->GetErrorCode(&errorCode);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ if (errorCode == nsILDAPErrors::SUCCESS || errorCode == nsILDAPErrors::SIZELIMIT_EXCEEDED)
+ return mResultListener->OnQueryResult(
+ nsIAbDirectoryQueryResultListener::queryResultComplete, 0);
+
+ return mResultListener->OnQueryResult(
+ nsIAbDirectoryQueryResultListener::queryResultError, errorCode);
+}
+
+// nsAbLDAPDirectoryQuery
+
+NS_IMPL_ISUPPORTS(nsAbLDAPDirectoryQuery, nsIAbDirectoryQuery,
+ nsIAbDirectoryQueryResultListener)
+
+nsAbLDAPDirectoryQuery::nsAbLDAPDirectoryQuery() :
+ mInitialized(false)
+{
+}
+
+nsAbLDAPDirectoryQuery::~nsAbLDAPDirectoryQuery()
+{
+}
+
+NS_IMETHODIMP nsAbLDAPDirectoryQuery::DoQuery(nsIAbDirectory *aDirectory,
+ nsIAbDirectoryQueryArguments* aArguments,
+ nsIAbDirSearchListener* aListener,
+ int32_t aResultLimit,
+ int32_t aTimeOut,
+ int32_t* _retval)
+{
+ NS_ENSURE_ARG_POINTER(aListener);
+ NS_ENSURE_ARG_POINTER(aArguments);
+
+ mListeners.AppendObject(aListener);
+
+ // Ensure existing query is stopped. Context id doesn't matter here
+ nsresult rv = StopQuery(0);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ mInitialized = true;
+
+ // Get the current directory as LDAP specific
+ nsCOMPtr<nsIAbLDAPDirectory> directory(do_QueryInterface(aDirectory, &rv));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ // We also need the current URL to check as well...
+ nsCOMPtr<nsILDAPURL> currentUrl;
+ rv = directory->GetLDAPURL(getter_AddRefs(currentUrl));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsAutoCString login;
+ rv = directory->GetAuthDn(login);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsAutoCString saslMechanism;
+ rv = directory->GetSaslMechanism(saslMechanism);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ uint32_t protocolVersion;
+ rv = directory->GetProtocolVersion(&protocolVersion);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ // To do:
+ // Ensure query is stopped
+ // If connection params have changed re-create connection
+ // else reuse existing connection
+
+ bool redoConnection = false;
+
+ if (!mConnection || !mDirectoryUrl)
+ {
+ mDirectoryUrl = currentUrl;
+ aDirectory->GetUuid(mDirectoryId);
+ mCurrentLogin = login;
+ mCurrentMechanism = saslMechanism;
+ mCurrentProtocolVersion = protocolVersion;
+ redoConnection = true;
+ }
+ else
+ {
+ bool equal;
+ rv = mDirectoryUrl->Equals(currentUrl, &equal);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ if (!equal)
+ {
+ mDirectoryUrl = currentUrl;
+ aDirectory->GetUuid(mDirectoryId);
+ mCurrentLogin = login;
+ mCurrentMechanism = saslMechanism;
+ mCurrentProtocolVersion = protocolVersion;
+ redoConnection = true;
+ }
+ else
+ {
+ // Has login or version changed?
+ if (login != mCurrentLogin ||
+ saslMechanism != mCurrentMechanism ||
+ protocolVersion != mCurrentProtocolVersion)
+ {
+ redoConnection = true;
+ mCurrentLogin = login;
+ mCurrentMechanism = saslMechanism;
+ mCurrentProtocolVersion = protocolVersion;
+ }
+ }
+ }
+
+ nsCOMPtr<nsIURI> uri;
+ rv = mDirectoryUrl->Clone(getter_AddRefs(uri));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsCOMPtr<nsILDAPURL> url(do_QueryInterface(uri, &rv));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ // Get/Set the return attributes
+ nsCOMPtr<nsISupports> iSupportsMap;
+ rv = aArguments->GetTypeSpecificArg(getter_AddRefs(iSupportsMap));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsCOMPtr<nsIAbLDAPAttributeMap> map = do_QueryInterface(iSupportsMap, &rv);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ // Require all attributes that are mapped to card properties
+ nsAutoCString returnAttributes;
+ rv = map->GetAllCardAttributes(returnAttributes);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ rv = url->SetAttributes(returnAttributes);
+ // Now do the error check
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ // Also require the objectClass attribute, it is used by
+ // nsAbLDAPCard::SetMetaProperties
+ rv = url->AddAttribute(NS_LITERAL_CSTRING("objectClass"));
+
+ nsAutoCString filter;
+
+ // Get filter from arguments if set:
+ rv = aArguments->GetFilter(filter);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ if (filter.IsEmpty()) {
+ // Get the filter
+ nsCOMPtr<nsISupports> supportsExpression;
+ rv = aArguments->GetExpression(getter_AddRefs(supportsExpression));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsCOMPtr<nsIAbBooleanExpression> expression(do_QueryInterface(supportsExpression, &rv));
+
+ // figure out how we map attribute names to addressbook fields for this
+ // query
+ rv = nsAbBoolExprToLDAPFilter::Convert(map, expression, filter);
+ NS_ENSURE_SUCCESS(rv, rv);
+ }
+
+ /*
+ * Mozilla itself cannot arrive here with a blank filter
+ * as the nsAbLDAPDirectory::StartSearch() disallows it.
+ * But 3rd party LDAP query integration with Mozilla begins
+ * in this method.
+ *
+ * Default the filter string if blank, otherwise it gets
+ * set to (objectclass=*) which returns everything. Set
+ * the default to (objectclass=inetorgperson) as this
+ * is the most appropriate default objectclass which is
+ * central to the makeup of the mozilla ldap address book
+ * entries.
+ */
+ if (filter.IsEmpty())
+ {
+ filter.AssignLiteral("(objectclass=inetorgperson)");
+ }
+
+ // get the directoryFilter from the directory url and merge it with the user's
+ // search filter
+ nsAutoCString urlFilter;
+ rv = mDirectoryUrl->GetFilter(urlFilter);
+
+ // if urlFilter is unset (or set to the default "objectclass=*"), there's
+ // no need to AND in an empty search term, so leave prefix and suffix empty
+
+ nsAutoCString searchFilter;
+ if (urlFilter.Length() && !urlFilter.EqualsLiteral("(objectclass=*)"))
+ {
+ // if urlFilter isn't parenthesized, we need to add in parens so that
+ // the filter works as a term to &
+ //
+ if (urlFilter[0] != '(')
+ {
+ searchFilter = NS_LITERAL_CSTRING("(&(");
+ searchFilter.Append(urlFilter);
+ searchFilter.AppendLiteral(")");
+ }
+ else
+ {
+ searchFilter = NS_LITERAL_CSTRING("(&");
+ searchFilter.Append(urlFilter);
+ }
+
+ searchFilter += filter;
+ searchFilter += ')';
+ }
+ else
+ searchFilter = filter;
+
+ rv = url->SetFilter(searchFilter);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ // Now formulate the search string
+
+ // Get the scope
+ int32_t scope;
+ bool doSubDirectories;
+ rv = aArguments->GetQuerySubDirectories (&doSubDirectories);
+ NS_ENSURE_SUCCESS(rv, rv);
+ scope = doSubDirectories ? nsILDAPURL::SCOPE_SUBTREE :
+ nsILDAPURL::SCOPE_ONELEVEL;
+
+ rv = url->SetScope(scope);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ // too soon? Do we need a new listener?
+ // If we already have a connection, and don't need to re-do it, give it the
+ // new search details and go for it...
+ if (!redoConnection)
+ {
+ nsAbQueryLDAPMessageListener *msgListener =
+ static_cast<nsAbQueryLDAPMessageListener *>(static_cast<nsILDAPMessageListener *>(mListener.get()));
+ if (msgListener)
+ {
+ // Ensure the urls are correct
+ msgListener->mDirectoryUrl = mDirectoryUrl;
+ msgListener->mSearchUrl = url;
+ // Also ensure we set the correct result limit
+ msgListener->mResultLimit = aResultLimit;
+ return msgListener->DoTask();
+ }
+ }
+
+ nsCOMPtr<nsIAbLDAPDirectory> abLDAPDir = do_QueryInterface(aDirectory, &rv);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsCOMPtr<nsIMutableArray> serverSearchControls;
+ rv = abLDAPDir->GetSearchServerControls(getter_AddRefs(serverSearchControls));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsCOMPtr<nsIMutableArray> clientSearchControls;
+ rv = abLDAPDir->GetSearchClientControls(getter_AddRefs(clientSearchControls));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ // Create the new connection (which cause the old one to be dropped if necessary)
+ mConnection = do_CreateInstance(NS_LDAPCONNECTION_CONTRACTID, &rv);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsCOMPtr<nsIAbDirectoryQueryResultListener> resultListener =
+ do_QueryInterface((nsIAbDirectoryQuery*)this, &rv);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ // Initiate LDAP message listener
+ nsAbQueryLDAPMessageListener* _messageListener =
+ new nsAbQueryLDAPMessageListener(resultListener, mDirectoryUrl, url,
+ mConnection, aArguments,
+ serverSearchControls, clientSearchControls,
+ mCurrentLogin, mCurrentMechanism,
+ aResultLimit, aTimeOut);
+ if (_messageListener == NULL)
+ return NS_ERROR_OUT_OF_MEMORY;
+
+ mListener = _messageListener;
+ *_retval = 1;
+
+ // Now lets initialize the LDAP connection properly. We'll kick
+ // off the bind operation in the callback function, |OnLDAPInit()|.
+ rv = mConnection->Init(mDirectoryUrl, mCurrentLogin,
+ mListener, nullptr, mCurrentProtocolVersion);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ return rv;
+}
+
+/* void stopQuery (in long contextID); */
+NS_IMETHODIMP nsAbLDAPDirectoryQuery::StopQuery(int32_t contextID)
+{
+ mInitialized = true;
+
+ if (!mListener)
+ return NS_OK;
+
+ nsAbQueryLDAPMessageListener *listener =
+ static_cast<nsAbQueryLDAPMessageListener *>(static_cast<nsILDAPMessageListener *>(mListener.get()));
+ if (listener)
+ return listener->Cancel();
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsAbLDAPDirectoryQuery::OnQueryFoundCard(nsIAbCard *aCard)
+{
+ aCard->SetDirectoryId(mDirectoryId);
+
+ for (int32_t i = 0; i < mListeners.Count(); ++i)
+ mListeners[i]->OnSearchFoundCard(aCard);
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsAbLDAPDirectoryQuery::OnQueryResult(int32_t aResult,
+ int32_t aErrorCode)
+{
+ uint32_t count = mListeners.Count();
+
+ // XXX: Temporary fix for crasher needs reviewing as part of bug 135231.
+ // Temporarily add a reference to ourselves, in case the only thing
+ // keeping us alive is the link with the listener.
+ NS_ADDREF_THIS();
+
+ for (int32_t i = count - 1; i >= 0; --i)
+ {
+ mListeners[i]->OnSearchFinished(aResult, EmptyString());
+ mListeners.RemoveObjectAt(i);
+ }
+
+ NS_RELEASE_THIS();
+
+ return NS_OK;
+}