summaryrefslogtreecommitdiff
path: root/usr/src/lib/libldap5/sources/ldap/common/result.c
diff options
context:
space:
mode:
Diffstat (limited to 'usr/src/lib/libldap5/sources/ldap/common/result.c')
-rw-r--r--usr/src/lib/libldap5/sources/ldap/common/result.c1473
1 files changed, 1473 insertions, 0 deletions
diff --git a/usr/src/lib/libldap5/sources/ldap/common/result.c b/usr/src/lib/libldap5/sources/ldap/common/result.c
new file mode 100644
index 0000000000..ecf807653a
--- /dev/null
+++ b/usr/src/lib/libldap5/sources/ldap/common/result.c
@@ -0,0 +1,1473 @@
+/*
+ * Copyright 2001-2003 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ *
+ * The contents of this file are subject to the Netscape Public License
+ * Version 1.0 (the "NPL"); you may not use this file except in
+ * compliance with the NPL. You may obtain a copy of the NPL at
+ * http://www.mozilla.org/NPL/
+ *
+ * Software distributed under the NPL is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
+ * for the specific language governing rights and limitations under the
+ * NPL.
+ *
+ * The Initial Developer of this code under the NPL is Netscape
+ * Communications Corporation. Portions created by Netscape are
+ * Copyright (C) 1998 Netscape Communications Corporation. All Rights
+ * Reserved.
+ */
+/*
+ * Copyright (c) 1990 Regents of the University of Michigan.
+ * All rights reserved.
+ */
+/*
+ * result.c - wait for an ldap result
+ */
+
+#if 0
+#ifndef lint
+static char copyright[] = "@(#) Copyright (c) 1990 Regents of the University of Michigan.\nAll rights reserved.\n";
+#endif
+#endif
+
+#include "ldap-int.h"
+
+#ifdef _SOLARIS_SDK
+/* high resolution timer usage */
+#include <sys/time.h>
+#endif
+
+static int check_response_queue( LDAP *ld, int msgid, int all,
+ int do_abandon_check, LDAPMessage **result );
+static int ldap_abandoned( LDAP *ld, int msgid );
+static int ldap_mark_abandoned( LDAP *ld, int msgid );
+static int wait4msg( LDAP *ld, int msgid, int all, int unlock_permitted,
+ struct timeval *timeout, LDAPMessage **result );
+static int read1msg( LDAP *ld, int msgid, int all, Sockbuf *sb, LDAPConn *lc,
+ LDAPMessage **result );
+static void check_for_refs( LDAP *ld, LDAPRequest *lr, BerElement *ber,
+ int ldapversion, int *totalcountp, int *chasingcountp );
+static int build_result_ber( LDAP *ld, BerElement **berp, LDAPRequest *lr );
+static void merge_error_info( LDAP *ld, LDAPRequest *parentr, LDAPRequest *lr );
+#if defined( CLDAP )
+static int cldap_select1( LDAP *ld, struct timeval *timeout );
+#endif
+static void link_pend( LDAP *ld, LDAPPend *lp );
+#if 0 /* these functions are no longer used */
+static void unlink_pend( LDAP *ld, LDAPPend *lp );
+static int unlink_msg( LDAP *ld, int msgid, int all );
+#endif /* 0 */
+
+/*
+ * ldap_result - wait for an ldap result response to a message from the
+ * ldap server. If msgid is -1, any message will be accepted, otherwise
+ * ldap_result will wait for a response with msgid. If all is 0 the
+ * first message with id msgid will be accepted, otherwise, ldap_result
+ * will wait for all responses with id msgid and then return a pointer to
+ * the entire list of messages. This is only useful for search responses,
+ * which can be of two message types (zero or more entries, followed by an
+ * ldap result). The type of the first message received is returned.
+ * When waiting, any messages that have been abandoned are discarded.
+ *
+ * Example:
+ * ldap_result( s, msgid, all, timeout, result )
+ */
+int
+LDAP_CALL
+ldap_result(
+ LDAP *ld,
+ int msgid,
+ int all,
+ struct timeval *timeout,
+ LDAPMessage **result
+)
+{
+ int rc;
+
+ LDAPDebug( LDAP_DEBUG_TRACE, "ldap_result\n", 0, 0, 0 );
+
+ if ( !NSLDAPI_VALID_LDAP_POINTER( ld )) {
+ return( -1 ); /* punt */
+ }
+
+ LDAP_MUTEX_LOCK( ld, LDAP_RESULT_LOCK );
+
+ rc = nsldapi_result_nolock(ld, msgid, all, 1, timeout, result);
+
+ LDAP_MUTEX_UNLOCK( ld, LDAP_RESULT_LOCK );
+
+ return( rc );
+}
+
+
+int
+nsldapi_result_nolock( LDAP *ld, int msgid, int all, int unlock_permitted,
+ struct timeval *timeout, LDAPMessage **result )
+{
+ int rc;
+
+ LDAPDebug( LDAP_DEBUG_TRACE,
+ "nsldapi_result_nolock (msgid=%d, all=%d)\n", msgid, all, 0 );
+
+ /*
+ * First, look through the list of responses we have received on
+ * this association and see if the response we're interested in
+ * is there. If it is, return it. If not, call wait4msg() to
+ * wait until it arrives or timeout occurs.
+ */
+
+ if ( result == NULL ) {
+ LDAP_SET_LDERRNO( ld, LDAP_PARAM_ERROR, NULL, NULL );
+ return( -1 );
+ }
+
+ if ( check_response_queue( ld, msgid, all, 1, result ) != 0 ) {
+ LDAP_SET_LDERRNO( ld, LDAP_SUCCESS, NULL, NULL );
+ rc = (*result)->lm_msgtype;
+ } else {
+ rc = wait4msg( ld, msgid, all, unlock_permitted, timeout,
+ result );
+ }
+
+ /*
+ * XXXmcs should use cache function pointers to hook in memcache
+ */
+ if ( ld->ld_memcache != NULL && NSLDAPI_SEARCH_RELATED_RESULT( rc ) &&
+ !((*result)->lm_fromcache )) {
+ ldap_memcache_append( ld, (*result)->lm_msgid,
+ (all || NSLDAPI_IS_SEARCH_RESULT( rc )), *result );
+ }
+
+ return( rc );
+}
+
+
+/*
+ * Look through the list of queued responses for a message that matches the
+ * criteria in the msgid and all parameters. msgid == LDAP_RES_ANY matches
+ * all ids.
+ *
+ * If an appropriate message is found, a non-zero value is returned and the
+ * message is dequeued and assigned to *result.
+ *
+ * If not, *result is set to NULL and this function returns 0.
+ */
+static int
+check_response_queue( LDAP *ld, int msgid, int all, int do_abandon_check,
+ LDAPMessage **result )
+{
+ LDAPMessage *lm, *lastlm, *nextlm;
+ LDAPRequest *lr;
+
+ LDAPDebug( LDAP_DEBUG_TRACE,
+ "=> check_response_queue (msgid=%d, all=%d)\n", msgid, all, 0 );
+
+ *result = NULL;
+ lastlm = NULL;
+ LDAP_MUTEX_LOCK( ld, LDAP_RESP_LOCK );
+ for ( lm = ld->ld_responses; lm != NULL; lm = nextlm ) {
+ nextlm = lm->lm_next;
+
+ if ( do_abandon_check && ldap_abandoned( ld, lm->lm_msgid ) ) {
+ ldap_mark_abandoned( ld, lm->lm_msgid );
+
+ if ( lastlm == NULL ) {
+ ld->ld_responses = lm->lm_next;
+ } else {
+ lastlm->lm_next = nextlm;
+ }
+
+ ldap_msgfree( lm );
+
+ continue;
+ }
+
+ if ( msgid == LDAP_RES_ANY || lm->lm_msgid == msgid ) {
+ LDAPMessage *tmp;
+
+ if ( all == 0
+ || (lm->lm_msgtype != LDAP_RES_SEARCH_RESULT
+ && lm->lm_msgtype != LDAP_RES_SEARCH_REFERENCE
+ && lm->lm_msgtype != LDAP_RES_SEARCH_ENTRY) )
+ break;
+
+ for ( tmp = lm; tmp != NULL; tmp = tmp->lm_chain ) {
+ if ( tmp->lm_msgtype == LDAP_RES_SEARCH_RESULT )
+ break;
+ }
+
+ if ( tmp == NULL ) {
+ LDAP_MUTEX_UNLOCK( ld, LDAP_RESP_LOCK );
+ LDAPDebug( LDAP_DEBUG_TRACE,
+ "<= check_response_queue NOT FOUND\n",
+ 0, 0, 0 );
+ return( 0 ); /* no message to return */
+ }
+
+ break;
+ }
+ lastlm = lm;
+ }
+
+ /*
+ * if we did not find a message OR if the one we found is a result for
+ * a request that is still pending, return failure.
+ */
+ if ( lm == NULL
+ || (( lr = nsldapi_find_request_by_msgid( ld, lm->lm_msgid ))
+ != NULL && lr->lr_outrefcnt > 0 )) {
+ LDAP_MUTEX_UNLOCK( ld, LDAP_RESP_LOCK );
+ LDAPDebug( LDAP_DEBUG_TRACE,
+ "<= check_response_queue NOT FOUND\n",
+ 0, 0, 0 );
+ return( 0 ); /* no message to return */
+ }
+
+ if ( all == 0 ) {
+ if ( lm->lm_chain == NULL ) {
+ if ( lastlm == NULL ) {
+ ld->ld_responses = lm->lm_next;
+ } else {
+ lastlm->lm_next = lm->lm_next;
+ }
+ } else {
+ if ( lastlm == NULL ) {
+ ld->ld_responses = lm->lm_chain;
+ ld->ld_responses->lm_next = lm->lm_next;
+ } else {
+ lastlm->lm_next = lm->lm_chain;
+ lastlm->lm_next->lm_next = lm->lm_next;
+ }
+ }
+ } else {
+ if ( lastlm == NULL ) {
+ ld->ld_responses = lm->lm_next;
+ } else {
+ lastlm->lm_next = lm->lm_next;
+ }
+ }
+
+ if ( all == 0 ) {
+ lm->lm_chain = NULL;
+ }
+ lm->lm_next = NULL;
+ LDAP_MUTEX_UNLOCK( ld, LDAP_RESP_LOCK );
+
+ *result = lm;
+ LDAPDebug( LDAP_DEBUG_TRACE,
+ "<= check_response_queue returning msgid %d type %d\n",
+ lm->lm_msgid, lm->lm_msgtype, 0 );
+ return( 1 ); /* a message was found and returned in *result */
+}
+
+
+static int
+wait4msg( LDAP *ld, int msgid, int all, int unlock_permitted,
+ struct timeval *timeout, LDAPMessage **result )
+{
+ int rc;
+ struct timeval tv, *tvp;
+#ifdef _SOLARIS_SDK
+ hrtime_t start_time = 0, tmp_time, tv_time;
+#else
+ long start_time = 0, tmp_time;
+#endif
+ LDAPConn *lc, *nextlc;
+ LDAPRequest *lr;
+
+#ifdef LDAP_DEBUG
+ if ( timeout == NULL ) {
+ LDAPDebug( LDAP_DEBUG_TRACE, "wait4msg (infinite timeout)\n",
+ 0, 0, 0 );
+ } else {
+ LDAPDebug( LDAP_DEBUG_TRACE, "wait4msg (timeout %ld sec, %ld usec)\n",
+ timeout->tv_sec, timeout->tv_usec, 0 );
+ }
+#endif /* LDAP_DEBUG */
+
+ /* check the cache */
+ if ( ld->ld_cache_on && ld->ld_cache_result != NULL ) {
+ /* if ( unlock_permitted ) LDAP_MUTEX_UNLOCK( ld ); */
+ LDAP_MUTEX_LOCK( ld, LDAP_CACHE_LOCK );
+ rc = (ld->ld_cache_result)( ld, msgid, all, timeout, result );
+ LDAP_MUTEX_UNLOCK( ld, LDAP_CACHE_LOCK );
+ /* if ( unlock_permitted ) LDAP_MUTEX_LOCK( ld ); */
+ if ( rc != 0 ) {
+ return( rc );
+ }
+ if ( ld->ld_cache_strategy == LDAP_CACHE_LOCALDB ) {
+ LDAP_SET_LDERRNO( ld, LDAP_TIMEOUT, NULL, NULL );
+ return( 0 ); /* timeout */
+ }
+ }
+
+ /*
+ * if we are looking for a specific msgid, check to see if it is
+ * associated with a dead connection and return an error if so.
+ */
+ if ( msgid != LDAP_RES_ANY && msgid != LDAP_RES_UNSOLICITED ) {
+ LDAP_MUTEX_LOCK( ld, LDAP_REQ_LOCK );
+ if (( lr = nsldapi_find_request_by_msgid( ld, msgid ))
+ == NULL ) {
+ LDAP_MUTEX_UNLOCK( ld, LDAP_REQ_LOCK );
+ LDAP_SET_LDERRNO( ld, LDAP_PARAM_ERROR, NULL,
+ nsldapi_strdup( dgettext(TEXT_DOMAIN,
+ "unknown message id") ));
+ return( -1 ); /* could not find request for msgid */
+ }
+ if ( lr->lr_conn != NULL &&
+ lr->lr_conn->lconn_status == LDAP_CONNST_DEAD ) {
+ nsldapi_free_request( ld, lr, 1 );
+ LDAP_MUTEX_UNLOCK( ld, LDAP_REQ_LOCK );
+ LDAP_SET_LDERRNO( ld, LDAP_SERVER_DOWN, NULL, NULL );
+ return( -1 ); /* connection dead */
+ }
+ LDAP_MUTEX_UNLOCK( ld, LDAP_REQ_LOCK );
+ }
+
+ if ( timeout == NULL ) {
+ tvp = NULL;
+ } else {
+ tv = *timeout;
+ tvp = &tv;
+#ifdef _SOLARIS_SDK
+ start_time = gethrtime();
+ tv_time = ((hrtime_t)tv.tv_sec * NANOSEC +
+ (hrtime_t)tv.tv_usec * (NANOSEC / MICROSEC));
+#else
+ start_time = (long)time( NULL );
+#endif
+ }
+
+ rc = -2;
+ while ( rc == -2 ) {
+#ifdef LDAP_DEBUG
+ if ( ldap_debug & LDAP_DEBUG_TRACE ) {
+ nsldapi_dump_connection( ld, ld->ld_conns, 1 );
+ nsldapi_dump_requests_and_responses( ld );
+ }
+#endif /* LDAP_DEBUG */
+ LDAP_MUTEX_LOCK( ld, LDAP_CONN_LOCK );
+ LDAP_MUTEX_LOCK( ld, LDAP_REQ_LOCK );
+ for ( lc = ld->ld_conns; lc != NULL; lc = lc->lconn_next ) {
+ if ( lc->lconn_sb->sb_ber.ber_ptr <
+ lc->lconn_sb->sb_ber.ber_end ) {
+ rc = read1msg( ld, msgid, all, lc->lconn_sb,
+ lc, result );
+ break;
+ }
+ }
+ LDAP_MUTEX_UNLOCK( ld, LDAP_REQ_LOCK );
+ LDAP_MUTEX_UNLOCK( ld, LDAP_CONN_LOCK );
+
+ if ( lc == NULL ) {
+ rc = nsldapi_iostatus_poll( ld, tvp );
+
+#if defined( LDAP_DEBUG ) && !defined( macintosh ) && !defined( DOS )
+ if ( rc == -1 ) {
+ LDAPDebug( LDAP_DEBUG_TRACE,
+ "nsldapi_iostatus_poll returned -1: errno %d\n",
+ LDAP_GET_ERRNO( ld ), 0, 0 );
+ }
+#endif
+
+#if !defined( macintosh ) && !defined( DOS )
+ if ( rc == 0 || ( rc == -1 && (( ld->ld_options &
+ LDAP_BITOPT_RESTART ) == 0 ||
+ LDAP_GET_ERRNO( ld ) != EINTR ))) {
+#else
+ if ( rc == -1 || rc == 0 ) {
+#endif
+ LDAP_SET_LDERRNO( ld, (rc == -1 ?
+ LDAP_SERVER_DOWN : LDAP_TIMEOUT), NULL,
+ NULL );
+ if ( rc == -1 ) {
+ LDAP_MUTEX_LOCK( ld, LDAP_REQ_LOCK );
+ nsldapi_connection_lost_nolock( ld,
+ NULL );
+ LDAP_MUTEX_UNLOCK( ld, LDAP_REQ_LOCK );
+ }
+ return( rc );
+ }
+
+ if ( rc == -1 ) {
+ rc = -2; /* select interrupted: loop */
+ } else {
+ rc = -2;
+ LDAP_MUTEX_LOCK( ld, LDAP_CONN_LOCK );
+ LDAP_MUTEX_LOCK( ld, LDAP_REQ_LOCK );
+ for ( lc = ld->ld_conns; rc == -2 && lc != NULL;
+ lc = nextlc ) {
+ nextlc = lc->lconn_next;
+ if ( lc->lconn_status ==
+ LDAP_CONNST_CONNECTED &&
+ nsldapi_iostatus_is_read_ready( ld,
+ lc->lconn_sb )) {
+ rc = read1msg( ld, msgid, all,
+ lc->lconn_sb, lc, result );
+ }
+ else if (ld->ld_options & LDAP_BITOPT_ASYNC) {
+ if ( lr
+ && lc->lconn_status == LDAP_CONNST_CONNECTING
+ && nsldapi_iostatus_is_write_ready( ld,
+ lc->lconn_sb ) ) {
+ rc = nsldapi_ber_flush( ld, lc->lconn_sb, lr->lr_ber, 0, 1 );
+ if ( rc == 0 ) {
+ rc = LDAP_RES_BIND;
+ lc->lconn_status = LDAP_CONNST_CONNECTED;
+
+ lr->lr_ber->ber_end = lr->lr_ber->ber_ptr;
+ lr->lr_ber->ber_ptr = lr->lr_ber->ber_buf;
+ nsldapi_iostatus_interest_read( ld, lc->lconn_sb );
+ }
+ else if ( rc == -1 ) {
+ LDAP_SET_LDERRNO( ld, LDAP_SERVER_DOWN, NULL, NULL );
+ nsldapi_free_request( ld, lr, 0 );
+ nsldapi_free_connection( ld, lc, NULL, NULL,
+ 0, 0 );
+ }
+ }
+
+ }
+ }
+ LDAP_MUTEX_UNLOCK( ld, LDAP_REQ_LOCK );
+ LDAP_MUTEX_UNLOCK( ld, LDAP_CONN_LOCK );
+ }
+ }
+
+ /*
+ * It is possible that recursion occurred while chasing
+ * referrals and as a result the message we are looking
+ * for may have been placed on the response queue. Look
+ * for it there before continuing so we don't end up
+ * waiting on the network for a message that we already
+ * received!
+ */
+ if ( rc == -2 &&
+ check_response_queue( ld, msgid, all, 0, result ) != 0 ) {
+ LDAP_SET_LDERRNO( ld, LDAP_SUCCESS, NULL, NULL );
+ rc = (*result)->lm_msgtype;
+ }
+
+ /*
+ * honor the timeout if specified
+ */
+ if ( rc == -2 && tvp != NULL ) {
+#ifdef _SOLARIS_SDK
+ tmp_time = gethrtime();
+ if ((tv_time -= (tmp_time - start_time)) <= 0) {
+#else
+ tmp_time = (long)time( NULL );
+ if (( tv.tv_sec -= ( tmp_time - start_time )) <= 0 ) {
+#endif
+ rc = 0; /* timed out */
+ LDAP_SET_LDERRNO( ld, LDAP_TIMEOUT, NULL,
+ NULL );
+ break;
+ }
+
+#ifdef _SOLARIS_SDK
+ tv.tv_sec = tv_time / NANOSEC;
+ tv.tv_usec = (tv_time % NANOSEC) / (NANOSEC / MICROSEC);
+#endif
+ LDAPDebug( LDAP_DEBUG_TRACE, "wait4msg: %ld secs to go\n",
+ tv.tv_sec, 0, 0 );
+ start_time = tmp_time;
+ }
+ }
+
+ return( rc );
+}
+
+
+/*
+ * read1msg() should be called with LDAP_CONN_LOCK and LDAP_REQ_LOCK locked.
+ */
+static int
+read1msg( LDAP *ld, int msgid, int all, Sockbuf *sb, LDAPConn *lc,
+ LDAPMessage **result )
+{
+ BerElement *ber;
+ LDAPMessage *new, *l, *prev, *chainprev, *tmp;
+ ber_int_t id;
+ ber_tag_t tag;
+ ber_len_t len;
+ int terrno, lderr, foundit = 0;
+ LDAPRequest *lr;
+ int rc, has_parent, message_can_be_returned;
+ int manufactured_result = 0;
+
+ LDAPDebug( LDAP_DEBUG_TRACE, "read1msg\n", 0, 0, 0 );
+
+ message_can_be_returned = 1; /* the usual case... */
+
+ /*
+ * if we are not already in the midst of reading a message, allocate
+ * a ber that is associated with this connection
+ */
+ if ( lc->lconn_ber == NULLBER && nsldapi_alloc_ber_with_options( ld,
+ &lc->lconn_ber ) != LDAP_SUCCESS ) {
+ return( -1 );
+ }
+
+ /*
+ * ber_get_next() doesn't set errno on EOF, so we pre-set it to
+ * zero to avoid getting tricked by leftover "EAGAIN" errors
+ */
+ LDAP_SET_ERRNO( ld, 0 );
+
+ /* get the next message */
+ if ( (tag = ber_get_next( sb, &len, lc->lconn_ber ))
+ != LDAP_TAG_MESSAGE ) {
+ terrno = LDAP_GET_ERRNO( ld );
+ if ( terrno == EWOULDBLOCK || terrno == EAGAIN ) {
+ return( -2 ); /* try again */
+ }
+ LDAP_SET_LDERRNO( ld, (tag == LBER_DEFAULT ? LDAP_SERVER_DOWN :
+ LDAP_LOCAL_ERROR), NULL, NULL );
+ if ( tag == LBER_DEFAULT ) {
+ nsldapi_connection_lost_nolock( ld, sb );
+ }
+ return( -1 );
+ }
+
+ /*
+ * Since we have received a complete message now, we pull this ber
+ * out of the connection structure and never read into it again.
+ */
+ ber = lc->lconn_ber;
+ lc->lconn_ber = NULLBER;
+
+ /* message id */
+ if ( ber_get_int( ber, &id ) == LBER_ERROR ) {
+ LDAP_SET_LDERRNO( ld, LDAP_DECODING_ERROR, NULL, NULL );
+ return( -1 );
+ }
+
+ /* if it's been abandoned, toss it */
+ if ( ldap_abandoned( ld, (int)id ) ) {
+ ber_free( ber, 1 );
+ return( -2 ); /* continue looking */
+ }
+
+ if ( id == LDAP_RES_UNSOLICITED ) {
+ lr = NULL;
+ } else if (( lr = nsldapi_find_request_by_msgid( ld, id )) == NULL ) {
+ LDAPDebug( LDAP_DEBUG_ANY,
+ "no request for response with msgid %ld (tossing)\n",
+ id, 0, 0 );
+ ber_free( ber, 1 );
+ return( -2 ); /* continue looking */
+ }
+
+ /* the message type */
+ if ( (tag = ber_peek_tag( ber, &len )) == LBER_ERROR ) {
+ LDAP_SET_LDERRNO( ld, LDAP_DECODING_ERROR, NULL, NULL );
+ return( -1 );
+ }
+ LDAPDebug( LDAP_DEBUG_TRACE, "got %s msgid %ld, original id %d\n",
+ ( tag == LDAP_RES_SEARCH_ENTRY ) ? "ENTRY" :
+ ( tag == LDAP_RES_SEARCH_REFERENCE ) ? "REFERENCE" : "RESULT", id,
+ ( lr == NULL ) ? id : lr->lr_origid );
+
+ if ( lr != NULL ) {
+ id = lr->lr_origid;
+ lr->lr_res_msgtype = tag;
+ }
+ rc = -2; /* default is to keep looking (no response found) */
+
+ if ( id != LDAP_RES_UNSOLICITED && ( tag == LDAP_RES_SEARCH_REFERENCE ||
+ tag != LDAP_RES_SEARCH_ENTRY )) {
+ int refchasing, reftotal, simple_request = 0;
+
+ check_for_refs( ld, lr, ber, lc->lconn_version, &reftotal,
+ &refchasing );
+
+ if ( refchasing > 0 || lr->lr_outrefcnt > 0 ) {
+ /*
+ * we're chasing one or more new refs...
+ */
+ ber_free( ber, 1 );
+ ber = NULLBER;
+ lr->lr_status = LDAP_REQST_CHASINGREFS;
+ message_can_be_returned = 0;
+
+ } else if ( tag != LDAP_RES_SEARCH_REFERENCE ) {
+ /*
+ * this request is complete...
+ */
+ has_parent = ( lr->lr_parent != NULL );
+
+ if ( lr->lr_outrefcnt <= 0 && !has_parent ) {
+ /* request without any refs */
+ simple_request = ( reftotal == 0 );
+ }
+
+ /*
+ * If this is not a child request and it is a bind
+ * request, reset the connection's bind DN and
+ * status based on the result of the operation.
+ */
+ if ( !has_parent &&
+ LDAP_RES_BIND == lr->lr_res_msgtype &&
+ lr->lr_conn != NULL ) {
+ if ( lr->lr_conn->lconn_binddn != NULL ) {
+ NSLDAPI_FREE(
+ lr->lr_conn->lconn_binddn );
+ }
+ if ( LDAP_SUCCESS == nsldapi_parse_result( ld,
+ lr->lr_res_msgtype, ber, &lderr, NULL,
+ NULL, NULL, NULL )
+ && LDAP_SUCCESS == lderr ) {
+ lr->lr_conn->lconn_bound = 1;
+ lr->lr_conn->lconn_binddn =
+ lr->lr_binddn;
+ lr->lr_binddn = NULL;
+ } else {
+ lr->lr_conn->lconn_bound = 0;
+ lr->lr_conn->lconn_binddn = NULL;
+ }
+ }
+
+ /*
+ * if this response is to a child request, we toss
+ * the message contents and just merge error info.
+ * into the parent.
+ */
+ if ( has_parent ) {
+ ber_free( ber, 1 );
+ ber = NULLBER;
+ }
+ while ( lr->lr_parent != NULL ) {
+ merge_error_info( ld, lr->lr_parent, lr );
+
+ lr = lr->lr_parent;
+ if ( --lr->lr_outrefcnt > 0 ) {
+ break; /* not completely done yet */
+ }
+ }
+
+ /*
+ * we recognize a request as complete when:
+ * 1) it has no outstanding referrals
+ * 2) it is not a child request
+ * 3) we have received a result for the request (i.e.,
+ * something other than an entry or a reference).
+ */
+ if ( lr->lr_outrefcnt <= 0 && lr->lr_parent == NULL &&
+ lr->lr_res_msgtype != LDAP_RES_SEARCH_ENTRY &&
+ lr->lr_res_msgtype != LDAP_RES_SEARCH_REFERENCE ) {
+ id = lr->lr_msgid;
+ tag = lr->lr_res_msgtype;
+ LDAPDebug( LDAP_DEBUG_TRACE,
+ "request %ld done\n", id, 0, 0 );
+LDAPDebug( LDAP_DEBUG_TRACE,
+"res_errno: %d, res_error: <%s>, res_matched: <%s>\n",
+lr->lr_res_errno, lr->lr_res_error ? lr->lr_res_error : "",
+lr->lr_res_matched ? lr->lr_res_matched : "" );
+ if ( !simple_request ) {
+ if ( ber != NULLBER ) {
+ ber_free( ber, 1 );
+ ber = NULLBER;
+ }
+ if ( build_result_ber( ld, &ber, lr )
+ != LDAP_SUCCESS ) {
+ rc = -1; /* fatal error */
+ } else {
+ manufactured_result = 1;
+ }
+ }
+
+ nsldapi_free_request( ld, lr, 1 );
+ } else {
+ message_can_be_returned = 0;
+ }
+ }
+ }
+
+ if ( ber == NULLBER ) {
+ return( rc );
+ }
+
+ /* make a new ldap message */
+ if ( (new = (LDAPMessage*)NSLDAPI_CALLOC( 1, sizeof(struct ldapmsg) ))
+ == NULL ) {
+ LDAP_SET_LDERRNO( ld, LDAP_NO_MEMORY, NULL, NULL );
+ return( -1 );
+ }
+ new->lm_msgid = (int)id;
+ new->lm_msgtype = tag;
+ new->lm_ber = ber;
+
+ /*
+ * if this is a search entry or if this request is complete (i.e.,
+ * there are no outstanding referrals) then add to cache and check
+ * to see if we should return this to the caller right away or not.
+ */
+ if ( message_can_be_returned ) {
+ if ( ld->ld_cache_on ) {
+ nsldapi_add_result_to_cache( ld, new );
+ }
+
+ if ( msgid == LDAP_RES_ANY || id == msgid ) {
+ if ( new->lm_msgtype == LDAP_RES_SEARCH_RESULT ) {
+ /*
+ * return the first response we have for this
+ * search request later (possibly an entire
+ * chain of messages).
+ */
+ foundit = 1;
+ } else if ( all == 0
+ || (new->lm_msgtype != LDAP_RES_SEARCH_REFERENCE
+ && new->lm_msgtype != LDAP_RES_SEARCH_ENTRY) ) {
+ *result = new;
+ LDAP_SET_LDERRNO( ld, LDAP_SUCCESS, NULL,
+ NULL );
+ return( tag );
+ }
+ }
+ }
+
+ /*
+ * if not, we must add it to the list of responses. if
+ * the msgid is already there, it must be part of an existing
+ * search response.
+ */
+
+ prev = NULL;
+ LDAP_MUTEX_LOCK( ld, LDAP_RESP_LOCK );
+ for ( l = ld->ld_responses; l != NULL; l = l->lm_next ) {
+ if ( l->lm_msgid == new->lm_msgid )
+ break;
+ prev = l;
+ }
+
+ /* not part of an existing search response */
+ if ( l == NULL ) {
+ if ( foundit ) {
+ LDAP_MUTEX_UNLOCK( ld, LDAP_RESP_LOCK );
+ *result = new;
+ LDAP_SET_LDERRNO( ld, LDAP_SUCCESS, NULL, NULL );
+ return( tag );
+ }
+
+ new->lm_next = ld->ld_responses;
+ ld->ld_responses = new;
+ LDAPDebug( LDAP_DEBUG_TRACE,
+ "adding new response id %d type %d (looking for id %d)\n",
+ new->lm_msgid, new->lm_msgtype, msgid );
+ LDAP_MUTEX_UNLOCK( ld, LDAP_RESP_LOCK );
+ if( message_can_be_returned )
+ POST( ld, new->lm_msgid, new );
+ return( -2 ); /* continue looking */
+ }
+
+ LDAPDebug( LDAP_DEBUG_TRACE,
+ "adding response id %d type %d (looking for id %d)\n",
+ new->lm_msgid, new->lm_msgtype, msgid );
+
+ /*
+ * part of a search response - add to end of list of entries
+ *
+ * the first step is to find the end of the list of entries and
+ * references. after the following loop is executed, tmp points to
+ * the last entry or reference in the chain. If there are none,
+ * tmp points to the search result.
+ */
+ chainprev = NULL;
+ for ( tmp = l; tmp->lm_chain != NULL &&
+ ( tmp->lm_chain->lm_msgtype == LDAP_RES_SEARCH_ENTRY
+ || tmp->lm_chain->lm_msgtype == LDAP_RES_SEARCH_REFERENCE );
+ tmp = tmp->lm_chain ) {
+ chainprev = tmp;
+ }
+
+ /*
+ * If this is a manufactured result message and a result is already
+ * queued we throw away the one that is queued and replace it with
+ * our new result. This is necessary so we don't end up returning
+ * more than one result.
+ */
+ if ( manufactured_result &&
+ tmp->lm_msgtype == LDAP_RES_SEARCH_RESULT ) {
+ /*
+ * the result is the only thing in the chain... replace it.
+ */
+ new->lm_chain = tmp->lm_chain;
+ new->lm_next = tmp->lm_next;
+ if ( chainprev == NULL ) {
+ if ( prev == NULL ) {
+ ld->ld_responses = new;
+ } else {
+ prev->lm_next = new;
+ }
+ } else {
+ chainprev->lm_chain = new;
+ }
+ if ( l == tmp ) {
+ l = new;
+ }
+ ldap_msgfree( tmp );
+
+ } else if ( manufactured_result && tmp->lm_chain != NULL
+ && tmp->lm_chain->lm_msgtype == LDAP_RES_SEARCH_RESULT ) {
+ /*
+ * entries or references are also present, so the result
+ * is the next entry after tmp. replace it.
+ */
+ new->lm_chain = tmp->lm_chain->lm_chain;
+ new->lm_next = tmp->lm_chain->lm_next;
+ ldap_msgfree( tmp->lm_chain );
+ tmp->lm_chain = new;
+
+ } else if ( tmp->lm_msgtype == LDAP_RES_SEARCH_RESULT ) {
+ /*
+ * the result is the only thing in the chain... add before it.
+ */
+ new->lm_chain = tmp;
+ if ( chainprev == NULL ) {
+ if ( prev == NULL ) {
+ ld->ld_responses = new;
+ } else {
+ prev->lm_next = new;
+ }
+ } else {
+ chainprev->lm_chain = new;
+ }
+ if ( l == tmp ) {
+ l = new;
+ }
+
+ } else {
+ /*
+ * entries and/or references are present... add to the end
+ * of the entry/reference part of the chain.
+ */
+ new->lm_chain = tmp->lm_chain;
+ tmp->lm_chain = new;
+ }
+
+ /*
+ * return the first response or the whole chain if that's what
+ * we were looking for....
+ */
+ if ( foundit ) {
+ if ( all == 0 && l->lm_chain != NULL ) {
+ /*
+ * only return the first response in the chain
+ */
+ if ( prev == NULL ) {
+ ld->ld_responses = l->lm_chain;
+ } else {
+ prev->lm_next = l->lm_chain;
+ }
+ l->lm_chain = NULL;
+ tag = l->lm_msgtype;
+ } else {
+ /*
+ * return all of the responses (may be a chain)
+ */
+ if ( prev == NULL ) {
+ ld->ld_responses = l->lm_next;
+ } else {
+ prev->lm_next = l->lm_next;
+ }
+ }
+ *result = l;
+ LDAP_MUTEX_UNLOCK( ld, LDAP_RESP_LOCK );
+ LDAP_SET_LDERRNO( ld, LDAP_SUCCESS, NULL, NULL );
+ return( tag );
+ }
+ LDAP_MUTEX_UNLOCK( ld, LDAP_RESP_LOCK );
+ return( -2 ); /* continue looking */
+}
+
+
+/*
+ * check for LDAPv2+ (UMich extension) or LDAPv3 referrals or references
+ * errors are merged in "lr".
+ */
+static void
+check_for_refs( LDAP *ld, LDAPRequest *lr, BerElement *ber,
+ int ldapversion, int *totalcountp, int *chasingcountp )
+{
+ int err, origerr;
+ char *errstr, *matcheddn, **v3refs;
+
+ LDAPDebug( LDAP_DEBUG_TRACE, "check_for_refs\n", 0, 0, 0 );
+
+ *chasingcountp = *totalcountp = 0;
+
+ if ( ldapversion < LDAP_VERSION2 || ( lr->lr_parent == NULL
+ && ( ld->ld_options & LDAP_BITOPT_REFERRALS ) == 0 )) {
+ /* referrals are not supported or are disabled */
+ return;
+ }
+
+ if ( lr->lr_res_msgtype == LDAP_RES_SEARCH_REFERENCE ) {
+ err = nsldapi_parse_reference( ld, ber, &v3refs, NULL );
+ origerr = LDAP_REFERRAL; /* a small lie... */
+ matcheddn = errstr = NULL;
+ } else {
+ err = nsldapi_parse_result( ld, lr->lr_res_msgtype, ber,
+ &origerr, &matcheddn, &errstr, &v3refs, NULL );
+ }
+
+ if ( err != LDAP_SUCCESS ) {
+ /* parse failed */
+ return;
+ }
+
+ if ( origerr == LDAP_REFERRAL ) { /* ldapv3 */
+ if ( v3refs != NULL ) {
+ err = nsldapi_chase_v3_refs( ld, lr, v3refs,
+ ( lr->lr_res_msgtype == LDAP_RES_SEARCH_REFERENCE ),
+ totalcountp, chasingcountp );
+ ldap_value_free( v3refs );
+ }
+ } else if ( ldapversion == LDAP_VERSION2
+ && origerr != LDAP_SUCCESS ) {
+ /* referrals may be present in the error string */
+ err = nsldapi_chase_v2_referrals( ld, lr, &errstr,
+ totalcountp, chasingcountp );
+ }
+
+ /* set LDAP errno, message, and matched string appropriately */
+ if ( lr->lr_res_error != NULL ) {
+ NSLDAPI_FREE( lr->lr_res_error );
+ }
+ lr->lr_res_error = errstr;
+
+ if ( lr->lr_res_matched != NULL ) {
+ NSLDAPI_FREE( lr->lr_res_matched );
+ }
+ lr->lr_res_matched = matcheddn;
+
+ if ( err == LDAP_SUCCESS && ( *chasingcountp == *totalcountp )) {
+ if ( *totalcountp > 0 && ( origerr == LDAP_PARTIAL_RESULTS
+ || origerr == LDAP_REFERRAL )) {
+ /* substitute success for referral error codes */
+ lr->lr_res_errno = LDAP_SUCCESS;
+ } else {
+ /* preserve existing non-referral error code */
+ lr->lr_res_errno = origerr;
+ }
+ } else if ( err != LDAP_SUCCESS ) {
+ /* error occurred while trying to chase referrals */
+ lr->lr_res_errno = err;
+ } else {
+ /* some referrals were not recognized */
+ lr->lr_res_errno = ( ldapversion == LDAP_VERSION2 )
+ ? LDAP_PARTIAL_RESULTS : LDAP_REFERRAL;
+ }
+
+ LDAPDebug( LDAP_DEBUG_TRACE,
+ "check_for_refs: new result: msgid %d, res_errno %d, ",
+ lr->lr_msgid, lr->lr_res_errno, 0 );
+ LDAPDebug( LDAP_DEBUG_TRACE, " res_error <%s>, res_matched <%s>\n",
+ lr->lr_res_error ? lr->lr_res_error : "",
+ lr->lr_res_matched ? lr->lr_res_matched : "", 0 );
+ LDAPDebug( LDAP_DEBUG_TRACE,
+ "check_for_refs: %d new refs(s); chasing %d of them\n",
+ *totalcountp, *chasingcountp, 0 );
+}
+
+
+/* returns an LDAP error code and also sets it in LDAP * */
+static int
+build_result_ber( LDAP *ld, BerElement **berp, LDAPRequest *lr )
+{
+ ber_len_t len;
+ ber_int_t along;
+ BerElement *ber;
+ int err;
+
+ if (( err = nsldapi_alloc_ber_with_options( ld, &ber ))
+ != LDAP_SUCCESS ) {
+ return( err );
+ }
+ *berp = ber;
+ if ( ber_printf( ber, "{it{ess}}", lr->lr_msgid,
+ (long)lr->lr_res_msgtype, lr->lr_res_errno,
+ lr->lr_res_matched ? lr->lr_res_matched : "",
+ lr->lr_res_error ? lr->lr_res_error : "" ) == -1 ) {
+ return( LDAP_ENCODING_ERROR );
+ }
+
+ ber_reset( ber, 1 );
+ if ( ber_skip_tag( ber, &len ) == LBER_ERROR ||
+ ber_get_int( ber, &along ) == LBER_ERROR ||
+ ber_peek_tag( ber, &len ) == LBER_ERROR ) {
+ return( LDAP_DECODING_ERROR );
+ }
+
+ return( LDAP_SUCCESS );
+}
+
+
+static void
+merge_error_info( LDAP *ld, LDAPRequest *parentr, LDAPRequest *lr )
+{
+/*
+ * Merge error information in "lr" with "parentr" error code and string.
+ */
+ if ( lr->lr_res_errno == LDAP_PARTIAL_RESULTS ) {
+ parentr->lr_res_errno = lr->lr_res_errno;
+ if ( lr->lr_res_error != NULL ) {
+ (void)nsldapi_append_referral( ld, &parentr->lr_res_error,
+ lr->lr_res_error );
+ }
+ } else if ( lr->lr_res_errno != LDAP_SUCCESS &&
+ parentr->lr_res_errno == LDAP_SUCCESS ) {
+ parentr->lr_res_errno = lr->lr_res_errno;
+ if ( parentr->lr_res_error != NULL ) {
+ NSLDAPI_FREE( parentr->lr_res_error );
+ }
+ parentr->lr_res_error = lr->lr_res_error;
+ lr->lr_res_error = NULL;
+ if ( NAME_ERROR( lr->lr_res_errno )) {
+ if ( parentr->lr_res_matched != NULL ) {
+ NSLDAPI_FREE( parentr->lr_res_matched );
+ }
+ parentr->lr_res_matched = lr->lr_res_matched;
+ lr->lr_res_matched = NULL;
+ }
+ }
+
+ LDAPDebug( LDAP_DEBUG_TRACE, "merged parent (id %d) error info: ",
+ parentr->lr_msgid, 0, 0 );
+ LDAPDebug( LDAP_DEBUG_TRACE, "result lderrno %d, error <%s>, matched <%s>\n",
+ parentr->lr_res_errno, parentr->lr_res_error ?
+ parentr->lr_res_error : "", parentr->lr_res_matched ?
+ parentr->lr_res_matched : "" );
+}
+
+#if defined( CLDAP )
+#if !defined( macintosh ) && !defined( DOS ) && !defined( _WINDOWS ) && !defined(XP_OS2)
+/* XXXmcs: was revised to support extended I/O callbacks but never compiled! */
+static int
+cldap_select1( LDAP *ld, struct timeval *timeout )
+{
+ int rc;
+ static int tblsize = 0;
+ NSLDAPIIOStatus *iosp = ld->ld_iostatus;
+
+ if ( tblsize == 0 ) {
+#ifdef USE_SYSCONF
+ tblsize = sysconf( _SC_OPEN_MAX );
+#else /* USE_SYSCONF */
+ tblsize = getdtablesize();
+#endif /* USE_SYSCONF */
+ }
+
+ if ( tblsize >= FD_SETSIZE ) {
+ /*
+ * clamp value so we don't overrun the fd_set structure
+ */
+ tblsize = FD_SETSIZE - 1;
+ }
+
+ if ( NSLDAPI_IOSTATUS_TYPE_OSNATIVE == iosp->ios_type ) {
+ fd_set readfds;
+
+ FD_ZERO( &readfds );
+ FD_SET( ld->ld_sbp->sb_sd, &readfds );
+
+ /* XXXmcs: UNIX platforms should use poll() */
+ rc = select( tblsize, &readfds, 0, 0, timeout ) );
+
+ } else if ( NSLDAPI_IOSTATUS_TYPE_CALLBACK == iosp->ios_type ) {
+ LDAP_X_PollFD pollfds[ 1 ];
+
+ pollfds[0].lpoll_fd = ld->ld_sbp->sb_sd;
+ pollfds[0].lpoll_arg = ld->ld_sbp->sb_arg;
+ pollfds[0].lpoll_events = LDAP_X_POLLIN;
+ pollfds[0].lpoll_revents = 0;
+ rc = ld->ld_extpoll_fn( pollfds, 1, nsldapi_tv2ms( timeout ),
+ ld->ld_ext_session_arg );
+ } else {
+ LDAPDebug( LDAP_DEBUG_ANY,
+ "nsldapi_iostatus_poll: unknown I/O type %d\n",
+ rc = 0; /* simulate a timeout (what else to do?) */
+ }
+
+ return( rc );
+}
+#endif /* !macintosh */
+
+
+#ifdef macintosh
+static int
+cldap_select1( LDAP *ld, struct timeval *timeout )
+{
+ /* XXXmcs: needs to be revised to support I/O callbacks */
+ return( tcpselect( ld->ld_sbp->sb_sd, timeout ));
+}
+#endif /* macintosh */
+
+
+#if (defined( DOS ) && defined( WINSOCK )) || defined( _WINDOWS ) || defined(XP_OS2)
+/* XXXmcs: needs to be revised to support extended I/O callbacks */
+static int
+cldap_select1( LDAP *ld, struct timeval *timeout )
+{
+ fd_set readfds;
+ int rc;
+
+ FD_ZERO( &readfds );
+ FD_SET( ld->ld_sbp->sb_sd, &readfds );
+
+ if ( NSLDAPI_IO_TYPE_STANDARD == ld->ldiou_type &&
+ NULL != ld->ld_select_fn ) {
+ rc = ld->ld_select_fn( 1, &readfds, 0, 0, timeout );
+ } else if ( NSLDAPI_IO_TYPE_EXTENDED == ld->ldiou_type &&
+ NULL != ld->ld_extselect_fn ) {
+ rc = ld->ld_extselect_fn( ld->ld_ext_session_arg, 1, &readfds, 0,
+ 0, timeout ) );
+ } else {
+ /* XXXmcs: UNIX platforms should use poll() */
+ rc = select( 1, &readfds, 0, 0, timeout ) );
+ }
+
+ return( rc == SOCKET_ERROR ? -1 : rc );
+}
+#endif /* WINSOCK || _WINDOWS */
+#endif /* CLDAP */
+
+int
+LDAP_CALL
+ldap_msgfree( LDAPMessage *lm )
+{
+ LDAPMessage *next;
+ int type = 0;
+
+ LDAPDebug( LDAP_DEBUG_TRACE, "ldap_msgfree\n", 0, 0, 0 );
+
+ for ( ; lm != NULL; lm = next ) {
+ next = lm->lm_chain;
+ type = lm->lm_msgtype;
+ ber_free( lm->lm_ber, 1 );
+ NSLDAPI_FREE( (char *) lm );
+ }
+
+ return( type );
+}
+
+/*
+ * ldap_msgdelete - delete a message. It returns:
+ * 0 if the entire message was deleted
+ * -1 if the message was not found, or only part of it was found
+ */
+int
+ldap_msgdelete( LDAP *ld, int msgid )
+{
+ LDAPMessage *lm, *prev;
+ int msgtype;
+
+ LDAPDebug( LDAP_DEBUG_TRACE, "ldap_msgdelete\n", 0, 0, 0 );
+
+ if ( !NSLDAPI_VALID_LDAP_POINTER( ld )) {
+ return( -1 ); /* punt */
+ }
+
+ prev = NULL;
+ LDAP_MUTEX_LOCK( ld, LDAP_RESP_LOCK );
+ for ( lm = ld->ld_responses; lm != NULL; lm = lm->lm_next ) {
+ if ( lm->lm_msgid == msgid )
+ break;
+ prev = lm;
+ }
+
+ if ( lm == NULL )
+ {
+ LDAP_MUTEX_UNLOCK( ld, LDAP_RESP_LOCK );
+ return( -1 );
+ }
+
+ if ( prev == NULL )
+ ld->ld_responses = lm->lm_next;
+ else
+ prev->lm_next = lm->lm_next;
+ LDAP_MUTEX_UNLOCK( ld, LDAP_RESP_LOCK );
+
+ msgtype = ldap_msgfree( lm );
+ if ( msgtype == LDAP_RES_SEARCH_ENTRY
+ || msgtype == LDAP_RES_SEARCH_REFERENCE ) {
+ return( -1 );
+ }
+
+ return( 0 );
+}
+
+
+/*
+ * return 1 if message msgid is waiting to be abandoned, 0 otherwise
+ */
+static int
+ldap_abandoned( LDAP *ld, int msgid )
+{
+ int i;
+
+ LDAP_MUTEX_LOCK( ld, LDAP_ABANDON_LOCK );
+ if ( ld->ld_abandoned == NULL )
+ {
+ LDAP_MUTEX_UNLOCK( ld, LDAP_ABANDON_LOCK );
+ return( 0 );
+ }
+
+ for ( i = 0; ld->ld_abandoned[i] != -1; i++ )
+ if ( ld->ld_abandoned[i] == msgid )
+ {
+ LDAP_MUTEX_UNLOCK( ld, LDAP_ABANDON_LOCK );
+ return( 1 );
+ }
+
+ LDAP_MUTEX_UNLOCK( ld, LDAP_ABANDON_LOCK );
+ return( 0 );
+}
+
+
+static int
+ldap_mark_abandoned( LDAP *ld, int msgid )
+{
+ int i;
+
+ LDAP_MUTEX_LOCK( ld, LDAP_ABANDON_LOCK );
+ if ( ld->ld_abandoned == NULL )
+ {
+ LDAP_MUTEX_UNLOCK( ld, LDAP_ABANDON_LOCK );
+ return( -1 );
+ }
+
+ for ( i = 0; ld->ld_abandoned[i] != -1; i++ )
+ if ( ld->ld_abandoned[i] == msgid )
+ break;
+
+ if ( ld->ld_abandoned[i] == -1 )
+ {
+ LDAP_MUTEX_UNLOCK( ld, LDAP_ABANDON_LOCK );
+ return( -1 );
+ }
+
+ for ( ; ld->ld_abandoned[i] != -1; i++ ) {
+ ld->ld_abandoned[i] = ld->ld_abandoned[i + 1];
+ }
+
+ LDAP_MUTEX_UNLOCK( ld, LDAP_ABANDON_LOCK );
+ return( 0 );
+}
+
+
+#ifdef CLDAP
+int
+cldap_getmsg( LDAP *ld, struct timeval *timeout, BerElement **ber )
+{
+ int rc;
+ ber_tag_t tag;
+ ber_len_t len;
+
+ if ( ld->ld_sbp->sb_ber.ber_ptr >= ld->ld_sbp->sb_ber.ber_end ) {
+ rc = cldap_select1( ld, timeout );
+ if ( rc == -1 || rc == 0 ) {
+ LDAP_SET_LDERRNO( ld, (rc == -1 ? LDAP_SERVER_DOWN :
+ LDAP_TIMEOUT), NULL, NULL );
+ return( rc );
+ }
+ }
+
+ /* get the next message */
+ if ( (tag = ber_get_next( ld->ld_sbp, &len, ber ))
+ != LDAP_TAG_MESSAGE ) {
+ LDAP_SET_LDERRNO( ld, (tag == LBER_DEFAULT ? LDAP_SERVER_DOWN :
+ LDAP_LOCAL_ERROR), NULL, NULL );
+ return( -1 );
+ }
+
+ return( tag );
+}
+#endif /* CLDAP */
+
+int
+nsldapi_post_result( LDAP *ld, int msgid, LDAPMessage *result )
+{
+ LDAPPend *lp;
+
+ LDAPDebug( LDAP_DEBUG_TRACE,
+ "nsldapi_post_result(ld=0x%x, msgid=%d, result=0x%x)\n",
+ ld, msgid, result );
+ LDAP_MUTEX_LOCK( ld, LDAP_PEND_LOCK );
+ if( msgid == LDAP_RES_ANY ) {
+ /*
+ * Look for any pending request for which someone is waiting.
+ */
+ for( lp = ld->ld_pend; lp != NULL; lp = lp->lp_next )
+ {
+ if ( lp->lp_sema != NULL ) {
+ break;
+ }
+ }
+ /*
+ * If we did't find a pending request, lp is NULL at this
+ * point, and we will leave this function without doing
+ * anything more -- which is exactly what we want to do.
+ */
+ }
+ else
+ {
+ /*
+ * Look for a pending request specific to this message id
+ */
+ for( lp = ld->ld_pend; lp != NULL; lp = lp->lp_next )
+ {
+ if( lp->lp_msgid == msgid )
+ break;
+ }
+
+ if( lp == NULL )
+ {
+ /*
+ * No pending requests for this response... append to
+ * our pending result list.
+ */
+ LDAPPend *newlp;
+ newlp = (LDAPPend *)NSLDAPI_CALLOC( 1,
+ sizeof( LDAPPend ));
+ if( newlp == NULL )
+ {
+ LDAP_MUTEX_UNLOCK( ld, LDAP_PEND_LOCK );
+ LDAP_SET_LDERRNO( ld, LDAP_NO_MEMORY, NULL,
+ NULL );
+ return (-1);
+ }
+ newlp->lp_msgid = msgid;
+ newlp->lp_result = result;
+ link_pend( ld, newlp );
+ }
+ }
+
+
+ if( lp != NULL )
+ {
+ /*
+ * Wake up a thread that is waiting for this result.
+ */
+ lp->lp_msgid = msgid;
+ lp->lp_result = result;
+ LDAP_SEMA_POST( ld, lp );
+ }
+
+ LDAP_MUTEX_UNLOCK( ld, LDAP_PEND_LOCK );
+ return (0);
+}
+
+static void
+link_pend( LDAP *ld, LDAPPend *lp )
+{
+ if (( lp->lp_next = ld->ld_pend ) != NULL )
+ {
+ lp->lp_next->lp_prev = lp;
+ }
+ ld->ld_pend = lp;
+ lp->lp_prev = NULL;
+}
+
+#if 0 /* these functions are no longer used */
+static void
+unlink_pend( LDAP *ld, LDAPPend *lp )
+{
+ if ( lp->lp_prev == NULL ) {
+ ld->ld_pend = lp->lp_next;
+ } else {
+ lp->lp_prev->lp_next = lp->lp_next;
+ }
+
+ if ( lp->lp_next != NULL ) {
+ lp->lp_next->lp_prev = lp->lp_prev;
+ }
+}
+
+static int
+unlink_msg( LDAP *ld, int msgid, int all )
+{
+ int rc;
+ LDAPMessage *lm, *lastlm, *nextlm;
+
+ lastlm = NULL;
+ LDAP_MUTEX_LOCK( ld, LDAP_RESP_LOCK );
+ for ( lm = ld->ld_responses; lm != NULL; lm = nextlm )
+ {
+ nextlm = lm->lm_next;
+
+ if ( lm->lm_msgid == msgid )
+ {
+ LDAPMessage *tmp;
+
+ if ( all == 0
+ || (lm->lm_msgtype != LDAP_RES_SEARCH_RESULT
+ && lm->lm_msgtype != LDAP_RES_SEARCH_REFERENCE
+ && lm->lm_msgtype != LDAP_RES_SEARCH_ENTRY) )
+ break;
+
+ for ( tmp = lm; tmp != NULL; tmp = tmp->lm_chain ) {
+ if ( tmp->lm_msgtype == LDAP_RES_SEARCH_RESULT )
+ break;
+ }
+ if( tmp != NULL )
+ break;
+ }
+ lastlm = lm;
+ }
+
+ if( lm != NULL )
+ {
+
+ if ( all == 0 )
+ {
+ if ( lm->lm_chain == NULL )
+ {
+ if ( lastlm == NULL )
+ ld->ld_responses = lm->lm_next;
+ else
+ lastlm->lm_next = lm->lm_next;
+ }
+ else
+ {
+ if ( lastlm == NULL )
+ {
+ ld->ld_responses = lm->lm_chain;
+ ld->ld_responses->lm_next = lm->lm_next;
+ }
+ else
+ {
+ lastlm->lm_next = lm->lm_chain;
+ lastlm->lm_next->lm_next = lm->lm_next;
+ }
+ }
+ }
+ else
+ {
+ if ( lastlm == NULL )
+ ld->ld_responses = lm->lm_next;
+ else
+ lastlm->lm_next = lm->lm_next;
+ }
+
+ if ( all == 0 )
+ lm->lm_chain = NULL;
+ lm->lm_next = NULL;
+ rc = lm->lm_msgtype;
+ }
+ else
+ {
+ rc = -2;
+ }
+ LDAP_MUTEX_UNLOCK( ld, LDAP_RESP_LOCK );
+ return ( rc );
+}
+#endif /* 0 */