]> git.sur5r.net Git - openldap/blob - libraries/libldap/result.c
LDAPv3 referrals changes by Steve Sonntag @ Novell
[openldap] / libraries / libldap / result.c
1 /* $OpenLDAP$ */
2 /*
3  * Copyright 1998-2000 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  * This notice applies to changes, created by or for Novell, Inc.,
11  * to preexisting works for which notices appear elsewhere in this file.
12  *
13  * Copyright (C) 1999, 2000 Novell, Inc. All Rights Reserved.
14  *
15  * THIS WORK IS SUBJECT TO U.S. AND INTERNATIONAL COPYRIGHT LAWS AND TREATIES.
16  * USE, MODIFICATION, AND REDISTRIBUTION OF THIS WORK IS SUBJECT TO VERSION
17  * 2.0.1 OF THE OPENLDAP PUBLIC LICENSE, A COPY OF WHICH IS AVAILABLE AT
18  * HTTP://WWW.OPENLDAP.ORG/LICENSE.HTML OR IN THE FILE "LICENSE" IN THE
19  * TOP-LEVEL DIRECTORY OF THE DISTRIBUTION. ANY USE OR EXPLOITATION OF THIS
20  * WORK OTHER THAN AS AUTHORIZED IN VERSION 2.0.1 OF THE OPENLDAP PUBLIC
21  * LICENSE, OR OTHER PRIOR WRITTEN CONSENT FROM NOVELL, COULD SUBJECT THE
22  * PERPETRATOR TO CRIMINAL AND CIVIL LIABILITY. 
23  ******************************************************************************
24  * Modification to OpenLDAP source by Novell, Inc.
25  * April 2000 sfs Add code to process V3 referrals and search results
26  *
27  *  result.c - wait for an ldap result
28  */
29
30 /*
31  * LDAPv3 (RFC2251)
32  *      LDAPResult ::= SEQUENCE {
33  *              resultCode              ENUMERATED { ... },
34  *              matchedDN               LDAPDN,
35  *              errorMessage    LDAPString,
36  *              referral                Referral OPTIONAL
37  *      }
38  *      Referral ::= SEQUENCE OF LDAPURL        (one or more)
39  *      LDAPURL ::= LDAPString                          (limited to URL chars)
40  */
41
42 #include "portable.h"
43
44 #include <stdio.h>
45
46 #include <ac/stdlib.h>
47
48 #include <ac/errno.h>
49 #include <ac/socket.h>
50 #include <ac/string.h>
51 #include <ac/time.h>
52 #include <ac/unistd.h>
53
54 #include "ldap-int.h"
55
56
57 static int ldap_abandoned LDAP_P(( LDAP *ld, ber_int_t msgid ));
58 static int ldap_mark_abandoned LDAP_P(( LDAP *ld, ber_int_t msgid ));
59 static int wait4msg LDAP_P(( LDAP *ld, ber_int_t msgid, int all, struct timeval *timeout,
60         LDAPMessage **result ));
61 static ber_tag_t try_read1msg LDAP_P(( LDAP *ld, ber_int_t msgid,
62         int all, Sockbuf *sb, LDAPConn *lc, LDAPMessage **result ));
63 static ber_tag_t build_result_ber LDAP_P(( LDAP *ld, BerElement **bp, LDAPRequest *lr ));
64 static void merge_error_info LDAP_P(( LDAP *ld, LDAPRequest *parentr, LDAPRequest *lr ));
65
66
67 /*
68  * ldap_result - wait for an ldap result response to a message from the
69  * ldap server.  If msgid is -1, any message will be accepted, otherwise
70  * ldap_result will wait for a response with msgid.  If all is 0 the
71  * first message with id msgid will be accepted, otherwise, ldap_result
72  * will wait for all responses with id msgid and then return a pointer to
73  * the entire list of messages.  This is only useful for search responses,
74  * which can be of two message types (zero or more entries, followed by an
75  * ldap result).  The type of the first message received is returned.
76  * When waiting, any messages that have been abandoned are discarded.
77  *
78  * Example:
79  *      ldap_result( s, msgid, all, timeout, result )
80  */
81 int
82 ldap_result(
83         LDAP *ld,
84         int msgid,
85         int all,
86         struct timeval *timeout,
87         LDAPMessage **result )
88 {
89         LDAPMessage     *lm, *lastlm, *nextlm;
90
91         assert( ld != NULL );
92         assert( result != NULL );
93
94         Debug( LDAP_DEBUG_TRACE, "ldap_result\n", 0, 0, 0 );
95
96         if( ld == NULL ) {
97                 return -1;
98         }
99
100         if( result == NULL ) {
101                 ld->ld_errno = LDAP_PARAM_ERROR;
102                 return -1;
103         }
104
105         /*
106          * First, look through the list of responses we have received on
107          * this association and see if the response we're interested in
108          * is there.  If it is, return it.  If not, call wait4msg() to
109          * wait until it arrives or timeout occurs.
110          */
111
112         *result = NULL;
113         lastlm = NULL;
114         for ( lm = ld->ld_responses; lm != NULL; lm = nextlm ) {
115                 nextlm = lm->lm_next;
116
117                 if ( ldap_abandoned( ld, lm->lm_msgid ) ) {
118                         ldap_mark_abandoned( ld, lm->lm_msgid );
119
120                         if ( lastlm == NULL ) {
121                                 ld->ld_responses = lm->lm_next;
122                         } else {
123                                 lastlm->lm_next = nextlm;
124                         }
125
126                         ldap_msgfree( lm );
127
128                         continue;
129                 }
130
131                 if ( msgid == LDAP_RES_ANY || lm->lm_msgid == msgid ) {
132                         LDAPMessage     *tmp;
133
134                         if ( all == LDAP_MSG_ONE
135                             || (lm->lm_msgtype != LDAP_RES_SEARCH_RESULT
136                             && lm->lm_msgtype != LDAP_RES_SEARCH_REFERENCE      /* LDAPv3 */
137                             && lm->lm_msgtype != LDAP_RES_SEARCH_ENTRY) )
138                                 break;
139
140                         for ( tmp = lm; tmp != NULL; tmp = tmp->lm_chain ) {
141                                 if ( tmp->lm_msgtype == LDAP_RES_SEARCH_RESULT )
142                                         break;
143                         }
144
145                         if ( tmp == NULL ) {
146                                 lm = NULL;
147                         }
148
149                         break;
150                 }
151                 lastlm = lm;
152         }
153         if ( lm == NULL ) {
154                 return( wait4msg( ld, msgid, all, timeout, result ) );
155         }
156
157         if ( lastlm == NULL ) {
158                 ld->ld_responses = (all == LDAP_MSG_ONE && lm->lm_chain != NULL
159                     ? lm->lm_chain : lm->lm_next);
160         } else {
161                 lastlm->lm_next = (all == LDAP_MSG_ONE && lm->lm_chain != NULL
162                     ? lm->lm_chain : lm->lm_next);
163         }
164         if ( all == LDAP_MSG_ONE && lm->lm_chain != NULL )
165         {
166                 lm->lm_chain->lm_next = lm->lm_next;
167                 lm->lm_chain = NULL;
168         }
169         lm->lm_next = NULL;
170
171         *result = lm;
172         ld->ld_errno = LDAP_SUCCESS;
173         return( lm->lm_msgtype );
174 }
175
176 static int
177 wait4msg(
178         LDAP *ld,
179         ber_int_t msgid,
180         int all,
181         struct timeval *timeout,
182         LDAPMessage **result )
183 {
184         int             rc;
185         struct timeval  tv, *tvp;
186         time_t          start_time = 0;
187         time_t          tmp_time;
188         LDAPConn        *lc, *nextlc;
189
190         assert( ld != NULL );
191         assert( result != NULL );
192
193 #ifdef LDAP_DEBUG
194         if ( timeout == NULL ) {
195                 Debug( LDAP_DEBUG_TRACE, "wait4msg (infinite timeout)\n",
196                     0, 0, 0 );
197         } else {
198                 Debug( LDAP_DEBUG_TRACE, "wait4msg (timeout %ld sec, %ld usec)\n",
199                        (long) timeout->tv_sec, (long) timeout->tv_usec, 0 );
200         }
201 #endif /* LDAP_DEBUG */
202
203         if ( timeout == NULL ) {
204                 tvp = NULL;
205         } else {
206                 tv = *timeout;
207                 tvp = &tv;
208                 start_time = time( NULL );
209         }
210                     
211         rc = -2;
212         while ( rc == -2 ) {
213 #ifdef LDAP_DEBUG
214                 if ( ldap_debug & LDAP_DEBUG_TRACE ) {
215                         ldap_dump_connection( ld, ld->ld_conns, 1 );
216                         ldap_dump_requests_and_responses( ld );
217                 }
218 #endif /* LDAP_DEBUG */
219                 for ( lc = ld->ld_conns; lc != NULL; lc = lc->lconn_next ) {
220                         if ( ber_pvt_sb_data_ready(lc->lconn_sb) ) {
221                                 rc = try_read1msg( ld, msgid, all, lc->lconn_sb,
222                                     lc, result );
223                                 break;
224                         }
225                 }
226
227                 if ( lc == NULL ) {
228                         rc = do_ldap_select( ld, tvp );
229
230
231 #ifdef LDAP_DEBUG
232                         if ( rc == -1 ) {
233                             Debug( LDAP_DEBUG_TRACE,
234                                     "do_ldap_select returned -1: errno %d\n",
235                                     errno, 0, 0 );
236                         }
237 #endif
238
239                         if ( rc == 0 || ( rc == -1 && (
240                                 !LDAP_BOOL_GET(&ld->ld_options, LDAP_BOOL_RESTART)
241                                 || errno != EINTR )))
242                         {
243                                 ld->ld_errno = (rc == -1 ? LDAP_SERVER_DOWN :
244                                     LDAP_TIMEOUT);
245                                 return( rc );
246                         }
247
248                         if ( rc == -1 ) {
249                                 rc = -2;        /* select interrupted: loop */
250                         } else {
251                                 rc = -2;
252                                 for ( lc = ld->ld_conns; rc == -2 && lc != NULL;
253                                     lc = nextlc ) {
254                                         nextlc = lc->lconn_next;
255                                         if ( lc->lconn_status ==
256                                             LDAP_CONNST_CONNECTED &&
257                                             ldap_is_read_ready( ld,
258                                             lc->lconn_sb )) {
259                                                 rc = try_read1msg( ld, msgid, all,
260                                                     lc->lconn_sb, lc, result );
261                                         }
262                                 }
263                         }
264                 }
265
266                 if ( rc == -2 && tvp != NULL ) {
267                         tmp_time = time( NULL );
268                         if (( tv.tv_sec -=  ( tmp_time - start_time )) <= 0 ) {
269                                 rc = 0; /* timed out */
270                                 ld->ld_errno = LDAP_TIMEOUT;
271                                 break;
272                         }
273
274                         Debug( LDAP_DEBUG_TRACE, "wait4msg:  %ld secs to go\n",
275                                (long) tv.tv_sec, 0, 0 );
276                         start_time = tmp_time;
277                 }
278         }
279
280         return( rc );
281 }
282
283
284 static ber_tag_t
285 try_read1msg(
286         LDAP *ld,
287         ber_int_t msgid,
288         int all,
289         Sockbuf *sb,
290     LDAPConn *lc,
291         LDAPMessage **result )
292 {
293         BerElement      *ber;
294         LDAPMessage     *new, *l, *prev, *tmp;
295         ber_int_t       id;
296         ber_tag_t       tag;
297         ber_len_t       len;
298         int             foundit = 0;
299         LDAPRequest     *lr, *tmplr;
300         BerElement      tmpber;
301         int             rc, refer_cnt, hadref, simple_request;
302         ber_int_t       lderr;
303         /*
304          * v3ref = flag for V3 referral / search reference
305          * 0 = not a ref, 1 = sucessfully chased ref, -1 = pass ref to application
306          */
307         int     v3ref;
308
309         assert( ld != NULL );
310         assert( lc != NULL );
311         
312         Debug( LDAP_DEBUG_TRACE, "read1msg\n", 0, 0, 0 );
313
314     if ( lc->lconn_ber == NULL ) {
315                 lc->lconn_ber = ldap_alloc_ber_with_options(ld);
316
317                 if( lc->lconn_ber == NULL ) {
318                         return -1;
319                 }
320     }
321
322         ber = lc->lconn_ber;
323         assert( BER_VALID (ber) );
324
325         /* get the next message */
326         errno = 0;
327         if ( (tag = ber_get_next( sb, &len, ber ))
328             != LDAP_TAG_MESSAGE ) {
329                 if ( tag == LBER_DEFAULT) {
330 #ifdef LDAP_DEBUG                  
331                         Debug( LDAP_DEBUG_CONNS,
332                               "ber_get_next failed.\n", 0, 0, 0 );
333 #endif             
334 #ifdef EWOULDBLOCK                      
335                         if (errno==EWOULDBLOCK) return -2;
336 #endif
337 #ifdef EAGAIN
338                         if (errno == EAGAIN) return -2;
339 #endif
340                         ld->ld_errno = LDAP_SERVER_DOWN;
341                         return -1;
342                 }
343                 ld->ld_errno = LDAP_LOCAL_ERROR;
344                 return -1;
345         }
346
347         /*
348      * We read a complete message.
349          * The connection should no longer need this ber.
350          */
351     lc->lconn_ber = NULL;
352
353         /* message id */
354         if ( ber_get_int( ber, &id ) == LBER_ERROR ) {
355                 ber_free( ber, 1 );
356                 ld->ld_errno = LDAP_DECODING_ERROR;
357                 return( -1 );
358         }
359
360         /* if it's been abandoned, toss it */
361         if ( ldap_abandoned( ld, id ) ) {
362                 ber_free( ber, 1 );
363                 Debug( LDAP_DEBUG_ANY, "abandoned\n", 0, 0, 0);
364                 return( -2 );   /* continue looking */
365         }
366
367         if (( lr = ldap_find_request_by_msgid( ld, id )) == NULL ) {
368                 Debug( LDAP_DEBUG_ANY,
369                     "no request for response with msgid %ld (tossing)\n",
370                     (long) id, 0, 0 );
371                 ber_free( ber, 1 );
372                 return( -2 );   /* continue looking */
373         }
374
375         /* the message type */
376         if ( (tag = ber_peek_tag( ber, &len )) == LBER_ERROR ) {
377                 ld->ld_errno = LDAP_DECODING_ERROR;
378                 ber_free( ber, 1 );
379                 return( -1 );
380         }
381
382         Debug( LDAP_DEBUG_TRACE, "ldap_read: message type %s msgid %ld, original id %ld\n",
383             ( tag == LDAP_RES_SEARCH_ENTRY ) ? "entry" : 
384                 ( tag == LDAP_RES_SEARCH_REFERENCE ) ? "reference" : "result",
385                 (long) lr->lr_msgid, (long) lr->lr_origid );
386
387         id = lr->lr_origid;
388         refer_cnt = 0;
389         hadref = simple_request = 0;
390         rc = -2;        /* default is to keep looking (no response found) */
391         lr->lr_res_msgtype = tag;
392
393         /*
394          * This code figures out if we are going to chase a
395          * referral / search reference, or pass it back to the application
396          */
397         v3ref = 0;      /* Assume not a V3 search reference or referral */
398         if( (tag != LDAP_RES_SEARCH_ENTRY) && (ld->ld_version > LDAP_VERSION2) ) {
399                 BerElement      tmpber = *ber;  /* struct copy */
400                 char **refs = NULL;
401
402                 if( tag == LDAP_RES_SEARCH_REFERENCE) {
403                         /* This is a V3 search reference */
404                         /* Assume we do not chase the reference, but pass it to application */
405                         v3ref = -1;
406                         if( LDAP_BOOL_GET(&ld->ld_options, LDAP_BOOL_REFERRALS) ||
407                                         (lr->lr_parent != NULL) )
408                         {
409                                 /* Get the referral list */
410                                 if ( ber_scanf( &tmpber, "{v}", &refs ) == LBER_ERROR ) {
411                                         rc = LDAP_DECODING_ERROR;
412                                 } else {
413                                         /* Note: refs arrary is freed by ldap_chase_v3referrals */
414                                         refer_cnt = ldap_chase_v3referrals( ld, lr, refs,
415                                             &lr->lr_res_error, &hadref );
416                                         if ( refer_cnt > 0 ) {  /* sucessfully chased reference */
417                                                 /* If haven't got end search, set chasing referrals */
418                                                 if( lr->lr_status != LDAP_REQST_COMPLETED) {
419                                                         lr->lr_status = LDAP_REQST_CHASINGREFS;
420                                                         Debug( LDAP_DEBUG_TRACE,
421                                                             "read1msg:  search ref chased, mark request chasing refs, id = %d\n",
422                                                             lr->lr_msgid, 0, 0);
423                                                 }
424                                                 v3ref = 1;      /* We sucessfully chased the reference */
425                                         }
426                                 }
427                         }
428                 } else {
429                         /* Check for V3 referral */
430                         ber_len_t len;
431                         if ( ber_scanf( &tmpber, "{iaa",/*}*/ &lderr,
432                                     &lr->lr_res_matched, &lr->lr_res_error )
433                                     != LBER_ERROR ) {
434                                 /* Check if V3 referral */
435                                 if( ber_peek_tag( &tmpber, &len) == LDAP_TAG_REFERRAL ) {
436                                         /* We have a V3 referral, assume we cannot chase it */
437                                         v3ref = -1;
438                                         if( LDAP_BOOL_GET(&ld->ld_options, LDAP_BOOL_REFERRALS)
439                                                          || (lr->lr_parent != NULL) )
440                                         {
441                                                 v3ref = -1;  /* Assume referral not chased and return it to app */
442                                                 /* Get the referral list */
443                                                 if( ber_scanf( &tmpber, "v", &refs) == LBER_ERROR) {
444                                                         rc = LDAP_DECODING_ERROR;
445                                                         lr->lr_status = LDAP_REQST_COMPLETED;
446                                                         Debug( LDAP_DEBUG_TRACE,
447                                                             "read1msg: referral decode error, mark request completed, id = %d\n",
448                                                                     lr->lr_msgid, 0, 0);
449                                                 } else {
450                                                         /* Chase the referral 
451                                                          * Note: refs arrary is freed by ldap_chase_v3referrals
452                                                          */
453                                                         refer_cnt = ldap_chase_v3referrals( ld, lr, refs,
454                                                             &lr->lr_res_error, &hadref );
455                                                         lr->lr_status = LDAP_REQST_COMPLETED;
456                                                         Debug( LDAP_DEBUG_TRACE,
457                                                             "read1msg:  referral chased, mark request completed, id = %d\n",
458                                                             lr->lr_msgid, 0, 0);
459                                                         if( refer_cnt > 0) {
460                                                                 v3ref = 1;  /* Referral successfully chased */
461                                                         }
462                                                 }
463                                         }
464                                 }
465                         }
466                 }
467         }
468
469         /* All results that just return a status, i.e. don't return data
470          * go through the following code.  This code also chases V2 referrals
471          * and checks if all referrals have been chased.
472          */
473         if ( (tag != LDAP_RES_SEARCH_ENTRY) && (v3ref > -1) ) {
474                 /* For a v3 search referral/reference, only come here if already chased it */
475                 if ( ld->ld_version >= LDAP_VERSION2 &&
476                         ( lr->lr_parent != NULL ||
477                         LDAP_BOOL_GET(&ld->ld_options, LDAP_BOOL_REFERRALS) ) )
478                 {
479                         tmpber = *ber;  /* struct copy */
480                         if ( v3ref == 1 ) {
481                                 ; /* V3 search reference or V3 referral sucessfully chased */
482                         } else
483                         if ( ber_scanf( &tmpber, "{iaa}", &lderr,
484                             &lr->lr_res_matched, &lr->lr_res_error )
485                             != LBER_ERROR ) {
486                                 if ( lderr != LDAP_SUCCESS ) {
487                                         /* referrals are in error string */
488                                         refer_cnt = ldap_chase_referrals( ld, lr,
489                                             &lr->lr_res_error, &hadref );
490                                         lr->lr_status = LDAP_REQST_COMPLETED;
491                                         Debug( LDAP_DEBUG_TRACE,
492                                             "read1msg:  V2 referral chased, mark request completed, id = %d\n", lr->lr_msgid, 0, 0);
493                                 }
494
495                                 /* save errno, message, and matched string */
496                                 if ( !hadref || lr->lr_res_error == NULL ) {
497                                         lr->lr_res_errno = ( lderr ==
498                                         LDAP_PARTIAL_RESULTS ) ? LDAP_SUCCESS
499                                         : lderr;
500                                 } else if ( ld->ld_errno != LDAP_SUCCESS ) {
501                                         lr->lr_res_errno = ld->ld_errno;
502                                 } else {
503                                         lr->lr_res_errno = LDAP_PARTIAL_RESULTS;
504                                 }
505 Debug( LDAP_DEBUG_TRACE,
506     "new result:  res_errno: %d, res_error: <%s>, res_matched: <%s>\n",
507     lr->lr_res_errno, lr->lr_res_error ? lr->lr_res_error : "",
508     lr->lr_res_matched ? lr->lr_res_matched : "" );
509                         }
510                 }
511
512                 Debug( LDAP_DEBUG_TRACE,
513                     "read1msg:  %d new referrals\n", refer_cnt, 0, 0 );
514
515                 if ( refer_cnt != 0 ) { /* chasing referrals */
516                         ber_free( ber, 1 );
517                         ber = NULL;
518                         if ( refer_cnt < 0 ) {
519                                 return( -1 );   /* fatal error */
520                         }
521                         lr->lr_res_errno = LDAP_SUCCESS; /* sucessfully chased referral */
522                 } else {
523                         if ( lr->lr_outrefcnt <= 0 && lr->lr_parent == NULL ) {
524                                 /* request without any referrals */
525                                 simple_request = ( hadref ? 0 : 1 );
526                         } else {
527                                 /* request with referrals or child request */
528                                 ber_free( ber, 1 );
529                                 ber = NULL;
530                         }
531
532                         lr->lr_status = LDAP_REQST_COMPLETED; /* declare this request done */
533                         Debug( LDAP_DEBUG_TRACE,
534                             "read1msg:  mark request completed, id = %d\n", lr->lr_msgid, 0, 0);
535                         while ( lr->lr_parent != NULL ) {
536                                 merge_error_info( ld, lr->lr_parent, lr );
537
538                                 lr = lr->lr_parent;
539                                 if ( --lr->lr_outrefcnt > 0 ) {
540                                         break;  /* not completely done yet */
541                                 }
542                         }
543
544                         /* Check if all requests are finished, lr is now parent */
545                         for(tmplr=lr ; tmplr != NULL; tmplr=tmplr->lr_refnext) {
546                                 if( tmplr->lr_status != LDAP_REQST_COMPLETED) {
547                                         break;
548                                 }
549                         }
550
551                         /* This is the parent request if the request has referrals */
552                         if ( lr->lr_outrefcnt <= 0 && lr->lr_parent == NULL && tmplr == NULL ) {
553                                 id = lr->lr_msgid;
554                                 tag = lr->lr_res_msgtype;
555                                 Debug( LDAP_DEBUG_ANY, "request %ld done\n",
556                                     (long) id, 0, 0 );
557 Debug( LDAP_DEBUG_TRACE,
558 "res_errno: %d, res_error: <%s>, res_matched: <%s>\n",
559 lr->lr_res_errno, lr->lr_res_error ? lr->lr_res_error : "",
560 lr->lr_res_matched ? lr->lr_res_matched : "" );
561                                 if ( !simple_request ) {
562                                         ber_free( ber, 1 );
563                                         ber = NULL;
564                                         if ( build_result_ber( ld, &ber, lr )
565                                             == LBER_ERROR ) {
566                                                 rc = -1; /* fatal error */
567                                         }
568                                 }
569
570                                 ldap_free_request( ld, lr );
571                         }
572
573                         if ( lc != NULL ) {
574                                 ldap_free_connection( ld, lc, 0, 1 );
575                         }
576                 }
577         }
578
579         if ( ber == NULL ) {
580                 return( rc );
581         }
582
583         /* make a new ldap message */
584         if ( (new = (LDAPMessage *) LDAP_CALLOC( 1, sizeof(LDAPMessage) ))
585             == NULL ) {
586                 ld->ld_errno = LDAP_NO_MEMORY;
587                 return( -1 );
588         }
589         new->lm_msgid = (int)id;
590         new->lm_msgtype = tag;
591         new->lm_ber = ber;
592
593 #ifndef LDAP_NOCACHE
594                 if ( ld->ld_cache != NULL ) {
595                         ldap_add_result_to_cache( ld, new );
596                 }
597 #endif /* LDAP_NOCACHE */
598
599         /* is this the one we're looking for? */
600         if ( msgid == LDAP_RES_ANY || id == msgid ) {
601                 if ( all == LDAP_MSG_ONE
602                     || (new->lm_msgtype != LDAP_RES_SEARCH_RESULT
603                     && new->lm_msgtype != LDAP_RES_SEARCH_ENTRY
604                     && new->lm_msgtype != LDAP_RES_SEARCH_REFERENCE) ) {
605                         *result = new;
606                         ld->ld_errno = LDAP_SUCCESS;
607                         return( tag );
608                 } else if ( new->lm_msgtype == LDAP_RES_SEARCH_RESULT) {
609                         foundit = 1;    /* return the chain later */
610                 }
611         }
612
613         /* 
614          * if not, we must add it to the list of responses.  if
615          * the msgid is already there, it must be part of an existing
616          * search response.
617          */
618
619         prev = NULL;
620         for ( l = ld->ld_responses; l != NULL; l = l->lm_next ) {
621                 if ( l->lm_msgid == new->lm_msgid )
622                         break;
623                 prev = l;
624         }
625
626         /* not part of an existing search response */
627         if ( l == NULL ) {
628                 if ( foundit ) {
629                         *result = new;
630                         ld->ld_errno = LDAP_SUCCESS;
631                         return( tag );
632                 }
633
634                 new->lm_next = ld->ld_responses;
635                 ld->ld_responses = new;
636                 return( -2 );   /* continue looking */
637         }
638
639         Debug( LDAP_DEBUG_TRACE, "adding response id %ld type %ld:\n",
640             (long) new->lm_msgid, (long) new->lm_msgtype, 0 );
641
642         /* part of a search response - add to end of list of entries */
643         for ( tmp = l; (tmp->lm_chain != NULL) &&
644                 ((tmp->lm_chain->lm_msgtype == LDAP_RES_SEARCH_ENTRY) ||
645                  (tmp->lm_chain->lm_msgtype == LDAP_RES_SEARCH_REFERENCE));
646             tmp = tmp->lm_chain )
647                 ;       /* NULL */
648         tmp->lm_chain = new;
649
650         /* return the whole chain if that's what we were looking for */
651         if ( foundit ) {
652                 if ( prev == NULL )
653                         ld->ld_responses = l->lm_next;
654                 else
655                         prev->lm_next = l->lm_next;
656                 *result = l;
657                 ld->ld_errno = LDAP_SUCCESS;
658 #ifdef LDAP_WORLD_P16
659                 /*
660                  * XXX questionable fix; see text for [P16] on
661                  * http://www.critical-angle.com/ldapworld/patch/
662                  *
663                  * inclusion of this patch causes searchs to hang on
664                  * multiple platforms
665                  */
666                 return( l->lm_msgtype );
667 #else   /* LDAP_WORLD_P16 */
668                 return( tag );
669 #endif  /* !LDAP_WORLD_P16 */
670         }
671
672         return( -2 );   /* continue looking */
673 }
674
675
676 static ber_tag_t
677 build_result_ber( LDAP *ld, BerElement **bp, LDAPRequest *lr )
678 {
679         ber_len_t       len;
680         ber_int_t       tag;
681         ber_int_t       along;
682         BerElement *ber;
683
684         *bp = NULL;
685         ber = ldap_alloc_ber_with_options( ld );
686
687         if( ber == NULL ) {
688                 ld->ld_errno = LDAP_NO_MEMORY;
689                 return LBER_ERROR;
690         }
691
692         if ( ber_printf( ber, "{it{ess}}", lr->lr_msgid,
693             lr->lr_res_msgtype, lr->lr_res_errno,
694             lr->lr_res_matched ? lr->lr_res_matched : "",
695             lr->lr_res_error ? lr->lr_res_error : "" ) == -1 ) {
696
697                 ld->ld_errno = LDAP_ENCODING_ERROR;
698                 ber_free(ber, 1);
699                 return( LBER_ERROR );
700         }
701
702         ber_reset( ber, 1 );
703
704         if ( ber_skip_tag( ber, &len ) == LBER_ERROR ) {
705                 ld->ld_errno = LDAP_DECODING_ERROR;
706                 ber_free(ber, 1);
707                 return( LBER_ERROR );
708         }
709
710         if ( ber_get_int( ber, &along ) == LBER_ERROR ) {
711                 ld->ld_errno = LDAP_DECODING_ERROR;
712                 ber_free(ber, 1);
713                 return( LBER_ERROR );
714         }
715
716         tag = ber_peek_tag( ber, &len );
717
718         if ( tag == LBER_ERROR ) {
719                 ld->ld_errno = LDAP_DECODING_ERROR;
720                 ber_free(ber, 1);
721                 return( LBER_ERROR );
722         }
723
724         *bp = ber;
725         return tag;
726 }
727
728
729 static void
730 merge_error_info( LDAP *ld, LDAPRequest *parentr, LDAPRequest *lr )
731 {
732 /*
733  * Merge error information in "lr" with "parentr" error code and string.
734  */
735         if ( lr->lr_res_errno == LDAP_PARTIAL_RESULTS ) {
736                 parentr->lr_res_errno = lr->lr_res_errno;
737                 if ( lr->lr_res_error != NULL ) {
738                         (void)ldap_append_referral( ld, &parentr->lr_res_error,
739                             lr->lr_res_error );
740                 }
741         } else if ( lr->lr_res_errno != LDAP_SUCCESS &&
742             parentr->lr_res_errno == LDAP_SUCCESS ) {
743                 parentr->lr_res_errno = lr->lr_res_errno;
744                 if ( parentr->lr_res_error != NULL ) {
745                         LDAP_FREE( parentr->lr_res_error );
746                 }
747                 parentr->lr_res_error = lr->lr_res_error;
748                 lr->lr_res_error = NULL;
749                 if ( LDAP_NAME_ERROR( lr->lr_res_errno )) {
750                         if ( parentr->lr_res_matched != NULL ) {
751                                 LDAP_FREE( parentr->lr_res_matched );
752                         }
753                         parentr->lr_res_matched = lr->lr_res_matched;
754                         lr->lr_res_matched = NULL;
755                 }
756         }
757
758         Debug( LDAP_DEBUG_TRACE, "merged parent (id %d) error info:  ",
759             parentr->lr_msgid, 0, 0 );
760         Debug( LDAP_DEBUG_TRACE, "result errno %d, error <%s>, matched <%s>\n",
761             parentr->lr_res_errno, parentr->lr_res_error ?
762             parentr->lr_res_error : "", parentr->lr_res_matched ?
763             parentr->lr_res_matched : "" );
764 }
765
766
767
768 int
769 ldap_msgtype( LDAPMessage *lm )
770 {
771         assert( lm != NULL );
772         return ( lm != NULL ) ? lm->lm_msgtype : -1;
773 }
774
775
776 int
777 ldap_msgid( LDAPMessage *lm )
778 {
779         assert( lm != NULL );
780
781         return ( lm != NULL ) ? lm->lm_msgid : -1;
782 }
783
784
785 int
786 ldap_msgfree( LDAPMessage *lm )
787 {
788         LDAPMessage     *next;
789         int             type = 0;
790
791         assert( lm != NULL );
792
793         Debug( LDAP_DEBUG_TRACE, "ldap_msgfree\n", 0, 0, 0 );
794
795         for ( ; lm != NULL; lm = next ) {
796                 next = lm->lm_chain;
797                 type = lm->lm_msgtype;
798                 ber_free( lm->lm_ber, 1 );
799                 LDAP_FREE( (char *) lm );
800         }
801
802         return( type );
803 }
804
805 /*
806  * ldap_msgdelete - delete a message.  It returns:
807  *      0       if the entire message was deleted
808  *      -1      if the message was not found, or only part of it was found
809  */
810 int
811 ldap_msgdelete( LDAP *ld, int msgid )
812 {
813         LDAPMessage     *lm, *prev;
814
815         assert( ld != NULL );
816
817         Debug( LDAP_DEBUG_TRACE, "ldap_msgdelete\n", 0, 0, 0 );
818
819         prev = NULL;
820         for ( lm = ld->ld_responses; lm != NULL; lm = lm->lm_next ) {
821                 if ( lm->lm_msgid == msgid )
822                         break;
823                 prev = lm;
824         }
825
826         if ( lm == NULL )
827                 return( -1 );
828
829         if ( prev == NULL )
830                 ld->ld_responses = lm->lm_next;
831         else
832                 prev->lm_next = lm->lm_next;
833
834         if ( ldap_msgfree( lm ) == LDAP_RES_SEARCH_ENTRY )
835                 return( -1 );
836
837         return( 0 );
838 }
839
840
841 /*
842  * return 1 if message msgid is waiting to be abandoned, 0 otherwise
843  */
844 static int
845 ldap_abandoned( LDAP *ld, ber_int_t msgid )
846 {
847         int     i;
848
849         if ( ld->ld_abandoned == NULL )
850                 return( 0 );
851
852         for ( i = 0; ld->ld_abandoned[i] != -1; i++ )
853                 if ( ld->ld_abandoned[i] == msgid )
854                         return( 1 );
855
856         return( 0 );
857 }
858
859
860 static int
861 ldap_mark_abandoned( LDAP *ld, ber_int_t msgid )
862 {
863         int     i;
864
865         if ( ld->ld_abandoned == NULL )
866                 return( -1 );
867
868         for ( i = 0; ld->ld_abandoned[i] != -1; i++ )
869                 if ( ld->ld_abandoned[i] == msgid )
870                         break;
871
872         if ( ld->ld_abandoned[i] == -1 )
873                 return( -1 );
874
875         for ( ; ld->ld_abandoned[i] != -1; i++ ) {
876                 ld->ld_abandoned[i] = ld->ld_abandoned[i + 1];
877         }
878
879         return( 0 );
880 }
881
882
883 #ifdef LDAP_CONNECTIONLESS
884 int
885 cldap_getmsg( LDAP *ld, struct timeval *timeout, BerElement *ber )
886 {
887         int     rc;
888         ber_tag_t       tag;
889         ber_len_t       len;
890
891         if ( ! ber_pvt_sb_data_ready(&ld->ld_sb) ) {
892                 /* restored from ldap_select1() in result.c version 1.24 */
893                 fd_set  readfds;
894                 if ( ldap_int_tblsize == 0 )
895                         ldap_int_ip_init();
896                 FD_ZERO( &readfds );
897                 FD_SET( ber_pvt_sb_get_desc(&ld->ld_sb), &readfds );
898                 rc = select( ldap_int_tblsize, &readfds, 0, 0, timeout );
899
900                 if ( rc == -1 || rc == 0 ) {
901                         ld->ld_errno = (rc == -1 ? LDAP_SERVER_DOWN :
902                             LDAP_TIMEOUT);
903                         return( rc );
904                 }
905         }
906
907         /* get the next message */
908         if ( (tag = ber_get_next( &ld->ld_sb, &len, ber ))
909             != LDAP_TAG_MESSAGE ) {
910                 ld->ld_errno = (tag == LBER_DEFAULT ? LDAP_SERVER_DOWN :
911                     LDAP_LOCAL_ERROR);
912                 return( -1 );
913         }
914
915         return( 0 );
916 }
917 #endif /* LDAP_CONNECTIONLESS */