1 /* 2 * Copyright (c) 2001 by Sun Microsystems, Inc. 3 * All rights reserved. 4 */ 5 6 #pragma ident "%Z%%M% %I% %E% SMI" 7 8 /* 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/ 13 * 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. 18 * 19 * The Original Code is Mozilla Communicator client code, released 20 * March 31, 1998. 21 * 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 25 * Rights Reserved. 26 * 27 * Contributor(s): 28 */ 29 /* 30 * Copyright (c) 1990 Regents of the University of Michigan. 31 * All rights reserved. 32 */ 33 /* 34 * ufn.c 35 */ 36 37 #if 0 38 #ifndef lint 39 static char copyright[] = "@(#) Copyright (c) 1993 Regents of the University of Michigan.\nAll rights reserved.\n"; 40 #endif 41 #endif 42 43 #include "ldap-int.h" 44 45 typedef int (LDAP_CALL *cancelptype)( void *cancelparm ); 46 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 ); 55 56 /* 57 * ldap_ufn_search_ctx - do user friendly searching; provide cancel feature; 58 * specify ldapfilter.conf tags for each phase of search 59 * 60 * ld LDAP descriptor 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 73 * 74 * Example: 75 * char *attrs[] = { "mail", "title", 0 }; 76 * char *ufncomp[] = { "howes", "umich", "us", 0 } 77 * LDAPMessage *res; 78 * error = ldap_ufn_search_ctx( ld, ufncomp, 3, NULL, attrs, attrsonly, 79 * &res, acancelproc, along, "ufn first", 80 * "ufn intermediate", "ufn last" ); 81 */ 82 83 static int 84 ldap_ufn_search_ctx( 85 LDAP *ld, 86 char **ufncomp, 87 int ncomp, 88 char *prefix, 89 char **attrs, 90 int attrsonly, 91 LDAPMessage **res, 92 LDAP_CANCELPROC_CALLBACK *cancelproc, 93 void *cancelparm, 94 char *tag1, 95 char *tag2, 96 char *tag3 97 ) 98 { 99 char *dn, *ftag = NULL; 100 char **dns = NULL; 101 int max, i, err, scope = 0, phase, tries; 102 LDAPFiltInfo *fi; 103 LDAPMessage *tmpcand; 104 LDAPMessage *candidates; 105 static char *objattrs[] = { "objectClass", NULL }; 106 107 /* 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. 119 */ 120 121 *res = NULL; 122 candidates = NULL; 123 phase = 1; 124 for ( ncomp--; ncomp != -1; ncomp-- ) { 125 if ( *ufncomp[ncomp] == '"' ) { 126 char *quote; 127 128 if ( (quote = strrchr( ufncomp[ncomp], '"' )) != NULL ) 129 *quote = '\0'; 130 strcpy( ufncomp[ncomp], ufncomp[ncomp] + 1 ); 131 } 132 if ( ncomp == 0 ) 133 phase = 3; 134 135 switch ( phase ) { 136 case 1: 137 ftag = tag1; 138 scope = LDAP_SCOPE_ONELEVEL; 139 break; 140 case 2: 141 ftag = tag2; 142 scope = LDAP_SCOPE_ONELEVEL; 143 break; 144 case 3: 145 ftag = tag3; 146 scope = LDAP_SCOPE_SUBTREE; 147 break; 148 } 149 150 /* 151 * construct an array of DN's to search below from the 152 * list of candidates. 153 */ 154 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 ); 161 return( err ); 162 } 163 dns[0] = nsldapi_strdup( prefix ); 164 dns[1] = NULL; 165 } else { 166 dns = NULL; 167 } 168 } else { 169 i = 0, max = 0; 170 for ( tmpcand = candidates; tmpcand != NULL && 171 tmpcand->lm_msgtype != LDAP_RES_SEARCH_RESULT; 172 tmpcand = tmpcand->lm_chain ) 173 { 174 if ( (dn = ldap_get_dn( ld, tmpcand )) == NULL ) 175 continue; 176 177 if ( dns == NULL ) { 178 if ( (dns = (char **)NSLDAPI_MALLOC( 179 sizeof(char *) * 8 )) == NULL ) { 180 err = LDAP_NO_MEMORY; 181 LDAP_SET_LDERRNO( ld, err, 182 NULL, NULL ); 183 return( err ); 184 } 185 max = 8; 186 } else if ( i >= max ) { 187 if ( (dns = (char **)NSLDAPI_REALLOC( 188 dns, sizeof(char *) * 2 * max )) 189 == NULL ) { 190 err = LDAP_NO_MEMORY; 191 LDAP_SET_LDERRNO( ld, err, 192 NULL, NULL ); 193 return( err ); 194 } 195 max *= 2; 196 } 197 dns[i++] = dn; 198 dns[i] = NULL; 199 } 200 ldap_msgfree( candidates ); 201 candidates = NULL; 202 } 203 tries = 0; 204 tryagain: 205 tries++; 206 for ( fi = ldap_getfirstfilter( ld->ld_filtd, ftag, 207 ufncomp[ncomp] ); fi != NULL; 208 fi = ldap_getnextfilter( ld->ld_filtd ) ) 209 { 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 ) 214 { 215 break; 216 } 217 218 if ( err == -1 || err == LDAP_USER_CANCELLED ) { 219 if ( dns != NULL ) { 220 ldap_value_free( dns ); 221 dns = NULL; 222 } 223 return( err ); 224 } 225 } 226 227 if ( candidates == NULL ) { 228 if ( tries < 2 && phase != 3 ) { 229 scope = LDAP_SCOPE_SUBTREE; 230 goto tryagain; 231 } else { 232 if ( dns != NULL ) { 233 ldap_value_free( dns ); 234 dns = NULL; 235 } 236 return( err ); 237 } 238 } 239 240 /* go on to the next component */ 241 if ( phase == 1 ) 242 phase++; 243 if ( dns != NULL ) { 244 ldap_value_free( dns ); 245 dns = NULL; 246 } 247 } 248 *res = candidates; 249 250 return( err ); 251 } 252 253 int 254 LDAP_CALL 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 ) 258 { 259 char **ufncomp, **prefixcomp; 260 char *pbuf; 261 int ncomp, pcomp, i, err = 0; 262 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 ); 267 return( err ); 268 } 269 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 ); 274 return( err ); 275 } 276 for ( ncomp = 0; ufncomp[ncomp] != NULL; ncomp++ ) 277 ; /* NULL */ 278 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 ); 283 284 if ( ldap_count_entries( ld, *res ) > 0 ) { 285 ldap_value_free( ufncomp ); 286 return( err ); 287 } else { 288 ldap_msgfree( *res ); 289 *res = NULL; 290 } 291 } 292 293 if ( ld->ld_ufnprefix == NULL ) { 294 ldap_value_free( ufncomp ); 295 return( err ); 296 } 297 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 ); 303 return( err ); 304 } 305 for ( pcomp = 0; prefixcomp[pcomp] != NULL; pcomp++ ) 306 ; /* NULL */ 307 if ( (pbuf = (char *)NSLDAPI_MALLOC( strlen( ld->ld_ufnprefix ) + 1 )) 308 == NULL ) { 309 ldap_value_free( ufncomp ); 310 ldap_value_free( prefixcomp ); 311 err = LDAP_NO_MEMORY; 312 LDAP_SET_LDERRNO( ld, err, NULL, NULL ); 313 return( err ); 314 } 315 316 for ( i = 0; i < pcomp; i++ ) { 317 int j; 318 319 *pbuf = '\0'; 320 for ( j = i; j < pcomp; j++ ) { 321 strcat( pbuf, prefixcomp[j] ); 322 if ( j + 1 < pcomp ) 323 strcat( pbuf, "," ); 324 } 325 err = ldap_ufn_search_ctx( ld, ufncomp, ncomp, pbuf, attrs, 326 attrsonly, res, cancelproc, cancelparm, tag1, tag2, tag3 ); 327 328 if ( ldap_count_entries( ld, *res ) > 0 ) { 329 break; 330 } else { 331 ldap_msgfree( *res ); 332 *res = NULL; 333 } 334 } 335 336 ldap_value_free( ufncomp ); 337 ldap_value_free( prefixcomp ); 338 NSLDAPI_FREE( pbuf ); 339 340 return( err ); 341 } 342 343 /* 344 * same as ldap_ufn_search_ct, except without the ability to specify 345 * ldapfilter.conf tags. 346 */ 347 int 348 LDAP_CALL 349 ldap_ufn_search_c( LDAP *ld, char *ufn, char **attrs, int attrsonly, 350 LDAPMessage **res, LDAP_CANCELPROC_CALLBACK *cancelproc, void *cancelparm ) 351 { 352 return( ldap_ufn_search_ct( ld, ufn, attrs, attrsonly, res, cancelproc, 353 cancelparm, "ufn first", "ufn intermediate", "ufn last" ) ); 354 } 355 356 /* 357 * same as ldap_ufn_search_c without the cancel function 358 */ 359 int 360 LDAP_CALL 361 ldap_ufn_search_s( LDAP *ld, char *ufn, char **attrs, int attrsonly, 362 LDAPMessage **res ) 363 { 364 struct timeval tv; 365 366 tv.tv_sec = ld->ld_timelimit; 367 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" ) ); 372 } 373 374 375 /* 376 * ldap_msg_merge - merge two ldap search result chains. the more 377 * serious of the two error result codes is kept. 378 */ 379 380 static LDAPMessage * 381 ldap_msg_merge( LDAP *ld, LDAPMessage *a, LDAPMessage *b ) 382 { 383 LDAPMessage *end, *aprev, *aend, *bprev, *bend; 384 385 if ( a == NULL ) 386 return( b ); 387 388 if ( b == NULL ) 389 return( a ); 390 391 /* find the ends of the a and b chains */ 392 aprev = NULL; 393 for ( aend = a; aend->lm_chain != NULL; aend = aend->lm_chain ) 394 aprev = aend; 395 bprev = NULL; 396 for ( bend = b; bend->lm_chain != NULL; bend = bend->lm_chain ) 397 bprev = bend; 398 399 /* keep result a */ 400 if ( ldap_result2error( ld, aend, 0 ) != LDAP_SUCCESS ) { 401 /* remove result b */ 402 ldap_msgfree( bend ); 403 if ( bprev != NULL ) 404 bprev->lm_chain = NULL; 405 else 406 b = NULL; 407 end = aend; 408 if ( aprev != NULL ) 409 aprev->lm_chain = NULL; 410 else 411 a = NULL; 412 /* keep result b */ 413 } else { 414 /* remove result a */ 415 ldap_msgfree( aend ); 416 if ( aprev != NULL ) 417 aprev->lm_chain = NULL; 418 else 419 a = NULL; 420 end = bend; 421 if ( bprev != NULL ) 422 bprev->lm_chain = NULL; 423 else 424 b = NULL; 425 } 426 427 if ( (a == NULL && b == NULL) || (a == NULL && bprev == NULL) || 428 (b == NULL && aprev == NULL) ) 429 return( end ); 430 431 if ( a == NULL ) { 432 bprev->lm_chain = end; 433 return( b ); 434 } else if ( b == NULL ) { 435 aprev->lm_chain = end; 436 return( a ); 437 } else { 438 bprev->lm_chain = end; 439 aprev->lm_chain = b; 440 return( a ); 441 } 442 } 443 444 static LDAPMessage * 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 ) 448 { 449 LDAPMessage *tmpcand, *tmpres; 450 char *dn; 451 int i, msgid; 452 struct timeval tv; 453 454 /* search for this component below the current candidates */ 455 tmpcand = NULL; 456 i = 0; 457 do { 458 if ( dns != NULL ) 459 dn = dns[i]; 460 else 461 dn = ""; 462 463 if (( msgid = ldap_search( ld, dn, scope, filter, attrs, 464 aonly )) == -1 ) { 465 ldap_msgfree( tmpcand ); 466 *err = LDAP_GET_LDERRNO( ld, NULL, NULL ); 467 return( NULL ); 468 } 469 470 tv.tv_sec = 0; 471 tv.tv_usec = 100000; /* 1/10 of a second */ 472 473 do { 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 ); 480 } 481 } while ( *err == 0 ); 482 483 if ( *err == LDAP_USER_CANCELLED || *err < 0 || 484 ( *err = ldap_result2error( ld, tmpres, 0 )) == -1 ) { 485 ldap_msgfree( tmpcand ); 486 return( NULL ); 487 } 488 489 tmpcand = ldap_msg_merge( ld, tmpcand, tmpres ); 490 491 i++; 492 } while ( dns != NULL && dns[i] != NULL ); 493 494 if ( ldap_count_entries( ld, tmpcand ) > 0 ) { 495 return( tmpcand ); 496 } else { 497 ldap_msgfree( tmpcand ); 498 return( NULL ); 499 } 500 } 501 502 /* 503 * ldap_ufn_setfilter - set the filter config file used in ufn searching 504 */ 505 506 LDAPFiltDesc * 507 LDAP_CALL 508 ldap_ufn_setfilter( LDAP *ld, char *fname ) 509 { 510 if ( ld->ld_filtd != NULL ) 511 ldap_getfilter_free( ld->ld_filtd ); 512 513 return( ld->ld_filtd = ldap_init_getfilter( fname ) ); 514 } 515 516 void 517 LDAP_CALL 518 ldap_ufn_setprefix( LDAP *ld, char *prefix ) 519 { 520 if ( ld->ld_ufnprefix != NULL ) 521 NSLDAPI_FREE( ld->ld_ufnprefix ); 522 523 ld->ld_ufnprefix = nsldapi_strdup( prefix ); 524 } 525 526 int 527 LDAP_C 528 ldap_ufn_timeout( void *tvparam ) 529 { 530 struct timeval *tv; 531 532 tv = (struct timeval *)tvparam; 533 534 if ( tv->tv_sec != 0 ) { 535 tv->tv_usec = tv->tv_sec * 1000000; /* sec => micro sec */ 536 tv->tv_sec = 0; 537 } 538 tv->tv_usec -= 100000; /* 1/10 of a second */ 539 540 return( tv->tv_usec <= 0 ? 1 : 0 ); 541 } 542