]> git.sur5r.net Git - openldap/blob - libraries/libldap/result.c
ITS#2484, set sasl_maxbuf to SASL_MAX_BUFF_SIZE if it was negotiated
[openldap] / libraries / libldap / result.c
1 /* $OpenLDAP$ */
2 /*
3  * Copyright 1998-2003 The OpenLDAP Foundation, All Rights Reserved.
4  * COPYING RESTRICTIONS APPLY, see COPYRIGHT file
5  */
6 /*  Portions
7  *  Copyright (c) 1990 Regents of the University of Michigan.
8  *  All rights reserved.
9  */
10 /*---
11  * This notice applies to changes, created by or for Novell, Inc.,
12  * to preexisting works for which notices appear elsewhere in this file.
13  *
14  * Copyright (C) 1999, 2000 Novell, Inc. All Rights Reserved.
15  *
16  * THIS WORK IS SUBJECT TO U.S. AND INTERNATIONAL COPYRIGHT LAWS AND TREATIES.
17  * USE, MODIFICATION, AND REDISTRIBUTION OF THIS WORK IS SUBJECT TO VERSION
18  * 2.0.1 OF THE OPENLDAP PUBLIC LICENSE, A COPY OF WHICH IS AVAILABLE AT
19  * HTTP://WWW.OPENLDAP.ORG/LICENSE.HTML OR IN THE FILE "LICENSE" IN THE
20  * TOP-LEVEL DIRECTORY OF THE DISTRIBUTION. ANY USE OR EXPLOITATION OF THIS
21  * WORK OTHER THAN AS AUTHORIZED IN VERSION 2.0.1 OF THE OPENLDAP PUBLIC
22  * LICENSE, OR OTHER PRIOR WRITTEN CONSENT FROM NOVELL, COULD SUBJECT THE
23  * PERPETRATOR TO CRIMINAL AND CIVIL LIABILITY. 
24  *---
25  * Modification to OpenLDAP source by Novell, Inc.
26  * April 2000 sfs Add code to process V3 referrals and search results
27  *
28  *  result.c - wait for an ldap result
29  */
30 /* Note: A verbatim copy of version 2.0.1 of the OpenLDAP Public License 
31  * can be found in the file "build/LICENSE-2.0.1" in this distribution
32  * of OpenLDAP Software.
33  */
34 /*
35  * Portions Copyright (C) The Internet Society (1997)
36  * ASN.1 fragments are from RFC 2251; see RFC for full legal notices.
37  */
38
39 /*
40  * LDAPv3 (RFC2251)
41  *      LDAPResult ::= SEQUENCE {
42  *              resultCode              ENUMERATED { ... },
43  *              matchedDN               LDAPDN,
44  *              errorMessage    LDAPString,
45  *              referral                Referral OPTIONAL
46  *      }
47  *      Referral ::= SEQUENCE OF LDAPURL        (one or more)
48  *      LDAPURL ::= LDAPString                          (limited to URL chars)
49  */
50
51 #include "portable.h"
52
53 #include <stdio.h>
54
55 #include <ac/stdlib.h>
56
57 #include <ac/errno.h>
58 #include <ac/socket.h>
59 #include <ac/string.h>
60 #include <ac/time.h>
61 #include <ac/unistd.h>
62
63 #include "ldap-int.h"
64 #include "ldap_log.h"
65
66 static int ldap_abandoned LDAP_P(( LDAP *ld, ber_int_t msgid ));
67 static int ldap_mark_abandoned LDAP_P(( LDAP *ld, ber_int_t msgid ));
68 static int wait4msg LDAP_P(( LDAP *ld, ber_int_t msgid, int all, struct timeval *timeout,
69         LDAPMessage **result ));
70 static ber_tag_t try_read1msg LDAP_P(( LDAP *ld, ber_int_t msgid,
71         int all, Sockbuf *sb, LDAPConn *lc, LDAPMessage **result ));
72 static ber_tag_t build_result_ber LDAP_P(( LDAP *ld, BerElement **bp, LDAPRequest *lr ));
73 static void merge_error_info LDAP_P(( LDAP *ld, LDAPRequest *parentr, LDAPRequest *lr ));
74 static LDAPMessage * chkResponseList LDAP_P(( LDAP *ld, int msgid, int all));
75
76
77 /*
78  * ldap_result - wait for an ldap result response to a message from the
79  * ldap server.  If msgid is LDAP_RES_ANY (-1), any message will be
80  * accepted.  If msgid is LDAP_RES_UNSOLICITED (0), any unsolicited
81  * message is accepted.  Otherwise ldap_result will wait for a response
82  * with msgid.  If all is LDAP_MSG_ONE (0) the first message with id
83  * msgid will be accepted, otherwise, ldap_result will wait for all
84  * responses with id msgid and then return a pointer to the entire list
85  * of messages.  In general, this is only useful for search responses,
86  * which can be of three message types (zero or more entries, zero or
87  * search references, followed by an ldap result).  An extension to
88  * LDAPv3 allows partial extended responses to be returned in response
89  * to any request.  The type of the first message received is returned.
90  * When waiting, any messages that have been abandoned are discarded.
91  *
92  * Example:
93  *      ldap_result( s, msgid, all, timeout, result )
94  */
95 int
96 ldap_result(
97         LDAP *ld,
98         int msgid,
99         int all,
100         struct timeval *timeout,
101         LDAPMessage **result )
102 {
103         LDAPMessage     *lm;
104         int     rc;
105
106         assert( ld != NULL );
107         assert( result != NULL );
108
109 #ifdef NEW_LOGGING
110         LDAP_LOG ( OPERATION, ARGS, "ldap_result msgid %d\n", msgid, 0, 0 );
111 #else
112         Debug( LDAP_DEBUG_TRACE, "ldap_result msgid %d\n", msgid, 0, 0 );
113 #endif
114
115 #ifdef LDAP_R_COMPILE
116         ldap_pvt_thread_mutex_lock( &ld->ld_res_mutex );
117 #endif
118         lm = chkResponseList(ld, msgid, all);
119
120         if ( lm == NULL ) {
121                 rc = wait4msg( ld, msgid, all, timeout, result );
122         } else {
123                 *result = lm;
124                 ld->ld_errno = LDAP_SUCCESS;
125                 rc = lm->lm_msgtype;
126         }
127 #ifdef LDAP_R_COMPILE
128         ldap_pvt_thread_mutex_unlock( &ld->ld_res_mutex );
129 #endif
130         return( rc );
131 }
132
133 static LDAPMessage *
134 chkResponseList(
135         LDAP *ld,
136         int msgid,
137         int all)
138 {
139         LDAPMessage     *lm, *lastlm, *nextlm;
140     /*
141          * Look through the list of responses we have received on
142          * this association and see if the response we're interested in
143          * is there.  If it is, return it.  If not, call wait4msg() to
144          * wait until it arrives or timeout occurs.
145          */
146
147 #ifdef NEW_LOGGING
148         LDAP_LOG ( OPERATION, ARGS, "ldap_chkResponseList for msgid=%d, all=%d\n", 
149                 msgid, all, 0 );
150 #else
151         Debug( LDAP_DEBUG_TRACE,
152                 "ldap_chkResponseList for msgid=%d, all=%d\n",
153             msgid, all, 0 );
154 #endif
155         lastlm = NULL;
156         for ( lm = ld->ld_responses; lm != NULL; lm = nextlm ) {
157                 nextlm = lm->lm_next;
158
159                 if ( ldap_abandoned( ld, lm->lm_msgid ) ) {
160 #ifdef NEW_LOGGING
161                         LDAP_LOG ( OPERATION, DETAIL1, 
162                                 "ldap_chkResponseList msg abandoned, msgid %d\n", msgid, 0, 0 );
163 #else
164                         Debug( LDAP_DEBUG_TRACE,
165                                 "ldap_chkResponseList msg abandoned, msgid %d\n",
166                             msgid, 0, 0 );
167 #endif
168                         ldap_mark_abandoned( ld, lm->lm_msgid );
169
170                         if ( lastlm == NULL ) {
171                                 /* Remove first entry in list */
172                                 ld->ld_responses = lm->lm_next;
173                         } else {
174                                 lastlm->lm_next = nextlm;
175                         }
176
177                         ldap_msgfree( lm );
178
179                         continue;
180                 }
181
182                 if ( msgid == LDAP_RES_ANY || lm->lm_msgid == msgid ) {
183                         LDAPMessage     *tmp;
184
185                         if ( all == LDAP_MSG_ONE || msgid == LDAP_RES_UNSOLICITED ) {
186                                 break;
187                         }
188
189                         for ( tmp = lm; tmp != NULL; tmp = tmp->lm_chain ) {
190                                 if ( tmp->lm_msgtype != LDAP_RES_SEARCH_ENTRY
191                                     && tmp->lm_msgtype != LDAP_RES_SEARCH_REFERENCE
192                                         && tmp->lm_msgtype != LDAP_RES_INTERMEDIATE )
193                                 {
194                                         break;
195                                 }
196                         }
197
198                         if ( tmp == NULL ) {
199                                 lm = NULL;
200                         }
201
202                         break;
203                 }
204                 lastlm = lm;
205         }
206
207     if ( lm != NULL ) {
208                 /* Found an entry, remove it from the list */
209             if ( lastlm == NULL ) {
210                     ld->ld_responses = (all == LDAP_MSG_ONE && lm->lm_chain != NULL
211                         ? lm->lm_chain : lm->lm_next);
212             } else {
213                     lastlm->lm_next = (all == LDAP_MSG_ONE && lm->lm_chain != NULL
214                         ? lm->lm_chain : lm->lm_next);
215             }
216             if ( all == LDAP_MSG_ONE && lm->lm_chain != NULL ) {
217                     lm->lm_chain->lm_next = lm->lm_next;
218                     lm->lm_chain = NULL;
219             }
220             lm->lm_next = NULL;
221     }
222
223 #ifdef LDAP_DEBUG
224         if( lm == NULL) {
225 #ifdef NEW_LOGGING
226                 LDAP_LOG ( OPERATION, RESULTS, "ldap_chkResponseList returns NULL\n",
227                         0, 0, 0 );
228 #else
229                 Debug( LDAP_DEBUG_TRACE,
230                         "ldap_chkResponseList returns NULL\n", 0, 0, 0);
231 #endif
232         } else {
233 #ifdef NEW_LOGGING
234                 LDAP_LOG ( OPERATION, RESULTS, 
235                         "ldap_chkResponseList returns msgid %d, type 0x%02lu\n",
236                         lm->lm_msgid, (unsigned long) lm->lm_msgtype, 0 );
237 #else
238                 Debug( LDAP_DEBUG_TRACE,
239                         "ldap_chkResponseList returns msgid %d, type 0x%02lu\n",
240                         lm->lm_msgid, (unsigned long) lm->lm_msgtype, 0);
241 #endif
242         }
243 #endif
244     return lm;
245 }
246
247 static int
248 wait4msg(
249         LDAP *ld,
250         ber_int_t msgid,
251         int all,
252         struct timeval *timeout,
253         LDAPMessage **result )
254 {
255         int             rc;
256         struct timeval  tv, *tvp;
257         time_t          start_time = 0;
258         time_t          tmp_time;
259         LDAPConn        *lc, *nextlc;
260
261         assert( ld != NULL );
262         assert( result != NULL );
263
264 #ifdef LDAP_DEBUG
265         if ( timeout == NULL ) {
266 #ifdef NEW_LOGGING
267                 LDAP_LOG ( OPERATION, ARGS, 
268                         "wait4msg (infinite timeout), msgid %d\n", msgid, 0, 0 );
269 #else
270                 Debug( LDAP_DEBUG_TRACE, "wait4msg (infinite timeout), msgid %d\n",
271                     msgid, 0, 0 );
272 #endif
273         } else {
274 #ifdef NEW_LOGGING
275                 LDAP_LOG ( OPERATION, ARGS, 
276                         "wait4msg (timeout %ld sec, %ld usec), msgid %d\n", 
277                         (long) timeout->tv_sec, (long) timeout->tv_usec, msgid );
278 #else
279                 Debug( LDAP_DEBUG_TRACE, "wait4msg (timeout %ld sec, %ld usec), msgid %d\n",
280                        (long) timeout->tv_sec, (long) timeout->tv_usec, msgid );
281 #endif
282         }
283 #endif /* LDAP_DEBUG */
284
285         if ( timeout == NULL ) {
286                 tvp = NULL;
287         } else {
288                 tv = *timeout;
289                 tvp = &tv;
290                 start_time = time( NULL );
291         }
292                     
293         rc = -2;
294         while ( rc == -2 ) {
295 #ifdef LDAP_DEBUG
296 #ifdef NEW_LOGGING
297                 LDAP_LOG ( OPERATION, ARGS, 
298                         "wait4msg continue, msgid %d, all %d\n", msgid, all, 0 );
299 #else
300                 Debug( LDAP_DEBUG_TRACE, "wait4msg continue, msgid %d, all %d\n",
301                     msgid, all, 0 );
302 #endif
303                 if ( ldap_debug & LDAP_DEBUG_TRACE ) {
304                         ldap_dump_connection( ld, ld->ld_conns, 1 );
305                         ldap_dump_requests_and_responses( ld );
306                 }
307 #endif /* LDAP_DEBUG */
308
309         if( (*result = chkResponseList(ld, msgid, all)) != NULL ) {
310             rc = (*result)->lm_msgtype;
311         } else {
312
313                         for ( lc = ld->ld_conns; lc != NULL; lc = lc->lconn_next ) {
314                                 if ( ber_sockbuf_ctrl( lc->lconn_sb,
315                                                 LBER_SB_OPT_DATA_READY, NULL ) ) {
316                                             rc = try_read1msg( ld, msgid, all, lc->lconn_sb,
317                                                 lc, result );
318                                     break;
319                                 }
320                 }
321
322                     if ( lc == NULL ) {
323                             rc = ldap_int_select( ld, tvp );
324 #ifdef LDAP_DEBUG
325                             if ( rc == -1 ) {
326 #ifdef NEW_LOGGING
327                                         LDAP_LOG ( OPERATION, ARGS, 
328                                                 "wait4msg: ldap_int_select returned -1: errno %d\n", 
329                                                 errno, 0, 0 );
330 #else
331                                 Debug( LDAP_DEBUG_TRACE,
332                                         "ldap_int_select returned -1: errno %d\n",
333                                         errno, 0, 0 );
334 #endif
335                             }
336 #endif
337
338                             if ( rc == 0 || ( rc == -1 && (
339                                     !LDAP_BOOL_GET(&ld->ld_options, LDAP_BOOL_RESTART)
340                                     || errno != EINTR )))
341                             {
342                                     ld->ld_errno = (rc == -1 ? LDAP_SERVER_DOWN :
343                                         LDAP_TIMEOUT);
344                                     return( rc );
345                             }
346
347                             if ( rc == -1 ) {
348                                     rc = -2;    /* select interrupted: loop */
349                             } else {
350                                     rc = -2;
351 #ifdef LDAP_R_COMPILE
352                                     ldap_pvt_thread_mutex_lock( &ld->ld_req_mutex );
353 #endif
354                                     if ( ld->ld_requests &&
355                                                 ld->ld_requests->lr_status == LDAP_REQST_WRITING &&
356                                                 ldap_is_write_ready( ld,
357                                                         ld->ld_requests->lr_conn->lconn_sb ) ) {
358                                                 ldap_int_flush_request( ld, ld->ld_requests );
359                                         }
360 #ifdef LDAP_R_COMPILE
361                                     ldap_pvt_thread_mutex_unlock( &ld->ld_req_mutex );
362 #endif
363                                     for ( lc = ld->ld_conns; rc == -2 && lc != NULL;
364                                         lc = nextlc ) {
365                                             nextlc = lc->lconn_next;
366                                             if ( lc->lconn_status ==
367                                                 LDAP_CONNST_CONNECTED &&
368                                                 ldap_is_read_ready( ld,
369                                                 lc->lconn_sb )) {
370                                                     rc = try_read1msg( ld, msgid, all,
371                                                         lc->lconn_sb, lc, result );
372                                             }
373                                     }
374                             }
375                     }
376                 }
377
378                 if ( rc == -2 && tvp != NULL ) {
379                         tmp_time = time( NULL );
380                         if (( tv.tv_sec -=  ( tmp_time - start_time )) <= 0 ) {
381                                 rc = 0; /* timed out */
382                                 ld->ld_errno = LDAP_TIMEOUT;
383                                 break;
384                         }
385
386 #ifdef NEW_LOGGING
387                         LDAP_LOG ( OPERATION, DETAIL1, 
388                                 "wait4msg: %ld secs to go\n", (long) tv.tv_sec, 0, 0 );
389 #else
390                         Debug( LDAP_DEBUG_TRACE, "wait4msg:  %ld secs to go\n",
391                                (long) tv.tv_sec, 0, 0 );
392 #endif
393                         start_time = tmp_time;
394                 }
395         }
396
397         return( rc );
398 }
399
400
401 static ber_tag_t
402 try_read1msg(
403         LDAP *ld,
404         ber_int_t msgid,
405         int all,
406         Sockbuf *sb,
407         LDAPConn *lc,
408         LDAPMessage **result )
409 {
410         BerElement      *ber;
411         LDAPMessage     *new, *l, *prev, *tmp;
412         ber_int_t       id;
413         ber_tag_t       tag;
414         ber_len_t       len;
415         int             foundit = 0;
416         LDAPRequest     *lr, *tmplr;
417         BerElement      tmpber;
418         int             rc, refer_cnt, hadref, simple_request;
419         ber_int_t       lderr;
420 #ifdef LDAP_CONNECTIONLESS
421         int             firstmsg = 1, moremsgs = 0, isv2 = 0;
422 #endif
423         /*
424          * v3ref = flag for V3 referral / search reference
425          * 0 = not a ref, 1 = sucessfully chased ref, -1 = pass ref to application
426          */
427         int     v3ref;
428
429         assert( ld != NULL );
430         assert( lc != NULL );
431         
432 #ifdef NEW_LOGGING
433         LDAP_LOG ( OPERATION, ARGS, "read1msg: msgid %d, all %d\n", msgid, all, 0 );
434 #else
435         Debug( LDAP_DEBUG_TRACE, "read1msg: msgid %d, all %d\n", msgid, all, 0 );
436 #endif
437
438 retry:
439         if ( lc->lconn_ber == NULL ) {
440                 lc->lconn_ber = ldap_alloc_ber_with_options(ld);
441
442                 if( lc->lconn_ber == NULL ) {
443                         return -1;
444                 }
445         }
446
447         ber = lc->lconn_ber;
448         assert( LBER_VALID (ber) );
449
450         /* get the next message */
451         errno = 0;
452 #ifdef LDAP_CONNECTIONLESS
453         if ( LDAP_IS_UDP(ld) ) {
454                 struct sockaddr from;
455                 ber_int_sb_read(sb, &from, sizeof(struct sockaddr));
456                 if (ld->ld_options.ldo_version == LDAP_VERSION2) isv2=1;
457         }
458 nextresp3:
459 #endif
460         tag = ber_get_next( sb, &len, ber );
461         if ( tag == LDAP_TAG_MESSAGE ) {
462                 /*
463                  * We read a complete message.
464                  * The connection should no longer need this ber.
465                  */
466                 lc->lconn_ber = NULL;
467         }
468         if ( tag != LDAP_TAG_MESSAGE ) {
469                 if ( tag == LBER_DEFAULT) {
470 #ifdef LDAP_DEBUG                  
471 #ifdef NEW_LOGGING
472                         LDAP_LOG ( OPERATION, DETAIL1, 
473                                 "read1msg: ber_get_next failed\n", 0, 0, 0 );
474 #else
475                         Debug( LDAP_DEBUG_CONNS,
476                               "ber_get_next failed.\n", 0, 0, 0 );
477 #endif             
478 #endif             
479 #ifdef EWOULDBLOCK                      
480                         if (errno==EWOULDBLOCK) return -2;
481 #endif
482 #ifdef EAGAIN
483                         if (errno == EAGAIN) return -2;
484 #endif
485                         ld->ld_errno = LDAP_SERVER_DOWN;
486                         return -1;
487                 }
488                 ld->ld_errno = LDAP_LOCAL_ERROR;
489                 return -1;
490         }
491
492         /* message id */
493         if ( ber_get_int( ber, &id ) == LBER_ERROR ) {
494                 ber_free( ber, 1 );
495                 ld->ld_errno = LDAP_DECODING_ERROR;
496                 return( -1 );
497         }
498
499         /* if it's been abandoned, toss it */
500         if ( ldap_abandoned( ld, id ) ) {
501 #ifdef NEW_LOGGING
502                 LDAP_LOG ( OPERATION, DETAIL1, "read1msg: abandoned\n", 0, 0, 0 );
503 #else
504                 Debug( LDAP_DEBUG_ANY, "abandoned\n", 0, 0, 0);
505 #endif
506 retry_ber:
507                 ber_free( ber, 1 );
508                 if ( ber_sockbuf_ctrl( sb, LBER_SB_OPT_DATA_READY, NULL ) ) {
509                         goto retry;
510                 }
511                 return( -2 );   /* continue looking */
512         }
513
514         if (( lr = ldap_find_request_by_msgid( ld, id )) == NULL ) {
515 #ifdef NEW_LOGGING
516                 LDAP_LOG ( OPERATION, DETAIL1, 
517                         "read1msg: no request for response with msgid %ld (tossing)\n",
518                         (long) id, 0, 0 );
519 #else
520                 Debug( LDAP_DEBUG_ANY,
521                     "no request for response with msgid %ld (tossing)\n",
522                     (long) id, 0, 0 );
523 #endif
524                 goto retry_ber;
525         }
526 #ifdef LDAP_CONNECTIONLESS
527         if (LDAP_IS_UDP(ld) && isv2) {
528                 ber_scanf(ber, "x{");
529         }
530 nextresp2:
531 #endif
532         /* the message type */
533         if ( (tag = ber_peek_tag( ber, &len )) == LBER_ERROR ) {
534                 ld->ld_errno = LDAP_DECODING_ERROR;
535                 ber_free( ber, 1 );
536                 return( -1 );
537         }
538
539 #ifdef NEW_LOGGING
540         LDAP_LOG ( OPERATION, DETAIL1, 
541                 "read1msg: ldap_read: message type %s msgid %ld, original id %ld\n",
542             ldap_int_msgtype2str( tag ),
543                 (long) lr->lr_msgid, (long) lr->lr_origid );
544 #else
545         Debug( LDAP_DEBUG_TRACE,
546                 "ldap_read: message type %s msgid %ld, original id %ld\n",
547             ldap_int_msgtype2str( tag ),
548                 (long) lr->lr_msgid, (long) lr->lr_origid );
549 #endif
550
551         id = lr->lr_origid;
552         refer_cnt = 0;
553         hadref = simple_request = 0;
554         rc = -2;        /* default is to keep looking (no response found) */
555         lr->lr_res_msgtype = tag;
556
557         /*
558          * This code figures out if we are going to chase a
559          * referral / search reference, or pass it back to the application
560          */
561         v3ref = 0;      /* Assume not a V3 search reference or referral */
562         if( (tag != LDAP_RES_SEARCH_ENTRY) && (ld->ld_version > LDAP_VERSION2) ) {
563                 BerElement      tmpber = *ber;  /* struct copy */
564                 char **refs = NULL;
565
566                 if( tag == LDAP_RES_SEARCH_REFERENCE) {
567                         /* This is a V3 search reference */
568                         /* Assume we do not chase the reference, but pass it to application */
569                         v3ref = -1;
570                         if( LDAP_BOOL_GET(&ld->ld_options, LDAP_BOOL_REFERRALS) ||
571                                         (lr->lr_parent != NULL) )
572                         {
573                                 /* Get the referral list */
574                                 if ( ber_scanf( &tmpber, "{v}", &refs ) == LBER_ERROR ) {
575                                         rc = LDAP_DECODING_ERROR;
576                                 } else {
577                                         /* Note: refs arrary is freed by ldap_chase_v3referrals */
578                                         refer_cnt = ldap_chase_v3referrals( ld, lr, refs,
579                                             1, &lr->lr_res_error, &hadref );
580                                         if ( refer_cnt > 0 ) {  /* sucessfully chased reference */
581                                                 /* If haven't got end search, set chasing referrals */
582                                                 if( lr->lr_status != LDAP_REQST_COMPLETED) {
583                                                         lr->lr_status = LDAP_REQST_CHASINGREFS;
584 #ifdef NEW_LOGGING
585                                                         LDAP_LOG ( OPERATION, DETAIL1, 
586                                                                 "read1msg: search ref chased,"
587                                                                 "mark request chasing refs, id =        %d\n",
588                                                                 lr->lr_msgid, 0, 0 );
589 #else
590                                                         Debug( LDAP_DEBUG_TRACE,
591                                                             "read1msg:  search ref chased, mark request chasing refs, id = %d\n",
592                                                             lr->lr_msgid, 0, 0);
593 #endif
594                                                 }
595                                                 v3ref = 1;      /* We sucessfully chased the reference */
596                                         }
597                                 }
598                         }
599                 } else {
600                         /* Check for V3 referral */
601                         ber_len_t len;
602                         if ( ber_scanf( &tmpber, "{iaa",/*}*/ &lderr,
603                                     &lr->lr_res_matched, &lr->lr_res_error )
604                                     != LBER_ERROR ) {
605                                 /* Check if V3 referral */
606                                 if( ber_peek_tag( &tmpber, &len) == LDAP_TAG_REFERRAL ) {
607                                         /* We have a V3 referral, assume we cannot chase it */
608                                         v3ref = -1;
609                                         if( LDAP_BOOL_GET(&ld->ld_options, LDAP_BOOL_REFERRALS)
610                                                          || (lr->lr_parent != NULL) )
611                                         {
612                                                 v3ref = -1;  /* Assume referral not chased and return it to app */
613                                                 /* Get the referral list */
614                                                 if( ber_scanf( &tmpber, "{v}", &refs) == LBER_ERROR) {
615                                                         rc = LDAP_DECODING_ERROR;
616                                                         lr->lr_status = LDAP_REQST_COMPLETED;
617 #ifdef NEW_LOGGING
618                                                         LDAP_LOG ( OPERATION, DETAIL1, 
619                                                                 "read1msg: referral decode error,"
620                                                                 "mark request completed, id =   %d\n",
621                                                                 lr->lr_msgid, 0, 0 );
622 #else
623                                                         Debug( LDAP_DEBUG_TRACE,
624                                                             "read1msg: referral decode error, mark request completed, id = %d\n",
625                                                                     lr->lr_msgid, 0, 0);
626 #endif
627                                                 } else {
628                                                         /* Chase the referral 
629                                                          * Note: refs arrary is freed by ldap_chase_v3referrals
630                                                          */
631                                                         refer_cnt = ldap_chase_v3referrals( ld, lr, refs,
632                                                             0, &lr->lr_res_error, &hadref );
633                                                         lr->lr_status = LDAP_REQST_COMPLETED;
634 #ifdef NEW_LOGGING
635                                                         LDAP_LOG ( OPERATION, DETAIL1, 
636                                                                 "read1msg: referral chased,"
637                                                                 "mark request completed, id =   %d\n",
638                                                                 lr->lr_msgid, 0, 0 );
639 #else
640                                                         Debug( LDAP_DEBUG_TRACE,
641                                                             "read1msg:  referral chased, mark request completed, id = %d\n",
642                                                             lr->lr_msgid, 0, 0);
643 #endif
644                                                         if( refer_cnt > 0) {
645                                                                 v3ref = 1;  /* Referral successfully chased */
646                                                         }
647                                                 }
648                                         }
649                                 }
650
651                                 if( lr->lr_res_matched != NULL ) {
652                                         LDAP_FREE( lr->lr_res_matched );
653                                         lr->lr_res_matched = NULL;
654                                 }
655                                 if( lr->lr_res_error != NULL ) {
656                                         LDAP_FREE( lr->lr_res_error );
657                                         lr->lr_res_error = NULL;
658                                 }
659                         }
660                 }
661         }
662
663         /* All results that just return a status, i.e. don't return data
664          * go through the following code.  This code also chases V2 referrals
665          * and checks if all referrals have been chased.
666          */
667         if ( (tag != LDAP_RES_SEARCH_ENTRY) && (v3ref > -1) &&
668                 (tag != LDAP_RES_INTERMEDIATE ))
669         {
670                 /* For a v3 search referral/reference, only come here if already chased it */
671                 if ( ld->ld_version >= LDAP_VERSION2 &&
672                         ( lr->lr_parent != NULL ||
673                         LDAP_BOOL_GET(&ld->ld_options, LDAP_BOOL_REFERRALS) ) )
674                 {
675                         tmpber = *ber;  /* struct copy */
676                         if ( v3ref == 1 ) {
677                                 /* V3 search reference or V3 referral
678                                  * sucessfully chased. If this message
679                                  * is a search result, then it has no more
680                                  * outstanding referrals.
681                                  */
682                                 if ( tag == LDAP_RES_SEARCH_RESULT )
683                                         refer_cnt = 0;
684                         } else if ( ber_scanf( &tmpber, "{iaa}", &lderr,
685                             &lr->lr_res_matched, &lr->lr_res_error )
686                             != LBER_ERROR ) {
687                                 if ( lderr != LDAP_SUCCESS ) {
688                                         /* referrals are in error string */
689                                         refer_cnt = ldap_chase_referrals( ld, lr,
690                                                 &lr->lr_res_error, -1, &hadref );
691                                         lr->lr_status = LDAP_REQST_COMPLETED;
692 #ifdef NEW_LOGGING
693                                         LDAP_LOG ( OPERATION, DETAIL1, 
694                                                 "read1msg: V2 referral chased,"
695                                                 "mark request completed, id =   %d\n",
696                                                 lr->lr_msgid, 0, 0 );
697 #else
698                                         Debug( LDAP_DEBUG_TRACE,
699                                             "read1msg:  V2 referral chased, mark request completed, id = %d\n", lr->lr_msgid, 0, 0);
700 #endif
701                                 }
702
703                                 /* save errno, message, and matched string */
704                                 if ( !hadref || lr->lr_res_error == NULL ) {
705                                         lr->lr_res_errno = ( lderr ==
706                                         LDAP_PARTIAL_RESULTS ) ? LDAP_SUCCESS
707                                         : lderr;
708                                 } else if ( ld->ld_errno != LDAP_SUCCESS ) {
709                                         lr->lr_res_errno = ld->ld_errno;
710                                 } else {
711                                         lr->lr_res_errno = LDAP_PARTIAL_RESULTS;
712                                 }
713 #ifdef NEW_LOGGING
714 LDAP_LOG ( OPERATION, DETAIL1, 
715         "read1msg: new result: res_errno: %d, res_error: <%s>, res_matched: <%s>\n",
716     lr->lr_res_errno, lr->lr_res_error ? lr->lr_res_error : "",
717     lr->lr_res_matched ? lr->lr_res_matched : "" );
718 #else
719 Debug( LDAP_DEBUG_TRACE,
720     "new result:  res_errno: %d, res_error: <%s>, res_matched: <%s>\n",
721     lr->lr_res_errno, lr->lr_res_error ? lr->lr_res_error : "",
722     lr->lr_res_matched ? lr->lr_res_matched : "" );
723 #endif
724                         }
725                 }
726
727 #ifdef NEW_LOGGING
728                 LDAP_LOG ( OPERATION, DETAIL1, "read1msg: %d new referrals\n", 
729                         refer_cnt, 0, 0 );
730 #else
731                 Debug( LDAP_DEBUG_TRACE,
732                     "read1msg:  %d new referrals\n", refer_cnt, 0, 0 );
733 #endif
734
735                 if ( refer_cnt != 0 ) { /* chasing referrals */
736                         ber_free( ber, 1 );
737                         ber = NULL;
738                         if ( refer_cnt < 0 ) {
739                                 return( -1 );   /* fatal error */
740                         }
741                         lr->lr_res_errno = LDAP_SUCCESS; /* sucessfully chased referral */
742                 } else {
743                         if ( lr->lr_outrefcnt <= 0 && lr->lr_parent == NULL ) {
744                                 /* request without any referrals */
745                                 simple_request = ( hadref ? 0 : 1 );
746                         } else {
747                                 /* request with referrals or child request */
748                                 ber_free( ber, 1 );
749                                 ber = NULL;
750                         }
751
752                         lr->lr_status = LDAP_REQST_COMPLETED; /* declare this request done */
753 #ifdef NEW_LOGGING
754                         LDAP_LOG ( OPERATION, DETAIL1, 
755                                 "read1msg: mark request completed, id = %d\n", 
756                                 lr->lr_msgid, 0, 0 );
757 #else
758                         Debug( LDAP_DEBUG_TRACE,
759                             "read1msg:  mark request completed, id = %d\n", lr->lr_msgid, 0, 0);
760 #endif
761                         while ( lr->lr_parent != NULL ) {
762                                 merge_error_info( ld, lr->lr_parent, lr );
763
764                                 lr = lr->lr_parent;
765                                 if ( --lr->lr_outrefcnt > 0 ) {
766                                         break;  /* not completely done yet */
767                                 }
768                         }
769
770                         /* Check if all requests are finished, lr is now parent */
771                         tmplr = lr;
772                         if (tmplr->lr_status == LDAP_REQST_COMPLETED) {
773                                 for(tmplr=lr->lr_child; tmplr != NULL; tmplr=tmplr->lr_refnext) {
774                                 if( tmplr->lr_status != LDAP_REQST_COMPLETED) {
775                                         break;
776                                         }
777                                 }
778                         }
779
780                         /* This is the parent request if the request has referrals */
781                         if ( lr->lr_outrefcnt <= 0 && lr->lr_parent == NULL && tmplr == NULL ) {
782                                 id = lr->lr_msgid;
783                                 tag = lr->lr_res_msgtype;
784 #ifdef NEW_LOGGING
785                         LDAP_LOG ( OPERATION, DETAIL1, 
786                                 "read1msg: request %ld done\n", (long) id, 0, 0 );
787                         LDAP_LOG ( OPERATION, DETAIL1, 
788                                 "read1msg: res_errno: %d,res_error: <%s>, res_matched: <%s>\n",
789                                 lr->lr_res_errno, lr->lr_res_error ? lr->lr_res_error : "",
790                                 lr->lr_res_matched ? lr->lr_res_matched : "" );
791 #else
792                                 Debug( LDAP_DEBUG_ANY, "request %ld done\n",
793                                     (long) id, 0, 0 );
794 Debug( LDAP_DEBUG_TRACE,
795 "res_errno: %d, res_error: <%s>, res_matched: <%s>\n",
796 lr->lr_res_errno, lr->lr_res_error ? lr->lr_res_error : "",
797 lr->lr_res_matched ? lr->lr_res_matched : "" );
798 #endif
799                                 if ( !simple_request ) {
800                                         ber_free( ber, 1 );
801                                         ber = NULL;
802                                         if ( build_result_ber( ld, &ber, lr )
803                                             == LBER_ERROR ) {
804                                                 rc = -1; /* fatal error */
805                                         }
806                                 }
807
808                                 ldap_free_request( ld, lr );
809                         }
810
811                         if ( lc != NULL ) {
812                                 ldap_free_connection( ld, lc, 0, 1 );
813                         }
814                 }
815         }
816
817         if ( ber == NULL ) {
818                 return( rc );
819         }
820
821         /* make a new ldap message */
822         if ( (new = (LDAPMessage *) LDAP_CALLOC( 1, sizeof(LDAPMessage) ))
823             == NULL ) {
824                 ld->ld_errno = LDAP_NO_MEMORY;
825                 return( -1 );
826         }
827         new->lm_msgid = (int)id;
828         new->lm_msgtype = tag;
829         new->lm_ber = ber;
830
831 #ifdef LDAP_CONNECTIONLESS
832         /* CLDAP replies all fit in a single datagram. In LDAPv2 RFC1798
833          * the responses are all a sequence wrapped in one message. In
834          * LDAPv3 each response is in its own message. The datagram must
835          * end with a SearchResult. We can't just parse each response in
836          * separate calls to try_read1msg because the header info is only
837          * present at the beginning of the datagram, not at the beginning
838          * of each response. So parse all the responses at once and queue
839          * them up, then pull off the first response to return to the
840          * caller when all parsing is complete.
841          */
842         if ( LDAP_IS_UDP(ld) ) {
843                 /* If not a result, look for more */
844                 if ( tag != LDAP_RES_SEARCH_RESULT ) {
845                         int ok = 0;
846                         moremsgs = 1;
847                         if (isv2) {
848                                 /* LDAPv2: dup the current ber, skip past the current
849                                  * response, and see if there are any more after it.
850                                  */
851                                 ber = ber_dup( ber );
852                                 ber_scanf( ber, "x" );
853                                 if (ber_peek_tag(ber, &len) != LBER_DEFAULT) {
854                                         /* There's more - dup the ber buffer so they can all be
855                                          * individually freed by ldap_msgfree.
856                                          */
857                                         struct berval bv;
858                                         ber_get_option(ber, LBER_OPT_BER_REMAINING_BYTES, &len);
859                                         bv.bv_val = LDAP_MALLOC(len);
860                                         if (bv.bv_val) {
861                                                 ok=1;
862                                                 ber_read(ber, bv.bv_val, len);
863                                                 bv.bv_len = len;
864                                                 ber_init2(ber, &bv, ld->ld_lberoptions );
865                                         }
866                                 }
867                         } else {
868                                 /* LDAPv3: Just allocate a new ber. Since this is a buffered
869                                  * datagram, if the sockbuf is readable we still have data
870                                  * to parse.
871                                  */
872                                 ber = ldap_alloc_ber_with_options(ld);
873                                 if (ber_sockbuf_ctrl(sb, LBER_SB_OPT_DATA_READY, NULL)) ok=1;
874                         }
875                         /* set up response chain */
876                         if ( firstmsg ) {
877                                 firstmsg = 0;
878                                 new->lm_next = ld->ld_responses;
879                                 ld->ld_responses = new;
880                         } else {
881                                 tmp->lm_chain = new;
882                         }
883                         tmp = new;
884                         /* "ok" means there's more to parse */
885                         if (ok) {
886                                 if (isv2) goto nextresp2;
887                                 else goto nextresp3;
888                         } else {
889                                 /* got to end of datagram without a SearchResult. Free
890                                  * our dup'd ber, but leave any buffer alone. For v2 case,
891                                  * the previous response is still using this buffer. For v3,
892                                  * the new ber has no buffer to free yet.
893                                  */
894                                 ber_free(ber, 0);
895                                 return -1;
896                         }
897                 } else if ( moremsgs ) {
898                 /* got search result, and we had multiple responses in 1 datagram.
899                  * stick the result onto the end of the chain, and then pull the
900                  * first response off the head of the chain.
901                  */
902                         tmp->lm_chain = new;
903                         *result = chkResponseList( ld, msgid, all );
904                         ld->ld_errno = LDAP_SUCCESS;
905                         return( (*result)->lm_msgtype );
906                 }
907         }
908 #endif
909
910         /* is this the one we're looking for? */
911         if ( msgid == LDAP_RES_ANY || id == msgid ) {
912                 if ( all == LDAP_MSG_ONE
913                     || (new->lm_msgtype != LDAP_RES_SEARCH_RESULT
914                     && new->lm_msgtype != LDAP_RES_SEARCH_ENTRY
915                     && new->lm_msgtype != LDAP_RES_SEARCH_REFERENCE) ) {
916                         *result = new;
917                         ld->ld_errno = LDAP_SUCCESS;
918                         return( tag );
919                 } else if ( new->lm_msgtype == LDAP_RES_SEARCH_RESULT) {
920                         foundit = 1;    /* return the chain later */
921                 }
922         }
923
924         /* 
925          * if not, we must add it to the list of responses.  if
926          * the msgid is already there, it must be part of an existing
927          * search response.
928          */
929
930         prev = NULL;
931         for ( l = ld->ld_responses; l != NULL; l = l->lm_next ) {
932                 if ( l->lm_msgid == new->lm_msgid )
933                         break;
934                 prev = l;
935         }
936
937         /* not part of an existing search response */
938         if ( l == NULL ) {
939                 if ( foundit ) {
940                         *result = new;
941                         goto exit;
942                 }
943
944                 new->lm_next = ld->ld_responses;
945                 ld->ld_responses = new;
946                 goto exit;
947         }
948
949 #ifdef NEW_LOGGING
950         LDAP_LOG ( OPERATION, DETAIL1, 
951                 "read1msg: adding response id %ld type %ld\n",
952                 (long) new->lm_msgid, (long) new->lm_msgtype, 0 );
953 #else
954         Debug( LDAP_DEBUG_TRACE, "adding response id %ld type %ld:\n",
955             (long) new->lm_msgid, (long) new->lm_msgtype, 0 );
956 #endif
957
958         /* part of a search response - add to end of list of entries */
959         for ( tmp = l; (tmp->lm_chain != NULL) &&
960                 ((tmp->lm_chain->lm_msgtype == LDAP_RES_SEARCH_ENTRY) ||
961                  (tmp->lm_chain->lm_msgtype == LDAP_RES_SEARCH_REFERENCE) ||
962                          (tmp->lm_chain->lm_msgtype == LDAP_RES_INTERMEDIATE ));
963             tmp = tmp->lm_chain )
964                 ;       /* NULL */
965         tmp->lm_chain = new;
966
967         /* return the whole chain if that's what we were looking for */
968         if ( foundit ) {
969                 if ( prev == NULL )
970                         ld->ld_responses = l->lm_next;
971                 else
972                         prev->lm_next = l->lm_next;
973                 *result = l;
974         }
975
976 exit:
977         if ( foundit ) {
978                 ld->ld_errno = LDAP_SUCCESS;
979                 return( tag );
980         }
981         if ( ber_sockbuf_ctrl( sb, LBER_SB_OPT_DATA_READY, NULL ) ) {
982                 goto retry;
983         }
984         return( -2 );   /* continue looking */
985 }
986
987
988 static ber_tag_t
989 build_result_ber( LDAP *ld, BerElement **bp, LDAPRequest *lr )
990 {
991         ber_len_t       len;
992         ber_tag_t       tag;
993         ber_int_t       along;
994         BerElement *ber;
995
996         *bp = NULL;
997         ber = ldap_alloc_ber_with_options( ld );
998
999         if( ber == NULL ) {
1000                 ld->ld_errno = LDAP_NO_MEMORY;
1001                 return LBER_ERROR;
1002         }
1003
1004         if ( ber_printf( ber, "{it{ess}}", lr->lr_msgid,
1005             lr->lr_res_msgtype, lr->lr_res_errno,
1006             lr->lr_res_matched ? lr->lr_res_matched : "",
1007             lr->lr_res_error ? lr->lr_res_error : "" ) == -1 ) {
1008
1009                 ld->ld_errno = LDAP_ENCODING_ERROR;
1010                 ber_free(ber, 1);
1011                 return( LBER_ERROR );
1012         }
1013
1014         ber_reset( ber, 1 );
1015
1016         if ( ber_skip_tag( ber, &len ) == LBER_ERROR ) {
1017                 ld->ld_errno = LDAP_DECODING_ERROR;
1018                 ber_free(ber, 1);
1019                 return( LBER_ERROR );
1020         }
1021
1022         if ( ber_get_int( ber, &along ) == LBER_ERROR ) {
1023                 ld->ld_errno = LDAP_DECODING_ERROR;
1024                 ber_free(ber, 1);
1025                 return( LBER_ERROR );
1026         }
1027
1028         tag = ber_peek_tag( ber, &len );
1029
1030         if ( tag == LBER_ERROR ) {
1031                 ld->ld_errno = LDAP_DECODING_ERROR;
1032                 ber_free(ber, 1);
1033                 return( LBER_ERROR );
1034         }
1035
1036         *bp = ber;
1037         return tag;
1038 }
1039
1040
1041 static void
1042 merge_error_info( LDAP *ld, LDAPRequest *parentr, LDAPRequest *lr )
1043 {
1044 /*
1045  * Merge error information in "lr" with "parentr" error code and string.
1046  */
1047         if ( lr->lr_res_errno == LDAP_PARTIAL_RESULTS ) {
1048                 parentr->lr_res_errno = lr->lr_res_errno;
1049                 if ( lr->lr_res_error != NULL ) {
1050                         (void)ldap_append_referral( ld, &parentr->lr_res_error,
1051                             lr->lr_res_error );
1052                 }
1053         } else if ( lr->lr_res_errno != LDAP_SUCCESS &&
1054             parentr->lr_res_errno == LDAP_SUCCESS ) {
1055                 parentr->lr_res_errno = lr->lr_res_errno;
1056                 if ( parentr->lr_res_error != NULL ) {
1057                         LDAP_FREE( parentr->lr_res_error );
1058                 }
1059                 parentr->lr_res_error = lr->lr_res_error;
1060                 lr->lr_res_error = NULL;
1061                 if ( LDAP_NAME_ERROR( lr->lr_res_errno )) {
1062                         if ( parentr->lr_res_matched != NULL ) {
1063                                 LDAP_FREE( parentr->lr_res_matched );
1064                         }
1065                         parentr->lr_res_matched = lr->lr_res_matched;
1066                         lr->lr_res_matched = NULL;
1067                 }
1068         }
1069
1070 #ifdef NEW_LOGGING
1071         LDAP_LOG( OPERATION, DETAIL1, "merged parent (id %d) error info:  ",
1072             parentr->lr_msgid, 0, 0 );
1073         LDAP_LOG( OPERATION, DETAIL1, "result errno %d, error <%s>, matched <%s>\n",
1074             parentr->lr_res_errno, parentr->lr_res_error ?
1075             parentr->lr_res_error : "", parentr->lr_res_matched ?
1076             parentr->lr_res_matched : "" );
1077 #else
1078         Debug( LDAP_DEBUG_TRACE, "merged parent (id %d) error info:  ",
1079             parentr->lr_msgid, 0, 0 );
1080         Debug( LDAP_DEBUG_TRACE, "result errno %d, error <%s>, matched <%s>\n",
1081             parentr->lr_res_errno, parentr->lr_res_error ?
1082             parentr->lr_res_error : "", parentr->lr_res_matched ?
1083             parentr->lr_res_matched : "" );
1084 #endif
1085 }
1086
1087
1088
1089 int
1090 ldap_msgtype( LDAPMessage *lm )
1091 {
1092         assert( lm != NULL );
1093         return ( lm != NULL ) ? lm->lm_msgtype : -1;
1094 }
1095
1096
1097 int
1098 ldap_msgid( LDAPMessage *lm )
1099 {
1100         assert( lm != NULL );
1101
1102         return ( lm != NULL ) ? lm->lm_msgid : -1;
1103 }
1104
1105
1106 char * ldap_int_msgtype2str( ber_tag_t tag )
1107 {
1108         switch( tag ) {
1109         case LDAP_RES_ADD: return "add";
1110         case LDAP_RES_BIND: return "bind";
1111         case LDAP_RES_COMPARE: return "compare";
1112         case LDAP_RES_DELETE: return "delete";
1113         case LDAP_RES_EXTENDED: return "extended-result";
1114         case LDAP_RES_INTERMEDIATE: return "intermediate";
1115         case LDAP_RES_MODIFY: return "modify";
1116         case LDAP_RES_RENAME: return "rename";
1117         case LDAP_RES_SEARCH_ENTRY: return "search-entry";
1118         case LDAP_RES_SEARCH_REFERENCE: return "search-reference";
1119         case LDAP_RES_SEARCH_RESULT: return "search-result";
1120         }
1121         return "unknown";
1122 }
1123
1124 int
1125 ldap_msgfree( LDAPMessage *lm )
1126 {
1127         LDAPMessage     *next;
1128         int             type = 0;
1129
1130 #ifdef NEW_LOGGING
1131         LDAP_LOG ( OPERATION, ENTRY, "ldap_msgfree\n", 0, 0, 0 );
1132 #else
1133         Debug( LDAP_DEBUG_TRACE, "ldap_msgfree\n", 0, 0, 0 );
1134 #endif
1135
1136         for ( ; lm != NULL; lm = next ) {
1137                 next = lm->lm_chain;
1138                 type = lm->lm_msgtype;
1139                 ber_free( lm->lm_ber, 1 );
1140                 LDAP_FREE( (char *) lm );
1141         }
1142
1143         return( type );
1144 }
1145
1146 /*
1147  * ldap_msgdelete - delete a message.  It returns:
1148  *      0       if the entire message was deleted
1149  *      -1      if the message was not found, or only part of it was found
1150  */
1151 int
1152 ldap_msgdelete( LDAP *ld, int msgid )
1153 {
1154         LDAPMessage     *lm, *prev;
1155         int rc = 0;
1156
1157         assert( ld != NULL );
1158
1159 #ifdef NEW_LOGGING
1160         LDAP_LOG ( OPERATION, ENTRY, "ldap_msgdelete\n", 0, 0, 0 );
1161 #else
1162         Debug( LDAP_DEBUG_TRACE, "ldap_msgdelete\n", 0, 0, 0 );
1163 #endif
1164
1165         prev = NULL;
1166 #ifdef LDAP_R_COMPILE
1167         ldap_pvt_thread_mutex_lock( &ld->ld_res_mutex );
1168 #endif
1169         for ( lm = ld->ld_responses; lm != NULL; lm = lm->lm_next ) {
1170                 if ( lm->lm_msgid == msgid )
1171                         break;
1172                 prev = lm;
1173         }
1174
1175         if ( lm == NULL ) {
1176                 rc = -1;
1177         } else {
1178                 if ( prev == NULL )
1179                         ld->ld_responses = lm->lm_next;
1180                 else
1181                         prev->lm_next = lm->lm_next;
1182         }
1183 #ifdef LDAP_R_COMPILE
1184         ldap_pvt_thread_mutex_unlock( &ld->ld_res_mutex );
1185 #endif
1186         if ( lm && ldap_msgfree( lm ) == LDAP_RES_SEARCH_ENTRY )
1187                 rc = -1;
1188
1189         return( rc );
1190 }
1191
1192
1193 /*
1194  * return 1 if message msgid is waiting to be abandoned, 0 otherwise
1195  */
1196 static int
1197 ldap_abandoned( LDAP *ld, ber_int_t msgid )
1198 {
1199         int     i;
1200
1201         if ( ld->ld_abandoned == NULL )
1202                 return( 0 );
1203
1204         for ( i = 0; ld->ld_abandoned[i] != -1; i++ )
1205                 if ( ld->ld_abandoned[i] == msgid )
1206                         return( 1 );
1207
1208         return( 0 );
1209 }
1210
1211
1212 static int
1213 ldap_mark_abandoned( LDAP *ld, ber_int_t msgid )
1214 {
1215         int     i;
1216
1217         if ( ld->ld_abandoned == NULL )
1218                 return( -1 );
1219
1220         for ( i = 0; ld->ld_abandoned[i] != -1; i++ )
1221                 if ( ld->ld_abandoned[i] == msgid )
1222                         break;
1223
1224         if ( ld->ld_abandoned[i] == -1 )
1225                 return( -1 );
1226
1227         for ( ; ld->ld_abandoned[i] != -1; i++ ) {
1228                 ld->ld_abandoned[i] = ld->ld_abandoned[i + 1];
1229         }
1230
1231         return( 0 );
1232 }