1 /*
   2  * Copyright (c) 1999, 2016, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.  Oracle designates this
   8  * particular file as subject to the "Classpath" exception as provided
   9  * by Oracle in the LICENSE file that accompanied this code.
  10  *
  11  * This code is distributed in the hope that it will be useful, but WITHOUT
  12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  14  * version 2 for more details (a copy is included in the LICENSE file that
  15  * accompanied this code).
  16  *
  17  * You should have received a copy of the GNU General Public License version
  18  * 2 along with this work; if not, write to the Free Software Foundation,
  19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  20  *
  21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  22  * or visit www.oracle.com if you need additional information or have any
  23  * questions.
  24  */
  25 
  26 package com.sun.jndi.ldap;
  27 
  28 import javax.naming.*;
  29 import javax.naming.directory.*;
  30 import javax.naming.spi.*;
  31 import javax.naming.ldap.*;
  32 
  33 import java.util.Hashtable;
  34 import java.util.StringTokenizer;
  35 import com.sun.jndi.toolkit.dir.SearchFilter;
  36 
  37 /**
  38  * A context for handling referrals.
  39  *
  40  * @author Vincent Ryan
  41  */
  42 final class LdapReferralContext implements DirContext, LdapContext {
  43 
  44     private DirContext refCtx = null;
  45     private Name urlName = null;   // override the supplied name
  46     private String urlAttrs = null;  // override attributes
  47     private String urlScope = null;  // override scope
  48     private String urlFilter = null; // override filter
  49 
  50     private LdapReferralException refEx = null;
  51     private boolean skipThisReferral = false;
  52     private int hopCount = 1;
  53     private NamingException previousEx = null;
  54 
  55     @SuppressWarnings("unchecked") // clone()
  56     LdapReferralContext(LdapReferralException ex,
  57         Hashtable<?,?> env,
  58         Control[] connCtls,
  59         Control[] reqCtls,
  60         String nextName,
  61         boolean skipThisReferral,
  62         int handleReferrals) throws NamingException {
  63 
  64         refEx = ex;
  65 
  66         if (this.skipThisReferral = skipThisReferral) {
  67             return; // don't create a DirContext for this referral
  68         }
  69 
  70         String referral;
  71 
  72         // Make copies of environment and connect controls for our own use.
  73         if (env != null) {
  74             env = (Hashtable<?,?>) env.clone();
  75             // Remove old connect controls from environment, unless we have new
  76             // ones that will override them anyway.
  77             if (connCtls == null) {
  78                 env.remove(LdapCtx.BIND_CONTROLS);
  79             }
  80         } else if (connCtls != null) {
  81             env = new Hashtable<String, Control[]>(5);
  82         }
  83         if (connCtls != null) {
  84             Control[] copiedCtls = new Control[connCtls.length];
  85             System.arraycopy(connCtls, 0, copiedCtls, 0, connCtls.length);
  86             // Add copied controls to environment, replacing any old ones.
  87             ((Hashtable<? super String, ? super Control[]>)env)
  88                     .put(LdapCtx.BIND_CONTROLS, copiedCtls);
  89         }
  90 
  91         while (true) {
  92             try {
  93                 referral = refEx.getNextReferral();
  94                 if (referral == null) {
  95                     if (previousEx != null) {
  96                         throw (NamingException)(previousEx.fillInStackTrace());
  97                     } else {
  98                         throw new NamingException(
  99                             "Illegal encoding: referral is empty");
 100                     }
 101                 }
 102 
 103             } catch (LdapReferralException e) {
 104 
 105                 if (handleReferrals == LdapClient.LDAP_REF_THROW) {
 106                     throw e;
 107                 } else {
 108                     refEx = e;
 109                     continue;
 110                 }
 111             }
 112 
 113             // Create a Reference containing the referral URL.
 114             Reference ref = new Reference("javax.naming.directory.DirContext",
 115                                           new StringRefAddr("URL", referral));
 116 
 117             Object obj;
 118             try {
 119                 obj = NamingManager.getObjectInstance(ref, null, null, env);
 120 
 121             } catch (NamingException e) {
 122 
 123                 if (handleReferrals == LdapClient.LDAP_REF_THROW) {
 124                     throw e;
 125                 }
 126 
 127                 // mask the exception and save it for later
 128                 previousEx = e;
 129 
 130                 // follow another referral
 131                 continue;
 132 
 133             } catch (Exception e) {
 134                 NamingException e2 =
 135                     new NamingException(
 136                         "problem generating object using object factory");
 137                 e2.setRootCause(e);
 138                 throw e2;
 139             }
 140             if (obj instanceof DirContext) {
 141                 refCtx = (DirContext)obj;
 142                 if (refCtx instanceof LdapContext && reqCtls != null) {
 143                     ((LdapContext)refCtx).setRequestControls(reqCtls);
 144                 }
 145                 initDefaults(referral, nextName);
 146 
 147                 break;
 148             } else {
 149                 NamingException ne = new NotContextException(
 150                     "Cannot create context for: " + referral);
 151                 ne.setRemainingName((new CompositeName()).add(nextName));
 152                 throw ne;
 153             }
 154         }
 155     }
 156 
 157     private void initDefaults(String referral, String nextName)
 158         throws NamingException {
 159         String urlString;
 160         try {
 161             // parse URL
 162             LdapURL url = new LdapURL(referral);
 163             urlString = url.getDN();
 164             urlAttrs = url.getAttributes();
 165             urlScope = url.getScope();
 166             urlFilter = url.getFilter();
 167 
 168         } catch (NamingException e) {
 169             // Not an LDAP URL; use original URL
 170             urlString = referral;
 171             urlAttrs = urlScope = urlFilter = null;
 172         }
 173 
 174         // reuse original name if URL DN is absent
 175         if (urlString == null) {
 176             urlString = nextName;
 177         } else {
 178             // concatenate with remaining name if URL DN is present
 179             urlString = "";
 180         }
 181 
 182         if (urlString == null) {
 183             urlName = null;
 184         } else {
 185             urlName = urlString.isEmpty() ? new CompositeName() :
 186                 new CompositeName().add(urlString);
 187         }
 188     }
 189 
 190 
 191     public void close() throws NamingException {
 192         if (refCtx != null) {
 193             refCtx.close();
 194             refCtx = null;
 195         }
 196         refEx = null;
 197     }
 198 
 199     void setHopCount(int hopCount) {
 200         this.hopCount = hopCount;
 201         if ((refCtx != null) && (refCtx instanceof LdapCtx)) {
 202             ((LdapCtx)refCtx).setHopCount(hopCount);
 203         }
 204     }
 205 
 206     public Object lookup(String name) throws NamingException {
 207         return lookup(toName(name));
 208     }
 209 
 210     public Object lookup(Name name) throws NamingException {
 211         if (skipThisReferral) {
 212             throw (NamingException)
 213                 ((refEx.appendUnprocessedReferrals(null)).fillInStackTrace());
 214         }
 215 
 216         return refCtx.lookup(overrideName(name));
 217     }
 218 
 219     public void bind(String name, Object obj) throws NamingException {
 220         bind(toName(name), obj);
 221     }
 222 
 223     public void bind(Name name, Object obj) throws NamingException {
 224         if (skipThisReferral) {
 225             throw (NamingException)
 226                 ((refEx.appendUnprocessedReferrals(null)).fillInStackTrace());
 227         }
 228 
 229         refCtx.bind(overrideName(name), obj);
 230     }
 231 
 232     public void rebind(String name, Object obj) throws NamingException {
 233         rebind(toName(name), obj);
 234     }
 235 
 236     public void rebind(Name name, Object obj) throws NamingException {
 237         if (skipThisReferral) {
 238             throw (NamingException)
 239                 ((refEx.appendUnprocessedReferrals(null)).fillInStackTrace());
 240         }
 241 
 242         refCtx.rebind(overrideName(name), obj);
 243     }
 244 
 245     public void unbind(String name) throws NamingException {
 246         unbind(toName(name));
 247     }
 248 
 249     public void unbind(Name name) throws NamingException {
 250         if (skipThisReferral) {
 251             throw (NamingException)
 252                 ((refEx.appendUnprocessedReferrals(null)).fillInStackTrace());
 253         }
 254 
 255         refCtx.unbind(overrideName(name));
 256     }
 257 
 258     public void rename(String oldName, String newName) throws NamingException {
 259         rename(toName(oldName), toName(newName));
 260     }
 261 
 262     public void rename(Name oldName, Name newName) throws NamingException {
 263         if (skipThisReferral) {
 264             throw (NamingException)
 265                 ((refEx.appendUnprocessedReferrals(null)).fillInStackTrace());
 266         }
 267 
 268         refCtx.rename(overrideName(oldName), toName(refEx.getNewRdn()));
 269     }
 270 
 271     public NamingEnumeration<NameClassPair> list(String name) throws NamingException {
 272         return list(toName(name));
 273     }
 274 
 275     @SuppressWarnings("unchecked")
 276     public NamingEnumeration<NameClassPair> list(Name name) throws NamingException {
 277         if (skipThisReferral) {
 278             throw (NamingException)
 279                 ((refEx.appendUnprocessedReferrals(null)).fillInStackTrace());
 280         }
 281         try {
 282             NamingEnumeration<NameClassPair> ne = null;
 283 
 284             if (urlScope != null && urlScope.equals("base")) {
 285                 SearchControls cons = new SearchControls();
 286                 cons.setReturningObjFlag(true);
 287                 cons.setSearchScope(SearchControls.OBJECT_SCOPE);
 288 
 289                 ne = (NamingEnumeration)
 290                         refCtx.search(overrideName(name), "(objectclass=*)", cons);
 291 
 292             } else {
 293                 ne = refCtx.list(overrideName(name));
 294             }
 295 
 296             refEx.setNameResolved(true);
 297 
 298             // append (referrals from) the exception that generated this
 299             // context to the new search results, so that referral processing
 300             // can continue
 301             ((ReferralEnumeration)ne).appendUnprocessedReferrals(refEx);
 302 
 303             return (ne);
 304 
 305         } catch (LdapReferralException e) {
 306 
 307             // append (referrals from) the exception that generated this
 308             // context to the new exception, so that referral processing
 309             // can continue
 310 
 311             e.appendUnprocessedReferrals(refEx);
 312             throw (NamingException)(e.fillInStackTrace());
 313 
 314         } catch (NamingException e) {
 315 
 316             // record the exception if there are no remaining referrals
 317             if ((refEx != null) && (! refEx.hasMoreReferrals())) {
 318                 refEx.setNamingException(e);
 319             }
 320             if ((refEx != null) &&
 321                 (refEx.hasMoreReferrals() ||
 322                  refEx.hasMoreReferralExceptions())) {
 323                 throw (NamingException)
 324                     ((refEx.appendUnprocessedReferrals(null)).fillInStackTrace());
 325             } else {
 326                 throw e;
 327             }
 328         }
 329     }
 330 
 331     public NamingEnumeration<Binding> listBindings(String name) throws
 332             NamingException {
 333         return listBindings(toName(name));
 334     }
 335 
 336     @SuppressWarnings("unchecked")
 337     public NamingEnumeration<Binding> listBindings(Name name) throws
 338             NamingException {
 339         if (skipThisReferral) {
 340             throw (NamingException)
 341                 ((refEx.appendUnprocessedReferrals(null)).fillInStackTrace());
 342         }
 343 
 344         try {
 345             NamingEnumeration<Binding> be = null;
 346 
 347             if (urlScope != null && urlScope.equals("base")) {
 348                 SearchControls cons = new SearchControls();
 349                 cons.setReturningObjFlag(true);
 350                 cons.setSearchScope(SearchControls.OBJECT_SCOPE);
 351 
 352                 be = (NamingEnumeration)refCtx.search(overrideName(name),
 353                         "(objectclass=*)", cons);
 354 
 355             } else {
 356                 be = refCtx.listBindings(overrideName(name));
 357             }
 358 
 359             refEx.setNameResolved(true);
 360 
 361             // append (referrals from) the exception that generated this
 362             // context to the new search results, so that referral processing
 363             // can continue
 364             ((ReferralEnumeration<Binding>)be).appendUnprocessedReferrals(refEx);
 365 
 366             return (be);
 367 
 368         } catch (LdapReferralException e) {
 369 
 370             // append (referrals from) the exception that generated this
 371             // context to the new exception, so that referral processing
 372             // can continue
 373 
 374             e.appendUnprocessedReferrals(refEx);
 375             throw (NamingException)(e.fillInStackTrace());
 376 
 377         } catch (NamingException e) {
 378 
 379             // record the exception if there are no remaining referrals
 380             if ((refEx != null) && (! refEx.hasMoreReferrals())) {
 381                 refEx.setNamingException(e);
 382             }
 383             if ((refEx != null) &&
 384                 (refEx.hasMoreReferrals() ||
 385                  refEx.hasMoreReferralExceptions())) {
 386                 throw (NamingException)
 387                     ((refEx.appendUnprocessedReferrals(null)).fillInStackTrace());
 388             } else {
 389                 throw e;
 390             }
 391         }
 392     }
 393 
 394     public void destroySubcontext(String name) throws NamingException {
 395         destroySubcontext(toName(name));
 396     }
 397 
 398     public void destroySubcontext(Name name) throws NamingException {
 399         if (skipThisReferral) {
 400             throw (NamingException)
 401                 ((refEx.appendUnprocessedReferrals(null)).fillInStackTrace());
 402         }
 403 
 404         refCtx.destroySubcontext(overrideName(name));
 405     }
 406 
 407     public Context createSubcontext(String name) throws NamingException {
 408         return createSubcontext(toName(name));
 409     }
 410 
 411     public Context createSubcontext(Name name) throws NamingException {
 412         if (skipThisReferral) {
 413             throw (NamingException)
 414                 ((refEx.appendUnprocessedReferrals(null)).fillInStackTrace());
 415         }
 416 
 417         return refCtx.createSubcontext(overrideName(name));
 418     }
 419 
 420     public Object lookupLink(String name) throws NamingException {
 421         return lookupLink(toName(name));
 422     }
 423 
 424     public Object lookupLink(Name name) throws NamingException {
 425         if (skipThisReferral) {
 426             throw (NamingException)
 427                 ((refEx.appendUnprocessedReferrals(null)).fillInStackTrace());
 428         }
 429 
 430         return refCtx.lookupLink(overrideName(name));
 431     }
 432 
 433     public NameParser getNameParser(String name) throws NamingException {
 434         return getNameParser(toName(name));
 435     }
 436 
 437     public NameParser getNameParser(Name name) throws NamingException {
 438         if (skipThisReferral) {
 439             throw (NamingException)
 440                 ((refEx.appendUnprocessedReferrals(null)).fillInStackTrace());
 441         }
 442 
 443         return refCtx.getNameParser(overrideName(name));
 444     }
 445 
 446     public String composeName(String name, String prefix)
 447             throws NamingException {
 448                 return composeName(toName(name), toName(prefix)).toString();
 449     }
 450 
 451     public Name composeName(Name name, Name prefix) throws NamingException {
 452         if (skipThisReferral) {
 453             throw (NamingException)
 454                 ((refEx.appendUnprocessedReferrals(null)).fillInStackTrace());
 455         }
 456         return refCtx.composeName(name, prefix);
 457     }
 458 
 459     public Object addToEnvironment(String propName, Object propVal)
 460             throws NamingException {
 461         if (skipThisReferral) {
 462             throw (NamingException)
 463                 ((refEx.appendUnprocessedReferrals(null)).fillInStackTrace());
 464         }
 465 
 466         return refCtx.addToEnvironment(propName, propVal);
 467     }
 468 
 469     public Object removeFromEnvironment(String propName)
 470             throws NamingException {
 471         if (skipThisReferral) {
 472             throw (NamingException)
 473                 ((refEx.appendUnprocessedReferrals(null)).fillInStackTrace());
 474         }
 475 
 476         return refCtx.removeFromEnvironment(propName);
 477     }
 478 
 479     public Hashtable<?,?> getEnvironment() throws NamingException {
 480         if (skipThisReferral) {
 481             throw (NamingException)
 482                 ((refEx.appendUnprocessedReferrals(null)).fillInStackTrace());
 483         }
 484 
 485         return refCtx.getEnvironment();
 486     }
 487 
 488     public Attributes getAttributes(String name) throws NamingException {
 489         return getAttributes(toName(name));
 490     }
 491 
 492     public Attributes getAttributes(Name name) throws NamingException {
 493         if (skipThisReferral) {
 494             throw (NamingException)
 495                 ((refEx.appendUnprocessedReferrals(null)).fillInStackTrace());
 496         }
 497 
 498         return refCtx.getAttributes(overrideName(name));
 499     }
 500 
 501     public Attributes getAttributes(String name, String[] attrIds)
 502             throws NamingException {
 503         return getAttributes(toName(name), attrIds);
 504     }
 505 
 506     public Attributes getAttributes(Name name, String[] attrIds)
 507             throws NamingException {
 508         if (skipThisReferral) {
 509             throw (NamingException)
 510                 ((refEx.appendUnprocessedReferrals(null)).fillInStackTrace());
 511         }
 512 
 513         return refCtx.getAttributes(overrideName(name), attrIds);
 514     }
 515 
 516     public void modifyAttributes(String name, int mod_op, Attributes attrs)
 517             throws NamingException {
 518         modifyAttributes(toName(name), mod_op, attrs);
 519     }
 520 
 521     public void modifyAttributes(Name name, int mod_op, Attributes attrs)
 522             throws NamingException {
 523         if (skipThisReferral) {
 524             throw (NamingException)
 525                 ((refEx.appendUnprocessedReferrals(null)).fillInStackTrace());
 526         }
 527 
 528         refCtx.modifyAttributes(overrideName(name), mod_op, attrs);
 529     }
 530 
 531     public void modifyAttributes(String name, ModificationItem[] mods)
 532             throws NamingException {
 533         modifyAttributes(toName(name), mods);
 534     }
 535 
 536     public void modifyAttributes(Name name, ModificationItem[] mods)
 537             throws NamingException {
 538         if (skipThisReferral) {
 539             throw (NamingException)
 540                 ((refEx.appendUnprocessedReferrals(null)).fillInStackTrace());
 541         }
 542 
 543         refCtx.modifyAttributes(overrideName(name), mods);
 544     }
 545 
 546     public void bind(String name, Object obj, Attributes attrs)
 547             throws NamingException {
 548         bind(toName(name), obj, attrs);
 549     }
 550 
 551     public void bind(Name name, Object obj, Attributes attrs)
 552             throws NamingException {
 553         if (skipThisReferral) {
 554             throw (NamingException)
 555                 ((refEx.appendUnprocessedReferrals(null)).fillInStackTrace());
 556         }
 557 
 558         refCtx.bind(overrideName(name), obj, attrs);
 559     }
 560 
 561     public void rebind(String name, Object obj, Attributes attrs)
 562             throws NamingException {
 563         rebind(toName(name), obj, attrs);
 564     }
 565 
 566     public void rebind(Name name, Object obj, Attributes attrs)
 567             throws NamingException {
 568         if (skipThisReferral) {
 569             throw (NamingException)
 570                 ((refEx.appendUnprocessedReferrals(null)).fillInStackTrace());
 571         }
 572 
 573         refCtx.rebind(overrideName(name), obj, attrs);
 574     }
 575 
 576     public DirContext createSubcontext(String name, Attributes attrs)
 577             throws NamingException {
 578         return createSubcontext(toName(name), attrs);
 579     }
 580 
 581     public DirContext createSubcontext(Name name, Attributes attrs)
 582             throws NamingException {
 583         if (skipThisReferral) {
 584             throw (NamingException)
 585                 ((refEx.appendUnprocessedReferrals(null)).fillInStackTrace());
 586         }
 587 
 588         return refCtx.createSubcontext(overrideName(name), attrs);
 589     }
 590 
 591     public DirContext getSchema(String name) throws NamingException {
 592         return getSchema(toName(name));
 593     }
 594 
 595     public DirContext getSchema(Name name) throws NamingException {
 596         if (skipThisReferral) {
 597             throw (NamingException)
 598                 ((refEx.appendUnprocessedReferrals(null)).fillInStackTrace());
 599         }
 600 
 601         return refCtx.getSchema(overrideName(name));
 602     }
 603 
 604     public DirContext getSchemaClassDefinition(String name)
 605             throws NamingException {
 606         return getSchemaClassDefinition(toName(name));
 607     }
 608 
 609     public DirContext getSchemaClassDefinition(Name name)
 610             throws NamingException {
 611         if (skipThisReferral) {
 612             throw (NamingException)
 613                 ((refEx.appendUnprocessedReferrals(null)).fillInStackTrace());
 614         }
 615 
 616       return refCtx.getSchemaClassDefinition(overrideName(name));
 617     }
 618 
 619     public NamingEnumeration<SearchResult> search(String name,
 620                                                   Attributes matchingAttributes)
 621             throws NamingException {
 622         return search(toName(name), SearchFilter.format(matchingAttributes),
 623             new SearchControls());
 624     }
 625 
 626     public NamingEnumeration<SearchResult> search(Name name,
 627                                                   Attributes matchingAttributes)
 628             throws NamingException {
 629         return search(name, SearchFilter.format(matchingAttributes),
 630             new SearchControls());
 631     }
 632 
 633     public NamingEnumeration<SearchResult> search(String name,
 634                                                   Attributes matchingAttributes,
 635                                                   String[] attributesToReturn)
 636             throws NamingException {
 637         SearchControls cons = new SearchControls();
 638         cons.setReturningAttributes(attributesToReturn);
 639 
 640         return search(toName(name), SearchFilter.format(matchingAttributes),
 641             cons);
 642     }
 643 
 644     public NamingEnumeration<SearchResult> search(Name name,
 645                                                   Attributes matchingAttributes,
 646                                                   String[] attributesToReturn)
 647             throws NamingException {
 648         SearchControls cons = new SearchControls();
 649         cons.setReturningAttributes(attributesToReturn);
 650 
 651         return search(name, SearchFilter.format(matchingAttributes), cons);
 652     }
 653 
 654     public NamingEnumeration<SearchResult> search(String name,
 655                                                   String filter,
 656                                                   SearchControls cons)
 657             throws NamingException {
 658         return search(toName(name), filter, cons);
 659     }
 660 
 661     public NamingEnumeration<SearchResult> search(Name name,
 662                                                   String filter,
 663         SearchControls cons) throws NamingException {
 664 
 665         if (skipThisReferral) {
 666             throw (NamingException)
 667                 ((refEx.appendUnprocessedReferrals(null)).fillInStackTrace());
 668         }
 669 
 670         try {
 671             NamingEnumeration<SearchResult> se =
 672                     refCtx.search(overrideName(name),
 673                                   overrideFilter(filter),
 674                                   overrideAttributesAndScope(cons));
 675 
 676             refEx.setNameResolved(true);
 677 
 678             // append (referrals from) the exception that generated this
 679             // context to the new search results, so that referral processing
 680             // can continue
 681             ((ReferralEnumeration)se).appendUnprocessedReferrals(refEx);
 682 
 683             return (se);
 684 
 685         } catch (LdapReferralException e) {
 686 
 687             // %%% setNameResolved(true);
 688 
 689             // append (referrals from) the exception that generated this
 690             // context to the new exception, so that referral processing
 691             // can continue
 692 
 693             e.appendUnprocessedReferrals(refEx);
 694             throw (NamingException)(e.fillInStackTrace());
 695 
 696         } catch (NamingException e) {
 697 
 698             // record the exception if there are no remaining referrals
 699             if ((refEx != null) && (! refEx.hasMoreReferrals())) {
 700                 refEx.setNamingException(e);
 701             }
 702             if ((refEx != null) &&
 703                 (refEx.hasMoreReferrals() ||
 704                  refEx.hasMoreReferralExceptions())) {
 705                 throw (NamingException)
 706                     ((refEx.appendUnprocessedReferrals(null)).fillInStackTrace());
 707             } else {
 708                 throw e;
 709             }
 710         }
 711     }
 712 
 713     public NamingEnumeration<SearchResult> search(String name,
 714                                                   String filterExpr,
 715                                                   Object[] filterArgs,
 716                                                   SearchControls cons)
 717             throws NamingException {
 718         return search(toName(name), filterExpr, filterArgs, cons);
 719     }
 720 
 721     public NamingEnumeration<SearchResult> search(Name name,
 722         String filterExpr,
 723         Object[] filterArgs,
 724         SearchControls cons) throws NamingException {
 725 
 726         if (skipThisReferral) {
 727             throw (NamingException)
 728                 ((refEx.appendUnprocessedReferrals(null)).fillInStackTrace());
 729         }
 730 
 731         try {
 732             NamingEnumeration<SearchResult> se;
 733 
 734             if (urlFilter != null) {
 735                 se = refCtx.search(overrideName(name), urlFilter,
 736                 overrideAttributesAndScope(cons));
 737             } else {
 738                 se = refCtx.search(overrideName(name), filterExpr,
 739                 filterArgs, overrideAttributesAndScope(cons));
 740             }
 741 
 742             refEx.setNameResolved(true);
 743 
 744             // append (referrals from) the exception that generated this
 745             // context to the new search results, so that referral processing
 746             // can continue
 747             ((ReferralEnumeration)se).appendUnprocessedReferrals(refEx);
 748 
 749             return (se);
 750 
 751         } catch (LdapReferralException e) {
 752 
 753             // append (referrals from) the exception that generated this
 754             // context to the new exception, so that referral processing
 755             // can continue
 756 
 757             e.appendUnprocessedReferrals(refEx);
 758             throw (NamingException)(e.fillInStackTrace());
 759 
 760         } catch (NamingException e) {
 761 
 762             // record the exception if there are no remaining referrals
 763             if ((refEx != null) && (! refEx.hasMoreReferrals())) {
 764                 refEx.setNamingException(e);
 765             }
 766             if ((refEx != null) &&
 767                 (refEx.hasMoreReferrals() ||
 768                  refEx.hasMoreReferralExceptions())) {
 769                 throw (NamingException)
 770                     ((refEx.appendUnprocessedReferrals(null)).fillInStackTrace());
 771             } else {
 772                 throw e;
 773             }
 774         }
 775     }
 776 
 777     public String getNameInNamespace() throws NamingException {
 778         if (skipThisReferral) {
 779             throw (NamingException)
 780                 ((refEx.appendUnprocessedReferrals(null)).fillInStackTrace());
 781         }
 782         return urlName != null && !urlName.isEmpty() ? urlName.get(0) : "";
 783     }
 784 
 785     // ---------------------- LdapContext ---------------------
 786 
 787     public ExtendedResponse extendedOperation(ExtendedRequest request)
 788         throws NamingException {
 789 
 790         if (skipThisReferral) {
 791             throw (NamingException)
 792                 ((refEx.appendUnprocessedReferrals(null)).fillInStackTrace());
 793         }
 794 
 795         if (!(refCtx instanceof LdapContext)) {
 796             throw new NotContextException(
 797                 "Referral context not an instance of LdapContext");
 798         }
 799 
 800         return ((LdapContext)refCtx).extendedOperation(request);
 801     }
 802 
 803     public LdapContext newInstance(Control[] requestControls)
 804         throws NamingException {
 805 
 806         if (skipThisReferral) {
 807             throw (NamingException)
 808                 ((refEx.appendUnprocessedReferrals(null)).fillInStackTrace());
 809         }
 810 
 811         if (!(refCtx instanceof LdapContext)) {
 812             throw new NotContextException(
 813                 "Referral context not an instance of LdapContext");
 814         }
 815 
 816         return ((LdapContext)refCtx).newInstance(requestControls);
 817     }
 818 
 819     public void reconnect(Control[] connCtls) throws NamingException {
 820         if (skipThisReferral) {
 821             throw (NamingException)
 822                 ((refEx.appendUnprocessedReferrals(null)).fillInStackTrace());
 823         }
 824 
 825         if (!(refCtx instanceof LdapContext)) {
 826             throw new NotContextException(
 827                 "Referral context not an instance of LdapContext");
 828         }
 829 
 830         ((LdapContext)refCtx).reconnect(connCtls);
 831     }
 832 
 833     public Control[] getConnectControls() throws NamingException {
 834         if (skipThisReferral) {
 835             throw (NamingException)
 836                 ((refEx.appendUnprocessedReferrals(null)).fillInStackTrace());
 837         }
 838 
 839         if (!(refCtx instanceof LdapContext)) {
 840             throw new NotContextException(
 841                 "Referral context not an instance of LdapContext");
 842         }
 843 
 844         return ((LdapContext)refCtx).getConnectControls();
 845     }
 846 
 847     public void setRequestControls(Control[] requestControls)
 848         throws NamingException {
 849 
 850         if (skipThisReferral) {
 851             throw (NamingException)
 852                 ((refEx.appendUnprocessedReferrals(null)).fillInStackTrace());
 853         }
 854 
 855         if (!(refCtx instanceof LdapContext)) {
 856             throw new NotContextException(
 857                 "Referral context not an instance of LdapContext");
 858         }
 859 
 860         ((LdapContext)refCtx).setRequestControls(requestControls);
 861     }
 862 
 863     public Control[] getRequestControls() throws NamingException {
 864         if (skipThisReferral) {
 865             throw (NamingException)
 866                 ((refEx.appendUnprocessedReferrals(null)).fillInStackTrace());
 867         }
 868 
 869         if (!(refCtx instanceof LdapContext)) {
 870             throw new NotContextException(
 871                 "Referral context not an instance of LdapContext");
 872         }
 873         return ((LdapContext)refCtx).getRequestControls();
 874     }
 875 
 876     public Control[] getResponseControls() throws NamingException {
 877         if (skipThisReferral) {
 878             throw (NamingException)
 879                 ((refEx.appendUnprocessedReferrals(null)).fillInStackTrace());
 880         }
 881 
 882         if (!(refCtx instanceof LdapContext)) {
 883             throw new NotContextException(
 884                 "Referral context not an instance of LdapContext");
 885         }
 886         return ((LdapContext)refCtx).getResponseControls();
 887     }
 888 
 889     // ---------------------- Private methods  ---------------------
 890     private Name toName(String name) throws InvalidNameException {
 891         return name.isEmpty() ? new CompositeName() :
 892             new CompositeName().add(name);
 893     }
 894 
 895     /*
 896      * Use the DN component from the LDAP URL (if present) to override the
 897      * supplied DN.
 898      */
 899     private Name overrideName(Name name) throws InvalidNameException {
 900         return (urlName == null ? name : urlName);
 901     }
 902 
 903     /*
 904      * Use the attributes and scope components from the LDAP URL (if present)
 905      * to override the corresponding components supplied in SearchControls.
 906      */
 907     private SearchControls overrideAttributesAndScope(SearchControls cons) {
 908         SearchControls urlCons;
 909 
 910         if ((urlScope != null) || (urlAttrs != null)) {
 911             urlCons = new SearchControls(cons.getSearchScope(),
 912                                         cons.getCountLimit(),
 913                                         cons.getTimeLimit(),
 914                                         cons.getReturningAttributes(),
 915                                         cons.getReturningObjFlag(),
 916                                         cons.getDerefLinkFlag());
 917 
 918             if (urlScope != null) {
 919                 if (urlScope.equals("base")) {
 920                     urlCons.setSearchScope(SearchControls.OBJECT_SCOPE);
 921                 } else if (urlScope.equals("one")) {
 922                     urlCons.setSearchScope(SearchControls.ONELEVEL_SCOPE);
 923                 } else if (urlScope.equals("sub")) {
 924                     urlCons.setSearchScope(SearchControls.SUBTREE_SCOPE);
 925                 }
 926             }
 927 
 928             if (urlAttrs != null) {
 929                 StringTokenizer tokens = new StringTokenizer(urlAttrs, ",");
 930                 int count = tokens.countTokens();
 931                 String[] attrs = new String[count];
 932                 for (int i = 0; i < count; i ++) {
 933                     attrs[i] = tokens.nextToken();
 934                 }
 935                 urlCons.setReturningAttributes(attrs);
 936             }
 937 
 938             return urlCons;
 939 
 940         } else {
 941             return cons;
 942         }
 943     }
 944 
 945     /*
 946      * Use the filter component from the LDAP URL (if present) to override the
 947      * supplied filter.
 948      */
 949     private String overrideFilter(String filter) {
 950         return (urlFilter == null ? filter : urlFilter);
 951     }
 952 
 953 }