src/share/classes/com/sun/jndi/ldap/LdapCtx.java

Print this page




 206     private static final ControlFactory myResponseControlFactory =
 207         new DefaultResponseControlFactory();
 208     private static final Control manageReferralControl =
 209         new ManageReferralControl(false);
 210 
 211     private static final HierMemDirCtx EMPTY_SCHEMA = new HierMemDirCtx();
 212     static {
 213         EMPTY_SCHEMA.setReadOnly(
 214             new SchemaViolationException("Cannot update schema object"));
 215     }
 216 
 217     // ------------ Package private instance variables ----------------
 218     // Cannot be private; used by enums
 219 
 220         // ------- Inherited by derived context instances
 221 
 222     int port_number;                    // port number of server
 223     String hostname = null;             // host name of server (no brackets
 224                                         //   for IPv6 literals)
 225     LdapClient clnt = null;             // connection handle
 226     Hashtable envprops = null;          // environment properties of context
 227     int handleReferrals = DEFAULT_REFERRAL_MODE; // how referral is handled
 228     boolean hasLdapsScheme = false;     // true if the context was created
 229                                         //  using an LDAPS URL.
 230 
 231         // ------- Not inherited by derived context instances
 232 
 233     String currentDN;                   // DN of this context
 234     Name currentParsedDN;               // DN of this context
 235     Vector respCtls = null;             // Response controls read
 236     Control[] reqCtls = null;           // Controls to be sent with each request
 237 
 238 
 239     // ------------- Private instance variables ------------------------
 240 
 241         // ------- Inherited by derived context instances
 242 
 243     private OutputStream trace = null;  // output stream for BER debug output
 244     private boolean netscapeSchemaBug = false;       // workaround
 245     private Control[] bindCtls = null;  // Controls to be sent with LDAP "bind"
 246     private int referralHopLimit = DEFAULT_REFERRAL_LIMIT;  // max referral
 247     private Hashtable schemaTrees = null; // schema root of this context
 248     private int batchSize = DEFAULT_BATCH_SIZE;      // batch size for search results
 249     private boolean deleteRDN = DEFAULT_DELETE_RDN;  // delete the old RDN when modifying DN
 250     private boolean typesOnly = DEFAULT_TYPES_ONLY;  // return attribute types (no values)
 251     private int derefAliases = DEFAULT_DEREF_ALIASES;// de-reference alias entries during searching
 252     private char addrEncodingSeparator = DEFAULT_REF_SEPARATOR;  // encoding RefAddr
 253 
 254     private Hashtable binaryAttrs = null;    // attr values returned as byte[]
 255     private int connectTimeout = -1;         // no timeout value
 256     private int readTimeout = -1;            // no timeout value
 257     private boolean waitForReply = true;     // wait for search response
 258     private int replyQueueSize  = -1;        // unlimited queue size
 259     private boolean useSsl = false;          // true if SSL protocol is active
 260     private boolean useDefaultPortNumber = false; // no port number was supplied
 261 
 262         // ------- Not inherited by derived context instances
 263 
 264     // True if this context was created by another LdapCtx.
 265     private boolean parentIsLdapCtx = false; // see composeName()
 266 
 267     private int hopCount = 1;                // current referral hop count
 268     private String url = null;               // URL of context; see getURL()
 269     private EventSupport eventSupport;       // Event support helper for this ctx
 270     private boolean unsolicited = false;     // if there unsolicited listeners
 271     private boolean sharable = true;         // can share connection with other ctx
 272 
 273     // -------------- Constructors  -----------------------------------
 274 
 275     public LdapCtx(String dn, String host, int port_number, Hashtable props,


 276             boolean useSsl) throws NamingException {
 277 
 278         this.useSsl = this.hasLdapsScheme = useSsl;
 279 
 280         if (props != null) {
 281             envprops = (Hashtable) props.clone();
 282 
 283             // SSL env prop overrides the useSsl argument
 284             if ("ssl".equals(envprops.get(Context.SECURITY_PROTOCOL))) {
 285                 this.useSsl = true;
 286             }
 287 
 288             // %%% These are only examined when the context is created
 289             // %%% because they are only for debugging or workaround purposes.
 290             trace = (OutputStream)envprops.get(TRACE_BER);
 291 
 292             if (props.get(NETSCAPE_SCHEMA_BUG) != null ||
 293                 props.get(OLD_NETSCAPE_SCHEMA_BUG) != null) {
 294                 netscapeSchemaBug = true;
 295             }
 296         }
 297 
 298         currentDN = (dn != null) ? dn : "";
 299         currentParsedDN = parser.parse(currentDN);
 300 
 301         hostname = (host != null && host.length() > 0) ? host : DEFAULT_HOST;
 302         if (hostname.charAt(0) == '[') {
 303             hostname = hostname.substring(1, hostname.length() - 1);
 304         }
 305 
 306         if (port_number > 0) {
 307             this.port_number = port_number;
 308         } else {
 309             this.port_number = this.useSsl ? DEFAULT_SSL_PORT : DEFAULT_PORT;
 310             this.useDefaultPortNumber = true;
 311         }
 312 
 313         schemaTrees = new Hashtable(11, 0.75f);
 314         initEnv();
 315         try {
 316             connect(false);
 317         } catch (NamingException e) {
 318             try {
 319                 close();
 320             } catch (Exception e2) {
 321                 // Nothing
 322             }
 323             throw e;
 324         }
 325     }
 326 
 327     LdapCtx(LdapCtx existing, String newDN) throws NamingException {
 328         useSsl = existing.useSsl;
 329         hasLdapsScheme = existing.hasLdapsScheme;
 330         useDefaultPortNumber = existing.useDefaultPortNumber;
 331 
 332         hostname = existing.hostname;
 333         port_number = existing.port_number;


 901 
 902     /**
 903      * Adds attributes from RDN to attrs if not already present.
 904      * Note that if attrs already contains an attribute by the same name,
 905      * or if the distinguished name is empty, then leave attrs unchanged.
 906      *
 907      * @param dn The non-null DN of the entry to add
 908      * @param attrs The non-null attributes of entry to add
 909      * @param directUpdate Whether attrs can be updated directly
 910      * @returns Non-null attributes with attributes from the RDN added
 911      */
 912     private static Attributes addRdnAttributes(String dn, Attributes attrs,
 913         boolean directUpdate) throws NamingException {
 914 
 915             // Handle the empty name
 916             if (dn.equals("")) {
 917                 return attrs;
 918             }
 919 
 920             // Parse string name into list of RDNs
 921             //List<Rdn> rdnList = (new LdapName(dn)).rdns();
 922             List rdnList = (new LdapName(dn)).getRdns();
 923 
 924             // Get leaf RDN
 925             //Rdn rdn = rdnList.get(rdnList.size() - 1);
 926             Rdn rdn = (Rdn) rdnList.get(rdnList.size() - 1);
 927             Attributes nameAttrs = rdn.toAttributes();
 928 
 929             // Add attributes of RDN to attrs if not already there
 930             NamingEnumeration enum_ = nameAttrs.getAll();
 931             Attribute nameAttr;
 932             while (enum_.hasMore()) {
 933                 nameAttr = (Attribute) enum_.next();
 934 
 935                 // If attrs already has the attribute, don't change or add to it
 936                 if (attrs.get(nameAttr.getID()) ==  null) {
 937 
 938                     /**
 939                      * When attrs.isCaseIgnored() is false, attrs.get() will
 940                      * return null when the case mis-matches for otherwise
 941                      * equal attrIDs.
 942                      * As the attrIDs' case is irrelevant for LDAP, ignore
 943                      * the case of attrIDs even when attrs.isCaseIgnored() is
 944                      * false. This is done by explicitly comparing the elements in
 945                      * the enumeration of IDs with their case ignored.
 946                      */
 947                     if (!attrs.isCaseIgnored() &&
 948                             containsIgnoreCase(attrs.getIDs(), nameAttr.getID())) {
 949                         continue;
 950                     }
 951 
 952                     if (!directUpdate) {
 953                         attrs = (Attributes)attrs.clone();
 954                         directUpdate = true;
 955                     }
 956                     attrs.put(nameAttr);
 957                 }
 958             }
 959 
 960             return attrs;
 961     }
 962 
 963 
 964     private static boolean containsIgnoreCase(NamingEnumeration enumStr,
 965                                 String str) throws NamingException {
 966         String strEntry;
 967 
 968         while (enumStr.hasMore()) {
 969              strEntry = (String) enumStr.next();
 970              if (strEntry.equalsIgnoreCase(str)) {
 971                 return true;
 972              }
 973         }
 974         return false;
 975     }
 976 
 977 
 978     private void adjustDeleteStatus(String fname, LdapResult answer) {
 979         if (answer.status == LdapClient.LDAP_NO_SUCH_OBJECT &&
 980             answer.matchedDN != null) {
 981             try {
 982                 // %%% RL: are there any implications for referrals?
 983 
 984                 Name orig = parser.parse(fname);
 985                 Name matched = parser.parse(answer.matchedDN);
 986                 if ((orig.size() - matched.size()) == 1)
 987                     answer.status = LdapClient.LDAP_SUCCESS;
 988             } catch (NamingException e) {}
 989         }
 990     }
 991 
 992     /*
 993      * Append the the second Vector onto the first Vector
 994      * (v2 must be non-null)
 995      */
 996     private static Vector appendVector(Vector v1, Vector v2) {
 997         if (v1 == null) {
 998             v1 = v2;
 999         } else {
1000             for (int i = 0; i < v2.size(); i++) {
1001                 v1.addElement(v2.elementAt(i));
1002             }
1003         }
1004         return v1;
1005     }
1006 
1007     // ------------- Lookups and Browsing -------------------------
1008     // lookup/lookupLink
1009     // list/listBindings
1010 
1011     protected Object c_lookupLink(Name name, Continuation cont)
1012             throws NamingException {
1013         return c_lookup(name, cont);
1014     }
1015 
1016     protected Object c_lookup(Name name, Continuation cont)


1021 
1022         try {
1023             SearchControls cons = new SearchControls();
1024             cons.setSearchScope(SearchControls.OBJECT_SCOPE);
1025             cons.setReturningAttributes(null); // ask for all attributes
1026             cons.setReturningObjFlag(true); // need values to construct obj
1027 
1028             LdapResult answer = doSearchOnce(name, "(objectClass=*)", cons, true);
1029             respCtls = answer.resControls; // retrieve response controls
1030 
1031             // should get back 1 SearchResponse and 1 SearchResult
1032 
1033             if (answer.status != LdapClient.LDAP_SUCCESS) {
1034                 processReturnCode(answer, name);
1035             }
1036 
1037             if (answer.entries == null || answer.entries.size() != 1) {
1038                 // found it but got no attributes
1039                 attrs = new BasicAttributes(LdapClient.caseIgnore);
1040             } else {
1041                 LdapEntry entry = (LdapEntry)answer.entries.elementAt(0);
1042                 attrs = entry.attributes;
1043 
1044                 Vector entryCtls = entry.respCtls; // retrieve entry controls
1045                 if (entryCtls != null) {
1046                     appendVector(respCtls, entryCtls); // concatenate controls
1047                 }
1048             }
1049 
1050             if (attrs.get(Obj.JAVA_ATTRIBUTES[Obj.CLASSNAME]) != null) {
1051                 // serialized object or object reference
1052                 obj = Obj.decodeObject(attrs);
1053             }
1054             if (obj == null) {
1055                 obj = new LdapCtx(this, fullyQualifiedName(name));
1056             }
1057         } catch (LdapReferralException e) {
1058             if (handleReferrals == LdapClient.LDAP_REF_THROW)
1059                 throw cont.fillInException(e);
1060 
1061             // process the referrals sequentially
1062             while (true) {
1063 
1064                 LdapReferralContext refCtx =


1080 
1081         } catch (NamingException e) {
1082             throw cont.fillInException(e);
1083         }
1084 
1085         try {
1086             return DirectoryManager.getObjectInstance(obj, name,
1087                 this, envprops, attrs);
1088 
1089         } catch (NamingException e) {
1090             throw cont.fillInException(e);
1091 
1092         } catch (Exception e) {
1093             NamingException e2 = new NamingException(
1094                     "problem generating object using object factory");
1095             e2.setRootCause(e);
1096             throw cont.fillInException(e2);
1097         }
1098     }
1099 
1100     protected NamingEnumeration c_list(Name name, Continuation cont)
1101             throws NamingException {
1102         SearchControls cons = new SearchControls();
1103         String[] classAttrs = new String[2];
1104 
1105         classAttrs[0] = Obj.JAVA_ATTRIBUTES[Obj.OBJECT_CLASS];
1106         classAttrs[1] = Obj.JAVA_ATTRIBUTES[Obj.CLASSNAME];
1107         cons.setReturningAttributes(classAttrs);
1108 
1109         // set this flag to override the typesOnly flag
1110         cons.setReturningObjFlag(true);
1111 
1112         cont.setError(this, name);
1113 
1114         LdapResult answer = null;
1115 
1116         try {
1117             answer = doSearch(name, "(objectClass=*)", cons, true, true);
1118 
1119             // list result may contain continuation references
1120             if ((answer.status != LdapClient.LDAP_SUCCESS) ||


1153             LdapNamingEnumeration res =
1154                 new LdapNamingEnumeration(this, answer, name, cont);
1155 
1156             res.setNamingException(
1157                     (LimitExceededException)cont.fillInException(e));
1158             return res;
1159 
1160         } catch (PartialResultException e) {
1161             LdapNamingEnumeration res =
1162                 new LdapNamingEnumeration(this, answer, name, cont);
1163 
1164             res.setNamingException(
1165                     (PartialResultException)cont.fillInException(e));
1166             return res;
1167 
1168         } catch (NamingException e) {
1169             throw cont.fillInException(e);
1170         }
1171     }
1172 
1173     protected NamingEnumeration c_listBindings(Name name, Continuation cont)

1174             throws NamingException {
1175 
1176         SearchControls cons = new SearchControls();
1177         cons.setReturningAttributes(null); // ask for all attributes
1178         cons.setReturningObjFlag(true); // need values to construct obj
1179 
1180         cont.setError(this, name);
1181 
1182         LdapResult answer = null;
1183 
1184         try {
1185             answer = doSearch(name, "(objectClass=*)", cons, true, true);
1186 
1187             // listBindings result may contain continuation references
1188             if ((answer.status != LdapClient.LDAP_SUCCESS) ||
1189                 (answer.referrals != null)) {
1190                 processReturnCode(answer, name);
1191             }
1192 
1193             return new LdapBindingEnumeration(this, answer, name, cont);

1194 
1195         } catch (LdapReferralException e) {
1196             if (handleReferrals == LdapClient.LDAP_REF_THROW)
1197                 throw cont.fillInException(e);
1198 
1199             // process the referrals sequentially
1200             while (true) {
1201 
1202                 LdapReferralContext refCtx =
1203                     (LdapReferralContext)e.getReferralContext(envprops, bindCtls);
1204 
1205                 // repeat the original operation at the new context
1206                 try {
1207 
1208                     return refCtx.listBindings(name);
1209 
1210                 } catch (LdapReferralException re) {
1211                     e = re;
1212                     continue;
1213 
1214                 } finally {
1215                     // Make sure we close referral context
1216                     refCtx.close();
1217                 }
1218             }
1219         } catch (LimitExceededException e) {
1220             LdapBindingEnumeration res =
1221                 new LdapBindingEnumeration(this, answer, name, cont);
1222 
1223             res.setNamingException(
1224                     (LimitExceededException)cont.fillInException(e));
1225             return res;
1226 
1227         } catch (PartialResultException e) {
1228             LdapBindingEnumeration res =
1229                 new LdapBindingEnumeration(this, answer, name, cont);
1230 
1231             res.setNamingException(
1232                     (PartialResultException)cont.fillInException(e));
1233             return res;
1234 
1235         } catch (NamingException e) {
1236             throw cont.fillInException(e);
1237         }
1238     }
1239 
1240     // --------------- Name-related Methods -----------------------
1241     // -- getNameParser/getNameInNamespace/composeName
1242 
1243     protected NameParser c_getNameParser(Name name, Continuation cont)
1244             throws NamingException
1245     {
1246         // ignore name, always return same parser
1247         cont.setSuccess();
1248         return parser;
1249     }
1250 
1251     public String getNameInNamespace() {
1252         return currentDN;
1253     }


1320         cont.setError(this, name);
1321 
1322         SearchControls cons = new SearchControls();
1323         cons.setSearchScope(SearchControls.OBJECT_SCOPE);
1324         cons.setReturningAttributes(attrIds);
1325 
1326         try {
1327             LdapResult answer =
1328                 doSearchOnce(name, "(objectClass=*)", cons, true);
1329             respCtls = answer.resControls; // retrieve response controls
1330 
1331             if (answer.status != LdapClient.LDAP_SUCCESS) {
1332                 processReturnCode(answer, name);
1333             }
1334 
1335             if (answer.entries == null || answer.entries.size() != 1) {
1336                 return new BasicAttributes(LdapClient.caseIgnore);
1337             }
1338 
1339             // get attributes from result
1340             LdapEntry entry = (LdapEntry) answer.entries.elementAt(0);
1341 
1342             Vector entryCtls = entry.respCtls; // retrieve entry controls
1343             if (entryCtls != null) {
1344                 appendVector(respCtls, entryCtls); // concatenate controls
1345             }
1346 
1347             // do this so attributes can find their schema
1348             setParents(entry.attributes, (Name) name.clone());
1349 
1350             return (entry.attributes);
1351 
1352         } catch (LdapReferralException e) {
1353             if (handleReferrals == LdapClient.LDAP_REF_THROW)
1354                 throw cont.fillInException(e);
1355 
1356             // process the referrals sequentially
1357             while (true) {
1358 
1359                 LdapReferralContext refCtx =
1360                     (LdapReferralContext)e.getReferralContext(envprops, bindCtls);
1361 
1362                 // repeat the original operation at the new context


1381 
1382     protected void c_modifyAttributes(Name name, int mod_op, Attributes attrs,
1383                                       Continuation cont)
1384             throws NamingException {
1385 
1386         cont.setError(this, name);
1387 
1388         try {
1389             ensureOpen();
1390 
1391             if (attrs == null || attrs.size() == 0) {
1392                 return; // nothing to do
1393             }
1394             String newDN = fullyQualifiedName(name);
1395             int jmod_op = convertToLdapModCode(mod_op);
1396 
1397             // construct mod list
1398             int[] jmods = new int[attrs.size()];
1399             Attribute[] jattrs = new Attribute[attrs.size()];
1400 
1401             NamingEnumeration ae = attrs.getAll();
1402             for(int i = 0; i < jmods.length && ae.hasMore(); i++) {
1403                 jmods[i] = jmod_op;
1404                 jattrs[i] = (Attribute)ae.next();
1405             }
1406 
1407             LdapResult answer = clnt.modify(newDN, jmods, jattrs, reqCtls);
1408             respCtls = answer.resControls; // retrieve response controls
1409 
1410             if (answer.status != LdapClient.LDAP_SUCCESS) {
1411                 processReturnCode(answer, name);
1412                 return;
1413             }
1414 
1415         } catch (LdapReferralException e) {
1416             if (handleReferrals == LdapClient.LDAP_REF_THROW)
1417                 throw cont.fillInException(e);
1418 
1419             // process the referrals sequentially
1420             while (true) {
1421 
1422                 LdapReferralContext refCtx =
1423                     (LdapReferralContext)e.getReferralContext(envprops, bindCtls);
1424 


1548             throws NamingException {
1549         cont.setError(this, name);
1550 
1551         try {
1552             // retrieve the objectClass attribute from LDAP
1553             Attribute objectClassAttr = c_getAttributes(name,
1554                 new String[]{"objectclass"}, cont).get("objectclass");
1555             if (objectClassAttr == null || objectClassAttr.size() == 0) {
1556                 return EMPTY_SCHEMA;
1557             }
1558 
1559             // retrieve the root of the ObjectClass schema tree
1560             Context ocSchema = (Context) c_getSchema(name, cont).lookup(
1561                 LdapSchemaParser.OBJECTCLASS_DEFINITION_NAME);
1562 
1563             // create a context to hold the schema objects representing the object
1564             // classes
1565             HierMemDirCtx objectClassCtx = new HierMemDirCtx();
1566             DirContext objectClassDef;
1567             String objectClassName;
1568             for (Enumeration objectClasses = objectClassAttr.getAll();
1569                 objectClasses.hasMoreElements(); ) {
1570                 objectClassName = (String)objectClasses.nextElement();
1571                 // %%% Should we fail if not found, or just continue?
1572                 objectClassDef = (DirContext)ocSchema.lookup(objectClassName);
1573                 objectClassCtx.bind(objectClassName, objectClassDef);
1574             }
1575 
1576             // Make context read-only
1577             objectClassCtx.setReadOnly(
1578                 new SchemaViolationException("Cannot update schema object"));
1579             return (DirContext)objectClassCtx;
1580 
1581         } catch (NamingException e) {
1582             throw cont.fillInException(e);
1583         }
1584     }
1585 
1586     /*
1587      * getSchemaTree first looks to see if we have already built a
1588      * schema tree for the given entry. If not, it builds a new one and
1589      * stores it in our private hash table
1590      */
1591     private DirContext getSchemaTree(Name name) throws NamingException {
1592         String subschemasubentry = getSchemaEntry(name, true);
1593 
1594         DirContext schemaTree = (DirContext)schemaTrees.get(subschemasubentry);
1595 
1596         if(schemaTree==null) {
1597             if(debug){System.err.println("LdapCtx: building new schema tree " + this);}
1598             schemaTree = buildSchemaTree(subschemasubentry);
1599             schemaTrees.put(subschemasubentry, schemaTree);
1600         }
1601 
1602         return schemaTree;
1603     }
1604 
1605     /*
1606      * buildSchemaTree builds the schema tree corresponding to the
1607      * given subschemasubentree
1608      */
1609     private DirContext buildSchemaTree(String subschemasubentry)
1610         throws NamingException {
1611 
1612         // get the schema entry itself
1613         // DO ask for return object here because we need it to
1614         // create context. Since asking for all attrs, we won't
1615         // be transmitting any specific attrIDs (like Java-specific ones).
1616         SearchControls constraints = new
1617             SearchControls(SearchControls.OBJECT_SCOPE,
1618                 0, 0, /* count and time limits */
1619                 SCHEMA_ATTRIBUTES /* return schema attrs */,
1620                 true /* return obj */,
1621                 false /*deref link */ );
1622 
1623         Name sse = (new CompositeName()).add(subschemasubentry);
1624         NamingEnumeration results =
1625             searchAux(sse, "(objectClass=subschema)", constraints,
1626             false, true, new Continuation());
1627 
1628         if(!results.hasMore()) {
1629             throw new OperationNotSupportedException(
1630                 "Cannot get read subschemasubentry: " + subschemasubentry);
1631         }
1632         SearchResult result = (SearchResult)results.next();
1633         results.close();
1634 
1635         Object obj = result.getObject();
1636         if(!(obj instanceof LdapCtx)) {
1637             throw new NamingException(
1638                 "Cannot get schema object as DirContext: " + subschemasubentry);
1639         }
1640 
1641         return LdapSchemaCtx.createSchemaTree(envprops, subschemasubentry,
1642             (LdapCtx)obj /* schema entry */,
1643             result.getAttributes() /* schema attributes */,
1644             netscapeSchemaBug);
1645    }
1646 
1647     /*
1648      * getSchemaEntree returns the DN of the subschemasubentree for the
1649      * given entree. It first looks to see if the given entry has
1650      * a subschema different from that of the root DIT (by looking for
1651      * a "subschemasubentry" attribute). If it doesn't find one, it returns
1652      * the one for the root of the DIT (by looking for the root's


1657      * queries. If this function trys a serarch on a v2 server that
1658      * doesn't support schema, one of these two things will happen:
1659      * 1) It will get an exception when querying the root DSE
1660      * 2) It will not find a subschemasubentry on the root DSE
1661      * If either of these things occur and the server is not v3, we
1662      * throw OperationNotSupported.
1663      *
1664      * the relative flag tells whether the given name is relative to this
1665      * context.
1666      */
1667     private String getSchemaEntry(Name name, boolean relative)
1668         throws NamingException {
1669 
1670         // Asks for operational attribute "subschemasubentry"
1671         SearchControls constraints = new SearchControls(SearchControls.OBJECT_SCOPE,
1672             0, 0, /* count and time limits */
1673             new String[]{"subschemasubentry"} /* attr to return */,
1674             false /* returning obj */,
1675             false /* deref link */);
1676 
1677         NamingEnumeration results;
1678         try {
1679             results = searchAux(name, "objectclass=*", constraints, relative,
1680                 true, new Continuation());
1681 
1682         } catch (NamingException ne) {
1683             if (!clnt.isLdapv3 && currentDN.length() == 0 && name.isEmpty()) {
1684                 // we got an error looking for a root entry on an ldapv2
1685                 // server. The server must not support schema.
1686                 throw new OperationNotSupportedException(
1687                     "Cannot get schema information from server");
1688             } else {
1689                 throw ne;
1690             }
1691         }
1692 
1693         if (!results.hasMoreElements()) {
1694             throw new ConfigurationException(
1695                 "Requesting schema of nonexistent entry: " + name);
1696         }
1697 
1698         SearchResult result = (SearchResult) results.next();
1699         results.close();
1700 
1701         Attribute schemaEntryAttr =
1702             result.getAttributes().get("subschemasubentry");
1703         //System.err.println("schema entry attrs: " + schemaEntryAttr);
1704 
1705         if (schemaEntryAttr == null || schemaEntryAttr.size() < 0) {
1706             if (currentDN.length() == 0 && name.isEmpty()) {
1707                 // the server doesn't have a subschemasubentry in its root DSE.
1708                 // therefore, it doesn't support schema.
1709                 throw new OperationNotSupportedException(
1710                     "Cannot read subschemasubentry of root DSE");
1711             } else {
1712                 return getSchemaEntry(new CompositeName(), false);
1713             }
1714         }
1715 
1716         return (String)(schemaEntryAttr.get()); // return schema entry name
1717     }
1718 
1719     // package-private; used by search enum.
1720     // Set attributes to point to this context in case some one
1721     // asked for their schema
1722     void setParents(Attributes attrs, Name name) throws NamingException {
1723         NamingEnumeration ae = attrs.getAll();
1724         while(ae.hasMore()) {
1725             ((LdapAttribute) ae.next()).setParent(this, name);
1726         }
1727     }
1728 
1729     /*
1730      * Returns the URL associated with this context; used by LdapAttribute
1731      * after deserialization to get pointer to this context.
1732      */
1733     String getURL() {
1734         if (url == null) {
1735             url = LdapURL.toUrlString(hostname, port_number, currentDN,
1736                 hasLdapsScheme);
1737         }
1738 
1739         return url;
1740     }
1741 
1742    // --------------------- Searches -----------------------------
1743     protected NamingEnumeration c_search(Name name,
1744                                          Attributes matchingAttributes,
1745                                          Continuation cont)
1746             throws NamingException {
1747         return c_search(name, matchingAttributes, null, cont);
1748     }
1749 
1750     protected NamingEnumeration c_search(Name name,
1751                                          Attributes matchingAttributes,
1752                                          String[] attributesToReturn,
1753                                          Continuation cont)
1754             throws NamingException {
1755         SearchControls cons = new SearchControls();
1756         cons.setReturningAttributes(attributesToReturn);
1757         String filter;
1758         try {
1759             filter = SearchFilter.format(matchingAttributes);
1760         } catch (NamingException e) {
1761             cont.setError(this, name);
1762             throw cont.fillInException(e);
1763         }
1764         return c_search(name, filter, cons, cont);
1765     }
1766 
1767     protected NamingEnumeration c_search(Name name,
1768                                          String filter,
1769                                          SearchControls cons,
1770                                          Continuation cont)
1771             throws NamingException {
1772         return searchAux(name, filter, cloneSearchControls(cons), true,
1773                  waitForReply, cont);
1774     }
1775 
1776     protected NamingEnumeration c_search(Name name,
1777                                          String filterExpr,
1778                                          Object[] filterArgs,
1779                                          SearchControls cons,
1780                                          Continuation cont)
1781             throws NamingException {
1782         String strfilter;
1783         try {
1784             strfilter = SearchFilter.format(filterExpr, filterArgs);
1785         } catch (NamingException e) {
1786             cont.setError(this, name);
1787             throw cont.fillInException(e);
1788         }
1789         return c_search(name, strfilter, cons, cont);
1790     }
1791 
1792         // Used by NamingNotifier
1793     NamingEnumeration searchAux(Name name,

1794         String filter,
1795         SearchControls cons,
1796         boolean relative,
1797         boolean waitForReply, Continuation cont) throws NamingException {
1798 
1799         LdapResult answer = null;
1800         String[] tokens = new String[2];    // stores ldap compare op. values
1801         String[] reqAttrs;                  // remember what was asked
1802 
1803         if (cons == null) {
1804             cons = new SearchControls();
1805         }
1806         reqAttrs = cons.getReturningAttributes();
1807 
1808         // if objects are requested then request the Java attributes too
1809         // so that the objects can be constructed
1810         if (cons.getReturningObjFlag()) {
1811             if (reqAttrs != null) {
1812 
1813                 // check for presence of "*" (user attributes wildcard)


1831             }
1832         }
1833 
1834         LdapCtx.SearchArgs args =
1835             new LdapCtx.SearchArgs(name, filter, cons, reqAttrs);
1836 
1837         cont.setError(this, name);
1838         try {
1839             // see if this can be done as a compare, otherwise do a search
1840             if (searchToCompare(filter, cons, tokens)){
1841                 //System.err.println("compare triggered");
1842                 answer = compare(name, tokens[0], tokens[1]);
1843                 if (! (answer.compareToSearchResult(fullyQualifiedName(name)))){
1844                     processReturnCode(answer, name);
1845                 }
1846             } else {
1847                 answer = doSearch(name, filter, cons, relative, waitForReply);
1848                 // search result may contain referrals
1849                 processReturnCode(answer, name);
1850             }
1851             return new LdapSearchEnumeration(this, answer,

1852                 fullyQualifiedName(name), args, cont);
1853 
1854         } catch (LdapReferralException e) {
1855             if (handleReferrals == LdapClient.LDAP_REF_THROW)
1856                 throw cont.fillInException(e);
1857 
1858             // process the referrals sequentially
1859             while (true) {
1860 
1861                 LdapReferralContext refCtx =
1862                     (LdapReferralContext)e.getReferralContext(envprops, bindCtls);
1863 
1864                 // repeat the original operation at the new context
1865                 try {
1866 
1867                     return refCtx.search(name, filter, cons);
1868 
1869                 } catch (LdapReferralException re) {
1870                     e = re;
1871                     continue;
1872 
1873                 } finally {
1874                     // Make sure we close referral context
1875                     refCtx.close();
1876                 }
1877             }
1878 
1879         } catch (LimitExceededException e) {
1880             LdapSearchEnumeration res =
1881                 new LdapSearchEnumeration(this, answer, fullyQualifiedName(name),
1882                                           args, cont);
1883             res.setNamingException(e);
1884             return res;
1885 
1886         } catch (PartialResultException e) {
1887             LdapSearchEnumeration res =
1888                 new LdapSearchEnumeration(this, answer, fullyQualifiedName(name),
1889                                           args, cont);
1890 
1891             res.setNamingException(e);
1892             return res;
1893 
1894         } catch (IOException e) {
1895             NamingException e2 = new CommunicationException(e.getMessage());
1896             e2.setRootCause(e);
1897             throw cont.fillInException(e2);
1898 
1899         } catch (NamingException e) {
1900             throw cont.fillInException(e);
1901         }
1902     }
1903 
1904 
1905     LdapResult getSearchReply(LdapClient eClnt, LdapResult res)
1906             throws NamingException {
1907         // ensureOpen() won't work here because
1908         // session was associated with previous connection
1909 
1910         // %%% RL: we can actually allow the enumeration to continue
1911         // using the old handle but other weird things might happen
1912         // when we hit a referral


2126         }
2127         String[] retAttrs = cons.getReturningAttributes();
2128         if (retAttrs != null) {
2129             String[] attrs = new String[retAttrs.length];
2130             System.arraycopy(retAttrs, 0, attrs, 0, retAttrs.length);
2131             retAttrs = attrs;
2132         }
2133         return new SearchControls(cons.getSearchScope(),
2134                                   cons.getCountLimit(),
2135                                   cons.getTimeLimit(),
2136                                   retAttrs,
2137                                   cons.getReturningObjFlag(),
2138                                   cons.getDerefLinkFlag());
2139     }
2140 
2141    // -------------- Environment Properties ------------------
2142 
2143     /**
2144      * Override with noncloning version.
2145      */
2146     protected Hashtable p_getEnvironment() {
2147         return envprops;
2148     }
2149 
2150     public Hashtable getEnvironment() throws NamingException {

2151         return (envprops == null
2152                 ? new Hashtable(5, 0.75f)
2153                 : (Hashtable)envprops.clone());
2154     }
2155 

2156     public Object removeFromEnvironment(String propName)
2157         throws NamingException {
2158 
2159         // not there; just return
2160         if (envprops == null || envprops.get(propName) == null) {
2161             return null;
2162         }
2163 
2164         if (propName.equals(REF_SEPARATOR)) {
2165             addrEncodingSeparator = DEFAULT_REF_SEPARATOR;
2166         } else if (propName.equals(TYPES_ONLY)) {

2167             typesOnly = DEFAULT_TYPES_ONLY;
2168         } else if (propName.equals(DELETE_RDN)) {

2169             deleteRDN = DEFAULT_DELETE_RDN;
2170         } else if (propName.equals(DEREF_ALIASES)) {

2171             derefAliases = DEFAULT_DEREF_ALIASES;
2172         } else if (propName.equals(Context.BATCHSIZE)) {

2173             batchSize = DEFAULT_BATCH_SIZE;
2174         } else if (propName.equals(REFERRAL_LIMIT)) {

2175             referralHopLimit = DEFAULT_REFERRAL_LIMIT;
2176         } else if (propName.equals(Context.REFERRAL)) {

2177             setReferralMode(null, true);
2178         } else if (propName.equals(BINARY_ATTRIBUTES)) {

2179             setBinaryAttributes(null);
2180         } else if (propName.equals(CONNECT_TIMEOUT)) {

2181             connectTimeout = -1;
2182         } else if (propName.equals(READ_TIMEOUT)) {

2183             readTimeout = -1;
2184         } else if (propName.equals(WAIT_FOR_REPLY)) {

2185             waitForReply = true;
2186         } else if (propName.equals(REPLY_QUEUE_SIZE)) {

2187             replyQueueSize = -1;

2188 
2189 // The following properties affect the connection
2190 
2191         } else if (propName.equals(Context.SECURITY_PROTOCOL)) {
2192             closeConnection(SOFT_CLOSE);
2193             // De-activate SSL and reset the context's url and port number
2194             if (useSsl && !hasLdapsScheme) {
2195                 useSsl = false;
2196                 url = null;
2197                 if (useDefaultPortNumber) {
2198                     port_number = DEFAULT_PORT;
2199                 }
2200             }
2201         } else if (propName.equals(VERSION) ||
2202             propName.equals(SOCKET_FACTORY)) {

2203             closeConnection(SOFT_CLOSE);
2204         } else if(propName.equals(Context.SECURITY_AUTHENTICATION) ||
2205             propName.equals(Context.SECURITY_PRINCIPAL) ||
2206             propName.equals(Context.SECURITY_CREDENTIALS)) {

2207             sharable = false;

2208         }
2209 
2210         // Update environment; reconnection will use new props
2211         envprops = (Hashtable)envprops.clone();
2212         return envprops.remove(propName);
2213     }
2214 

2215     public Object addToEnvironment(String propName, Object propVal)
2216         throws NamingException {
2217 
2218             // If adding null, call remove
2219             if (propVal == null) {
2220                 return removeFromEnvironment(propName);
2221             }
2222 
2223             if (propName.equals(REF_SEPARATOR)) {
2224                 setRefSeparator((String)propVal);
2225             } else if (propName.equals(TYPES_ONLY)) {

2226                 setTypesOnly((String)propVal);
2227             } else if (propName.equals(DELETE_RDN)) {

2228                 setDeleteRDN((String)propVal);
2229             } else if (propName.equals(DEREF_ALIASES)) {

2230                 setDerefAliases((String)propVal);
2231             } else if (propName.equals(Context.BATCHSIZE)) {

2232                 setBatchSize((String)propVal);
2233             } else if (propName.equals(REFERRAL_LIMIT)) {

2234                 setReferralLimit((String)propVal);
2235             } else if (propName.equals(Context.REFERRAL)) {

2236                 setReferralMode((String)propVal, true);
2237             } else if (propName.equals(BINARY_ATTRIBUTES)) {

2238                 setBinaryAttributes((String)propVal);
2239             } else if (propName.equals(CONNECT_TIMEOUT)) {

2240                 setConnectTimeout((String)propVal);
2241             } else if (propName.equals(READ_TIMEOUT)) {

2242                 setReadTimeout((String)propVal);
2243             } else if (propName.equals(WAIT_FOR_REPLY)) {

2244                 setWaitForReply((String)propVal);
2245             } else if (propName.equals(REPLY_QUEUE_SIZE)) {

2246                 setReplyQueueSize((String)propVal);

2247 
2248 // The following properties affect the connection
2249 
2250             } else if (propName.equals(Context.SECURITY_PROTOCOL)) {
2251                 closeConnection(SOFT_CLOSE);
2252                 // Activate SSL and reset the context's url and port number
2253                 if ("ssl".equals(propVal)) {
2254                     useSsl = true;
2255                     url = null;
2256                     if (useDefaultPortNumber) {
2257                         port_number = DEFAULT_SSL_PORT;
2258                     }
2259                 }
2260             } else if (propName.equals(VERSION) ||
2261                 propName.equals(SOCKET_FACTORY)) {

2262                 closeConnection(SOFT_CLOSE);
2263             } else if (propName.equals(Context.SECURITY_AUTHENTICATION) ||
2264                 propName.equals(Context.SECURITY_PRINCIPAL) ||
2265                 propName.equals(Context.SECURITY_CREDENTIALS)) {

2266                 sharable = false;

2267             }
2268 
2269             // Update environment; reconnection will use new props
2270             envprops = (envprops == null
2271                 ? new Hashtable(5, 0.75f)
2272                 : (Hashtable)envprops.clone());
2273             return envprops.put(propName, propVal);
2274     }
2275 
2276     /**
2277      * Sets the URL that created the context in the java.naming.provider.url
2278      * property.
2279      */
2280     void setProviderUrl(String providerUrl) { // called by LdapCtxFactory
2281         if (envprops != null) {
2282             envprops.put(Context.PROVIDER_URL, providerUrl);
2283         }
2284     }
2285 
2286     /**
2287      * Sets the domain name for the context in the com.sun.jndi.ldap.domainname
2288      * property.
2289      * Used for hostname verification by Start TLS
2290      */
2291     void setDomainName(String domainName) { // called by LdapCtxFactory
2292         if (envprops != null) {


2363 
2364     /**
2365      * Sets the batch size of this context;
2366      */
2367     private void setBatchSize(String batchSizeProp) {
2368         // set batchsize
2369         if (batchSizeProp != null) {
2370             batchSize = Integer.parseInt(batchSizeProp);
2371         } else {
2372             batchSize = DEFAULT_BATCH_SIZE;
2373         }
2374     }
2375 
2376     /**
2377      * Sets the referral mode of this context to 'follow', 'throw' or 'ignore'.
2378      * If referral mode is 'ignore' then activate the manageReferral control.
2379      */
2380     private void setReferralMode(String ref, boolean update) {
2381         // First determine the referral mode
2382         if (ref != null) {
2383             if (ref.equals("follow")) {

2384                 handleReferrals = LdapClient.LDAP_REF_FOLLOW;
2385             } else if (ref.equals("throw")) {

2386                 handleReferrals = LdapClient.LDAP_REF_THROW;
2387             } else if (ref.equals("ignore")) {

2388                 handleReferrals = LdapClient.LDAP_REF_IGNORE;
2389             } else {

2390                 throw new IllegalArgumentException(
2391                     "Illegal value for " + Context.REFERRAL + " property.");
2392             }
2393         } else {
2394             handleReferrals = DEFAULT_REFERRAL_MODE;
2395         }
2396 
2397         if (handleReferrals == LdapClient.LDAP_REF_IGNORE) {
2398             // If ignoring referrals, add manageReferralControl
2399             reqCtls = addControl(reqCtls, manageReferralControl);
2400 
2401         } else if (update) {
2402 
2403             // If we're update an existing context, remove the control
2404             reqCtls = removeControl(reqCtls, manageReferralControl);
2405 
2406         } // else, leave alone; need not update
2407     }
2408 
2409     /**
2410      * Set whether aliases are derefereced during resolution and searches.
2411      */
2412     private void setDerefAliases(String deref) {
2413         if (deref != null) {
2414             if (deref.equals("never")) {

2415                 derefAliases = 0; // never de-reference aliases
2416             } else if (deref.equals("searching")) {

2417                 derefAliases = 1; // de-reference aliases during searching
2418             } else if (deref.equals("finding")) {

2419                 derefAliases = 2; // de-reference during name resolution
2420             } else if (deref.equals("always")) {

2421                 derefAliases = 3; // always de-reference aliases
2422             } else {

2423                 throw new IllegalArgumentException("Illegal value for " +
2424                     DEREF_ALIASES + " property.");
2425             }
2426         } else {
2427             derefAliases = DEFAULT_DEREF_ALIASES;
2428         }
2429     }
2430 
2431     private void setRefSeparator(String sepStr) throws NamingException {
2432         if (sepStr != null && sepStr.length() > 0) {
2433             addrEncodingSeparator = sepStr.charAt(0);
2434         } else {
2435             addrEncodingSeparator = DEFAULT_REF_SEPARATOR;
2436         }
2437     }
2438 
2439     /**
2440      * Sets the limit on referral chains
2441      */
2442     private void setReferralLimit(String referralLimitProp) {


2498 
2499     /**
2500      * Sets the read timeout value
2501      */
2502     private void setReadTimeout(String readTimeoutProp) {
2503         if (readTimeoutProp != null) {
2504            readTimeout = Integer.parseInt(readTimeoutProp);
2505         } else {
2506             readTimeout = -1;
2507         }
2508     }
2509 
2510     /*
2511      * Extract URLs from a string. The format of the string is:
2512      *
2513      *     <urlstring > ::= "Referral:" <ldapurls>
2514      *     <ldapurls>   ::= <separator> <ldapurl> | <ldapurls>
2515      *     <separator>  ::= ASCII linefeed character (0x0a)
2516      *     <ldapurl>    ::= LDAP URL format (RFC 1959)
2517      */
2518     private static Vector extractURLs(String refString) {
2519 
2520         int separator = 0;
2521         int urlCount = 0;
2522 
2523         // count the number of URLs
2524         while ((separator = refString.indexOf('\n', separator)) >= 0) {
2525             separator++;
2526             urlCount++;
2527         }
2528 
2529         Vector referrals = new Vector(urlCount);
2530         int iURL;
2531         int i = 0;
2532 
2533         separator = refString.indexOf('\n');
2534         iURL = separator + 1;
2535         while ((separator = refString.indexOf('\n', iURL)) >= 0) {
2536             referrals.addElement(refString.substring(iURL, separator));
2537             iURL = separator + 1;
2538         }
2539         referrals.addElement(refString.substring(iURL));
2540 
2541         return referrals;
2542     }
2543 
2544     /*
2545      * Argument is a space-separated list of attribute IDs
2546      * Converts attribute IDs to lowercase before adding to built-in list.
2547      */
2548     private void setBinaryAttributes(String attrIds) {
2549         if (attrIds == null) {
2550             binaryAttrs = null;
2551         } else {
2552             binaryAttrs = new Hashtable(11, 0.75f);
2553             StringTokenizer tokens =
2554                 new StringTokenizer(attrIds.toLowerCase(), " ");
2555 
2556             while (tokens.hasMoreTokens()) {
2557                 binaryAttrs.put(tokens.nextToken(), Boolean.TRUE);
2558             }
2559         }
2560     }
2561 
2562    // ----------------- Connection  ---------------------
2563 
2564     protected void finalize() {
2565         try {
2566             close();
2567         } catch (NamingException e) {
2568             // ignore failures
2569         }
2570     }
2571 
2572     synchronized public void close() throws NamingException {


2584         // Enumerations that are keeping the connection alive
2585         if (enumCount > 0) {
2586             if (debug)
2587                 System.err.println("LdapCtx: close deferred");
2588             closeRequested = true;
2589             return;
2590         }
2591         closeConnection(SOFT_CLOSE);
2592 
2593 // %%%: RL: There is no need to set these to null, as they're just
2594 // variables whose contents and references will automatically
2595 // be cleaned up when they're no longer referenced.
2596 // Also, setting these to null creates problems for the attribute
2597 // schema-related methods, which need these to work.
2598 /*
2599         schemaTrees = null;
2600         envprops = null;
2601 */
2602     }
2603 

2604     public void reconnect(Control[] connCtls) throws NamingException {
2605         // Update environment
2606         envprops = (envprops == null
2607                 ? new Hashtable(5, 0.75f)
2608                 : (Hashtable)envprops.clone());
2609 
2610         if (connCtls == null) {
2611             envprops.remove(BIND_CONTROLS);
2612             bindCtls = null;
2613         } else {
2614             envprops.put(BIND_CONTROLS, bindCtls = cloneControls(connCtls));
2615         }
2616 
2617         sharable = false;  // can't share with existing contexts
2618         ensureOpen();      // open or reauthenticated
2619     }
2620 
2621     private void ensureOpen() throws NamingException {
2622         ensureOpen(false);
2623     }
2624 
2625     private void ensureOpen(boolean startTLS) throws NamingException {
2626 
2627         try {
2628             if (clnt == null) {
2629                 if (debug) {
2630                     System.err.println("LdapCtx: Reconnecting " + this);
2631                 }
2632 
2633                 // reset the cache before a new connection is established
2634                 schemaTrees = new Hashtable(11, 0.75f);
2635                 connect(startTLS);
2636 
2637             } else if (!sharable || startTLS) {
2638 
2639                 synchronized (clnt) {
2640                     if (!clnt.isLdapv3
2641                         || clnt.referenceCount > 1
2642                         || clnt.usingSaslStreams()) {
2643                         closeConnection(SOFT_CLOSE);
2644                     }
2645                 }
2646                 // reset the cache before a new connection is established
2647                 schemaTrees = new Hashtable(11, 0.75f);
2648                 connect(startTLS);
2649             }
2650 
2651         } finally {
2652             sharable = true;   // connection is now either new or single-use
2653                                // OK for others to start sharing again
2654         }
2655     }
2656 
2657     private void connect(boolean startTLS) throws NamingException {
2658         if (debug) { System.err.println("LdapCtx: Connecting " + this); }
2659 
2660         String user = null;             // authenticating user
2661         Object passwd = null;           // password for authenticating user
2662         String secProtocol = null;      // security protocol (e.g. "ssl")
2663         String socketFactory = null;    // socket factory
2664         String authMechanism = null;    // authentication mechanism
2665         String ver = null;
2666         int ldapVersion;                // LDAP protocol version
2667         boolean usePool = false;        // enable connection pooling


2829     }
2830 
2831 
2832    // ------------ Return code and Error messages  -----------------------
2833 
2834     protected void processReturnCode(LdapResult answer) throws NamingException {
2835         processReturnCode(answer, null, this, null, envprops, null);
2836     }
2837 
2838     void processReturnCode(LdapResult answer, Name remainName)
2839     throws NamingException {
2840         processReturnCode(answer,
2841                           (new CompositeName()).add(currentDN),
2842                           this,
2843                           remainName,
2844                           envprops,
2845                           fullyQualifiedName(remainName));
2846     }
2847 
2848     protected void processReturnCode(LdapResult res, Name resolvedName,
2849         Object resolvedObj, Name remainName, Hashtable envprops, String fullDN)
2850     throws NamingException {
2851 
2852         String msg = LdapClient.getErrorMessage(res.status, res.errorMessage);
2853         NamingException e;
2854         LdapReferralException r = null;
2855 
2856         switch (res.status) {
2857 
2858         case LdapClient.LDAP_SUCCESS:
2859 
2860             // handle Search continuation references
2861             if (res.referrals != null) {
2862 
2863                 msg = "Unprocessed Continuation Reference(s)";
2864 
2865                 if (handleReferrals == LdapClient.LDAP_REF_IGNORE) {
2866                     e = new PartialResultException(msg);
2867                     break;
2868                 }
2869 


2949          * Handle SLAPD-style referrals.
2950          *
2951          * Referrals received during name resolution should be followed
2952          * until one succeeds - the target entry is located. An exception
2953          * is thrown now to handle these.
2954          *
2955          * Referrals received during a search operation point to unexplored
2956          * parts of the directory and each should be followed. An exception
2957          * is thrown later (during results enumeration) to handle these.
2958          */
2959 
2960         case LdapClient.LDAP_PARTIAL_RESULTS:
2961 
2962             if (handleReferrals == LdapClient.LDAP_REF_IGNORE) {
2963                 e = new PartialResultException(msg);
2964                 break;
2965             }
2966 
2967             // extract SLAPD-style referrals from errorMessage
2968             if ((res.errorMessage != null) && (!res.errorMessage.equals(""))) {
2969                 res.referrals = extractURLs(res.errorMessage);


2970             } else {
2971                 e = new PartialResultException(msg);
2972                 break;
2973             }
2974 
2975             // build exception
2976             r = new LdapReferralException(resolvedName,
2977                 resolvedObj,
2978                 remainName,
2979                 msg,
2980                 envprops,
2981                 fullDN,
2982                 handleReferrals,
2983                 reqCtls);
2984 
2985             if (hopCount > 1) {
2986                 r.setHopCount(hopCount);
2987             }
2988             /*
2989              * %%%
2990              * SLAPD-style referrals received during name resolution
2991              * cannot be distinguished from those received during a
2992              * search operation. Since both must be handled differently
2993              * the following rule is applied:
2994              *
2995              *     If 1 referral and 0 entries is received then
2996              *     assume name resolution has not yet completed.
2997              */
2998             if (((res.entries == null) || (res.entries.size() == 0)) &&
2999                 (res.referrals.size() == 1)) {
3000 
3001                 r.setReferralInfo((Vector)res.referrals, false);
3002 
3003                 // check the hop limit
3004                 if (hopCount > referralHopLimit) {
3005                     NamingException lee =
3006                         new LimitExceededException("Referral limit exceeded");
3007                     lee.setRootCause(r);
3008                     e = lee;
3009 
3010                 } else {
3011                     e = r;
3012                 }
3013 
3014             } else {
3015                 r.setReferralInfo(res.referrals, true);
3016                 res.refEx = r;
3017                 return;
3018             }
3019             break;
3020 
3021         case LdapClient.LDAP_INVALID_DN_SYNTAX:


3267             this.reqCtls = addControl(reqCtls, manageReferralControl);
3268         } else {
3269             this.reqCtls = cloneControls(reqCtls);
3270         }
3271     }
3272 
3273     public Control[] getRequestControls() throws NamingException {
3274         return cloneControls(reqCtls);
3275     }
3276 
3277     public Control[] getConnectControls() throws NamingException {
3278         return cloneControls(bindCtls);
3279     }
3280 
3281     public Control[] getResponseControls() throws NamingException {
3282         return (respCtls != null)? convertControls(respCtls) : null;
3283     }
3284 
3285     /**
3286      * Narrow controls using own default factory and ControlFactory.
3287      * @param ctls A non-null Vector
3288      */
3289     Control[] convertControls(Vector ctls) throws NamingException {
3290         int count = ctls.size();
3291 
3292         if (count == 0) {
3293             return null;
3294         }
3295 
3296         Control[] controls = new Control[count];
3297 
3298         for (int i = 0; i < count; i++) {
3299             // Try own factory first
3300             controls[i] = myResponseControlFactory.getControlInstance(
3301                 (Control)ctls.elementAt(i));
3302 
3303             // Try assigned factories if own produced null
3304             if (controls[i] == null) {
3305                 controls[i] = ControlFactory.getControlInstance(
3306                 (Control)ctls.elementAt(i), this, envprops);
3307             }
3308         }
3309         return controls;
3310     }
3311 
3312     private static Control[] addControl(Control[] prevCtls, Control addition) {
3313         if (prevCtls == null) {
3314             return new Control[]{addition};
3315         }
3316 
3317         // Find it
3318         int found = findControl(prevCtls, addition);
3319         if (found != -1) {
3320             return prevCtls;  // no need to do it again
3321         }
3322 
3323         Control[] newCtls = new Control[prevCtls.length+1];
3324         System.arraycopy(prevCtls, 0, newCtls, 0, prevCtls.length);
3325         newCtls[prevCtls.length] = addition;
3326         return newCtls;




 206     private static final ControlFactory myResponseControlFactory =
 207         new DefaultResponseControlFactory();
 208     private static final Control manageReferralControl =
 209         new ManageReferralControl(false);
 210 
 211     private static final HierMemDirCtx EMPTY_SCHEMA = new HierMemDirCtx();
 212     static {
 213         EMPTY_SCHEMA.setReadOnly(
 214             new SchemaViolationException("Cannot update schema object"));
 215     }
 216 
 217     // ------------ Package private instance variables ----------------
 218     // Cannot be private; used by enums
 219 
 220         // ------- Inherited by derived context instances
 221 
 222     int port_number;                    // port number of server
 223     String hostname = null;             // host name of server (no brackets
 224                                         //   for IPv6 literals)
 225     LdapClient clnt = null;             // connection handle
 226     Hashtable<String, java.lang.Object> envprops = null; // environment properties of context
 227     int handleReferrals = DEFAULT_REFERRAL_MODE; // how referral is handled
 228     boolean hasLdapsScheme = false;     // true if the context was created
 229                                         //  using an LDAPS URL.
 230 
 231         // ------- Not inherited by derived context instances
 232 
 233     String currentDN;                   // DN of this context
 234     Name currentParsedDN;               // DN of this context
 235     Vector<Control> respCtls = null;    // Response controls read
 236     Control[] reqCtls = null;           // Controls to be sent with each request
 237 
 238 
 239     // ------------- Private instance variables ------------------------
 240 
 241         // ------- Inherited by derived context instances
 242 
 243     private OutputStream trace = null;  // output stream for BER debug output
 244     private boolean netscapeSchemaBug = false;       // workaround
 245     private Control[] bindCtls = null;  // Controls to be sent with LDAP "bind"
 246     private int referralHopLimit = DEFAULT_REFERRAL_LIMIT;  // max referral
 247     private Hashtable<String, DirContext> schemaTrees = null; // schema root of this context
 248     private int batchSize = DEFAULT_BATCH_SIZE;      // batch size for search results
 249     private boolean deleteRDN = DEFAULT_DELETE_RDN;  // delete the old RDN when modifying DN
 250     private boolean typesOnly = DEFAULT_TYPES_ONLY;  // return attribute types (no values)
 251     private int derefAliases = DEFAULT_DEREF_ALIASES;// de-reference alias entries during searching
 252     private char addrEncodingSeparator = DEFAULT_REF_SEPARATOR;  // encoding RefAddr
 253 
 254     private Hashtable<String, Boolean> binaryAttrs = null; // attr values returned as byte[]
 255     private int connectTimeout = -1;         // no timeout value
 256     private int readTimeout = -1;            // no timeout value
 257     private boolean waitForReply = true;     // wait for search response
 258     private int replyQueueSize  = -1;        // unlimited queue size
 259     private boolean useSsl = false;          // true if SSL protocol is active
 260     private boolean useDefaultPortNumber = false; // no port number was supplied
 261 
 262         // ------- Not inherited by derived context instances
 263 
 264     // True if this context was created by another LdapCtx.
 265     private boolean parentIsLdapCtx = false; // see composeName()
 266 
 267     private int hopCount = 1;                // current referral hop count
 268     private String url = null;               // URL of context; see getURL()
 269     private EventSupport eventSupport;       // Event support helper for this ctx
 270     private boolean unsolicited = false;     // if there unsolicited listeners
 271     private boolean sharable = true;         // can share connection with other ctx
 272 
 273     // -------------- Constructors  -----------------------------------
 274 
 275     @SuppressWarnings("unchecked")
 276     public LdapCtx(String dn, String host, int port_number,
 277             Hashtable<?,?> props,
 278             boolean useSsl) throws NamingException {
 279 
 280         this.useSsl = this.hasLdapsScheme = useSsl;
 281 
 282         if (props != null) {
 283             envprops = (Hashtable<String, java.lang.Object>) props.clone();
 284 
 285             // SSL env prop overrides the useSsl argument
 286             if ("ssl".equals(envprops.get(Context.SECURITY_PROTOCOL))) {
 287                 this.useSsl = true;
 288             }
 289 
 290             // %%% These are only examined when the context is created
 291             // %%% because they are only for debugging or workaround purposes.
 292             trace = (OutputStream)envprops.get(TRACE_BER);
 293 
 294             if (props.get(NETSCAPE_SCHEMA_BUG) != null ||
 295                 props.get(OLD_NETSCAPE_SCHEMA_BUG) != null) {
 296                 netscapeSchemaBug = true;
 297             }
 298         }
 299 
 300         currentDN = (dn != null) ? dn : "";
 301         currentParsedDN = parser.parse(currentDN);
 302 
 303         hostname = (host != null && host.length() > 0) ? host : DEFAULT_HOST;
 304         if (hostname.charAt(0) == '[') {
 305             hostname = hostname.substring(1, hostname.length() - 1);
 306         }
 307 
 308         if (port_number > 0) {
 309             this.port_number = port_number;
 310         } else {
 311             this.port_number = this.useSsl ? DEFAULT_SSL_PORT : DEFAULT_PORT;
 312             this.useDefaultPortNumber = true;
 313         }
 314 
 315         schemaTrees = new Hashtable<>(11, 0.75f);
 316         initEnv();
 317         try {
 318             connect(false);
 319         } catch (NamingException e) {
 320             try {
 321                 close();
 322             } catch (Exception e2) {
 323                 // Nothing
 324             }
 325             throw e;
 326         }
 327     }
 328 
 329     LdapCtx(LdapCtx existing, String newDN) throws NamingException {
 330         useSsl = existing.useSsl;
 331         hasLdapsScheme = existing.hasLdapsScheme;
 332         useDefaultPortNumber = existing.useDefaultPortNumber;
 333 
 334         hostname = existing.hostname;
 335         port_number = existing.port_number;


 903 
 904     /**
 905      * Adds attributes from RDN to attrs if not already present.
 906      * Note that if attrs already contains an attribute by the same name,
 907      * or if the distinguished name is empty, then leave attrs unchanged.
 908      *
 909      * @param dn The non-null DN of the entry to add
 910      * @param attrs The non-null attributes of entry to add
 911      * @param directUpdate Whether attrs can be updated directly
 912      * @returns Non-null attributes with attributes from the RDN added
 913      */
 914     private static Attributes addRdnAttributes(String dn, Attributes attrs,
 915         boolean directUpdate) throws NamingException {
 916 
 917             // Handle the empty name
 918             if (dn.equals("")) {
 919                 return attrs;
 920             }
 921 
 922             // Parse string name into list of RDNs
 923             List<Rdn> rdnList = (new LdapName(dn)).getRdns();

 924 
 925             // Get leaf RDN
 926             Rdn rdn = rdnList.get(rdnList.size() - 1);

 927             Attributes nameAttrs = rdn.toAttributes();
 928 
 929             // Add attributes of RDN to attrs if not already there
 930             NamingEnumeration<? extends Attribute> enum_ = nameAttrs.getAll();
 931             Attribute nameAttr;
 932             while (enum_.hasMore()) {
 933                 nameAttr = enum_.next();
 934 
 935                 // If attrs already has the attribute, don't change or add to it
 936                 if (attrs.get(nameAttr.getID()) ==  null) {
 937 
 938                     /**
 939                      * When attrs.isCaseIgnored() is false, attrs.get() will
 940                      * return null when the case mis-matches for otherwise
 941                      * equal attrIDs.
 942                      * As the attrIDs' case is irrelevant for LDAP, ignore
 943                      * the case of attrIDs even when attrs.isCaseIgnored() is
 944                      * false. This is done by explicitly comparing the elements in
 945                      * the enumeration of IDs with their case ignored.
 946                      */
 947                     if (!attrs.isCaseIgnored() &&
 948                             containsIgnoreCase(attrs.getIDs(), nameAttr.getID())) {
 949                         continue;
 950                     }
 951 
 952                     if (!directUpdate) {
 953                         attrs = (Attributes)attrs.clone();
 954                         directUpdate = true;
 955                     }
 956                     attrs.put(nameAttr);
 957                 }
 958             }
 959 
 960             return attrs;
 961     }
 962 
 963 
 964     private static boolean containsIgnoreCase(NamingEnumeration<String> enumStr,
 965                                 String str) throws NamingException {
 966         String strEntry;
 967 
 968         while (enumStr.hasMore()) {
 969              strEntry = enumStr.next();
 970              if (strEntry.equalsIgnoreCase(str)) {
 971                 return true;
 972              }
 973         }
 974         return false;
 975     }
 976 
 977 
 978     private void adjustDeleteStatus(String fname, LdapResult answer) {
 979         if (answer.status == LdapClient.LDAP_NO_SUCH_OBJECT &&
 980             answer.matchedDN != null) {
 981             try {
 982                 // %%% RL: are there any implications for referrals?
 983 
 984                 Name orig = parser.parse(fname);
 985                 Name matched = parser.parse(answer.matchedDN);
 986                 if ((orig.size() - matched.size()) == 1)
 987                     answer.status = LdapClient.LDAP_SUCCESS;
 988             } catch (NamingException e) {}
 989         }
 990     }
 991 
 992     /*
 993      * Append the the second Vector onto the first Vector
 994      * (v2 must be non-null)
 995      */
 996     private static <T> Vector<T> appendVector(Vector<T> v1, Vector<T> v2) {
 997         if (v1 == null) {
 998             v1 = v2;
 999         } else {
1000             for (int i = 0; i < v2.size(); i++) {
1001                 v1.addElement(v2.elementAt(i));
1002             }
1003         }
1004         return v1;
1005     }
1006 
1007     // ------------- Lookups and Browsing -------------------------
1008     // lookup/lookupLink
1009     // list/listBindings
1010 
1011     protected Object c_lookupLink(Name name, Continuation cont)
1012             throws NamingException {
1013         return c_lookup(name, cont);
1014     }
1015 
1016     protected Object c_lookup(Name name, Continuation cont)


1021 
1022         try {
1023             SearchControls cons = new SearchControls();
1024             cons.setSearchScope(SearchControls.OBJECT_SCOPE);
1025             cons.setReturningAttributes(null); // ask for all attributes
1026             cons.setReturningObjFlag(true); // need values to construct obj
1027 
1028             LdapResult answer = doSearchOnce(name, "(objectClass=*)", cons, true);
1029             respCtls = answer.resControls; // retrieve response controls
1030 
1031             // should get back 1 SearchResponse and 1 SearchResult
1032 
1033             if (answer.status != LdapClient.LDAP_SUCCESS) {
1034                 processReturnCode(answer, name);
1035             }
1036 
1037             if (answer.entries == null || answer.entries.size() != 1) {
1038                 // found it but got no attributes
1039                 attrs = new BasicAttributes(LdapClient.caseIgnore);
1040             } else {
1041                 LdapEntry entry = answer.entries.elementAt(0);
1042                 attrs = entry.attributes;
1043 
1044                 Vector<Control> entryCtls = entry.respCtls; // retrieve entry controls
1045                 if (entryCtls != null) {
1046                     appendVector(respCtls, entryCtls); // concatenate controls
1047                 }
1048             }
1049 
1050             if (attrs.get(Obj.JAVA_ATTRIBUTES[Obj.CLASSNAME]) != null) {
1051                 // serialized object or object reference
1052                 obj = Obj.decodeObject(attrs);
1053             }
1054             if (obj == null) {
1055                 obj = new LdapCtx(this, fullyQualifiedName(name));
1056             }
1057         } catch (LdapReferralException e) {
1058             if (handleReferrals == LdapClient.LDAP_REF_THROW)
1059                 throw cont.fillInException(e);
1060 
1061             // process the referrals sequentially
1062             while (true) {
1063 
1064                 LdapReferralContext refCtx =


1080 
1081         } catch (NamingException e) {
1082             throw cont.fillInException(e);
1083         }
1084 
1085         try {
1086             return DirectoryManager.getObjectInstance(obj, name,
1087                 this, envprops, attrs);
1088 
1089         } catch (NamingException e) {
1090             throw cont.fillInException(e);
1091 
1092         } catch (Exception e) {
1093             NamingException e2 = new NamingException(
1094                     "problem generating object using object factory");
1095             e2.setRootCause(e);
1096             throw cont.fillInException(e2);
1097         }
1098     }
1099 
1100     protected NamingEnumeration<NameClassPair> c_list(Name name, Continuation cont)
1101             throws NamingException {
1102         SearchControls cons = new SearchControls();
1103         String[] classAttrs = new String[2];
1104 
1105         classAttrs[0] = Obj.JAVA_ATTRIBUTES[Obj.OBJECT_CLASS];
1106         classAttrs[1] = Obj.JAVA_ATTRIBUTES[Obj.CLASSNAME];
1107         cons.setReturningAttributes(classAttrs);
1108 
1109         // set this flag to override the typesOnly flag
1110         cons.setReturningObjFlag(true);
1111 
1112         cont.setError(this, name);
1113 
1114         LdapResult answer = null;
1115 
1116         try {
1117             answer = doSearch(name, "(objectClass=*)", cons, true, true);
1118 
1119             // list result may contain continuation references
1120             if ((answer.status != LdapClient.LDAP_SUCCESS) ||


1153             LdapNamingEnumeration res =
1154                 new LdapNamingEnumeration(this, answer, name, cont);
1155 
1156             res.setNamingException(
1157                     (LimitExceededException)cont.fillInException(e));
1158             return res;
1159 
1160         } catch (PartialResultException e) {
1161             LdapNamingEnumeration res =
1162                 new LdapNamingEnumeration(this, answer, name, cont);
1163 
1164             res.setNamingException(
1165                     (PartialResultException)cont.fillInException(e));
1166             return res;
1167 
1168         } catch (NamingException e) {
1169             throw cont.fillInException(e);
1170         }
1171     }
1172 
1173     @SuppressWarnings("unchecked")
1174     protected NamingEnumeration<Binding> c_listBindings(Name name, Continuation cont)
1175             throws NamingException {
1176 
1177         SearchControls cons = new SearchControls();
1178         cons.setReturningAttributes(null); // ask for all attributes
1179         cons.setReturningObjFlag(true); // need values to construct obj
1180 
1181         cont.setError(this, name);
1182 
1183         LdapResult answer = null;
1184 
1185         try {
1186             answer = doSearch(name, "(objectClass=*)", cons, true, true);
1187 
1188             // listBindings result may contain continuation references
1189             if ((answer.status != LdapClient.LDAP_SUCCESS) ||
1190                 (answer.referrals != null)) {
1191                 processReturnCode(answer, name);
1192             }
1193 
1194             return (NamingEnumeration)
1195                     new LdapBindingEnumeration(this, answer, name, cont);
1196 
1197         } catch (LdapReferralException e) {
1198             if (handleReferrals == LdapClient.LDAP_REF_THROW)
1199                 throw cont.fillInException(e);
1200 
1201             // process the referrals sequentially
1202             while (true) {
1203 
1204                 LdapReferralContext refCtx =
1205                     (LdapReferralContext)e.getReferralContext(envprops, bindCtls);
1206 
1207                 // repeat the original operation at the new context
1208                 try {
1209 
1210                     return refCtx.listBindings(name);
1211 
1212                 } catch (LdapReferralException re) {
1213                     e = re;
1214                     continue;
1215 
1216                 } finally {
1217                     // Make sure we close referral context
1218                     refCtx.close();
1219                 }
1220             }
1221         } catch (LimitExceededException e) {
1222             LdapBindingEnumeration res =
1223                 new LdapBindingEnumeration(this, answer, name, cont);
1224 
1225             res.setNamingException(
1226                     (LimitExceededException)cont.fillInException(e));
1227             return (NamingEnumeration)res;
1228 
1229         } catch (PartialResultException e) {
1230             LdapBindingEnumeration res =
1231                 new LdapBindingEnumeration(this, answer, name, cont);
1232 
1233             res.setNamingException(
1234                     (PartialResultException)cont.fillInException(e));
1235             return (NamingEnumeration)res;
1236 
1237         } catch (NamingException e) {
1238             throw cont.fillInException(e);
1239         }
1240     }
1241 
1242     // --------------- Name-related Methods -----------------------
1243     // -- getNameParser/getNameInNamespace/composeName
1244 
1245     protected NameParser c_getNameParser(Name name, Continuation cont)
1246             throws NamingException
1247     {
1248         // ignore name, always return same parser
1249         cont.setSuccess();
1250         return parser;
1251     }
1252 
1253     public String getNameInNamespace() {
1254         return currentDN;
1255     }


1322         cont.setError(this, name);
1323 
1324         SearchControls cons = new SearchControls();
1325         cons.setSearchScope(SearchControls.OBJECT_SCOPE);
1326         cons.setReturningAttributes(attrIds);
1327 
1328         try {
1329             LdapResult answer =
1330                 doSearchOnce(name, "(objectClass=*)", cons, true);
1331             respCtls = answer.resControls; // retrieve response controls
1332 
1333             if (answer.status != LdapClient.LDAP_SUCCESS) {
1334                 processReturnCode(answer, name);
1335             }
1336 
1337             if (answer.entries == null || answer.entries.size() != 1) {
1338                 return new BasicAttributes(LdapClient.caseIgnore);
1339             }
1340 
1341             // get attributes from result
1342             LdapEntry entry = answer.entries.elementAt(0);
1343 
1344             Vector<Control> entryCtls = entry.respCtls; // retrieve entry controls
1345             if (entryCtls != null) {
1346                 appendVector(respCtls, entryCtls); // concatenate controls
1347             }
1348 
1349             // do this so attributes can find their schema
1350             setParents(entry.attributes, (Name) name.clone());
1351 
1352             return (entry.attributes);
1353 
1354         } catch (LdapReferralException e) {
1355             if (handleReferrals == LdapClient.LDAP_REF_THROW)
1356                 throw cont.fillInException(e);
1357 
1358             // process the referrals sequentially
1359             while (true) {
1360 
1361                 LdapReferralContext refCtx =
1362                     (LdapReferralContext)e.getReferralContext(envprops, bindCtls);
1363 
1364                 // repeat the original operation at the new context


1383 
1384     protected void c_modifyAttributes(Name name, int mod_op, Attributes attrs,
1385                                       Continuation cont)
1386             throws NamingException {
1387 
1388         cont.setError(this, name);
1389 
1390         try {
1391             ensureOpen();
1392 
1393             if (attrs == null || attrs.size() == 0) {
1394                 return; // nothing to do
1395             }
1396             String newDN = fullyQualifiedName(name);
1397             int jmod_op = convertToLdapModCode(mod_op);
1398 
1399             // construct mod list
1400             int[] jmods = new int[attrs.size()];
1401             Attribute[] jattrs = new Attribute[attrs.size()];
1402 
1403             NamingEnumeration<? extends Attribute> ae = attrs.getAll();
1404             for(int i = 0; i < jmods.length && ae.hasMore(); i++) {
1405                 jmods[i] = jmod_op;
1406                 jattrs[i] = ae.next();
1407             }
1408 
1409             LdapResult answer = clnt.modify(newDN, jmods, jattrs, reqCtls);
1410             respCtls = answer.resControls; // retrieve response controls
1411 
1412             if (answer.status != LdapClient.LDAP_SUCCESS) {
1413                 processReturnCode(answer, name);
1414                 return;
1415             }
1416 
1417         } catch (LdapReferralException e) {
1418             if (handleReferrals == LdapClient.LDAP_REF_THROW)
1419                 throw cont.fillInException(e);
1420 
1421             // process the referrals sequentially
1422             while (true) {
1423 
1424                 LdapReferralContext refCtx =
1425                     (LdapReferralContext)e.getReferralContext(envprops, bindCtls);
1426 


1550             throws NamingException {
1551         cont.setError(this, name);
1552 
1553         try {
1554             // retrieve the objectClass attribute from LDAP
1555             Attribute objectClassAttr = c_getAttributes(name,
1556                 new String[]{"objectclass"}, cont).get("objectclass");
1557             if (objectClassAttr == null || objectClassAttr.size() == 0) {
1558                 return EMPTY_SCHEMA;
1559             }
1560 
1561             // retrieve the root of the ObjectClass schema tree
1562             Context ocSchema = (Context) c_getSchema(name, cont).lookup(
1563                 LdapSchemaParser.OBJECTCLASS_DEFINITION_NAME);
1564 
1565             // create a context to hold the schema objects representing the object
1566             // classes
1567             HierMemDirCtx objectClassCtx = new HierMemDirCtx();
1568             DirContext objectClassDef;
1569             String objectClassName;
1570             for (Enumeration<?> objectClasses = objectClassAttr.getAll();
1571                 objectClasses.hasMoreElements(); ) {
1572                 objectClassName = (String)objectClasses.nextElement();
1573                 // %%% Should we fail if not found, or just continue?
1574                 objectClassDef = (DirContext)ocSchema.lookup(objectClassName);
1575                 objectClassCtx.bind(objectClassName, objectClassDef);
1576             }
1577 
1578             // Make context read-only
1579             objectClassCtx.setReadOnly(
1580                 new SchemaViolationException("Cannot update schema object"));
1581             return (DirContext)objectClassCtx;
1582 
1583         } catch (NamingException e) {
1584             throw cont.fillInException(e);
1585         }
1586     }
1587 
1588     /*
1589      * getSchemaTree first looks to see if we have already built a
1590      * schema tree for the given entry. If not, it builds a new one and
1591      * stores it in our private hash table
1592      */
1593     private DirContext getSchemaTree(Name name) throws NamingException {
1594         String subschemasubentry = getSchemaEntry(name, true);
1595 
1596         DirContext schemaTree = schemaTrees.get(subschemasubentry);
1597 
1598         if(schemaTree==null) {
1599             if(debug){System.err.println("LdapCtx: building new schema tree " + this);}
1600             schemaTree = buildSchemaTree(subschemasubentry);
1601             schemaTrees.put(subschemasubentry, schemaTree);
1602         }
1603 
1604         return schemaTree;
1605     }
1606 
1607     /*
1608      * buildSchemaTree builds the schema tree corresponding to the
1609      * given subschemasubentree
1610      */
1611     private DirContext buildSchemaTree(String subschemasubentry)
1612         throws NamingException {
1613 
1614         // get the schema entry itself
1615         // DO ask for return object here because we need it to
1616         // create context. Since asking for all attrs, we won't
1617         // be transmitting any specific attrIDs (like Java-specific ones).
1618         SearchControls constraints = new
1619             SearchControls(SearchControls.OBJECT_SCOPE,
1620                 0, 0, /* count and time limits */
1621                 SCHEMA_ATTRIBUTES /* return schema attrs */,
1622                 true /* return obj */,
1623                 false /*deref link */ );
1624 
1625         Name sse = (new CompositeName()).add(subschemasubentry);
1626         NamingEnumeration<SearchResult> results =
1627             searchAux(sse, "(objectClass=subschema)", constraints,
1628             false, true, new Continuation());
1629 
1630         if(!results.hasMore()) {
1631             throw new OperationNotSupportedException(
1632                 "Cannot get read subschemasubentry: " + subschemasubentry);
1633         }
1634         SearchResult result = results.next();
1635         results.close();
1636 
1637         Object obj = result.getObject();
1638         if(!(obj instanceof LdapCtx)) {
1639             throw new NamingException(
1640                 "Cannot get schema object as DirContext: " + subschemasubentry);
1641         }
1642 
1643         return LdapSchemaCtx.createSchemaTree(envprops, subschemasubentry,
1644             (LdapCtx)obj /* schema entry */,
1645             result.getAttributes() /* schema attributes */,
1646             netscapeSchemaBug);
1647    }
1648 
1649     /*
1650      * getSchemaEntree returns the DN of the subschemasubentree for the
1651      * given entree. It first looks to see if the given entry has
1652      * a subschema different from that of the root DIT (by looking for
1653      * a "subschemasubentry" attribute). If it doesn't find one, it returns
1654      * the one for the root of the DIT (by looking for the root's


1659      * queries. If this function trys a serarch on a v2 server that
1660      * doesn't support schema, one of these two things will happen:
1661      * 1) It will get an exception when querying the root DSE
1662      * 2) It will not find a subschemasubentry on the root DSE
1663      * If either of these things occur and the server is not v3, we
1664      * throw OperationNotSupported.
1665      *
1666      * the relative flag tells whether the given name is relative to this
1667      * context.
1668      */
1669     private String getSchemaEntry(Name name, boolean relative)
1670         throws NamingException {
1671 
1672         // Asks for operational attribute "subschemasubentry"
1673         SearchControls constraints = new SearchControls(SearchControls.OBJECT_SCOPE,
1674             0, 0, /* count and time limits */
1675             new String[]{"subschemasubentry"} /* attr to return */,
1676             false /* returning obj */,
1677             false /* deref link */);
1678 
1679         NamingEnumeration<SearchResult> results;
1680         try {
1681             results = searchAux(name, "objectclass=*", constraints, relative,
1682                 true, new Continuation());
1683 
1684         } catch (NamingException ne) {
1685             if (!clnt.isLdapv3 && currentDN.length() == 0 && name.isEmpty()) {
1686                 // we got an error looking for a root entry on an ldapv2
1687                 // server. The server must not support schema.
1688                 throw new OperationNotSupportedException(
1689                     "Cannot get schema information from server");
1690             } else {
1691                 throw ne;
1692             }
1693         }
1694 
1695         if (!results.hasMoreElements()) {
1696             throw new ConfigurationException(
1697                 "Requesting schema of nonexistent entry: " + name);
1698         }
1699 
1700         SearchResult result = results.next();
1701         results.close();
1702 
1703         Attribute schemaEntryAttr =
1704             result.getAttributes().get("subschemasubentry");
1705         //System.err.println("schema entry attrs: " + schemaEntryAttr);
1706 
1707         if (schemaEntryAttr == null || schemaEntryAttr.size() < 0) {
1708             if (currentDN.length() == 0 && name.isEmpty()) {
1709                 // the server doesn't have a subschemasubentry in its root DSE.
1710                 // therefore, it doesn't support schema.
1711                 throw new OperationNotSupportedException(
1712                     "Cannot read subschemasubentry of root DSE");
1713             } else {
1714                 return getSchemaEntry(new CompositeName(), false);
1715             }
1716         }
1717 
1718         return (String)(schemaEntryAttr.get()); // return schema entry name
1719     }
1720 
1721     // package-private; used by search enum.
1722     // Set attributes to point to this context in case some one
1723     // asked for their schema
1724     void setParents(Attributes attrs, Name name) throws NamingException {
1725         NamingEnumeration<? extends Attribute> ae = attrs.getAll();
1726         while(ae.hasMore()) {
1727             ((LdapAttribute) ae.next()).setParent(this, name);
1728         }
1729     }
1730 
1731     /*
1732      * Returns the URL associated with this context; used by LdapAttribute
1733      * after deserialization to get pointer to this context.
1734      */
1735     String getURL() {
1736         if (url == null) {
1737             url = LdapURL.toUrlString(hostname, port_number, currentDN,
1738                 hasLdapsScheme);
1739         }
1740 
1741         return url;
1742     }
1743 
1744    // --------------------- Searches -----------------------------
1745     protected NamingEnumeration<SearchResult> c_search(Name name,
1746                                          Attributes matchingAttributes,
1747                                          Continuation cont)
1748             throws NamingException {
1749         return c_search(name, matchingAttributes, null, cont);
1750     }
1751 
1752     protected NamingEnumeration<SearchResult> c_search(Name name,
1753                                          Attributes matchingAttributes,
1754                                          String[] attributesToReturn,
1755                                          Continuation cont)
1756             throws NamingException {
1757         SearchControls cons = new SearchControls();
1758         cons.setReturningAttributes(attributesToReturn);
1759         String filter;
1760         try {
1761             filter = SearchFilter.format(matchingAttributes);
1762         } catch (NamingException e) {
1763             cont.setError(this, name);
1764             throw cont.fillInException(e);
1765         }
1766         return c_search(name, filter, cons, cont);
1767     }
1768 
1769     protected NamingEnumeration<SearchResult> c_search(Name name,
1770                                          String filter,
1771                                          SearchControls cons,
1772                                          Continuation cont)
1773             throws NamingException {
1774         return searchAux(name, filter, cloneSearchControls(cons), true,
1775                  waitForReply, cont);
1776     }
1777 
1778     protected NamingEnumeration<SearchResult> c_search(Name name,
1779                                          String filterExpr,
1780                                          Object[] filterArgs,
1781                                          SearchControls cons,
1782                                          Continuation cont)
1783             throws NamingException {
1784         String strfilter;
1785         try {
1786             strfilter = SearchFilter.format(filterExpr, filterArgs);
1787         } catch (NamingException e) {
1788             cont.setError(this, name);
1789             throw cont.fillInException(e);
1790         }
1791         return c_search(name, strfilter, cons, cont);
1792     }
1793 
1794         // Used by NamingNotifier
1795     @SuppressWarnings("unchecked")
1796     NamingEnumeration<SearchResult> searchAux(Name name,
1797         String filter,
1798         SearchControls cons,
1799         boolean relative,
1800         boolean waitForReply, Continuation cont) throws NamingException {
1801 
1802         LdapResult answer = null;
1803         String[] tokens = new String[2];    // stores ldap compare op. values
1804         String[] reqAttrs;                  // remember what was asked
1805 
1806         if (cons == null) {
1807             cons = new SearchControls();
1808         }
1809         reqAttrs = cons.getReturningAttributes();
1810 
1811         // if objects are requested then request the Java attributes too
1812         // so that the objects can be constructed
1813         if (cons.getReturningObjFlag()) {
1814             if (reqAttrs != null) {
1815 
1816                 // check for presence of "*" (user attributes wildcard)


1834             }
1835         }
1836 
1837         LdapCtx.SearchArgs args =
1838             new LdapCtx.SearchArgs(name, filter, cons, reqAttrs);
1839 
1840         cont.setError(this, name);
1841         try {
1842             // see if this can be done as a compare, otherwise do a search
1843             if (searchToCompare(filter, cons, tokens)){
1844                 //System.err.println("compare triggered");
1845                 answer = compare(name, tokens[0], tokens[1]);
1846                 if (! (answer.compareToSearchResult(fullyQualifiedName(name)))){
1847                     processReturnCode(answer, name);
1848                 }
1849             } else {
1850                 answer = doSearch(name, filter, cons, relative, waitForReply);
1851                 // search result may contain referrals
1852                 processReturnCode(answer, name);
1853             }
1854             return (NamingEnumeration)
1855                 new LdapSearchEnumeration(this, answer,
1856                     fullyQualifiedName(name), args, cont);
1857 
1858         } catch (LdapReferralException e) {
1859             if (handleReferrals == LdapClient.LDAP_REF_THROW)
1860                 throw cont.fillInException(e);
1861 
1862             // process the referrals sequentially
1863             while (true) {
1864 
1865                 LdapReferralContext refCtx =
1866                     (LdapReferralContext)e.getReferralContext(envprops, bindCtls);
1867 
1868                 // repeat the original operation at the new context
1869                 try {
1870 
1871                     return refCtx.search(name, filter, cons);
1872 
1873                 } catch (LdapReferralException re) {
1874                     e = re;
1875                     continue;
1876 
1877                 } finally {
1878                     // Make sure we close referral context
1879                     refCtx.close();
1880                 }
1881             }
1882 
1883         } catch (LimitExceededException e) {
1884             LdapSearchEnumeration res =
1885                 new LdapSearchEnumeration(this, answer, fullyQualifiedName(name),
1886                                           args, cont);
1887             res.setNamingException(e);
1888             return (NamingEnumeration)res;
1889 
1890         } catch (PartialResultException e) {
1891             LdapSearchEnumeration res =
1892                 new LdapSearchEnumeration(this, answer, fullyQualifiedName(name),
1893                                           args, cont);
1894 
1895             res.setNamingException(e);
1896             return (NamingEnumeration)res;
1897 
1898         } catch (IOException e) {
1899             NamingException e2 = new CommunicationException(e.getMessage());
1900             e2.setRootCause(e);
1901             throw cont.fillInException(e2);
1902 
1903         } catch (NamingException e) {
1904             throw cont.fillInException(e);
1905         }
1906     }
1907 
1908 
1909     LdapResult getSearchReply(LdapClient eClnt, LdapResult res)
1910             throws NamingException {
1911         // ensureOpen() won't work here because
1912         // session was associated with previous connection
1913 
1914         // %%% RL: we can actually allow the enumeration to continue
1915         // using the old handle but other weird things might happen
1916         // when we hit a referral


2130         }
2131         String[] retAttrs = cons.getReturningAttributes();
2132         if (retAttrs != null) {
2133             String[] attrs = new String[retAttrs.length];
2134             System.arraycopy(retAttrs, 0, attrs, 0, retAttrs.length);
2135             retAttrs = attrs;
2136         }
2137         return new SearchControls(cons.getSearchScope(),
2138                                   cons.getCountLimit(),
2139                                   cons.getTimeLimit(),
2140                                   retAttrs,
2141                                   cons.getReturningObjFlag(),
2142                                   cons.getDerefLinkFlag());
2143     }
2144 
2145    // -------------- Environment Properties ------------------
2146 
2147     /**
2148      * Override with noncloning version.
2149      */
2150     protected Hashtable<String, Object> p_getEnvironment() {
2151         return envprops;
2152     }
2153 
2154     @SuppressWarnings("unchecked")
2155     public Hashtable<String, Object> getEnvironment() throws NamingException {
2156         return (envprops == null
2157                 ? new Hashtable<String, Object>(5, 0.75f)
2158                 : (Hashtable<String, Object>)envprops.clone());
2159     }
2160 
2161     @SuppressWarnings("unchecked")
2162     public Object removeFromEnvironment(String propName)
2163         throws NamingException {
2164 
2165         // not there; just return
2166         if (envprops == null || envprops.get(propName) == null) {
2167             return null;
2168         }
2169         switch (propName) {
2170             case REF_SEPARATOR:
2171                 addrEncodingSeparator = DEFAULT_REF_SEPARATOR;
2172                 break;
2173             case TYPES_ONLY:
2174                 typesOnly = DEFAULT_TYPES_ONLY;
2175                 break;
2176             case DELETE_RDN:
2177                 deleteRDN = DEFAULT_DELETE_RDN;
2178                 break;
2179             case DEREF_ALIASES:
2180                 derefAliases = DEFAULT_DEREF_ALIASES;
2181                 break;
2182             case Context.BATCHSIZE:
2183                 batchSize = DEFAULT_BATCH_SIZE;
2184                 break;
2185             case REFERRAL_LIMIT:
2186                 referralHopLimit = DEFAULT_REFERRAL_LIMIT;
2187                 break;
2188             case Context.REFERRAL:
2189                 setReferralMode(null, true);
2190                 break;
2191             case BINARY_ATTRIBUTES:
2192                 setBinaryAttributes(null);
2193                 break;
2194             case CONNECT_TIMEOUT:
2195                 connectTimeout = -1;
2196                 break;
2197             case READ_TIMEOUT:
2198                 readTimeout = -1;
2199                 break;
2200             case WAIT_FOR_REPLY:
2201                 waitForReply = true;
2202                 break;
2203             case REPLY_QUEUE_SIZE:
2204                 replyQueueSize = -1;
2205                 break;
2206     
2207             // The following properties affect the connection
2208 
2209             case Context.SECURITY_PROTOCOL:
2210                 closeConnection(SOFT_CLOSE);
2211                 // De-activate SSL and reset the context's url and port number
2212                 if (useSsl && !hasLdapsScheme) {
2213                     useSsl = false;
2214                     url = null;
2215                     if (useDefaultPortNumber) {
2216                         port_number = DEFAULT_PORT;
2217                     }
2218                 }
2219                 break;
2220             case VERSION:
2221             case SOCKET_FACTORY:
2222                 closeConnection(SOFT_CLOSE);
2223                 break;
2224             case Context.SECURITY_AUTHENTICATION:
2225             case Context.SECURITY_PRINCIPAL:
2226             case Context.SECURITY_CREDENTIALS:
2227                 sharable = false;
2228                 break;
2229         }
2230 
2231         // Update environment; reconnection will use new props
2232         envprops = (Hashtable<String, Object>)envprops.clone();
2233         return envprops.remove(propName);
2234     }
2235 
2236     @SuppressWarnings("unchecked")
2237     public Object addToEnvironment(String propName, Object propVal)
2238         throws NamingException {
2239 
2240             // If adding null, call remove
2241             if (propVal == null) {
2242                 return removeFromEnvironment(propName);
2243             }
2244         switch (propName) {
2245             case REF_SEPARATOR:
2246                 setRefSeparator((String)propVal);
2247                 break;
2248             case TYPES_ONLY:
2249                 setTypesOnly((String)propVal);
2250                 break;
2251             case DELETE_RDN:
2252                 setDeleteRDN((String)propVal);
2253                 break;
2254             case DEREF_ALIASES:
2255                 setDerefAliases((String)propVal);
2256                 break;
2257             case Context.BATCHSIZE:
2258                 setBatchSize((String)propVal);
2259                 break;
2260             case REFERRAL_LIMIT:
2261                 setReferralLimit((String)propVal);
2262                 break;
2263             case Context.REFERRAL:
2264                 setReferralMode((String)propVal, true);
2265                 break;
2266             case BINARY_ATTRIBUTES:
2267                 setBinaryAttributes((String)propVal);
2268                 break;
2269             case CONNECT_TIMEOUT:
2270                 setConnectTimeout((String)propVal);
2271                 break;
2272             case READ_TIMEOUT:
2273                 setReadTimeout((String)propVal);
2274                 break;
2275             case WAIT_FOR_REPLY:
2276                 setWaitForReply((String)propVal);
2277                 break;
2278             case REPLY_QUEUE_SIZE:
2279                 setReplyQueueSize((String)propVal);
2280                 break;
2281 
2282 // The following properties affect the connection
2283 
2284             case Context.SECURITY_PROTOCOL:
2285                 closeConnection(SOFT_CLOSE);
2286                 // Activate SSL and reset the context's url and port number
2287                 if ("ssl".equals(propVal)) {
2288                     useSsl = true;
2289                     url = null;
2290                     if (useDefaultPortNumber) {
2291                         port_number = DEFAULT_SSL_PORT;
2292                     }
2293                 }
2294                 break;
2295             case VERSION:
2296             case SOCKET_FACTORY:
2297                 closeConnection(SOFT_CLOSE);
2298                 break;
2299             case Context.SECURITY_AUTHENTICATION:
2300             case Context.SECURITY_PRINCIPAL:
2301             case Context.SECURITY_CREDENTIALS:
2302                 sharable = false;
2303                 break;
2304         }
2305 
2306             // Update environment; reconnection will use new props
2307             envprops = (envprops == null
2308                 ? new Hashtable<String, Object>(5, 0.75f)
2309                 : (Hashtable<String, Object>)envprops.clone());
2310             return envprops.put(propName, propVal);
2311     }
2312 
2313     /**
2314      * Sets the URL that created the context in the java.naming.provider.url
2315      * property.
2316      */
2317     void setProviderUrl(String providerUrl) { // called by LdapCtxFactory
2318         if (envprops != null) {
2319             envprops.put(Context.PROVIDER_URL, providerUrl);
2320         }
2321     }
2322 
2323     /**
2324      * Sets the domain name for the context in the com.sun.jndi.ldap.domainname
2325      * property.
2326      * Used for hostname verification by Start TLS
2327      */
2328     void setDomainName(String domainName) { // called by LdapCtxFactory
2329         if (envprops != null) {


2400 
2401     /**
2402      * Sets the batch size of this context;
2403      */
2404     private void setBatchSize(String batchSizeProp) {
2405         // set batchsize
2406         if (batchSizeProp != null) {
2407             batchSize = Integer.parseInt(batchSizeProp);
2408         } else {
2409             batchSize = DEFAULT_BATCH_SIZE;
2410         }
2411     }
2412 
2413     /**
2414      * Sets the referral mode of this context to 'follow', 'throw' or 'ignore'.
2415      * If referral mode is 'ignore' then activate the manageReferral control.
2416      */
2417     private void setReferralMode(String ref, boolean update) {
2418         // First determine the referral mode
2419         if (ref != null) {
2420             switch (ref) {
2421                 case "follow":
2422                     handleReferrals = LdapClient.LDAP_REF_FOLLOW;
2423                     break;
2424                 case "throw":
2425                     handleReferrals = LdapClient.LDAP_REF_THROW;
2426                     break;
2427                 case "ignore":
2428                     handleReferrals = LdapClient.LDAP_REF_IGNORE;
2429                     break;
2430                 default:
2431                     throw new IllegalArgumentException(
2432                         "Illegal value for " + Context.REFERRAL + " property.");
2433             }
2434         } else {
2435             handleReferrals = DEFAULT_REFERRAL_MODE;
2436         }
2437 
2438         if (handleReferrals == LdapClient.LDAP_REF_IGNORE) {
2439             // If ignoring referrals, add manageReferralControl
2440             reqCtls = addControl(reqCtls, manageReferralControl);
2441 
2442         } else if (update) {
2443 
2444             // If we're update an existing context, remove the control
2445             reqCtls = removeControl(reqCtls, manageReferralControl);
2446 
2447         } // else, leave alone; need not update
2448     }
2449 
2450     /**
2451      * Set whether aliases are derefereced during resolution and searches.
2452      */
2453     private void setDerefAliases(String deref) {
2454         if (deref != null) {
2455             switch (deref) {
2456                 case "never":
2457                     derefAliases = 0; // never de-reference aliases
2458                     break;
2459                 case "searching":
2460                     derefAliases = 1; // de-reference aliases during searching
2461                     break;
2462                 case "finding":
2463                     derefAliases = 2; // de-reference during name resolution
2464                     break;
2465                 case "always":
2466                     derefAliases = 3; // always de-reference aliases
2467                     break;
2468                 default:
2469                     throw new IllegalArgumentException("Illegal value for " +
2470                         DEREF_ALIASES + " property.");
2471             }
2472         } else {
2473             derefAliases = DEFAULT_DEREF_ALIASES;
2474         }
2475     }
2476 
2477     private void setRefSeparator(String sepStr) throws NamingException {
2478         if (sepStr != null && sepStr.length() > 0) {
2479             addrEncodingSeparator = sepStr.charAt(0);
2480         } else {
2481             addrEncodingSeparator = DEFAULT_REF_SEPARATOR;
2482         }
2483     }
2484 
2485     /**
2486      * Sets the limit on referral chains
2487      */
2488     private void setReferralLimit(String referralLimitProp) {


2544 
2545     /**
2546      * Sets the read timeout value
2547      */
2548     private void setReadTimeout(String readTimeoutProp) {
2549         if (readTimeoutProp != null) {
2550            readTimeout = Integer.parseInt(readTimeoutProp);
2551         } else {
2552             readTimeout = -1;
2553         }
2554     }
2555 
2556     /*
2557      * Extract URLs from a string. The format of the string is:
2558      *
2559      *     <urlstring > ::= "Referral:" <ldapurls>
2560      *     <ldapurls>   ::= <separator> <ldapurl> | <ldapurls>
2561      *     <separator>  ::= ASCII linefeed character (0x0a)
2562      *     <ldapurl>    ::= LDAP URL format (RFC 1959)
2563      */
2564     private static Vector<String> extractURLs(String refString) {
2565 
2566         int separator = 0;
2567         int urlCount = 0;
2568 
2569         // count the number of URLs
2570         while ((separator = refString.indexOf('\n', separator)) >= 0) {
2571             separator++;
2572             urlCount++;
2573         }
2574 
2575         Vector<String> referrals = new Vector<>(urlCount);
2576         int iURL;
2577         int i = 0;
2578 
2579         separator = refString.indexOf('\n');
2580         iURL = separator + 1;
2581         while ((separator = refString.indexOf('\n', iURL)) >= 0) {
2582             referrals.addElement(refString.substring(iURL, separator));
2583             iURL = separator + 1;
2584         }
2585         referrals.addElement(refString.substring(iURL));
2586 
2587         return referrals;
2588     }
2589 
2590     /*
2591      * Argument is a space-separated list of attribute IDs
2592      * Converts attribute IDs to lowercase before adding to built-in list.
2593      */
2594     private void setBinaryAttributes(String attrIds) {
2595         if (attrIds == null) {
2596             binaryAttrs = null;
2597         } else {
2598             binaryAttrs = new Hashtable<>(11, 0.75f);
2599             StringTokenizer tokens =
2600                 new StringTokenizer(attrIds.toLowerCase(), " ");
2601 
2602             while (tokens.hasMoreTokens()) {
2603                 binaryAttrs.put(tokens.nextToken(), Boolean.TRUE);
2604             }
2605         }
2606     }
2607 
2608    // ----------------- Connection  ---------------------
2609 
2610     protected void finalize() {
2611         try {
2612             close();
2613         } catch (NamingException e) {
2614             // ignore failures
2615         }
2616     }
2617 
2618     synchronized public void close() throws NamingException {


2630         // Enumerations that are keeping the connection alive
2631         if (enumCount > 0) {
2632             if (debug)
2633                 System.err.println("LdapCtx: close deferred");
2634             closeRequested = true;
2635             return;
2636         }
2637         closeConnection(SOFT_CLOSE);
2638 
2639 // %%%: RL: There is no need to set these to null, as they're just
2640 // variables whose contents and references will automatically
2641 // be cleaned up when they're no longer referenced.
2642 // Also, setting these to null creates problems for the attribute
2643 // schema-related methods, which need these to work.
2644 /*
2645         schemaTrees = null;
2646         envprops = null;
2647 */
2648     }
2649 
2650     @SuppressWarnings("unchecked")
2651     public void reconnect(Control[] connCtls) throws NamingException {
2652         // Update environment
2653         envprops = (envprops == null
2654                 ? new Hashtable<String, Object>(5, 0.75f)
2655                 : (Hashtable<String, Object>)envprops.clone());
2656 
2657         if (connCtls == null) {
2658             envprops.remove(BIND_CONTROLS);
2659             bindCtls = null;
2660         } else {
2661             envprops.put(BIND_CONTROLS, bindCtls = cloneControls(connCtls));
2662         }
2663 
2664         sharable = false;  // can't share with existing contexts
2665         ensureOpen();      // open or reauthenticated
2666     }
2667 
2668     private void ensureOpen() throws NamingException {
2669         ensureOpen(false);
2670     }
2671 
2672     private void ensureOpen(boolean startTLS) throws NamingException {
2673 
2674         try {
2675             if (clnt == null) {
2676                 if (debug) {
2677                     System.err.println("LdapCtx: Reconnecting " + this);
2678                 }
2679 
2680                 // reset the cache before a new connection is established
2681                 schemaTrees = new Hashtable<>(11, 0.75f);
2682                 connect(startTLS);
2683 
2684             } else if (!sharable || startTLS) {
2685 
2686                 synchronized (clnt) {
2687                     if (!clnt.isLdapv3
2688                         || clnt.referenceCount > 1
2689                         || clnt.usingSaslStreams()) {
2690                         closeConnection(SOFT_CLOSE);
2691                     }
2692                 }
2693                 // reset the cache before a new connection is established
2694                 schemaTrees = new Hashtable<>(11, 0.75f);
2695                 connect(startTLS);
2696             }
2697 
2698         } finally {
2699             sharable = true;   // connection is now either new or single-use
2700                                // OK for others to start sharing again
2701         }
2702     }
2703 
2704     private void connect(boolean startTLS) throws NamingException {
2705         if (debug) { System.err.println("LdapCtx: Connecting " + this); }
2706 
2707         String user = null;             // authenticating user
2708         Object passwd = null;           // password for authenticating user
2709         String secProtocol = null;      // security protocol (e.g. "ssl")
2710         String socketFactory = null;    // socket factory
2711         String authMechanism = null;    // authentication mechanism
2712         String ver = null;
2713         int ldapVersion;                // LDAP protocol version
2714         boolean usePool = false;        // enable connection pooling


2876     }
2877 
2878 
2879    // ------------ Return code and Error messages  -----------------------
2880 
2881     protected void processReturnCode(LdapResult answer) throws NamingException {
2882         processReturnCode(answer, null, this, null, envprops, null);
2883     }
2884 
2885     void processReturnCode(LdapResult answer, Name remainName)
2886     throws NamingException {
2887         processReturnCode(answer,
2888                           (new CompositeName()).add(currentDN),
2889                           this,
2890                           remainName,
2891                           envprops,
2892                           fullyQualifiedName(remainName));
2893     }
2894 
2895     protected void processReturnCode(LdapResult res, Name resolvedName,
2896         Object resolvedObj, Name remainName, Hashtable<?,?> envprops, String fullDN)
2897     throws NamingException {
2898 
2899         String msg = LdapClient.getErrorMessage(res.status, res.errorMessage);
2900         NamingException e;
2901         LdapReferralException r = null;
2902 
2903         switch (res.status) {
2904 
2905         case LdapClient.LDAP_SUCCESS:
2906 
2907             // handle Search continuation references
2908             if (res.referrals != null) {
2909 
2910                 msg = "Unprocessed Continuation Reference(s)";
2911 
2912                 if (handleReferrals == LdapClient.LDAP_REF_IGNORE) {
2913                     e = new PartialResultException(msg);
2914                     break;
2915                 }
2916 


2996          * Handle SLAPD-style referrals.
2997          *
2998          * Referrals received during name resolution should be followed
2999          * until one succeeds - the target entry is located. An exception
3000          * is thrown now to handle these.
3001          *
3002          * Referrals received during a search operation point to unexplored
3003          * parts of the directory and each should be followed. An exception
3004          * is thrown later (during results enumeration) to handle these.
3005          */
3006 
3007         case LdapClient.LDAP_PARTIAL_RESULTS:
3008 
3009             if (handleReferrals == LdapClient.LDAP_REF_IGNORE) {
3010                 e = new PartialResultException(msg);
3011                 break;
3012             }
3013 
3014             // extract SLAPD-style referrals from errorMessage
3015             if ((res.errorMessage != null) && (!res.errorMessage.equals(""))) {
3016                 @SuppressWarnings("unchecked")
3017                 Vector<Object> temp = (Vector)extractURLs(res.errorMessage);
3018                 res.referrals = temp;
3019             } else {
3020                 e = new PartialResultException(msg);
3021                 break;
3022             }
3023 
3024             // build exception
3025             r = new LdapReferralException(resolvedName,
3026                 resolvedObj,
3027                 remainName,
3028                 msg,
3029                 envprops,
3030                 fullDN,
3031                 handleReferrals,
3032                 reqCtls);
3033 
3034             if (hopCount > 1) {
3035                 r.setHopCount(hopCount);
3036             }
3037             /*
3038              * %%%
3039              * SLAPD-style referrals received during name resolution
3040              * cannot be distinguished from those received during a
3041              * search operation. Since both must be handled differently
3042              * the following rule is applied:
3043              *
3044              *     If 1 referral and 0 entries is received then
3045              *     assume name resolution has not yet completed.
3046              */
3047             if (((res.entries == null) || (res.entries.size() == 0)) &&
3048                 (res.referrals.size() == 1)) {
3049 
3050                 r.setReferralInfo(res.referrals, false);
3051 
3052                 // check the hop limit
3053                 if (hopCount > referralHopLimit) {
3054                     NamingException lee =
3055                         new LimitExceededException("Referral limit exceeded");
3056                     lee.setRootCause(r);
3057                     e = lee;
3058 
3059                 } else {
3060                     e = r;
3061                 }
3062 
3063             } else {
3064                 r.setReferralInfo(res.referrals, true);
3065                 res.refEx = r;
3066                 return;
3067             }
3068             break;
3069 
3070         case LdapClient.LDAP_INVALID_DN_SYNTAX:


3316             this.reqCtls = addControl(reqCtls, manageReferralControl);
3317         } else {
3318             this.reqCtls = cloneControls(reqCtls);
3319         }
3320     }
3321 
3322     public Control[] getRequestControls() throws NamingException {
3323         return cloneControls(reqCtls);
3324     }
3325 
3326     public Control[] getConnectControls() throws NamingException {
3327         return cloneControls(bindCtls);
3328     }
3329 
3330     public Control[] getResponseControls() throws NamingException {
3331         return (respCtls != null)? convertControls(respCtls) : null;
3332     }
3333 
3334     /**
3335      * Narrow controls using own default factory and ControlFactory.
3336      * @param ctls A non-null Vector<Control>
3337      */
3338     Control[] convertControls(Vector<Control> ctls) throws NamingException {
3339         int count = ctls.size();
3340 
3341         if (count == 0) {
3342             return null;
3343         }
3344 
3345         Control[] controls = new Control[count];
3346 
3347         for (int i = 0; i < count; i++) {
3348             // Try own factory first
3349             controls[i] = myResponseControlFactory.getControlInstance(
3350                 ctls.elementAt(i));
3351 
3352             // Try assigned factories if own produced null
3353             if (controls[i] == null) {
3354                 controls[i] = ControlFactory.getControlInstance(
3355                 ctls.elementAt(i), this, envprops);
3356             }
3357         }
3358         return controls;
3359     }
3360 
3361     private static Control[] addControl(Control[] prevCtls, Control addition) {
3362         if (prevCtls == null) {
3363             return new Control[]{addition};
3364         }
3365 
3366         // Find it
3367         int found = findControl(prevCtls, addition);
3368         if (found != -1) {
3369             return prevCtls;  // no need to do it again
3370         }
3371 
3372         Control[] newCtls = new Control[prevCtls.length+1];
3373         System.arraycopy(prevCtls, 0, newCtls, 0, prevCtls.length);
3374         newCtls[prevCtls.length] = addition;
3375         return newCtls;