1/* $OpenLDAP$ */ 2/* This work is part of OpenLDAP Software <http://www.openldap.org/>. 3 * 4 * Copyright 2000-2011 The OpenLDAP Foundation. 5 * All rights reserved. 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted only as authorized by the OpenLDAP 9 * Public License. 10 * 11 * A copy of this license is available in the file LICENSE in the 12 * top-level directory of the distribution or, alternatively, at 13 * <http://www.OpenLDAP.org/license.html>. 14 */ 15/* ACKNOWLEDGEMENT: 16 * This work was initially developed by Pierangelo Masarati for 17 * inclusion in OpenLDAP Software. 18 */ 19 20#include <portable.h> 21 22#include "rewrite-int.h" 23 24/* 25 * Compares two struct rewrite_context based on the name; 26 * used by avl stuff 27 */ 28static int 29rewrite_context_cmp( 30 const void *c1, 31 const void *c2 32) 33{ 34 const struct rewrite_context *lc1, *lc2; 35 36 lc1 = (const struct rewrite_context *)c1; 37 lc2 = (const struct rewrite_context *)c2; 38 39 assert( c1 != NULL ); 40 assert( c2 != NULL ); 41 assert( lc1->lc_name != NULL ); 42 assert( lc2->lc_name != NULL ); 43 44 return strcasecmp( lc1->lc_name, lc2->lc_name ); 45} 46 47/* 48 * Returns -1 in case a duplicate struct rewrite_context 49 * has been inserted; used by avl stuff 50 */ 51static int 52rewrite_context_dup( 53 void *c1, 54 void *c2 55 ) 56{ 57 struct rewrite_context *lc1, *lc2; 58 59 lc1 = (struct rewrite_context *)c1; 60 lc2 = (struct rewrite_context *)c2; 61 62 assert( c1 != NULL ); 63 assert( c2 != NULL ); 64 assert( lc1->lc_name != NULL ); 65 assert( lc2->lc_name != NULL ); 66 67 return( strcasecmp( lc1->lc_name, lc2->lc_name) == 0 ? -1 : 0 ); 68} 69 70/* 71 * Finds the context named rewriteContext in the context tree 72 */ 73struct rewrite_context * 74rewrite_context_find( 75 struct rewrite_info *info, 76 const char *rewriteContext 77) 78{ 79 struct rewrite_context *context, c; 80 81 assert( info != NULL ); 82 assert( rewriteContext != NULL ); 83 84 /* 85 * Fetches the required rewrite context 86 */ 87 c.lc_name = (char *)rewriteContext; 88 context = (struct rewrite_context *)avl_find( info->li_context, 89 (caddr_t)&c, rewrite_context_cmp ); 90 if ( context == NULL ) { 91 return NULL; 92 } 93 94 /* 95 * De-aliases the context if required 96 */ 97 if ( context->lc_alias ) { 98 return context->lc_alias; 99 } 100 101 return context; 102} 103 104/* 105 * Creates a new context called rewriteContext and stores in into the tree 106 */ 107struct rewrite_context * 108rewrite_context_create( 109 struct rewrite_info *info, 110 const char *rewriteContext 111) 112{ 113 struct rewrite_context *context; 114 int rc; 115 116 assert( info != NULL ); 117 assert( rewriteContext != NULL ); 118 119 context = calloc( sizeof( struct rewrite_context ), 1 ); 120 if ( context == NULL ) { 121 return NULL; 122 } 123 124 /* 125 * Context name 126 */ 127 context->lc_name = strdup( rewriteContext ); 128 if ( context->lc_name == NULL ) { 129 free( context ); 130 return NULL; 131 } 132 133 /* 134 * The first, empty rule 135 */ 136 context->lc_rule = calloc( sizeof( struct rewrite_rule ), 1 ); 137 if ( context->lc_rule == NULL ) { 138 free( context->lc_name ); 139 free( context ); 140 return NULL; 141 } 142 memset( context->lc_rule, 0, sizeof( struct rewrite_rule ) ); 143 144 /* 145 * Add context to tree 146 */ 147 rc = avl_insert( &info->li_context, (caddr_t)context, 148 rewrite_context_cmp, rewrite_context_dup ); 149 if ( rc == -1 ) { 150 free( context->lc_rule ); 151 free( context->lc_name ); 152 free( context ); 153 return NULL; 154 } 155 156 return context; 157} 158 159/* 160 * Finds the next rule according to a goto action statement, 161 * or null in case of error. 162 * Helper for rewrite_context_apply. 163 */ 164static struct rewrite_rule * 165rewrite_action_goto( 166 struct rewrite_action *action, 167 struct rewrite_rule *rule 168) 169{ 170 int n; 171 172 assert( action != NULL ); 173 assert( action->la_args != NULL ); 174 assert( rule != NULL ); 175 176 n = ((int *)action->la_args)[ 0 ]; 177 178 if ( n > 0 ) { 179 for ( ; n > 1 && rule != NULL ; n-- ) { 180 rule = rule->lr_next; 181 } 182 } else if ( n <= 0 ) { 183 for ( ; n < 1 && rule != NULL ; n++ ) { 184 rule = rule->lr_prev; 185 } 186 } 187 188 return rule; 189} 190 191/* 192 * Rewrites string according to context; may return: 193 * OK: fine; if *result != NULL rule matched and rewrite succeeded. 194 * STOP: fine, rule matched; stop processing following rules 195 * UNWILL: rule matched; force 'unwilling to perform' 196 */ 197int 198rewrite_context_apply( 199 struct rewrite_info *info, 200 struct rewrite_op *op, 201 struct rewrite_context *context, 202 const char *string, 203 char **result 204) 205{ 206 struct rewrite_rule *rule; 207 char *s, *res = NULL; 208 int return_code = REWRITE_REGEXEC_OK; 209 210 assert( info != NULL ); 211 assert( op != NULL ); 212 assert( context != NULL ); 213 assert( context->lc_rule != NULL ); 214 assert( string != NULL ); 215 assert( result != NULL ); 216 217 op->lo_depth++; 218 219 Debug( LDAP_DEBUG_TRACE, "==> rewrite_context_apply" 220 " [depth=%d] string='%s'\n", 221 op->lo_depth, string, 0 ); 222 assert( op->lo_depth > 0 ); 223 224 s = (char *)string; 225 226 for ( rule = context->lc_rule->lr_next; 227 rule != NULL && op->lo_num_passes < info->li_max_passes; 228 rule = rule->lr_next, op->lo_num_passes++ ) { 229 int rc; 230 231 /* 232 * Apply a single rule 233 */ 234 rc = rewrite_rule_apply( info, op, rule, s, &res ); 235 236 /* 237 * A rule may return: 238 * OK with result != NULL if matched 239 * ERR if anything was wrong 240 * UNWILLING if the server should drop the request 241 * the latter case in honored immediately; 242 * the other two may require some special actions to take 243 * place. 244 */ 245 switch ( rc ) { 246 247 case REWRITE_REGEXEC_ERR: 248 Debug( LDAP_DEBUG_ANY, "==> rewrite_context_apply" 249 " error ...\n", 0, 0, 0); 250 251 /* 252 * Checks for special actions to be taken 253 * in case of error ... 254 */ 255 if ( rule->lr_action != NULL ) { 256 struct rewrite_action *action; 257 int do_continue = 0; 258 259 for ( action = rule->lr_action; 260 action != NULL; 261 action = action->la_next ) { 262 switch ( action->la_type ) { 263 264 /* 265 * This action takes precedence 266 * over the others in case of failure 267 */ 268 case REWRITE_ACTION_IGNORE_ERR: 269 Debug( LDAP_DEBUG_ANY, 270 "==> rewrite_context_apply" 271 " ignoring error ...\n", 0, 0, 0 ); 272 do_continue = 1; 273 break; 274 275 /* 276 * Goto is honored only if it comes 277 * after ignore error 278 */ 279 case REWRITE_ACTION_GOTO: 280 if ( do_continue ) { 281 rule = rewrite_action_goto( action, rule ); 282 if ( rule == NULL ) { 283 return_code = REWRITE_REGEXEC_ERR; 284 goto rc_end_of_context; 285 } 286 } 287 break; 288 289 /* 290 * Other actions are ignored 291 */ 292 default: 293 break; 294 } 295 } 296 297 if ( do_continue ) { 298 if ( rule->lr_next == NULL ) { 299 res = s; 300 } 301 goto rc_continue; 302 } 303 } 304 305 /* 306 * Default behavior is to bail out ... 307 */ 308 return_code = REWRITE_REGEXEC_ERR; 309 goto rc_end_of_context; 310 311 /* 312 * OK means there were no errors or special return codes; 313 * if res is defined, it means the rule matched and we 314 * got a sucessful rewriting 315 */ 316 case REWRITE_REGEXEC_OK: 317 318 /* 319 * It matched! Check for actions ... 320 */ 321 if ( res != NULL ) { 322 struct rewrite_action *action; 323 324 if ( s != string && s != res ) { 325 free( s ); 326 } 327 s = res; 328 329 for ( action = rule->lr_action; 330 action != NULL; 331 action = action->la_next ) { 332 333 switch ( action->la_type ) { 334 335 /* 336 * This ends the rewrite context 337 * successfully 338 */ 339 case REWRITE_ACTION_STOP: 340 goto rc_end_of_context; 341 342 /* 343 * This instructs the server to return 344 * an `unwilling to perform' error 345 * message 346 */ 347 case REWRITE_ACTION_UNWILLING: 348 return_code = REWRITE_REGEXEC_UNWILLING; 349 goto rc_end_of_context; 350 351 /* 352 * This causes the processing to 353 * jump n rules back and forth 354 */ 355 case REWRITE_ACTION_GOTO: 356 rule = rewrite_action_goto( action, rule ); 357 if ( rule == NULL ) { 358 return_code = REWRITE_REGEXEC_ERR; 359 goto rc_end_of_context; 360 } 361 break; 362 363 /* 364 * This ends the rewrite context 365 * and returns a user-defined 366 * error code 367 */ 368 case REWRITE_ACTION_USER: 369 return_code = ((int *)action->la_args)[ 0 ]; 370 goto rc_end_of_context; 371 372 default: 373 /* ... */ 374 break; 375 } 376 } 377 378 /* 379 * If result was OK and string didn't match, 380 * in case of last rule we need to set the 381 * result back to the string 382 */ 383 } else if ( rule->lr_next == NULL ) { 384 res = s; 385 } 386 387 break; 388 389 /* 390 * A STOP has propagated ... 391 */ 392 case REWRITE_REGEXEC_STOP: 393 goto rc_end_of_context; 394 395 /* 396 * This will instruct the server to return 397 * an `unwilling to perform' error message 398 */ 399 case REWRITE_REGEXEC_UNWILLING: 400 return_code = REWRITE_REGEXEC_UNWILLING; 401 goto rc_end_of_context; 402 403 /* 404 * A user-defined error code has propagated ... 405 */ 406 default: 407 assert( rc >= REWRITE_REGEXEC_USER ); 408 goto rc_end_of_context; 409 410 } 411 412rc_continue:; /* sent here by actions that require to continue */ 413 414 } 415 416rc_end_of_context:; 417 *result = res; 418 419 Debug( LDAP_DEBUG_TRACE, "==> rewrite_context_apply" 420 " [depth=%d] res={%d,'%s'}\n", 421 op->lo_depth, return_code, ( res ? res : "NULL" ) ); 422 423 assert( op->lo_depth > 0 ); 424 op->lo_depth--; 425 426 return return_code; 427} 428 429void 430rewrite_context_free( 431 void *tmp 432) 433{ 434 struct rewrite_context *context = (struct rewrite_context *)tmp; 435 436 assert( tmp != NULL ); 437 438 rewrite_context_destroy( &context ); 439} 440 441int 442rewrite_context_destroy( 443 struct rewrite_context **pcontext 444) 445{ 446 struct rewrite_context *context; 447 struct rewrite_rule *r; 448 449 assert( pcontext != NULL ); 450 assert( *pcontext != NULL ); 451 452 context = *pcontext; 453 454 assert( context->lc_rule != NULL ); 455 456 for ( r = context->lc_rule->lr_next; r; ) { 457 struct rewrite_rule *cr = r; 458 459 r = r->lr_next; 460 rewrite_rule_destroy( &cr ); 461 } 462 463 free( context->lc_rule ); 464 context->lc_rule = NULL; 465 466 assert( context->lc_name != NULL ); 467 free( context->lc_name ); 468 context->lc_name = NULL; 469 470 free( context ); 471 *pcontext = NULL; 472 473 return 0; 474} 475