/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- * * The contents of this file are subject to the Mozilla Public * License Version 1.1 (the "License"); you may not use this file * except in compliance with the License. You may obtain a copy of * the License at http://www.mozilla.org/MPL/ * * Software distributed under the License is distributed on an "AS * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or * implied. See the License for the specific language governing * rights and limitations under the License. * * The Original Code is the mozilla.org LDAP XPCOM SDK. * * The Initial Developer of the Original Code is Netscape * Communications Corporation. Portions created by Netscape are * Copyright (C) 2000 Netscape Communications Corporation. All * Rights Reserved. * * Contributor(s): Dan Mosedale * * Alternatively, the contents of this file may be used under the * terms of the GNU General Public License Version 2 or later (the * "GPL"), in which case the provisions of the GPL are applicable * instead of those above. If you wish to allow use of your * version of this file only under the terms of the GPL and not to * allow others to use your version of this file under the MPL, * indicate your decision by deleting the provisions above and * replace them with the notice and other provisions required by * the GPL. If you do not delete the provisions above, a recipient * may use your version of this file under either the MPL or the * GPL. */ #include "nsLDAP.h" #include "nsLDAPOperation.h" #include "nsLDAPConnection.h" #include "nsILDAPMessage.h" #include "nsIComponentManager.h" #include "nsXPIDLString.h" #include "nspr.h" // constructor nsLDAPOperation::nsLDAPOperation() { } // destructor nsLDAPOperation::~nsLDAPOperation() { } NS_IMPL_THREADSAFE_ISUPPORTS1(nsLDAPOperation, nsILDAPOperation); /** * Initializes this operation. Must be called prior to use. * * @param aConnection connection this operation should use * @param aMessageListener where are the results are called back to. */ NS_IMETHODIMP nsLDAPOperation::Init(nsILDAPConnection *aConnection, nsILDAPMessageListener *aMessageListener, nsISupports *aClosure) { if (!aConnection) { return NS_ERROR_ILLEGAL_VALUE; } // so we know that the operation is not yet running (and therefore don't // try and call ldap_abandon_ext() on it) or remove it from the queue. // mMsgID = 0; // set the member vars // mConnection = aConnection; mMessageListener = aMessageListener; mClosure = aClosure; // cache the connection handle // mConnectionHandle = NS_STATIC_CAST(nsLDAPConnection *, aConnection)->mConnectionHandle; return NS_OK; } NS_IMETHODIMP nsLDAPOperation::GetClosure(nsISupports **_retval) { if (!_retval) { return NS_ERROR_ILLEGAL_VALUE; } NS_IF_ADDREF(*_retval = mClosure); return NS_OK; } NS_IMETHODIMP nsLDAPOperation::SetClosure(nsISupports *aClosure) { mClosure = aClosure; return NS_OK; } NS_IMETHODIMP nsLDAPOperation::GetConnection(nsILDAPConnection* *aConnection) { if (!aConnection) { return NS_ERROR_ILLEGAL_VALUE; } *aConnection = mConnection; NS_IF_ADDREF(*aConnection); return NS_OK; } NS_IMETHODIMP nsLDAPOperation::GetMessageListener(nsILDAPMessageListener **aMessageListener) { if (!aMessageListener) { return NS_ERROR_ILLEGAL_VALUE; } *aMessageListener = mMessageListener; NS_IF_ADDREF(*aMessageListener); return NS_OK; } // wrapper for ldap_simple_bind() // NS_IMETHODIMP nsLDAPOperation::SimpleBind(const nsACString& passwd) { nsresult rv; nsCAutoString bindName; NS_PRECONDITION(mMessageListener != 0, "MessageListener not set"); rv = mConnection->GetBindName(bindName); if (NS_FAILED(rv)) return rv; PR_LOG(gLDAPLogModule, PR_LOG_DEBUG, ("nsLDAPOperation::SimpleBind(): called; bindName = '%s'; ", bindName.get())); mMsgID = ldap_simple_bind(mConnectionHandle, bindName.get(), PromiseFlatCString(passwd).get()); if (mMsgID == -1) { const int lderrno = ldap_get_lderrno(mConnectionHandle, 0, 0); switch (lderrno) { case LDAP_ENCODING_ERROR: return NS_ERROR_LDAP_ENCODING_ERROR; case LDAP_CONNECT_ERROR: return NS_ERROR_LDAP_CONNECT_ERROR; case LDAP_SERVER_DOWN: // XXXdmose rebind here? return NS_ERROR_LDAP_SERVER_DOWN; case LDAP_NO_MEMORY: return NS_ERROR_OUT_OF_MEMORY; default: return NS_ERROR_UNEXPECTED; } } // make sure the connection knows where to call back once the messages // for this operation start coming in // rv = NS_STATIC_CAST(nsLDAPConnection *, NS_STATIC_CAST(nsILDAPConnection *, mConnection.get()))->AddPendingOperation(this); switch (rv) { case NS_OK: break; // note that the return value of ldap_abandon_ext() is ignored, as // there's nothing useful to do with it case NS_ERROR_OUT_OF_MEMORY: (void *)ldap_abandon_ext(mConnectionHandle, mMsgID, 0, 0); return NS_ERROR_OUT_OF_MEMORY; break; case NS_ERROR_UNEXPECTED: case NS_ERROR_ILLEGAL_VALUE: default: (void *)ldap_abandon_ext(mConnectionHandle, mMsgID, 0, 0); return NS_ERROR_UNEXPECTED; } return NS_OK; } // wrappers for ldap_search_ext // int nsLDAPOperation::SearchExt(const nsACString& base, // base DN to search int scope, // SCOPE_{BASE,ONELEVEL,SUBTREE} const nsACString& filter, // search filter char **attrs, // attribute types to be returned int attrsOnly, // attrs only, or values too? LDAPControl **serverctrls, LDAPControl **clientctrls, struct timeval *timeoutp, // how long to wait int sizelimit) // max # of entries to return { if (mMessageListener == 0) { NS_ERROR("nsLDAPOperation::SearchExt(): mMessageListener not set"); return NS_ERROR_NOT_INITIALIZED; } return ldap_search_ext(mConnectionHandle, PromiseFlatCString(base).get(), scope, PromiseFlatCString(filter).get(), attrs, attrsOnly, serverctrls, clientctrls, timeoutp, sizelimit, &mMsgID); } /** * wrapper for ldap_search_ext(): kicks off an async search request. * * @param aBaseDn Base DN to search * @param aScope One of SCOPE_{BASE,ONELEVEL,SUBTREE} * @param aFilter Search filter * @param aAttrCount Number of attributes we request (0 for all) * @param aAttributes Array of strings, holding the attributes we need * @param aTimeOut How long to wait * @param aSizeLimit Maximum number of entries to return. * * XXX doesn't currently handle LDAPControl params * * void searchExt(in AUTF8String aBaseDn, in PRInt32 aScope, * in AUTF8String aFilter, PRUint32 aAttrCount, * const char **aAttributes, in PRIntervalTime aTimeOut, * in PRInt32 aSizeLimit); */ NS_IMETHODIMP nsLDAPOperation::SearchExt(const nsACString& aBaseDn, PRInt32 aScope, const nsACString& aFilter, PRUint32 aAttrCount, const char **aAttributes, PRIntervalTime aTimeOut, PRInt32 aSizeLimit) { PR_LOG(gLDAPLogModule, PR_LOG_DEBUG, ("nsLDAPOperation::SearchExt(): called with aBaseDn = '%s'; " "aFilter = '%s', aAttrCounts = %u, aSizeLimit = %d", PromiseFlatCString(aBaseDn).get(), PromiseFlatCString(aFilter).get(), aAttrCount, aSizeLimit)); char **attrs = 0; // Convert our XPCOM style C-Array to one that the C-SDK will like, i.e. // add a last NULL element. // if (aAttrCount && aAttributes) { attrs = NS_STATIC_CAST(char **, nsMemory::Alloc((aAttrCount + 1) * sizeof(char *))); if (!attrs) { NS_ERROR("nsLDAPOperation::SearchExt: out of memory "); return NS_ERROR_OUT_OF_MEMORY; } memcpy(attrs, aAttributes, aAttrCount * sizeof(char *)); attrs[aAttrCount] = 0; } // XXX deal with timeouts // int retVal = SearchExt(aBaseDn, aScope, aFilter, attrs, 0, 0, 0, 0, aSizeLimit); if (attrs) { nsMemory::Free(attrs); } switch (retVal) { case LDAP_SUCCESS: break; case LDAP_ENCODING_ERROR: return NS_ERROR_LDAP_ENCODING_ERROR; case LDAP_SERVER_DOWN: return NS_ERROR_LDAP_SERVER_DOWN; case LDAP_NO_MEMORY: return NS_ERROR_OUT_OF_MEMORY; case LDAP_NOT_SUPPORTED: return NS_ERROR_LDAP_NOT_SUPPORTED; case LDAP_PARAM_ERROR: return NS_ERROR_INVALID_ARG; case LDAP_FILTER_ERROR: return NS_ERROR_LDAP_FILTER_ERROR; default: NS_ERROR("nsLDAPOperation::SearchExt(): unexpected return value"); return NS_ERROR_UNEXPECTED; } // make sure the connection knows where to call back once the messages // for this operation start coming in // nsresult rv = NS_STATIC_CAST(nsLDAPConnection *, NS_STATIC_CAST( nsILDAPConnection *, mConnection.get()))->AddPendingOperation(this); if (NS_FAILED(rv)) { switch (rv) { case NS_ERROR_OUT_OF_MEMORY: (void *)ldap_abandon_ext(mConnectionHandle, mMsgID, 0, 0); return NS_ERROR_OUT_OF_MEMORY; default: (void *)ldap_abandon_ext(mConnectionHandle, mMsgID, 0, 0); NS_ERROR("nsLDAPOperation::SearchExt(): unexpected error in " "mConnection->AddPendingOperation"); return NS_ERROR_UNEXPECTED; } } return NS_OK; } NS_IMETHODIMP nsLDAPOperation::GetMessageID(PRInt32 *aMsgID) { if (!aMsgID) { return NS_ERROR_ILLEGAL_VALUE; } *aMsgID = mMsgID; return NS_OK; } // as far as I can tell from reading the LDAP C SDK code, abandoning something // that has already been abandoned does not return an error // nsresult nsLDAPOperation::AbandonExt(LDAPControl **serverctrls, LDAPControl **clientctrls) { nsresult rv; nsresult retStatus = NS_OK; if ( mMessageListener == 0 || mMsgID == 0 ) { NS_ERROR("nsLDAPOperation::AbandonExt(): mMessageListener or " "mMsgId not initialized"); return NS_ERROR_NOT_INITIALIZED; } rv = ldap_abandon_ext(mConnectionHandle, mMsgID, serverctrls, clientctrls); switch (rv) { case LDAP_SUCCESS: break; case LDAP_ENCODING_ERROR: return NS_ERROR_LDAP_ENCODING_ERROR; case LDAP_SERVER_DOWN: retStatus = NS_ERROR_LDAP_SERVER_DOWN; break; case LDAP_NO_MEMORY: return NS_ERROR_OUT_OF_MEMORY; case LDAP_PARAM_ERROR: return NS_ERROR_INVALID_ARG; default: NS_ERROR("nsLDAPOperation::AbandonExt(): unexpected return value from " "ldap_abandon_ext"); return NS_ERROR_UNEXPECTED; } // try to remove it from the pendingOperations queue, if it's there. // even if something goes wrong here, the abandon() has already succeeded // succeeded (and there's nothing else the caller can reasonably do), // so we only pay attention to this in debug builds. // rv = NS_STATIC_CAST(nsLDAPConnection *, NS_STATIC_CAST( nsILDAPConnection *, mConnection.get()))->RemovePendingOperation(this); if (NS_FAILED(rv)) { // XXXdmose should we use keep Abandon from happening on multiple // threads at the same time? that's when this condition is most // likely to occur. i _think_ the LDAP C SDK is ok with this; need // to verify. // NS_WARNING("nsLDAPOperation::AbandonExt: " "mConnection->RemovePendingOperation(this) failed."); } return retStatus; } NS_IMETHODIMP nsLDAPOperation::Abandon(void) { return nsLDAPOperation::AbandonExt(0, 0); }