1 /* 2 * Copyright 2006 Sun Microsystems, Inc. All rights reserved. 3 * Use is subject to license terms. 4 */ 5 6 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- 7 * 8 * The contents of this file are subject to the Netscape Public License 9 * Version 1.0 (the "NPL"); you may not use this file except in 10 * compliance with the NPL. You may obtain a copy of the NPL at 11 * http://www.mozilla.org/NPL/ 12 * 13 * Software distributed under the NPL is distributed on an "AS IS" basis, 14 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL 15 * for the specific language governing rights and limitations under the 16 * NPL. 17 * 18 * The Initial Developer of this code under the NPL is Netscape 19 * Communications Corporation. Portions created by Netscape are 20 * Copyright (C) 1998 Netscape Communications Corporation. All Rights 21 * Reserved. 22 */ 23 /* 24 * Copyright (c) 1993 Regents of the University of Michigan. 25 * All rights reserved. 26 */ 27 /* 28 * getfilter.c -- optional add-on to libldap 29 */ 30 31 #if 0 32 #ifndef lint 33 static char copyright[] = "@(#) Copyright (c) 1993 Regents of the University of Michigan.\nAll rights reserved.\n"; 34 #endif 35 #endif 36 37 #include "ldap-int.h" 38 #include "regex.h" 39 #include <stdio.h> /* sprintf */ 40 41 static int break_into_words( char *str, char *delims, char ***wordsp ); 42 43 #if !defined( macintosh ) && !defined( DOS ) 44 extern char * LDAP_CALL re_comp(); 45 extern int LDAP_CALL re_exec( char *lp ); 46 #endif 47 48 #define FILT_MAX_LINE_LEN 1024 49 50 LDAPFiltDesc * 51 LDAP_CALL 52 ldap_init_getfilter( char *fname ) 53 { 54 FILE *fp; 55 char *buf; 56 ssize_t rlen, len; 57 int eof; 58 LDAPFiltDesc *lfdp; 59 60 if (( fp = fopen( fname, "rF" )) == NULL ) { 61 return( NULL ); 62 } 63 64 if ( fseek( fp, 0L, SEEK_END ) != 0 ) { /* move to end to get len */ 65 fclose( fp ); 66 return( NULL ); 67 } 68 69 len = ftell( fp ); 70 71 if ( fseek( fp, 0L, SEEK_SET ) != 0 ) { /* back to start of file */ 72 fclose( fp ); 73 return( NULL ); 74 } 75 76 if (( buf = NSLDAPI_MALLOC( (size_t)len )) == NULL ) { 77 fclose( fp ); 78 return( NULL ); 79 } 80 81 rlen = fread( buf, 1, (size_t)len, fp ); 82 eof = feof( fp ); 83 fclose( fp ); 84 85 if ( rlen != len && !eof ) { /* error: didn't get the whole file */ 86 NSLDAPI_FREE( buf ); 87 return( NULL ); 88 } 89 90 91 lfdp = ldap_init_getfilter_buf( buf, rlen ); 92 NSLDAPI_FREE( buf ); 93 94 return( lfdp ); 95 } 96 97 98 LDAPFiltDesc * 99 LDAP_CALL 100 ldap_init_getfilter_buf( char *buf, ssize_t buflen ) 101 { 102 LDAPFiltDesc *lfdp; 103 LDAPFiltList *flp, *nextflp; 104 LDAPFiltInfo *fip, *nextfip; 105 char *tag, **tok; 106 int tokcnt, i; 107 long buffer_len = (long)buflen; 108 109 if ( (buf == NULL) || (buflen < 0) || 110 ( lfdp = (LDAPFiltDesc *)NSLDAPI_CALLOC(1, sizeof( LDAPFiltDesc))) 111 == NULL ) { 112 return( NULL ); 113 } 114 115 flp = nextflp = NULL; 116 fip = NULL; 117 tag = NULL; 118 119 while ( buflen > 0 && ( tokcnt = ldap_next_line_tokens( &buf, 120 &buffer_len, &tok )) > 0 ) { 121 switch( tokcnt ) { 122 case 1: /* tag line */ 123 if ( tag != NULL ) { 124 NSLDAPI_FREE( tag ); 125 } 126 tag = tok[ 0 ]; 127 NSLDAPI_FREE( tok ); 128 break; 129 case 4: 130 case 5: /* start of filter info. list */ 131 if (( nextflp = (LDAPFiltList *)NSLDAPI_CALLOC( 1, 132 sizeof( LDAPFiltList ))) == NULL ) { 133 ldap_getfilter_free( lfdp ); 134 return( NULL ); 135 } 136 nextflp->lfl_tag = nsldapi_strdup( tag ); 137 nextflp->lfl_pattern = tok[ 0 ]; 138 if ( re_comp( nextflp->lfl_pattern ) != NULL ) { 139 char msg[256]; 140 ldap_getfilter_free( lfdp ); 141 sprintf( msg, dgettext(TEXT_DOMAIN, 142 "bad regular expresssion %s\n"), 143 nextflp->lfl_pattern ); 144 ber_err_print( msg ); 145 ldap_free_strarray( tok ); 146 return( NULL ); 147 } 148 149 nextflp->lfl_delims = tok[ 1 ]; 150 nextflp->lfl_ilist = NULL; 151 nextflp->lfl_next = NULL; 152 if ( flp == NULL ) { /* first one */ 153 lfdp->lfd_filtlist = nextflp; 154 } else { 155 flp->lfl_next = nextflp; 156 } 157 flp = nextflp; 158 fip = NULL; 159 for ( i = 2; i < 5; ++i ) { 160 tok[ i - 2 ] = tok[ i ]; 161 } 162 /* fall through */ 163 164 case 2: 165 case 3: /* filter, desc, and optional search scope */ 166 if ( nextflp != NULL ) { /* add to info list */ 167 if (( nextfip = (LDAPFiltInfo *)NSLDAPI_CALLOC( 1, 168 sizeof( LDAPFiltInfo ))) == NULL ) { 169 ldap_getfilter_free( lfdp ); 170 ldap_free_strarray( tok ); 171 return( NULL ); 172 } 173 if ( fip == NULL ) { /* first one */ 174 nextflp->lfl_ilist = nextfip; 175 } else { 176 fip->lfi_next = nextfip; 177 } 178 fip = nextfip; 179 nextfip->lfi_next = NULL; 180 nextfip->lfi_filter = tok[ 0 ]; 181 nextfip->lfi_desc = tok[ 1 ]; 182 if ( tok[ 2 ] != NULL ) { 183 if ( strcasecmp( tok[ 2 ], "subtree" ) == 0 ) { 184 nextfip->lfi_scope = LDAP_SCOPE_SUBTREE; 185 } else if ( strcasecmp( tok[ 2 ], "onelevel" ) == 0 ) { 186 nextfip->lfi_scope = LDAP_SCOPE_ONELEVEL; 187 } else if ( strcasecmp( tok[ 2 ], "base" ) == 0 ) { 188 nextfip->lfi_scope = LDAP_SCOPE_BASE; 189 } else { 190 ldap_free_strarray( tok ); 191 ldap_getfilter_free( lfdp ); 192 return( NULL ); 193 } 194 NSLDAPI_FREE( tok[ 2 ] ); 195 tok[ 2 ] = NULL; 196 } else { 197 nextfip->lfi_scope = LDAP_SCOPE_SUBTREE; /* default */ 198 } 199 nextfip->lfi_isexact = ( strchr( tok[ 0 ], '*' ) == NULL && 200 strchr( tok[ 0 ], '~' ) == NULL ); 201 NSLDAPI_FREE( tok ); 202 } 203 break; 204 205 default: 206 ldap_free_strarray( tok ); 207 ldap_getfilter_free( lfdp ); 208 return( NULL ); 209 } 210 } 211 212 if ( tag != NULL ) { 213 NSLDAPI_FREE( tag ); 214 } 215 216 return( lfdp ); 217 } 218 219 220 int 221 LDAP_CALL 222 ldap_set_filter_additions( LDAPFiltDesc *lfdp, char *prefix, char *suffix ) 223 { 224 if ( lfdp == NULL ) { 225 return( LDAP_PARAM_ERROR ); 226 } 227 228 if ( lfdp->lfd_filtprefix != NULL ) { 229 NSLDAPI_FREE( lfdp->lfd_filtprefix ); 230 } 231 lfdp->lfd_filtprefix = ( prefix == NULL ) ? NULL : nsldapi_strdup( prefix ); 232 233 if ( lfdp->lfd_filtsuffix != NULL ) { 234 NSLDAPI_FREE( lfdp->lfd_filtsuffix ); 235 } 236 lfdp->lfd_filtsuffix = ( suffix == NULL ) ? NULL : nsldapi_strdup( suffix ); 237 238 return( LDAP_SUCCESS ); 239 } 240 241 242 /* 243 * ldap_setfilteraffixes() is deprecated -- use ldap_set_filter_additions() 244 */ 245 void 246 LDAP_CALL 247 ldap_setfilteraffixes( LDAPFiltDesc *lfdp, char *prefix, char *suffix ) 248 { 249 (void)ldap_set_filter_additions( lfdp, prefix, suffix ); 250 } 251 252 253 LDAPFiltInfo * 254 LDAP_CALL 255 ldap_getfirstfilter( LDAPFiltDesc *lfdp, char *tagpat, char *value ) 256 { 257 LDAPFiltList *flp; 258 259 if ( lfdp == NULL || tagpat == NULL || value == NULL ) { 260 return( NULL ); /* punt */ 261 } 262 263 if ( lfdp->lfd_curvalcopy != NULL ) { 264 NSLDAPI_FREE( lfdp->lfd_curvalcopy ); 265 NSLDAPI_FREE( lfdp->lfd_curvalwords ); 266 } 267 268 lfdp->lfd_curval = value; 269 lfdp->lfd_curfip = NULL; 270 271 for ( flp = lfdp->lfd_filtlist; flp != NULL; flp = flp->lfl_next ) { 272 if ( re_comp( tagpat ) == NULL && re_exec( flp->lfl_tag ) == 1 273 && re_comp( flp->lfl_pattern ) == NULL 274 && re_exec( lfdp->lfd_curval ) == 1 ) { 275 lfdp->lfd_curfip = flp->lfl_ilist; 276 break; 277 } 278 } 279 280 if ( lfdp->lfd_curfip == NULL ) { 281 return( NULL ); 282 } 283 284 if (( lfdp->lfd_curvalcopy = nsldapi_strdup( value )) == NULL ) { 285 return( NULL ); 286 } 287 288 if ( break_into_words( lfdp->lfd_curvalcopy, flp->lfl_delims, 289 &lfdp->lfd_curvalwords ) < 0 ) { 290 NSLDAPI_FREE( lfdp->lfd_curvalwords ); 291 lfdp->lfd_curvalwords = NULL; 292 return( NULL ); 293 } 294 295 return( ldap_getnextfilter( lfdp )); 296 } 297 298 299 LDAPFiltInfo * 300 LDAP_CALL 301 ldap_getnextfilter( LDAPFiltDesc *lfdp ) 302 { 303 LDAPFiltInfo *fip; 304 305 if ( lfdp == NULL || ( fip = lfdp->lfd_curfip ) == NULL ) { 306 return( NULL ); 307 } 308 309 lfdp->lfd_curfip = fip->lfi_next; 310 311 ldap_build_filter( lfdp->lfd_filter, LDAP_FILT_MAXSIZ, fip->lfi_filter, 312 lfdp->lfd_filtprefix, lfdp->lfd_filtsuffix, NULL, 313 lfdp->lfd_curval, lfdp->lfd_curvalwords ); 314 lfdp->lfd_retfi.lfi_filter = lfdp->lfd_filter; 315 lfdp->lfd_retfi.lfi_desc = fip->lfi_desc; 316 lfdp->lfd_retfi.lfi_scope = fip->lfi_scope; 317 lfdp->lfd_retfi.lfi_isexact = fip->lfi_isexact; 318 319 return( &lfdp->lfd_retfi ); 320 } 321 322 323 static char* 324 filter_add_strn( char *f, char *flimit, char *v, size_t vlen ) 325 /* Copy v into f. If flimit is too small, return NULL; 326 * otherwise return (f + vlen). 327 */ 328 { 329 auto size_t flen = flimit - f; 330 if ( vlen > flen ) { /* flimit is too small */ 331 if ( flen > 0 ) SAFEMEMCPY( f, v, flen ); 332 return NULL; 333 } 334 if ( vlen > 0 ) SAFEMEMCPY( f, v, vlen ); 335 return f + vlen; 336 } 337 338 static char* 339 filter_add_value( char *f, char *flimit, char *v, int escape_all ) 340 /* Copy v into f, but with parentheses escaped. But only escape * and \ 341 * if escape_all is non-zero so that either "*" or "\2a" can be used in 342 * v, with different meanings. 343 * If flimit is too small, return NULL; otherwise 344 * return (f + the number of bytes copied). 345 */ 346 { 347 auto char x[4]; 348 auto size_t slen; 349 while ( f && *v ) { 350 switch ( *v ) { 351 case '*': 352 if ( escape_all ) { 353 f = filter_add_strn( f, flimit, "\\2a", 3 ); 354 v++; 355 } else { 356 if ( f < flimit ) { 357 *f++ = *v++; 358 } else { 359 f = NULL; /* overflow */ 360 } 361 } 362 break; 363 364 case '(': 365 case ')': 366 sprintf( x, "\\%02x", (unsigned)*v ); 367 f = filter_add_strn( f, flimit, x, 3 ); 368 v++; 369 break; 370 371 case '\\': 372 if ( escape_all ) { 373 f = filter_add_strn( f, flimit, "\\5c", 3 ); 374 v++; 375 } else { 376 slen = (ldap_utf8isxdigit( v+1 ) && 377 ldap_utf8isxdigit( v+2 )) ? 3 : (v[1] ? 2 : 1); 378 f = filter_add_strn( f, flimit, v, slen ); 379 v += slen; 380 } 381 break; 382 383 default: 384 if ( f < flimit ) { 385 *f++ = *v++; 386 } else { 387 f = NULL; /* overflow */ 388 } 389 break; 390 } 391 } 392 return f; 393 } 394 395 int 396 LDAP_CALL 397 ldap_create_filter( char *filtbuf, unsigned long buflen, char *pattern, 398 char *prefix, char *suffix, char *attr, char *value, char **valwords ) 399 { 400 char *p, *f, *flimit; 401 int i, wordcount, wordnum, endwordnum, escape_all; 402 403 /* 404 * there is some confusion on what to create for a filter if 405 * attr or value are null pointers. For now we just leave them 406 * as TO BE DEALT with 407 */ 408 409 if ( filtbuf == NULL || buflen == 0 || pattern == NULL ){ 410 return( LDAP_PARAM_ERROR ); 411 } 412 413 if ( valwords == NULL ) { 414 wordcount = 0; 415 } else { 416 for ( wordcount = 0; valwords[ wordcount ] != NULL; ++wordcount ) { 417 ; 418 } 419 } 420 421 f = filtbuf; 422 flimit = filtbuf + buflen - 1; 423 424 if ( prefix != NULL ) { 425 f = filter_add_strn( f, flimit, prefix, strlen( prefix )); 426 } 427 428 for ( p = pattern; f != NULL && *p != '\0'; ++p ) { 429 if ( *p == '%' ) { 430 ++p; 431 if ( *p == 'v' || *p == 'e' ) { 432 escape_all = ( *p == 'e' ); 433 if ( ldap_utf8isdigit( p+1 )) { 434 ++p; 435 wordnum = *p - '1'; 436 if ( *(p+1) == '-' ) { 437 ++p; 438 if ( ldap_utf8isdigit( p+1 )) { 439 ++p; 440 endwordnum = *p - '1'; /* e.g., "%v2-4" */ 441 if ( endwordnum > wordcount - 1 ) { 442 endwordnum = wordcount - 1; 443 } 444 } else { 445 endwordnum = wordcount - 1; /* e.g., "%v2-" */ 446 } 447 } else { 448 endwordnum = wordnum; /* e.g., "%v2" */ 449 } 450 451 if ( wordcount > 0 ) { 452 for ( i = wordnum; i <= endwordnum; ++i ) { 453 if ( i > wordnum ) { /* add blank btw words */ 454 f = filter_add_strn( f, flimit, " ", 1 ); 455 if ( f == NULL ) break; 456 } 457 f = filter_add_value( f, flimit, valwords[ i ], 458 escape_all ); 459 if ( f == NULL ) break; 460 } 461 } 462 } else if ( *(p+1) == '$' ) { 463 ++p; 464 if ( wordcount > 0 ) { 465 wordnum = wordcount - 1; 466 f = filter_add_value( f, flimit, 467 valwords[ wordnum ], escape_all ); 468 } 469 } else if ( value != NULL ) { 470 f = filter_add_value( f, flimit, value, escape_all ); 471 } 472 } else if ( *p == 'a' && attr != NULL ) { 473 f = filter_add_strn( f, flimit, attr, strlen( attr )); 474 } else { 475 *f++ = *p; 476 } 477 } else { 478 *f++ = *p; 479 } 480 if ( f > flimit ) { /* overflow */ 481 f = NULL; 482 } 483 } 484 485 if ( suffix != NULL && f != NULL) { 486 f = filter_add_strn( f, flimit, suffix, strlen( suffix )); 487 } 488 489 if ( f == NULL ) { 490 *flimit = '\0'; 491 return( LDAP_SIZELIMIT_EXCEEDED ); 492 } 493 *f = '\0'; 494 return( LDAP_SUCCESS ); 495 } 496 497 498 /* 499 * ldap_build_filter() is deprecated -- use ldap_create_filter() instead 500 */ 501 void 502 LDAP_CALL 503 ldap_build_filter( char *filtbuf, size_t buflen, char *pattern, 504 char *prefix, char *suffix, char *attr, char *value, char **valwords ) 505 { 506 (void)ldap_create_filter( filtbuf, buflen, pattern, prefix, suffix, attr, 507 value, valwords ); 508 } 509 510 511 static int 512 break_into_words( char *str, char *delims, char ***wordsp ) 513 { 514 char *word, **words; 515 int count; 516 char *lasts; 517 518 if (( words = (char **)NSLDAPI_CALLOC( 1, sizeof( char * ))) == NULL ) { 519 return( -1 ); 520 } 521 count = 0; 522 words[ count ] = NULL; 523 524 word = ldap_utf8strtok_r( str, delims, &lasts ); 525 while ( word != NULL ) { 526 if (( words = (char **)NSLDAPI_REALLOC( words, 527 ( count + 2 ) * sizeof( char * ))) == NULL ) { 528 return( -1 ); 529 } 530 531 words[ count ] = word; 532 words[ ++count ] = NULL; 533 word = ldap_utf8strtok_r( NULL, delims, &lasts ); 534 } 535 536 *wordsp = words; 537 return( count ); 538 } 539