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;
|