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.
39 static char copyright
[] = "@(#) Copyright (c) 1993 Regents of the University of Michigan.\nAll rights reserved.\n";
45 typedef int (LDAP_CALL
*cancelptype
)( void *cancelparm
);
47 static int ldap_ufn_search_ctx( LDAP
*ld
, char **ufncomp
, int ncomp
,
48 char *prefix
, char **attrs
, int attrsonly
,
49 LDAPMessage
**res
, LDAP_CANCELPROC_CALLBACK
*cancelproc
, void *cancelparm
,
50 char *tag1
, char *tag2
, char *tag3
);
51 static LDAPMessage
*ldap_msg_merge( LDAP
*ld
, LDAPMessage
*a
, LDAPMessage
*b
);
52 static LDAPMessage
*ldap_ufn_expand( LDAP
*ld
,
53 LDAP_CANCELPROC_CALLBACK
*cancelproc
, void *cancelparm
, char **dns
,
54 char *filter
, int scope
, char **attrs
, int aonly
, int *err
);
57 * ldap_ufn_search_ctx - do user friendly searching; provide cancel feature;
58 * specify ldapfilter.conf tags for each phase of search
61 * ufncomp the exploded user friendly name to look for
62 * ncomp number of elements in ufncomp
63 * prefix where to start searching
64 * attrs list of attribute types to return for matches
65 * attrsonly 1 => attributes only 0 => attributes and values
66 * res will contain the result of the search
67 * cancelproc routine that returns non-zero if operation should be
68 * cancelled. This can be NULL. If it is non-NULL, the
69 * routine will be called periodically.
70 * cancelparm void * that is passed to cancelproc
71 * tag[123] the ldapfilter.conf tag that will be used in phases
72 * 1, 2, and 3 of the search, respectively
75 * char *attrs[] = { "mail", "title", 0 };
76 * char *ufncomp[] = { "howes", "umich", "us", 0 }
78 * error = ldap_ufn_search_ctx( ld, ufncomp, 3, NULL, attrs, attrsonly,
79 * &res, acancelproc, along, "ufn first",
80 * "ufn intermediate", "ufn last" );
92 LDAP_CANCELPROC_CALLBACK
*cancelproc
,
99 char *dn
, *ftag
= NULL
;
101 int max
, i
, err
, scope
= 0, phase
, tries
;
103 LDAPMessage
*tmpcand
;
104 LDAPMessage
*candidates
;
105 static char *objattrs
[] = { "objectClass", NULL
};
108 * look up ufn components from most to least significant.
109 * there are 3 phases.
110 * phase 1 search the root for orgs or countries
111 * phase 2 search for orgs
112 * phase 3 search for a person
113 * in phases 1 and 2, we are building a list of candidate DNs,
114 * below which we will search for the final component of the ufn.
115 * for each component we try the filters listed in the
116 * filterconfig file, first one-level (except the last compoment),
117 * then subtree. if any of them produce any results, we go on to
118 * the next component.
124 for ( ncomp
--; ncomp
!= -1; ncomp
-- ) {
125 if ( *ufncomp
[ncomp
] == '"' ) {
128 if ( (quote
= strrchr( ufncomp
[ncomp
], '"' )) != NULL
)
130 strcpy( ufncomp
[ncomp
], ufncomp
[ncomp
] + 1 );
138 scope
= LDAP_SCOPE_ONELEVEL
;
142 scope
= LDAP_SCOPE_ONELEVEL
;
146 scope
= LDAP_SCOPE_SUBTREE
;
151 * construct an array of DN's to search below from the
152 * list of candidates.
155 if ( candidates
== NULL
) {
156 if ( prefix
!= NULL
) {
157 if ( (dns
= (char **)NSLDAPI_MALLOC(
158 sizeof(char *) * 2 )) == NULL
) {
159 err
= LDAP_NO_MEMORY
;
160 LDAP_SET_LDERRNO( ld
, err
, NULL
, NULL
);
163 dns
[0] = nsldapi_strdup( prefix
);
170 for ( tmpcand
= candidates
; tmpcand
!= NULL
&&
171 tmpcand
->lm_msgtype
!= LDAP_RES_SEARCH_RESULT
;
172 tmpcand
= tmpcand
->lm_chain
)
174 if ( (dn
= ldap_get_dn( ld
, tmpcand
)) == NULL
)
178 if ( (dns
= (char **)NSLDAPI_MALLOC(
179 sizeof(char *) * 8 )) == NULL
) {
180 err
= LDAP_NO_MEMORY
;
181 LDAP_SET_LDERRNO( ld
, err
,
186 } else if ( i
>= max
) {
187 if ( (dns
= (char **)NSLDAPI_REALLOC(
188 dns
, sizeof(char *) * 2 * max
))
190 err
= LDAP_NO_MEMORY
;
191 LDAP_SET_LDERRNO( ld
, err
,
200 ldap_msgfree( candidates
);
206 for ( fi
= ldap_getfirstfilter( ld
->ld_filtd
, ftag
,
207 ufncomp
[ncomp
] ); fi
!= NULL
;
208 fi
= ldap_getnextfilter( ld
->ld_filtd
) )
210 if ( (candidates
= ldap_ufn_expand( ld
, cancelproc
,
211 cancelparm
, dns
, fi
->lfi_filter
, scope
,
212 phase
== 3 ? attrs
: objattrs
,
213 phase
== 3 ? attrsonly
: 1, &err
)) != NULL
)
218 if ( err
== -1 || err
== LDAP_USER_CANCELLED
) {
220 ldap_value_free( dns
);
227 if ( candidates
== NULL
) {
228 if ( tries
< 2 && phase
!= 3 ) {
229 scope
= LDAP_SCOPE_SUBTREE
;
233 ldap_value_free( dns
);
240 /* go on to the next component */
244 ldap_value_free( dns
);
255 ldap_ufn_search_ct( LDAP
*ld
, char *ufn
, char **attrs
, int attrsonly
,
256 LDAPMessage
**res
, LDAP_CANCELPROC_CALLBACK
*cancelproc
, void *cancelparm
,
257 char *tag1
, char *tag2
, char *tag3
)
259 char **ufncomp
, **prefixcomp
;
261 int ncomp
, pcomp
, i
, err
= 0;
263 /* getfilter stuff must be inited before we are called */
264 if ( ld
->ld_filtd
== NULL
) {
265 err
= LDAP_PARAM_ERROR
;
266 LDAP_SET_LDERRNO( ld
, err
, NULL
, NULL
);
270 /* call ldap_explode_dn() to break the ufn into its components */
271 if ( (ufncomp
= ldap_explode_dn( ufn
, 0 )) == NULL
) {
272 err
= LDAP_LOCAL_ERROR
;
273 LDAP_SET_LDERRNO( ld
, err
, NULL
, NULL
);
276 for ( ncomp
= 0; ufncomp
[ncomp
] != NULL
; ncomp
++ )
279 /* more than two components => try it fully qualified first */
280 if ( ncomp
> 2 || ld
->ld_ufnprefix
== NULL
) {
281 err
= ldap_ufn_search_ctx( ld
, ufncomp
, ncomp
, NULL
, attrs
,
282 attrsonly
, res
, cancelproc
, cancelparm
, tag1
, tag2
, tag3
);
284 if ( ldap_count_entries( ld
, *res
) > 0 ) {
285 ldap_value_free( ufncomp
);
288 ldap_msgfree( *res
);
293 if ( ld
->ld_ufnprefix
== NULL
) {
294 ldap_value_free( ufncomp
);
298 /* if that failed, or < 2 components, use the prefix */
299 if ( (prefixcomp
= ldap_explode_dn( ld
->ld_ufnprefix
, 0 )) == NULL
) {
300 ldap_value_free( ufncomp
);
301 err
= LDAP_LOCAL_ERROR
;
302 LDAP_SET_LDERRNO( ld
, err
, NULL
, NULL
);
305 for ( pcomp
= 0; prefixcomp
[pcomp
] != NULL
; pcomp
++ )
307 if ( (pbuf
= (char *)NSLDAPI_MALLOC( strlen( ld
->ld_ufnprefix
) + 1 ))
309 ldap_value_free( ufncomp
);
310 ldap_value_free( prefixcomp
);
311 err
= LDAP_NO_MEMORY
;
312 LDAP_SET_LDERRNO( ld
, err
, NULL
, NULL
);
316 for ( i
= 0; i
< pcomp
; i
++ ) {
320 for ( j
= i
; j
< pcomp
; j
++ ) {
321 strcat( pbuf
, prefixcomp
[j
] );
325 err
= ldap_ufn_search_ctx( ld
, ufncomp
, ncomp
, pbuf
, attrs
,
326 attrsonly
, res
, cancelproc
, cancelparm
, tag1
, tag2
, tag3
);
328 if ( ldap_count_entries( ld
, *res
) > 0 ) {
331 ldap_msgfree( *res
);
336 ldap_value_free( ufncomp
);
337 ldap_value_free( prefixcomp
);
338 NSLDAPI_FREE( pbuf
);
344 * same as ldap_ufn_search_ct, except without the ability to specify
345 * ldapfilter.conf tags.
349 ldap_ufn_search_c( LDAP
*ld
, char *ufn
, char **attrs
, int attrsonly
,
350 LDAPMessage
**res
, LDAP_CANCELPROC_CALLBACK
*cancelproc
, void *cancelparm
)
352 return( ldap_ufn_search_ct( ld
, ufn
, attrs
, attrsonly
, res
, cancelproc
,
353 cancelparm
, "ufn first", "ufn intermediate", "ufn last" ) );
357 * same as ldap_ufn_search_c without the cancel function
361 ldap_ufn_search_s( LDAP
*ld
, char *ufn
, char **attrs
, int attrsonly
,
366 tv
.tv_sec
= ld
->ld_timelimit
;
368 return( ldap_ufn_search_ct( ld
, ufn
, attrs
, attrsonly
, res
,
369 ld
->ld_timelimit
? ldap_ufn_timeout
: NULL
,
370 ld
->ld_timelimit
? (void *) &tv
: NULL
,
371 "ufn first", "ufn intermediate", "ufn last" ) );
376 * ldap_msg_merge - merge two ldap search result chains. the more
377 * serious of the two error result codes is kept.
381 ldap_msg_merge( LDAP
*ld
, LDAPMessage
*a
, LDAPMessage
*b
)
383 LDAPMessage
*end
, *aprev
, *aend
, *bprev
, *bend
;
391 /* find the ends of the a and b chains */
393 for ( aend
= a
; aend
->lm_chain
!= NULL
; aend
= aend
->lm_chain
)
396 for ( bend
= b
; bend
->lm_chain
!= NULL
; bend
= bend
->lm_chain
)
400 if ( ldap_result2error( ld
, aend
, 0 ) != LDAP_SUCCESS
) {
401 /* remove result b */
402 ldap_msgfree( bend
);
404 bprev
->lm_chain
= NULL
;
409 aprev
->lm_chain
= NULL
;
414 /* remove result a */
415 ldap_msgfree( aend
);
417 aprev
->lm_chain
= NULL
;
422 bprev
->lm_chain
= NULL
;
427 if ( (a
== NULL
&& b
== NULL
) || (a
== NULL
&& bprev
== NULL
) ||
428 (b
== NULL
&& aprev
== NULL
) )
432 bprev
->lm_chain
= end
;
434 } else if ( b
== NULL
) {
435 aprev
->lm_chain
= end
;
438 bprev
->lm_chain
= end
;
445 ldap_ufn_expand( LDAP
*ld
, LDAP_CANCELPROC_CALLBACK
*cancelproc
,
446 void *cancelparm
, char **dns
, char *filter
, int scope
,
447 char **attrs
, int aonly
, int *err
)
449 LDAPMessage
*tmpcand
, *tmpres
;
454 /* search for this component below the current candidates */
463 if (( msgid
= ldap_search( ld
, dn
, scope
, filter
, attrs
,
465 ldap_msgfree( tmpcand
);
466 *err
= LDAP_GET_LDERRNO( ld
, NULL
, NULL
);
471 tv
.tv_usec
= 100000; /* 1/10 of a second */
474 *err
= ldap_result( ld
, msgid
, 1, &tv
, &tmpres
);
475 if ( *err
== 0 && cancelproc
!= NULL
&&
476 (*cancelproc
)( cancelparm
) != 0 ) {
477 ldap_abandon( ld
, msgid
);
478 *err
= LDAP_USER_CANCELLED
;
479 LDAP_SET_LDERRNO( ld
, *err
, NULL
, NULL
);
481 } while ( *err
== 0 );
483 if ( *err
== LDAP_USER_CANCELLED
|| *err
< 0 ||
484 ( *err
= ldap_result2error( ld
, tmpres
, 0 )) == -1 ) {
485 ldap_msgfree( tmpcand
);
489 tmpcand
= ldap_msg_merge( ld
, tmpcand
, tmpres
);
492 } while ( dns
!= NULL
&& dns
[i
] != NULL
);
494 if ( ldap_count_entries( ld
, tmpcand
) > 0 ) {
497 ldap_msgfree( tmpcand
);
503 * ldap_ufn_setfilter - set the filter config file used in ufn searching
508 ldap_ufn_setfilter( LDAP
*ld
, char *fname
)
510 if ( ld
->ld_filtd
!= NULL
)
511 ldap_getfilter_free( ld
->ld_filtd
);
513 return( ld
->ld_filtd
= ldap_init_getfilter( fname
) );
518 ldap_ufn_setprefix( LDAP
*ld
, char *prefix
)
520 if ( ld
->ld_ufnprefix
!= NULL
)
521 NSLDAPI_FREE( ld
->ld_ufnprefix
);
523 ld
->ld_ufnprefix
= nsldapi_strdup( prefix
);
528 ldap_ufn_timeout( void *tvparam
)
532 tv
= (struct timeval
*)tvparam
;
534 if ( tv
->tv_sec
!= 0 ) {
535 tv
->tv_usec
= tv
->tv_sec
* 1000000; /* sec => micro sec */
538 tv
->tv_usec
-= 100000; /* 1/10 of a second */
540 return( tv
->tv_usec
<= 0 ? 1 : 0 );