2 * Copyright (c) 2001 by Sun Microsystems, Inc.
6 #pragma ident "%Z%%M% %I% %E% SMI"
9 * The contents of this file are subject to the Netscape Public
10 * License Version 1.1 (the "License"); you may not use this file
11 * except in compliance with the License. You may obtain a copy of
12 * the License at http://www.mozilla.org/NPL/
14 * Software distributed under the License is distributed on an "AS
15 * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
16 * implied. See the License for the specific language governing
17 * rights and limitations under the License.
19 * The Original Code is Mozilla Communicator client code, released
22 * The Initial Developer of the Original Code is Netscape
23 * Communications Corporation. Portions created by Netscape are
24 * Copyright (C) 1998-1999 Netscape Communications Corporation. All
30 * Copyright (c) 1990 Regents of the University of Michigan.
31 * All rights reserved.
38 static char copyright
[] = "@(#) Copyright (c) 1993 Regents of the University of Michigan.\nAll rights reserved.\n";
43 typedef int (LDAP_CALL
*cancelptype
)( void *cancelparm
);
45 static int ldap_ufn_search_ctx( LDAP
*ld
, char **ufncomp
, int ncomp
,
46 char *prefix
, char **attrs
, int attrsonly
,
47 LDAPMessage
**res
, LDAP_CANCELPROC_CALLBACK
*cancelproc
, void *cancelparm
,
48 char *tag1
, char *tag2
, char *tag3
);
49 static LDAPMessage
*ldap_msg_merge( LDAP
*ld
, LDAPMessage
*a
, LDAPMessage
*b
);
50 static LDAPMessage
*ldap_ufn_expand( LDAP
*ld
,
51 LDAP_CANCELPROC_CALLBACK
*cancelproc
, void *cancelparm
, char **dns
,
52 char *filter
, int scope
, char **attrs
, int aonly
, int *err
);
55 * ldap_ufn_search_ctx - do user friendly searching; provide cancel feature;
56 * specify ldapfilter.conf tags for each phase of search
59 * ufncomp the exploded user friendly name to look for
60 * ncomp number of elements in ufncomp
61 * prefix where to start searching
62 * attrs list of attribute types to return for matches
63 * attrsonly 1 => attributes only 0 => attributes and values
64 * res will contain the result of the search
65 * cancelproc routine that returns non-zero if operation should be
66 * cancelled. This can be NULL. If it is non-NULL, the
67 * routine will be called periodically.
68 * cancelparm void * that is passed to cancelproc
69 * tag[123] the ldapfilter.conf tag that will be used in phases
70 * 1, 2, and 3 of the search, respectively
73 * char *attrs[] = { "mail", "title", 0 };
74 * char *ufncomp[] = { "howes", "umich", "us", 0 }
76 * error = ldap_ufn_search_ctx( ld, ufncomp, 3, NULL, attrs, attrsonly,
77 * &res, acancelproc, along, "ufn first",
78 * "ufn intermediate", "ufn last" );
90 LDAP_CANCELPROC_CALLBACK
*cancelproc
,
97 char *dn
, *ftag
= NULL
;
99 int max
, i
, err
, scope
= 0, phase
, tries
;
101 LDAPMessage
*tmpcand
;
102 LDAPMessage
*candidates
;
103 static char *objattrs
[] = { "objectClass", NULL
};
106 * look up ufn components from most to least significant.
107 * there are 3 phases.
108 * phase 1 search the root for orgs or countries
109 * phase 2 search for orgs
110 * phase 3 search for a person
111 * in phases 1 and 2, we are building a list of candidate DNs,
112 * below which we will search for the final component of the ufn.
113 * for each component we try the filters listed in the
114 * filterconfig file, first one-level (except the last compoment),
115 * then subtree. if any of them produce any results, we go on to
116 * the next component.
122 for ( ncomp
--; ncomp
!= -1; ncomp
-- ) {
123 if ( *ufncomp
[ncomp
] == '"' ) {
126 if ( (quote
= strrchr( ufncomp
[ncomp
], '"' )) != NULL
)
128 strcpy( ufncomp
[ncomp
], ufncomp
[ncomp
] + 1 );
136 scope
= LDAP_SCOPE_ONELEVEL
;
140 scope
= LDAP_SCOPE_ONELEVEL
;
144 scope
= LDAP_SCOPE_SUBTREE
;
149 * construct an array of DN's to search below from the
150 * list of candidates.
153 if ( candidates
== NULL
) {
154 if ( prefix
!= NULL
) {
155 if ( (dns
= (char **)NSLDAPI_MALLOC(
156 sizeof(char *) * 2 )) == NULL
) {
157 err
= LDAP_NO_MEMORY
;
158 LDAP_SET_LDERRNO( ld
, err
, NULL
, NULL
);
161 dns
[0] = nsldapi_strdup( prefix
);
168 for ( tmpcand
= candidates
; tmpcand
!= NULL
&&
169 tmpcand
->lm_msgtype
!= LDAP_RES_SEARCH_RESULT
;
170 tmpcand
= tmpcand
->lm_chain
)
172 if ( (dn
= ldap_get_dn( ld
, tmpcand
)) == NULL
)
176 if ( (dns
= (char **)NSLDAPI_MALLOC(
177 sizeof(char *) * 8 )) == NULL
) {
178 err
= LDAP_NO_MEMORY
;
179 LDAP_SET_LDERRNO( ld
, err
,
184 } else if ( i
>= max
) {
185 if ( (dns
= (char **)NSLDAPI_REALLOC(
186 dns
, sizeof(char *) * 2 * max
))
188 err
= LDAP_NO_MEMORY
;
189 LDAP_SET_LDERRNO( ld
, err
,
198 ldap_msgfree( candidates
);
204 for ( fi
= ldap_getfirstfilter( ld
->ld_filtd
, ftag
,
205 ufncomp
[ncomp
] ); fi
!= NULL
;
206 fi
= ldap_getnextfilter( ld
->ld_filtd
) )
208 if ( (candidates
= ldap_ufn_expand( ld
, cancelproc
,
209 cancelparm
, dns
, fi
->lfi_filter
, scope
,
210 phase
== 3 ? attrs
: objattrs
,
211 phase
== 3 ? attrsonly
: 1, &err
)) != NULL
)
216 if ( err
== -1 || err
== LDAP_USER_CANCELLED
) {
218 ldap_value_free( dns
);
225 if ( candidates
== NULL
) {
226 if ( tries
< 2 && phase
!= 3 ) {
227 scope
= LDAP_SCOPE_SUBTREE
;
231 ldap_value_free( dns
);
238 /* go on to the next component */
242 ldap_value_free( dns
);
253 ldap_ufn_search_ct( LDAP
*ld
, char *ufn
, char **attrs
, int attrsonly
,
254 LDAPMessage
**res
, LDAP_CANCELPROC_CALLBACK
*cancelproc
, void *cancelparm
,
255 char *tag1
, char *tag2
, char *tag3
)
257 char **ufncomp
, **prefixcomp
;
259 int ncomp
, pcomp
, i
, err
= 0;
261 /* getfilter stuff must be inited before we are called */
262 if ( ld
->ld_filtd
== NULL
) {
263 err
= LDAP_PARAM_ERROR
;
264 LDAP_SET_LDERRNO( ld
, err
, NULL
, NULL
);
268 /* call ldap_explode_dn() to break the ufn into its components */
269 if ( (ufncomp
= ldap_explode_dn( ufn
, 0 )) == NULL
) {
270 err
= LDAP_LOCAL_ERROR
;
271 LDAP_SET_LDERRNO( ld
, err
, NULL
, NULL
);
274 for ( ncomp
= 0; ufncomp
[ncomp
] != NULL
; ncomp
++ )
277 /* more than two components => try it fully qualified first */
278 if ( ncomp
> 2 || ld
->ld_ufnprefix
== NULL
) {
279 err
= ldap_ufn_search_ctx( ld
, ufncomp
, ncomp
, NULL
, attrs
,
280 attrsonly
, res
, cancelproc
, cancelparm
, tag1
, tag2
, tag3
);
282 if ( ldap_count_entries( ld
, *res
) > 0 ) {
283 ldap_value_free( ufncomp
);
286 ldap_msgfree( *res
);
291 if ( ld
->ld_ufnprefix
== NULL
) {
292 ldap_value_free( ufncomp
);
296 /* if that failed, or < 2 components, use the prefix */
297 if ( (prefixcomp
= ldap_explode_dn( ld
->ld_ufnprefix
, 0 )) == NULL
) {
298 ldap_value_free( ufncomp
);
299 err
= LDAP_LOCAL_ERROR
;
300 LDAP_SET_LDERRNO( ld
, err
, NULL
, NULL
);
303 for ( pcomp
= 0; prefixcomp
[pcomp
] != NULL
; pcomp
++ )
305 if ( (pbuf
= (char *)NSLDAPI_MALLOC( strlen( ld
->ld_ufnprefix
) + 1 ))
307 ldap_value_free( ufncomp
);
308 ldap_value_free( prefixcomp
);
309 err
= LDAP_NO_MEMORY
;
310 LDAP_SET_LDERRNO( ld
, err
, NULL
, NULL
);
314 for ( i
= 0; i
< pcomp
; i
++ ) {
318 for ( j
= i
; j
< pcomp
; j
++ ) {
319 strcat( pbuf
, prefixcomp
[j
] );
323 err
= ldap_ufn_search_ctx( ld
, ufncomp
, ncomp
, pbuf
, attrs
,
324 attrsonly
, res
, cancelproc
, cancelparm
, tag1
, tag2
, tag3
);
326 if ( ldap_count_entries( ld
, *res
) > 0 ) {
329 ldap_msgfree( *res
);
334 ldap_value_free( ufncomp
);
335 ldap_value_free( prefixcomp
);
336 NSLDAPI_FREE( pbuf
);
342 * same as ldap_ufn_search_ct, except without the ability to specify
343 * ldapfilter.conf tags.
347 ldap_ufn_search_c( LDAP
*ld
, char *ufn
, char **attrs
, int attrsonly
,
348 LDAPMessage
**res
, LDAP_CANCELPROC_CALLBACK
*cancelproc
, void *cancelparm
)
350 return( ldap_ufn_search_ct( ld
, ufn
, attrs
, attrsonly
, res
, cancelproc
,
351 cancelparm
, "ufn first", "ufn intermediate", "ufn last" ) );
355 * same as ldap_ufn_search_c without the cancel function
359 ldap_ufn_search_s( LDAP
*ld
, char *ufn
, char **attrs
, int attrsonly
,
364 tv
.tv_sec
= ld
->ld_timelimit
;
366 return( ldap_ufn_search_ct( ld
, ufn
, attrs
, attrsonly
, res
,
367 ld
->ld_timelimit
? ldap_ufn_timeout
: NULL
,
368 ld
->ld_timelimit
? (void *) &tv
: NULL
,
369 "ufn first", "ufn intermediate", "ufn last" ) );
374 * ldap_msg_merge - merge two ldap search result chains. the more
375 * serious of the two error result codes is kept.
379 ldap_msg_merge( LDAP
*ld
, LDAPMessage
*a
, LDAPMessage
*b
)
381 LDAPMessage
*end
, *aprev
, *aend
, *bprev
, *bend
;
389 /* find the ends of the a and b chains */
391 for ( aend
= a
; aend
->lm_chain
!= NULL
; aend
= aend
->lm_chain
)
394 for ( bend
= b
; bend
->lm_chain
!= NULL
; bend
= bend
->lm_chain
)
398 if ( ldap_result2error( ld
, aend
, 0 ) != LDAP_SUCCESS
) {
399 /* remove result b */
400 ldap_msgfree( bend
);
402 bprev
->lm_chain
= NULL
;
407 aprev
->lm_chain
= NULL
;
412 /* remove result a */
413 ldap_msgfree( aend
);
415 aprev
->lm_chain
= NULL
;
420 bprev
->lm_chain
= NULL
;
425 if ( (a
== NULL
&& b
== NULL
) || (a
== NULL
&& bprev
== NULL
) ||
426 (b
== NULL
&& aprev
== NULL
) )
430 bprev
->lm_chain
= end
;
432 } else if ( b
== NULL
) {
433 aprev
->lm_chain
= end
;
436 bprev
->lm_chain
= end
;
443 ldap_ufn_expand( LDAP
*ld
, LDAP_CANCELPROC_CALLBACK
*cancelproc
,
444 void *cancelparm
, char **dns
, char *filter
, int scope
,
445 char **attrs
, int aonly
, int *err
)
447 LDAPMessage
*tmpcand
, *tmpres
;
452 /* search for this component below the current candidates */
461 if (( msgid
= ldap_search( ld
, dn
, scope
, filter
, attrs
,
463 ldap_msgfree( tmpcand
);
464 *err
= LDAP_GET_LDERRNO( ld
, NULL
, NULL
);
469 tv
.tv_usec
= 100000; /* 1/10 of a second */
472 *err
= ldap_result( ld
, msgid
, 1, &tv
, &tmpres
);
473 if ( *err
== 0 && cancelproc
!= NULL
&&
474 (*cancelproc
)( cancelparm
) != 0 ) {
475 ldap_abandon( ld
, msgid
);
476 *err
= LDAP_USER_CANCELLED
;
477 LDAP_SET_LDERRNO( ld
, *err
, NULL
, NULL
);
479 } while ( *err
== 0 );
481 if ( *err
== LDAP_USER_CANCELLED
|| *err
< 0 ||
482 ( *err
= ldap_result2error( ld
, tmpres
, 0 )) == -1 ) {
483 ldap_msgfree( tmpcand
);
487 tmpcand
= ldap_msg_merge( ld
, tmpcand
, tmpres
);
490 } while ( dns
!= NULL
&& dns
[i
] != NULL
);
492 if ( ldap_count_entries( ld
, tmpcand
) > 0 ) {
495 ldap_msgfree( tmpcand
);
501 * ldap_ufn_setfilter - set the filter config file used in ufn searching
506 ldap_ufn_setfilter( LDAP
*ld
, char *fname
)
508 if ( ld
->ld_filtd
!= NULL
)
509 ldap_getfilter_free( ld
->ld_filtd
);
511 return( ld
->ld_filtd
= ldap_init_getfilter( fname
) );
516 ldap_ufn_setprefix( LDAP
*ld
, char *prefix
)
518 if ( ld
->ld_ufnprefix
!= NULL
)
519 NSLDAPI_FREE( ld
->ld_ufnprefix
);
521 ld
->ld_ufnprefix
= nsldapi_strdup( prefix
);
526 ldap_ufn_timeout( void *tvparam
)
530 tv
= (struct timeval
*)tvparam
;
532 if ( tv
->tv_sec
!= 0 ) {
533 tv
->tv_usec
= tv
->tv_sec
* 1000000; /* sec => micro sec */
536 tv
->tv_usec
-= 100000; /* 1/10 of a second */
538 return( tv
->tv_usec
<= 0 ? 1 : 0 );