416 lines
12 KiB
C++
416 lines
12 KiB
C++
/* -*- 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 <dmose@mozilla.org>
|
|
*
|
|
* 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);
|
|
}
|