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