Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
PojavLauncherTeam
GitHub Repository: PojavLauncherTeam/mobile
Path: blob/master/src/java.naming/share/classes/com/sun/jndi/ldap/LdapCtx.java
41161 views
1
/*
2
* Copyright (c) 1999, 2021, Oracle and/or its affiliates. All rights reserved.
3
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4
*
5
* This code is free software; you can redistribute it and/or modify it
6
* under the terms of the GNU General Public License version 2 only, as
7
* published by the Free Software Foundation. Oracle designates this
8
* particular file as subject to the "Classpath" exception as provided
9
* by Oracle in the LICENSE file that accompanied this code.
10
*
11
* This code is distributed in the hope that it will be useful, but WITHOUT
12
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14
* version 2 for more details (a copy is included in the LICENSE file that
15
* accompanied this code).
16
*
17
* You should have received a copy of the GNU General Public License version
18
* 2 along with this work; if not, write to the Free Software Foundation,
19
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20
*
21
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22
* or visit www.oracle.com if you need additional information or have any
23
* questions.
24
*/
25
26
package com.sun.jndi.ldap;
27
28
import javax.naming.*;
29
import javax.naming.directory.*;
30
import javax.naming.spi.*;
31
import javax.naming.event.*;
32
import javax.naming.ldap.*;
33
import javax.naming.ldap.LdapName;
34
import javax.naming.ldap.Rdn;
35
36
import java.security.AccessController;
37
import java.security.PrivilegedAction;
38
import java.util.Arrays;
39
import java.util.Collections;
40
import java.util.Locale;
41
import java.util.Set;
42
import java.util.Vector;
43
import java.util.Hashtable;
44
import java.util.List;
45
import java.util.StringTokenizer;
46
import java.util.Enumeration;
47
import java.util.function.Predicate;
48
import java.util.stream.Collectors;
49
50
import java.io.IOException;
51
import java.io.OutputStream;
52
53
import com.sun.jndi.toolkit.ctx.*;
54
import com.sun.jndi.toolkit.dir.HierMemDirCtx;
55
import com.sun.jndi.toolkit.dir.SearchFilter;
56
import com.sun.jndi.ldap.ext.StartTlsResponseImpl;
57
58
/**
59
* The LDAP context implementation.
60
*
61
* Implementation is not thread-safe. Caller must sync as per JNDI spec.
62
* Members that are used directly or indirectly by internal worker threads
63
* (Connection, EventQueue, NamingEventNotifier) must be thread-safe.
64
* Connection - calls LdapClient.processUnsolicited(), which in turn calls
65
* LdapCtx.convertControls() and LdapCtx.fireUnsolicited().
66
* convertControls() - no sync; reads envprops and 'this'
67
* fireUnsolicited() - sync on eventSupport for all references to 'unsolicited'
68
* (even those in other methods); don't sync on LdapCtx in case caller
69
* is already sync'ing on it - this would prevent Unsol events from firing
70
* and the Connection thread to block (thus preventing any other data
71
* from being read from the connection)
72
* References to 'eventSupport' need not be sync'ed because these
73
* methods can only be called after eventSupport has been set first
74
* (via addNamingListener()).
75
* EventQueue - no direct or indirect calls to LdapCtx
76
* NamingEventNotifier - calls newInstance() to get instance for run() to use;
77
* no sync needed for methods invoked on new instance;
78
*
79
* LdapAttribute links to LdapCtx in order to process getAttributeDefinition()
80
* and getAttributeSyntaxDefinition() calls. It invokes LdapCtx.getSchema(),
81
* which uses schemaTrees (a Hashtable - already sync). Potential conflict
82
* of duplicating construction of tree for same subschemasubentry
83
* but no inconsistency problems.
84
*
85
* NamingEnumerations link to LdapCtx for the following:
86
* 1. increment/decrement enum count so that ctx doesn't close the
87
* underlying connection
88
* 2. LdapClient handle to get next batch of results
89
* 3. Sets LdapCtx's response controls
90
* 4. Process return code
91
* 5. For narrowing response controls (using ctx's factories)
92
* Since processing of NamingEnumeration by client is treated the same as method
93
* invocation on LdapCtx, caller is responsible for locking.
94
*
95
* @author Vincent Ryan
96
* @author Rosanna Lee
97
*/
98
99
public final class LdapCtx extends ComponentDirContext
100
implements EventDirContext, LdapContext {
101
102
/*
103
* Used to store arguments to the search method.
104
*/
105
static final class SearchArgs {
106
Name name;
107
String filter;
108
SearchControls cons;
109
String[] reqAttrs; // those attributes originally requested
110
111
SearchArgs(Name name, String filter, SearchControls cons, String[] ra) {
112
this.name = name;
113
this.filter = filter;
114
this.cons = cons;
115
this.reqAttrs = ra;
116
}
117
}
118
119
private static final boolean debug = false;
120
121
private static final boolean HARD_CLOSE = true;
122
private static final boolean SOFT_CLOSE = false;
123
124
// ----------------- Constants -----------------
125
126
public static final int DEFAULT_PORT = 389;
127
public static final int DEFAULT_SSL_PORT = 636;
128
public static final String DEFAULT_HOST = "localhost";
129
130
private static final boolean DEFAULT_DELETE_RDN = true;
131
private static final boolean DEFAULT_TYPES_ONLY = false;
132
private static final int DEFAULT_DEREF_ALIASES = 3; // always deref
133
private static final int DEFAULT_LDAP_VERSION = LdapClient.LDAP_VERSION3_VERSION2;
134
private static final int DEFAULT_BATCH_SIZE = 1;
135
private static final int DEFAULT_REFERRAL_MODE = LdapClient.LDAP_REF_IGNORE;
136
private static final char DEFAULT_REF_SEPARATOR = '#';
137
138
// Used by LdapPoolManager
139
static final String DEFAULT_SSL_FACTORY =
140
"javax.net.ssl.SSLSocketFactory"; // use Sun's SSL
141
private static final int DEFAULT_REFERRAL_LIMIT = 10;
142
private static final String STARTTLS_REQ_OID = "1.3.6.1.4.1.1466.20037";
143
144
// schema operational and user attributes
145
private static final String[] SCHEMA_ATTRIBUTES =
146
{ "objectClasses", "attributeTypes", "matchingRules", "ldapSyntaxes" };
147
148
// --------------- Environment property names ----------
149
150
// LDAP protocol version: "2", "3"
151
private static final String VERSION = "java.naming.ldap.version";
152
153
// Binary-valued attributes. Space separated string of attribute names.
154
private static final String BINARY_ATTRIBUTES =
155
"java.naming.ldap.attributes.binary";
156
157
// Delete old RDN during modifyDN: "true", "false"
158
private static final String DELETE_RDN = "java.naming.ldap.deleteRDN";
159
160
// De-reference aliases: "never", "searching", "finding", "always"
161
private static final String DEREF_ALIASES = "java.naming.ldap.derefAliases";
162
163
// Return only attribute types (no values)
164
private static final String TYPES_ONLY = "java.naming.ldap.typesOnly";
165
166
// Separator character for encoding Reference's RefAddrs; default is '#'
167
private static final String REF_SEPARATOR = "java.naming.ldap.ref.separator";
168
169
// Socket factory
170
private static final String SOCKET_FACTORY = "java.naming.ldap.factory.socket";
171
172
// Bind Controls (used by LdapReferralException)
173
static final String BIND_CONTROLS = "java.naming.ldap.control.connect";
174
175
private static final String REFERRAL_LIMIT =
176
"java.naming.ldap.referral.limit";
177
178
// trace BER (java.io.OutputStream)
179
private static final String TRACE_BER = "com.sun.jndi.ldap.trace.ber";
180
181
// Get around Netscape Schema Bugs
182
private static final String NETSCAPE_SCHEMA_BUG =
183
"com.sun.jndi.ldap.netscape.schemaBugs";
184
// deprecated
185
private static final String OLD_NETSCAPE_SCHEMA_BUG =
186
"com.sun.naming.netscape.schemaBugs"; // for backward compatibility
187
188
// Timeout for socket connect
189
private static final String CONNECT_TIMEOUT =
190
"com.sun.jndi.ldap.connect.timeout";
191
192
// Timeout for reading responses
193
private static final String READ_TIMEOUT =
194
"com.sun.jndi.ldap.read.timeout";
195
196
// Environment property for connection pooling
197
private static final String ENABLE_POOL = "com.sun.jndi.ldap.connect.pool";
198
199
// Environment property for the domain name (derived from this context's DN)
200
private static final String DOMAIN_NAME = "com.sun.jndi.ldap.domainname";
201
202
// Block until the first search reply is received
203
private static final String WAIT_FOR_REPLY =
204
"com.sun.jndi.ldap.search.waitForReply";
205
206
// Size of the queue of unprocessed search replies
207
private static final String REPLY_QUEUE_SIZE =
208
"com.sun.jndi.ldap.search.replyQueueSize";
209
210
// System and environment property name to control allowed list of
211
// authentication mechanisms: "all" or "" or "mech1,mech2,...,mechN"
212
// "all": allow all mechanisms,
213
// "": allow none
214
// or comma separated list of allowed authentication mechanisms
215
// Note: "none" or "anonymous" are always allowed.
216
private static final String ALLOWED_MECHS_SP =
217
"jdk.jndi.ldap.mechsAllowedToSendCredentials";
218
219
// System property value
220
private static final String ALLOWED_MECHS_SP_VALUE =
221
getMechsAllowedToSendCredentials();
222
223
// Set of authentication mechanisms allowed by the system property
224
private static final Set<String> MECHS_ALLOWED_BY_SP =
225
getMechsFromPropertyValue(ALLOWED_MECHS_SP_VALUE);
226
227
// The message to use in NamingException if the transmission of plain credentials are not allowed
228
private static final String UNSECURED_CRED_TRANSMIT_MSG =
229
"Transmission of credentials over unsecured connection is not allowed";
230
231
// ----------------- Fields that don't change -----------------------
232
private static final NameParser parser = new LdapNameParser();
233
234
// controls that Provider needs
235
private static final ControlFactory myResponseControlFactory =
236
new DefaultResponseControlFactory();
237
private static final Control manageReferralControl =
238
new ManageReferralControl(false);
239
240
private static final HierMemDirCtx EMPTY_SCHEMA = new HierMemDirCtx();
241
static {
242
EMPTY_SCHEMA.setReadOnly(
243
new SchemaViolationException("Cannot update schema object"));
244
}
245
246
// ------------ Package private instance variables ----------------
247
// Cannot be private; used by enums
248
249
// ------- Inherited by derived context instances
250
251
int port_number; // port number of server
252
String hostname = null; // host name of server (no brackets
253
// for IPv6 literals)
254
LdapClient clnt = null; // connection handle
255
Hashtable<String, java.lang.Object> envprops = null; // environment properties of context
256
int handleReferrals = DEFAULT_REFERRAL_MODE; // how referral is handled
257
boolean hasLdapsScheme = false; // true if the context was created
258
// using an LDAPS URL.
259
260
// ------- Not inherited by derived context instances
261
262
String currentDN; // DN of this context
263
Name currentParsedDN; // DN of this context
264
Vector<Control> respCtls = null; // Response controls read
265
Control[] reqCtls = null; // Controls to be sent with each request
266
// Used to track if context was seen to be secured with STARTTLS extended operation
267
volatile boolean contextSeenStartTlsEnabled;
268
269
// ------------- Private instance variables ------------------------
270
271
// ------- Inherited by derived context instances
272
273
private OutputStream trace = null; // output stream for BER debug output
274
private boolean netscapeSchemaBug = false; // workaround
275
private Control[] bindCtls = null; // Controls to be sent with LDAP "bind"
276
private int referralHopLimit = DEFAULT_REFERRAL_LIMIT; // max referral
277
private Hashtable<String, DirContext> schemaTrees = null; // schema root of this context
278
private int batchSize = DEFAULT_BATCH_SIZE; // batch size for search results
279
private boolean deleteRDN = DEFAULT_DELETE_RDN; // delete the old RDN when modifying DN
280
private boolean typesOnly = DEFAULT_TYPES_ONLY; // return attribute types (no values)
281
private int derefAliases = DEFAULT_DEREF_ALIASES;// de-reference alias entries during searching
282
private char addrEncodingSeparator = DEFAULT_REF_SEPARATOR; // encoding RefAddr
283
284
private Hashtable<String, Boolean> binaryAttrs = null; // attr values returned as byte[]
285
private int connectTimeout = -1; // no timeout value
286
private int readTimeout = -1; // no timeout value
287
private boolean waitForReply = true; // wait for search response
288
private int replyQueueSize = -1; // unlimited queue size
289
private boolean useSsl = false; // true if SSL protocol is active
290
private boolean useDefaultPortNumber = false; // no port number was supplied
291
292
// ------- Not inherited by derived context instances
293
294
// True if this context was created by another LdapCtx.
295
private boolean parentIsLdapCtx = false; // see composeName()
296
297
private int hopCount = 1; // current referral hop count
298
private String url = null; // URL of context; see getURL()
299
private EventSupport eventSupport; // Event support helper for this ctx
300
private boolean unsolicited = false; // if there unsolicited listeners
301
private boolean sharable = true; // can share connection with other ctx
302
303
// -------------- Constructors -----------------------------------
304
305
@SuppressWarnings("unchecked")
306
public LdapCtx(String dn, String host, int port_number,
307
Hashtable<?,?> props,
308
boolean useSsl) throws NamingException {
309
310
this.useSsl = this.hasLdapsScheme = useSsl;
311
312
if (props != null) {
313
envprops = (Hashtable<String, java.lang.Object>) props.clone();
314
315
// SSL env prop overrides the useSsl argument
316
if ("ssl".equals(envprops.get(Context.SECURITY_PROTOCOL))) {
317
this.useSsl = true;
318
}
319
320
// %%% These are only examined when the context is created
321
// %%% because they are only for debugging or workaround purposes.
322
trace = (OutputStream)envprops.get(TRACE_BER);
323
324
if (props.get(NETSCAPE_SCHEMA_BUG) != null ||
325
props.get(OLD_NETSCAPE_SCHEMA_BUG) != null) {
326
netscapeSchemaBug = true;
327
}
328
}
329
330
currentDN = (dn != null) ? dn : "";
331
currentParsedDN = parser.parse(currentDN);
332
333
hostname = (host != null && host.length() > 0) ? host : DEFAULT_HOST;
334
if (hostname.charAt(0) == '[') {
335
hostname = hostname.substring(1, hostname.length() - 1);
336
}
337
338
if (port_number > 0) {
339
this.port_number = port_number;
340
} else {
341
this.port_number = this.useSsl ? DEFAULT_SSL_PORT : DEFAULT_PORT;
342
this.useDefaultPortNumber = true;
343
}
344
345
schemaTrees = new Hashtable<>(11, 0.75f);
346
initEnv();
347
try {
348
connect(false);
349
} catch (NamingException e) {
350
try {
351
close();
352
} catch (Exception e2) {
353
// Nothing
354
}
355
throw e;
356
}
357
}
358
359
LdapCtx(LdapCtx existing, String newDN) throws NamingException {
360
useSsl = existing.useSsl;
361
hasLdapsScheme = existing.hasLdapsScheme;
362
useDefaultPortNumber = existing.useDefaultPortNumber;
363
364
hostname = existing.hostname;
365
port_number = existing.port_number;
366
currentDN = newDN;
367
if (existing.currentDN == currentDN) {
368
currentParsedDN = existing.currentParsedDN;
369
} else {
370
currentParsedDN = parser.parse(currentDN);
371
}
372
373
envprops = existing.envprops;
374
schemaTrees = existing.schemaTrees;
375
376
clnt = existing.clnt;
377
clnt.incRefCount();
378
379
parentIsLdapCtx = ((newDN == null || newDN.equals(existing.currentDN))
380
? existing.parentIsLdapCtx
381
: true);
382
383
// inherit these debugging/workaround flags
384
trace = existing.trace;
385
netscapeSchemaBug = existing.netscapeSchemaBug;
386
387
initEnv();
388
}
389
390
public LdapContext newInstance(Control[] reqCtls) throws NamingException {
391
392
LdapContext clone = new LdapCtx(this, currentDN);
393
394
// Connection controls are inherited from environment
395
396
// Set clone's request controls
397
// setRequestControls() will clone reqCtls
398
clone.setRequestControls(reqCtls);
399
return clone;
400
}
401
402
// --------------- Namespace Updates ---------------------
403
// -- bind/rebind/unbind
404
// -- rename
405
// -- createSubcontext/destroySubcontext
406
407
protected void c_bind(Name name, Object obj, Continuation cont)
408
throws NamingException {
409
c_bind(name, obj, null, cont);
410
}
411
412
/*
413
* attrs == null
414
* if obj is DirContext, attrs = obj.getAttributes()
415
* if attrs == null && obj == null
416
* disallow (cannot determine objectclass to use)
417
* if obj == null
418
* just create entry using attrs
419
* else
420
* objAttrs = create attributes for representing obj
421
* attrs += objAttrs
422
* create entry using attrs
423
*/
424
protected void c_bind(Name name, Object obj, Attributes attrs,
425
Continuation cont)
426
throws NamingException {
427
428
cont.setError(this, name);
429
430
Attributes inputAttrs = attrs; // Attributes supplied by caller
431
try {
432
ensureOpen();
433
434
if (obj == null) {
435
if (attrs == null) {
436
throw new IllegalArgumentException(
437
"cannot bind null object with no attributes");
438
}
439
} else {
440
attrs = Obj.determineBindAttrs(addrEncodingSeparator, obj, attrs,
441
false, name, this, envprops); // not cloned
442
}
443
444
String newDN = fullyQualifiedName(name);
445
attrs = addRdnAttributes(newDN, attrs, inputAttrs != attrs);
446
LdapEntry entry = new LdapEntry(newDN, attrs);
447
448
LdapResult answer = clnt.add(entry, reqCtls);
449
respCtls = answer.resControls; // retrieve response controls
450
451
if (answer.status != LdapClient.LDAP_SUCCESS) {
452
processReturnCode(answer, name);
453
}
454
455
} catch (LdapReferralException e) {
456
if (handleReferrals == LdapClient.LDAP_REF_THROW)
457
throw cont.fillInException(e);
458
459
// process the referrals sequentially
460
while (true) {
461
462
LdapReferralContext refCtx =
463
(LdapReferralContext)e.getReferralContext(envprops, bindCtls);
464
465
// repeat the original operation at the new context
466
try {
467
468
refCtx.bind(name, obj, inputAttrs);
469
return;
470
471
} catch (LdapReferralException re) {
472
e = re;
473
continue;
474
475
} finally {
476
// Make sure we close referral context
477
refCtx.close();
478
}
479
}
480
481
} catch (IOException e) {
482
NamingException e2 = new CommunicationException(e.getMessage());
483
e2.setRootCause(e);
484
throw cont.fillInException(e2);
485
486
} catch (NamingException e) {
487
throw cont.fillInException(e);
488
}
489
}
490
491
protected void c_rebind(Name name, Object obj, Continuation cont)
492
throws NamingException {
493
c_rebind(name, obj, null, cont);
494
}
495
496
497
/*
498
* attrs == null
499
* if obj is DirContext, attrs = obj.getAttributes().
500
* if attrs == null
501
* leave any existing attributes alone
502
* (set attrs = {objectclass=top} if object doesn't exist)
503
* else
504
* replace all existing attributes with attrs
505
* if obj == null
506
* just create entry using attrs
507
* else
508
* objAttrs = create attributes for representing obj
509
* attrs += objAttrs
510
* create entry using attrs
511
*/
512
protected void c_rebind(Name name, Object obj, Attributes attrs,
513
Continuation cont) throws NamingException {
514
515
cont.setError(this, name);
516
517
Attributes inputAttrs = attrs;
518
519
try {
520
Attributes origAttrs = null;
521
522
// Check if name is bound
523
try {
524
origAttrs = c_getAttributes(name, null, cont);
525
} catch (NameNotFoundException e) {}
526
527
// Name not bound, just add it
528
if (origAttrs == null) {
529
c_bind(name, obj, attrs, cont);
530
return;
531
}
532
533
// there's an object there already, need to figure out
534
// what to do about its attributes
535
536
if (attrs == null && obj instanceof DirContext) {
537
attrs = ((DirContext)obj).getAttributes("");
538
}
539
Attributes keepAttrs = (Attributes)origAttrs.clone();
540
541
if (attrs == null) {
542
// we're not changing any attrs, leave old attributes alone
543
544
// Remove Java-related object classes from objectclass attribute
545
Attribute origObjectClass =
546
origAttrs.get(Obj.JAVA_ATTRIBUTES[Obj.OBJECT_CLASS]);
547
548
if (origObjectClass != null) {
549
// clone so that keepAttrs is not affected
550
origObjectClass = (Attribute)origObjectClass.clone();
551
for (int i = 0; i < Obj.JAVA_OBJECT_CLASSES.length; i++) {
552
origObjectClass.remove(Obj.JAVA_OBJECT_CLASSES_LOWER[i]);
553
origObjectClass.remove(Obj.JAVA_OBJECT_CLASSES[i]);
554
}
555
// update;
556
origAttrs.put(origObjectClass);
557
}
558
559
// remove all Java-related attributes except objectclass
560
for (int i = 1; i < Obj.JAVA_ATTRIBUTES.length; i++) {
561
origAttrs.remove(Obj.JAVA_ATTRIBUTES[i]);
562
}
563
564
attrs = origAttrs;
565
}
566
if (obj != null) {
567
attrs =
568
Obj.determineBindAttrs(addrEncodingSeparator, obj, attrs,
569
inputAttrs != attrs, name, this, envprops);
570
}
571
572
String newDN = fullyQualifiedName(name);
573
// remove entry
574
LdapResult answer = clnt.delete(newDN, reqCtls);
575
respCtls = answer.resControls; // retrieve response controls
576
577
if (answer.status != LdapClient.LDAP_SUCCESS) {
578
processReturnCode(answer, name);
579
return;
580
}
581
582
Exception addEx = null;
583
try {
584
attrs = addRdnAttributes(newDN, attrs, inputAttrs != attrs);
585
586
// add it back using updated attrs
587
LdapEntry entry = new LdapEntry(newDN, attrs);
588
answer = clnt.add(entry, reqCtls);
589
if (answer.resControls != null) {
590
respCtls = appendVector(respCtls, answer.resControls);
591
}
592
} catch (NamingException | IOException ae) {
593
addEx = ae;
594
}
595
596
if ((addEx != null && !(addEx instanceof LdapReferralException)) ||
597
answer.status != LdapClient.LDAP_SUCCESS) {
598
// Attempt to restore old entry
599
LdapResult answer2 =
600
clnt.add(new LdapEntry(newDN, keepAttrs), reqCtls);
601
if (answer2.resControls != null) {
602
respCtls = appendVector(respCtls, answer2.resControls);
603
}
604
605
if (addEx == null) {
606
processReturnCode(answer, name);
607
}
608
}
609
610
// Rethrow exception
611
if (addEx instanceof NamingException) {
612
throw (NamingException)addEx;
613
} else if (addEx instanceof IOException) {
614
throw (IOException)addEx;
615
}
616
617
} catch (LdapReferralException e) {
618
if (handleReferrals == LdapClient.LDAP_REF_THROW)
619
throw cont.fillInException(e);
620
621
// process the referrals sequentially
622
while (true) {
623
624
LdapReferralContext refCtx =
625
(LdapReferralContext)e.getReferralContext(envprops, bindCtls);
626
627
// repeat the original operation at the new context
628
try {
629
630
refCtx.rebind(name, obj, inputAttrs);
631
return;
632
633
} catch (LdapReferralException re) {
634
e = re;
635
continue;
636
637
} finally {
638
// Make sure we close referral context
639
refCtx.close();
640
}
641
}
642
643
} catch (IOException e) {
644
NamingException e2 = new CommunicationException(e.getMessage());
645
e2.setRootCause(e);
646
throw cont.fillInException(e2);
647
648
} catch (NamingException e) {
649
throw cont.fillInException(e);
650
}
651
}
652
653
protected void c_unbind(Name name, Continuation cont)
654
throws NamingException {
655
cont.setError(this, name);
656
657
try {
658
ensureOpen();
659
660
String fname = fullyQualifiedName(name);
661
LdapResult answer = clnt.delete(fname, reqCtls);
662
respCtls = answer.resControls; // retrieve response controls
663
664
adjustDeleteStatus(fname, answer);
665
666
if (answer.status != LdapClient.LDAP_SUCCESS) {
667
processReturnCode(answer, name);
668
}
669
670
} catch (LdapReferralException e) {
671
if (handleReferrals == LdapClient.LDAP_REF_THROW)
672
throw cont.fillInException(e);
673
674
// process the referrals sequentially
675
while (true) {
676
677
LdapReferralContext refCtx =
678
(LdapReferralContext)e.getReferralContext(envprops, bindCtls);
679
680
// repeat the original operation at the new context
681
try {
682
683
refCtx.unbind(name);
684
return;
685
686
} catch (LdapReferralException re) {
687
e = re;
688
continue;
689
690
} finally {
691
// Make sure we close referral context
692
refCtx.close();
693
}
694
}
695
696
} catch (IOException e) {
697
NamingException e2 = new CommunicationException(e.getMessage());
698
e2.setRootCause(e);
699
throw cont.fillInException(e2);
700
701
} catch (NamingException e) {
702
throw cont.fillInException(e);
703
}
704
}
705
706
protected void c_rename(Name oldName, Name newName, Continuation cont)
707
throws NamingException
708
{
709
Name oldParsed, newParsed;
710
Name oldParent, newParent;
711
String newRDN = null;
712
String newSuperior = null;
713
714
// assert (oldName instanceOf CompositeName);
715
716
cont.setError(this, oldName);
717
718
try {
719
ensureOpen();
720
721
// permit oldName to be empty (for processing referral contexts)
722
if (oldName.isEmpty()) {
723
oldParent = parser.parse("");
724
} else {
725
oldParsed = parser.parse(oldName.get(0)); // extract DN & parse
726
oldParent = oldParsed.getPrefix(oldParsed.size() - 1);
727
}
728
729
if (newName instanceof CompositeName) {
730
newParsed = parser.parse(newName.get(0)); // extract DN & parse
731
} else {
732
newParsed = newName; // CompoundName/LdapName is already parsed
733
}
734
newParent = newParsed.getPrefix(newParsed.size() - 1);
735
736
if(!oldParent.equals(newParent)) {
737
if (!clnt.isLdapv3) {
738
throw new InvalidNameException(
739
"LDAPv2 doesn't support changing " +
740
"the parent as a result of a rename");
741
} else {
742
newSuperior = fullyQualifiedName(newParent.toString());
743
}
744
}
745
746
newRDN = newParsed.get(newParsed.size() - 1);
747
748
LdapResult answer = clnt.moddn(fullyQualifiedName(oldName),
749
newRDN,
750
deleteRDN,
751
newSuperior,
752
reqCtls);
753
respCtls = answer.resControls; // retrieve response controls
754
755
if (answer.status != LdapClient.LDAP_SUCCESS) {
756
processReturnCode(answer, oldName);
757
}
758
759
} catch (LdapReferralException e) {
760
761
// Record the new RDN (for use after the referral is followed).
762
e.setNewRdn(newRDN);
763
764
// Cannot continue when a referral has been received and a
765
// newSuperior name was supplied (because the newSuperior is
766
// relative to a naming context BEFORE the referral is followed).
767
if (newSuperior != null) {
768
PartialResultException pre = new PartialResultException(
769
"Cannot continue referral processing when newSuperior is " +
770
"nonempty: " + newSuperior);
771
pre.setRootCause(cont.fillInException(e));
772
throw cont.fillInException(pre);
773
}
774
775
if (handleReferrals == LdapClient.LDAP_REF_THROW)
776
throw cont.fillInException(e);
777
778
// process the referrals sequentially
779
while (true) {
780
781
LdapReferralContext refCtx =
782
(LdapReferralContext)e.getReferralContext(envprops, bindCtls);
783
784
// repeat the original operation at the new context
785
try {
786
787
refCtx.rename(oldName, newName);
788
return;
789
790
} catch (LdapReferralException re) {
791
e = re;
792
continue;
793
794
} finally {
795
// Make sure we close referral context
796
refCtx.close();
797
}
798
}
799
800
} catch (IOException e) {
801
NamingException e2 = new CommunicationException(e.getMessage());
802
e2.setRootCause(e);
803
throw cont.fillInException(e2);
804
805
} catch (NamingException e) {
806
throw cont.fillInException(e);
807
}
808
}
809
810
protected Context c_createSubcontext(Name name, Continuation cont)
811
throws NamingException {
812
return c_createSubcontext(name, null, cont);
813
}
814
815
protected DirContext c_createSubcontext(Name name, Attributes attrs,
816
Continuation cont)
817
throws NamingException {
818
cont.setError(this, name);
819
820
Attributes inputAttrs = attrs;
821
try {
822
ensureOpen();
823
if (attrs == null) {
824
// add structural objectclass; name needs to have "cn"
825
Attribute oc = new BasicAttribute(
826
Obj.JAVA_ATTRIBUTES[Obj.OBJECT_CLASS],
827
Obj.JAVA_OBJECT_CLASSES[Obj.STRUCTURAL]);
828
oc.add("top");
829
attrs = new BasicAttributes(true); // case ignore
830
attrs.put(oc);
831
}
832
String newDN = fullyQualifiedName(name);
833
attrs = addRdnAttributes(newDN, attrs, inputAttrs != attrs);
834
835
LdapEntry entry = new LdapEntry(newDN, attrs);
836
837
LdapResult answer = clnt.add(entry, reqCtls);
838
respCtls = answer.resControls; // retrieve response controls
839
840
if (answer.status != LdapClient.LDAP_SUCCESS) {
841
processReturnCode(answer, name);
842
return null;
843
}
844
845
// creation successful, get back live object
846
return new LdapCtx(this, newDN);
847
848
} catch (LdapReferralException e) {
849
if (handleReferrals == LdapClient.LDAP_REF_THROW)
850
throw cont.fillInException(e);
851
852
// process the referrals sequentially
853
while (true) {
854
855
LdapReferralContext refCtx =
856
(LdapReferralContext)e.getReferralContext(envprops, bindCtls);
857
858
// repeat the original operation at the new context
859
try {
860
861
return refCtx.createSubcontext(name, inputAttrs);
862
863
} catch (LdapReferralException re) {
864
e = re;
865
continue;
866
867
} finally {
868
// Make sure we close referral context
869
refCtx.close();
870
}
871
}
872
873
} catch (IOException e) {
874
NamingException e2 = new CommunicationException(e.getMessage());
875
e2.setRootCause(e);
876
throw cont.fillInException(e2);
877
878
} catch (NamingException e) {
879
throw cont.fillInException(e);
880
}
881
}
882
883
protected void c_destroySubcontext(Name name, Continuation cont)
884
throws NamingException {
885
cont.setError(this, name);
886
887
try {
888
ensureOpen();
889
890
String fname = fullyQualifiedName(name);
891
LdapResult answer = clnt.delete(fname, reqCtls);
892
respCtls = answer.resControls; // retrieve response controls
893
894
adjustDeleteStatus(fname, answer);
895
896
if (answer.status != LdapClient.LDAP_SUCCESS) {
897
processReturnCode(answer, name);
898
}
899
900
} catch (LdapReferralException e) {
901
if (handleReferrals == LdapClient.LDAP_REF_THROW)
902
throw cont.fillInException(e);
903
904
// process the referrals sequentially
905
while (true) {
906
907
LdapReferralContext refCtx =
908
(LdapReferralContext)e.getReferralContext(envprops, bindCtls);
909
910
// repeat the original operation at the new context
911
try {
912
913
refCtx.destroySubcontext(name);
914
return;
915
} catch (LdapReferralException re) {
916
e = re;
917
continue;
918
} finally {
919
// Make sure we close referral context
920
refCtx.close();
921
}
922
}
923
} catch (IOException e) {
924
NamingException e2 = new CommunicationException(e.getMessage());
925
e2.setRootCause(e);
926
throw cont.fillInException(e2);
927
} catch (NamingException e) {
928
throw cont.fillInException(e);
929
}
930
}
931
932
/**
933
* Adds attributes from RDN to attrs if not already present.
934
* Note that if attrs already contains an attribute by the same name,
935
* or if the distinguished name is empty, then leave attrs unchanged.
936
*
937
* @param dn The non-null DN of the entry to add
938
* @param attrs The non-null attributes of entry to add
939
* @param directUpdate Whether attrs can be updated directly
940
* @return Non-null attributes with attributes from the RDN added
941
*/
942
private static Attributes addRdnAttributes(String dn, Attributes attrs,
943
boolean directUpdate) throws NamingException {
944
945
// Handle the empty name
946
if (dn.isEmpty()) {
947
return attrs;
948
}
949
950
// Parse string name into list of RDNs
951
List<Rdn> rdnList = (new LdapName(dn)).getRdns();
952
953
// Get leaf RDN
954
Rdn rdn = rdnList.get(rdnList.size() - 1);
955
Attributes nameAttrs = rdn.toAttributes();
956
957
// Add attributes of RDN to attrs if not already there
958
NamingEnumeration<? extends Attribute> enum_ = nameAttrs.getAll();
959
Attribute nameAttr;
960
while (enum_.hasMore()) {
961
nameAttr = enum_.next();
962
963
// If attrs already has the attribute, don't change or add to it
964
if (attrs.get(nameAttr.getID()) == null) {
965
966
/**
967
* When attrs.isCaseIgnored() is false, attrs.get() will
968
* return null when the case mis-matches for otherwise
969
* equal attrIDs.
970
* As the attrIDs' case is irrelevant for LDAP, ignore
971
* the case of attrIDs even when attrs.isCaseIgnored() is
972
* false. This is done by explicitly comparing the elements in
973
* the enumeration of IDs with their case ignored.
974
*/
975
if (!attrs.isCaseIgnored() &&
976
containsIgnoreCase(attrs.getIDs(), nameAttr.getID())) {
977
continue;
978
}
979
980
if (!directUpdate) {
981
attrs = (Attributes)attrs.clone();
982
directUpdate = true;
983
}
984
attrs.put(nameAttr);
985
}
986
}
987
988
return attrs;
989
}
990
991
992
private static boolean containsIgnoreCase(NamingEnumeration<String> enumStr,
993
String str) throws NamingException {
994
String strEntry;
995
996
while (enumStr.hasMore()) {
997
strEntry = enumStr.next();
998
if (strEntry.equalsIgnoreCase(str)) {
999
return true;
1000
}
1001
}
1002
return false;
1003
}
1004
1005
1006
private void adjustDeleteStatus(String fname, LdapResult answer) {
1007
if (answer.status == LdapClient.LDAP_NO_SUCH_OBJECT &&
1008
answer.matchedDN != null) {
1009
try {
1010
// %%% RL: are there any implications for referrals?
1011
1012
Name orig = parser.parse(fname);
1013
Name matched = parser.parse(answer.matchedDN);
1014
if ((orig.size() - matched.size()) == 1)
1015
answer.status = LdapClient.LDAP_SUCCESS;
1016
} catch (NamingException e) {}
1017
}
1018
}
1019
1020
/*
1021
* Append the second Vector onto the first Vector
1022
* (v2 must be non-null)
1023
*/
1024
private static <T> Vector<T> appendVector(Vector<T> v1, Vector<T> v2) {
1025
if (v1 == null) {
1026
v1 = v2;
1027
} else {
1028
for (int i = 0; i < v2.size(); i++) {
1029
v1.addElement(v2.elementAt(i));
1030
}
1031
}
1032
return v1;
1033
}
1034
1035
// ------------- Lookups and Browsing -------------------------
1036
// lookup/lookupLink
1037
// list/listBindings
1038
1039
protected Object c_lookupLink(Name name, Continuation cont)
1040
throws NamingException {
1041
return c_lookup(name, cont);
1042
}
1043
1044
protected Object c_lookup(Name name, Continuation cont)
1045
throws NamingException {
1046
cont.setError(this, name);
1047
Object obj = null;
1048
Attributes attrs;
1049
1050
try {
1051
SearchControls cons = new SearchControls();
1052
cons.setSearchScope(SearchControls.OBJECT_SCOPE);
1053
cons.setReturningAttributes(null); // ask for all attributes
1054
cons.setReturningObjFlag(true); // need values to construct obj
1055
1056
LdapResult answer = doSearchOnce(name, "(objectClass=*)", cons, true);
1057
respCtls = answer.resControls; // retrieve response controls
1058
1059
// should get back 1 SearchResponse and 1 SearchResult
1060
1061
if (answer.status != LdapClient.LDAP_SUCCESS) {
1062
processReturnCode(answer, name);
1063
}
1064
1065
if (answer.entries == null || answer.entries.size() != 1) {
1066
// found it but got no attributes
1067
attrs = new BasicAttributes(LdapClient.caseIgnore);
1068
} else {
1069
LdapEntry entry = answer.entries.elementAt(0);
1070
attrs = entry.attributes;
1071
1072
Vector<Control> entryCtls = entry.respCtls; // retrieve entry controls
1073
if (entryCtls != null) {
1074
appendVector(respCtls, entryCtls); // concatenate controls
1075
}
1076
}
1077
1078
if (attrs.get(Obj.JAVA_ATTRIBUTES[Obj.CLASSNAME]) != null) {
1079
// serialized object or object reference
1080
obj = Obj.decodeObject(attrs);
1081
}
1082
if (obj == null) {
1083
obj = new LdapCtx(this, fullyQualifiedName(name));
1084
}
1085
} catch (LdapReferralException e) {
1086
if (handleReferrals == LdapClient.LDAP_REF_THROW)
1087
throw cont.fillInException(e);
1088
1089
// process the referrals sequentially
1090
while (true) {
1091
1092
LdapReferralContext refCtx =
1093
(LdapReferralContext)e.getReferralContext(envprops, bindCtls);
1094
// repeat the original operation at the new context
1095
try {
1096
1097
return refCtx.lookup(name);
1098
1099
} catch (LdapReferralException re) {
1100
e = re;
1101
continue;
1102
1103
} finally {
1104
// Make sure we close referral context
1105
refCtx.close();
1106
}
1107
}
1108
1109
} catch (NamingException e) {
1110
throw cont.fillInException(e);
1111
}
1112
1113
try {
1114
return DirectoryManager.getObjectInstance(obj, name,
1115
this, envprops, attrs);
1116
1117
} catch (NamingException e) {
1118
throw cont.fillInException(e);
1119
1120
} catch (Exception e) {
1121
NamingException e2 = new NamingException(
1122
"problem generating object using object factory");
1123
e2.setRootCause(e);
1124
throw cont.fillInException(e2);
1125
}
1126
}
1127
1128
protected NamingEnumeration<NameClassPair> c_list(Name name, Continuation cont)
1129
throws NamingException {
1130
SearchControls cons = new SearchControls();
1131
String[] classAttrs = new String[2];
1132
1133
classAttrs[0] = Obj.JAVA_ATTRIBUTES[Obj.OBJECT_CLASS];
1134
classAttrs[1] = Obj.JAVA_ATTRIBUTES[Obj.CLASSNAME];
1135
cons.setReturningAttributes(classAttrs);
1136
1137
// set this flag to override the typesOnly flag
1138
cons.setReturningObjFlag(true);
1139
1140
cont.setError(this, name);
1141
1142
LdapResult answer = null;
1143
1144
try {
1145
answer = doSearch(name, "(objectClass=*)", cons, true, true);
1146
1147
// list result may contain continuation references
1148
if ((answer.status != LdapClient.LDAP_SUCCESS) ||
1149
(answer.referrals != null)) {
1150
processReturnCode(answer, name);
1151
}
1152
1153
return new LdapNamingEnumeration(this, answer, name, cont);
1154
1155
} catch (LdapReferralException e) {
1156
if (handleReferrals == LdapClient.LDAP_REF_THROW)
1157
throw cont.fillInException(e);
1158
1159
// process the referrals sequentially
1160
while (true) {
1161
1162
LdapReferralContext refCtx =
1163
(LdapReferralContext)e.getReferralContext(envprops, bindCtls);
1164
1165
// repeat the original operation at the new context
1166
try {
1167
1168
return refCtx.list(name);
1169
1170
} catch (LdapReferralException re) {
1171
e = re;
1172
continue;
1173
1174
} finally {
1175
// Make sure we close referral context
1176
refCtx.close();
1177
}
1178
}
1179
1180
} catch (LimitExceededException e) {
1181
LdapNamingEnumeration res =
1182
new LdapNamingEnumeration(this, answer, name, cont);
1183
1184
res.setNamingException(
1185
(LimitExceededException)cont.fillInException(e));
1186
return res;
1187
1188
} catch (PartialResultException e) {
1189
LdapNamingEnumeration res =
1190
new LdapNamingEnumeration(this, answer, name, cont);
1191
1192
res.setNamingException(
1193
(PartialResultException)cont.fillInException(e));
1194
return res;
1195
1196
} catch (NamingException e) {
1197
throw cont.fillInException(e);
1198
}
1199
}
1200
1201
protected NamingEnumeration<Binding> c_listBindings(Name name, Continuation cont)
1202
throws NamingException {
1203
1204
SearchControls cons = new SearchControls();
1205
cons.setReturningAttributes(null); // ask for all attributes
1206
cons.setReturningObjFlag(true); // need values to construct obj
1207
1208
cont.setError(this, name);
1209
1210
LdapResult answer = null;
1211
1212
try {
1213
answer = doSearch(name, "(objectClass=*)", cons, true, true);
1214
1215
// listBindings result may contain continuation references
1216
if ((answer.status != LdapClient.LDAP_SUCCESS) ||
1217
(answer.referrals != null)) {
1218
processReturnCode(answer, name);
1219
}
1220
1221
return new LdapBindingEnumeration(this, answer, name, cont);
1222
1223
} catch (LdapReferralException e) {
1224
if (handleReferrals == LdapClient.LDAP_REF_THROW)
1225
throw cont.fillInException(e);
1226
1227
// process the referrals sequentially
1228
while (true) {
1229
@SuppressWarnings("unchecked")
1230
LdapReferralContext refCtx =
1231
(LdapReferralContext)e.getReferralContext(envprops, bindCtls);
1232
1233
// repeat the original operation at the new context
1234
try {
1235
1236
return refCtx.listBindings(name);
1237
1238
} catch (LdapReferralException re) {
1239
e = re;
1240
continue;
1241
1242
} finally {
1243
// Make sure we close referral context
1244
refCtx.close();
1245
}
1246
}
1247
} catch (LimitExceededException e) {
1248
LdapBindingEnumeration res =
1249
new LdapBindingEnumeration(this, answer, name, cont);
1250
1251
res.setNamingException(cont.fillInException(e));
1252
return res;
1253
1254
} catch (PartialResultException e) {
1255
LdapBindingEnumeration res =
1256
new LdapBindingEnumeration(this, answer, name, cont);
1257
1258
res.setNamingException(cont.fillInException(e));
1259
return res;
1260
1261
} catch (NamingException e) {
1262
throw cont.fillInException(e);
1263
}
1264
}
1265
1266
// --------------- Name-related Methods -----------------------
1267
// -- getNameParser/getNameInNamespace/composeName
1268
1269
protected NameParser c_getNameParser(Name name, Continuation cont)
1270
throws NamingException
1271
{
1272
// ignore name, always return same parser
1273
cont.setSuccess();
1274
return parser;
1275
}
1276
1277
public String getNameInNamespace() {
1278
return currentDN;
1279
}
1280
1281
public Name composeName(Name name, Name prefix)
1282
throws NamingException
1283
{
1284
Name result;
1285
1286
// Handle compound names. A pair of LdapNames is an easy case.
1287
if ((name instanceof LdapName) && (prefix instanceof LdapName)) {
1288
result = (Name)(prefix.clone());
1289
result.addAll(name);
1290
return new CompositeName().add(result.toString());
1291
}
1292
if (!(name instanceof CompositeName)) {
1293
name = new CompositeName().add(name.toString());
1294
}
1295
if (!(prefix instanceof CompositeName)) {
1296
prefix = new CompositeName().add(prefix.toString());
1297
}
1298
1299
int prefixLast = prefix.size() - 1;
1300
1301
if (name.isEmpty() || prefix.isEmpty() ||
1302
name.get(0).isEmpty() || prefix.get(prefixLast).isEmpty()) {
1303
return super.composeName(name, prefix);
1304
}
1305
1306
result = (Name)(prefix.clone());
1307
result.addAll(name);
1308
1309
if (parentIsLdapCtx) {
1310
String ldapComp = concatNames(result.get(prefixLast + 1),
1311
result.get(prefixLast));
1312
result.remove(prefixLast + 1);
1313
result.remove(prefixLast);
1314
result.add(prefixLast, ldapComp);
1315
}
1316
return result;
1317
}
1318
1319
private String fullyQualifiedName(Name rel) {
1320
return rel.isEmpty()
1321
? currentDN
1322
: fullyQualifiedName(rel.get(0));
1323
}
1324
1325
private String fullyQualifiedName(String rel) {
1326
return (concatNames(rel, currentDN));
1327
}
1328
1329
// used by LdapSearchEnumeration
1330
private static String concatNames(String lesser, String greater) {
1331
if (lesser == null || lesser.isEmpty()) {
1332
return greater;
1333
} else if (greater == null || greater.isEmpty()) {
1334
return lesser;
1335
} else {
1336
return (lesser + "," + greater);
1337
}
1338
}
1339
1340
// --------------- Reading and Updating Attributes
1341
// getAttributes/modifyAttributes
1342
1343
protected Attributes c_getAttributes(Name name, String[] attrIds,
1344
Continuation cont)
1345
throws NamingException {
1346
cont.setError(this, name);
1347
1348
SearchControls cons = new SearchControls();
1349
cons.setSearchScope(SearchControls.OBJECT_SCOPE);
1350
cons.setReturningAttributes(attrIds);
1351
1352
try {
1353
LdapResult answer =
1354
doSearchOnce(name, "(objectClass=*)", cons, true);
1355
respCtls = answer.resControls; // retrieve response controls
1356
1357
if (answer.status != LdapClient.LDAP_SUCCESS) {
1358
processReturnCode(answer, name);
1359
}
1360
1361
if (answer.entries == null || answer.entries.size() != 1) {
1362
return new BasicAttributes(LdapClient.caseIgnore);
1363
}
1364
1365
// get attributes from result
1366
LdapEntry entry = answer.entries.elementAt(0);
1367
1368
Vector<Control> entryCtls = entry.respCtls; // retrieve entry controls
1369
if (entryCtls != null) {
1370
appendVector(respCtls, entryCtls); // concatenate controls
1371
}
1372
1373
// do this so attributes can find their schema
1374
setParents(entry.attributes, (Name) name.clone());
1375
1376
return (entry.attributes);
1377
1378
} catch (LdapReferralException e) {
1379
if (handleReferrals == LdapClient.LDAP_REF_THROW)
1380
throw cont.fillInException(e);
1381
1382
// process the referrals sequentially
1383
while (true) {
1384
1385
LdapReferralContext refCtx =
1386
(LdapReferralContext)e.getReferralContext(envprops, bindCtls);
1387
1388
// repeat the original operation at the new context
1389
try {
1390
1391
return refCtx.getAttributes(name, attrIds);
1392
1393
} catch (LdapReferralException re) {
1394
e = re;
1395
continue;
1396
1397
} finally {
1398
// Make sure we close referral context
1399
refCtx.close();
1400
}
1401
}
1402
1403
} catch (NamingException e) {
1404
throw cont.fillInException(e);
1405
}
1406
}
1407
1408
protected void c_modifyAttributes(Name name, int mod_op, Attributes attrs,
1409
Continuation cont)
1410
throws NamingException {
1411
1412
cont.setError(this, name);
1413
1414
try {
1415
ensureOpen();
1416
1417
if (attrs == null || attrs.size() == 0) {
1418
return; // nothing to do
1419
}
1420
String newDN = fullyQualifiedName(name);
1421
int jmod_op = convertToLdapModCode(mod_op);
1422
1423
// construct mod list
1424
int[] jmods = new int[attrs.size()];
1425
Attribute[] jattrs = new Attribute[attrs.size()];
1426
1427
NamingEnumeration<? extends Attribute> ae = attrs.getAll();
1428
for(int i = 0; i < jmods.length && ae.hasMore(); i++) {
1429
jmods[i] = jmod_op;
1430
jattrs[i] = ae.next();
1431
}
1432
1433
LdapResult answer = clnt.modify(newDN, jmods, jattrs, reqCtls);
1434
respCtls = answer.resControls; // retrieve response controls
1435
1436
if (answer.status != LdapClient.LDAP_SUCCESS) {
1437
processReturnCode(answer, name);
1438
return;
1439
}
1440
1441
} catch (LdapReferralException e) {
1442
if (handleReferrals == LdapClient.LDAP_REF_THROW)
1443
throw cont.fillInException(e);
1444
1445
// process the referrals sequentially
1446
while (true) {
1447
1448
LdapReferralContext refCtx =
1449
(LdapReferralContext)e.getReferralContext(envprops, bindCtls);
1450
1451
// repeat the original operation at the new context
1452
try {
1453
1454
refCtx.modifyAttributes(name, mod_op, attrs);
1455
return;
1456
1457
} catch (LdapReferralException re) {
1458
e = re;
1459
continue;
1460
1461
} finally {
1462
// Make sure we close referral context
1463
refCtx.close();
1464
}
1465
}
1466
1467
} catch (IOException e) {
1468
NamingException e2 = new CommunicationException(e.getMessage());
1469
e2.setRootCause(e);
1470
throw cont.fillInException(e2);
1471
1472
} catch (NamingException e) {
1473
throw cont.fillInException(e);
1474
}
1475
}
1476
1477
protected void c_modifyAttributes(Name name, ModificationItem[] mods,
1478
Continuation cont)
1479
throws NamingException {
1480
cont.setError(this, name);
1481
1482
try {
1483
ensureOpen();
1484
1485
if (mods == null || mods.length == 0) {
1486
return; // nothing to do
1487
}
1488
String newDN = fullyQualifiedName(name);
1489
1490
// construct mod list
1491
int[] jmods = new int[mods.length];
1492
Attribute[] jattrs = new Attribute[mods.length];
1493
ModificationItem mod;
1494
for (int i = 0; i < jmods.length; i++) {
1495
mod = mods[i];
1496
jmods[i] = convertToLdapModCode(mod.getModificationOp());
1497
jattrs[i] = mod.getAttribute();
1498
}
1499
1500
LdapResult answer = clnt.modify(newDN, jmods, jattrs, reqCtls);
1501
respCtls = answer.resControls; // retrieve response controls
1502
1503
if (answer.status != LdapClient.LDAP_SUCCESS) {
1504
processReturnCode(answer, name);
1505
}
1506
1507
} catch (LdapReferralException e) {
1508
if (handleReferrals == LdapClient.LDAP_REF_THROW)
1509
throw cont.fillInException(e);
1510
1511
// process the referrals sequentially
1512
while (true) {
1513
1514
LdapReferralContext refCtx =
1515
(LdapReferralContext)e.getReferralContext(envprops, bindCtls);
1516
1517
// repeat the original operation at the new context
1518
try {
1519
1520
refCtx.modifyAttributes(name, mods);
1521
return;
1522
1523
} catch (LdapReferralException re) {
1524
e = re;
1525
continue;
1526
1527
} finally {
1528
// Make sure we close referral context
1529
refCtx.close();
1530
}
1531
}
1532
1533
} catch (IOException e) {
1534
NamingException e2 = new CommunicationException(e.getMessage());
1535
e2.setRootCause(e);
1536
throw cont.fillInException(e2);
1537
1538
} catch (NamingException e) {
1539
throw cont.fillInException(e);
1540
}
1541
}
1542
1543
private static int convertToLdapModCode(int mod_op) {
1544
switch (mod_op) {
1545
case DirContext.ADD_ATTRIBUTE:
1546
return(LdapClient.ADD);
1547
1548
case DirContext.REPLACE_ATTRIBUTE:
1549
return (LdapClient.REPLACE);
1550
1551
case DirContext.REMOVE_ATTRIBUTE:
1552
return (LdapClient.DELETE);
1553
1554
default:
1555
throw new IllegalArgumentException("Invalid modification code");
1556
}
1557
}
1558
1559
// ------------------- Schema -----------------------
1560
1561
protected DirContext c_getSchema(Name name, Continuation cont)
1562
throws NamingException {
1563
cont.setError(this, name);
1564
try {
1565
return getSchemaTree(name);
1566
1567
} catch (NamingException e) {
1568
throw cont.fillInException(e);
1569
}
1570
}
1571
1572
protected DirContext c_getSchemaClassDefinition(Name name,
1573
Continuation cont)
1574
throws NamingException {
1575
cont.setError(this, name);
1576
1577
try {
1578
// retrieve the objectClass attribute from LDAP
1579
Attribute objectClassAttr = c_getAttributes(name,
1580
new String[]{"objectclass"}, cont).get("objectclass");
1581
if (objectClassAttr == null || objectClassAttr.size() == 0) {
1582
return EMPTY_SCHEMA;
1583
}
1584
1585
// retrieve the root of the ObjectClass schema tree
1586
Context ocSchema = (Context) c_getSchema(name, cont).lookup(
1587
LdapSchemaParser.OBJECTCLASS_DEFINITION_NAME);
1588
1589
// create a context to hold the schema objects representing the object
1590
// classes
1591
HierMemDirCtx objectClassCtx = new HierMemDirCtx();
1592
DirContext objectClassDef;
1593
String objectClassName;
1594
for (Enumeration<?> objectClasses = objectClassAttr.getAll();
1595
objectClasses.hasMoreElements(); ) {
1596
objectClassName = (String)objectClasses.nextElement();
1597
// %%% Should we fail if not found, or just continue?
1598
objectClassDef = (DirContext)ocSchema.lookup(objectClassName);
1599
objectClassCtx.bind(objectClassName, objectClassDef);
1600
}
1601
1602
// Make context read-only
1603
objectClassCtx.setReadOnly(
1604
new SchemaViolationException("Cannot update schema object"));
1605
return (DirContext)objectClassCtx;
1606
1607
} catch (NamingException e) {
1608
throw cont.fillInException(e);
1609
}
1610
}
1611
1612
/*
1613
* getSchemaTree first looks to see if we have already built a
1614
* schema tree for the given entry. If not, it builds a new one and
1615
* stores it in our private hash table
1616
*/
1617
private DirContext getSchemaTree(Name name) throws NamingException {
1618
String subschemasubentry = getSchemaEntry(name, true);
1619
1620
DirContext schemaTree = schemaTrees.get(subschemasubentry);
1621
1622
if(schemaTree==null) {
1623
if(debug){System.err.println("LdapCtx: building new schema tree " + this);}
1624
schemaTree = buildSchemaTree(subschemasubentry);
1625
schemaTrees.put(subschemasubentry, schemaTree);
1626
}
1627
1628
return schemaTree;
1629
}
1630
1631
/*
1632
* buildSchemaTree builds the schema tree corresponding to the
1633
* given subschemasubentree
1634
*/
1635
private DirContext buildSchemaTree(String subschemasubentry)
1636
throws NamingException {
1637
1638
// get the schema entry itself
1639
// DO ask for return object here because we need it to
1640
// create context. Since asking for all attrs, we won't
1641
// be transmitting any specific attrIDs (like Java-specific ones).
1642
SearchControls constraints = new
1643
SearchControls(SearchControls.OBJECT_SCOPE,
1644
0, 0, /* count and time limits */
1645
SCHEMA_ATTRIBUTES /* return schema attrs */,
1646
true /* return obj */,
1647
false /*deref link */ );
1648
1649
Name sse = (new CompositeName()).add(subschemasubentry);
1650
NamingEnumeration<SearchResult> results =
1651
searchAux(sse, "(objectClass=subschema)", constraints,
1652
false, true, new Continuation());
1653
1654
if(!results.hasMore()) {
1655
throw new OperationNotSupportedException(
1656
"Cannot get read subschemasubentry: " + subschemasubentry);
1657
}
1658
SearchResult result = results.next();
1659
results.close();
1660
1661
Object obj = result.getObject();
1662
if(!(obj instanceof LdapCtx)) {
1663
throw new NamingException(
1664
"Cannot get schema object as DirContext: " + subschemasubentry);
1665
}
1666
1667
return LdapSchemaCtx.createSchemaTree(envprops, subschemasubentry,
1668
(LdapCtx)obj /* schema entry */,
1669
result.getAttributes() /* schema attributes */,
1670
netscapeSchemaBug);
1671
}
1672
1673
/*
1674
* getSchemaEntree returns the DN of the subschemasubentree for the
1675
* given entree. It first looks to see if the given entry has
1676
* a subschema different from that of the root DIT (by looking for
1677
* a "subschemasubentry" attribute). If it doesn't find one, it returns
1678
* the one for the root of the DIT (by looking for the root's
1679
* "subschemasubentry" attribute).
1680
*
1681
* This function is called regardless of the server's version, since
1682
* an administrator may have setup the server to support client schema
1683
* queries. If this function tries a search on a v2 server that
1684
* doesn't support schema, one of these two things will happen:
1685
* 1) It will get an exception when querying the root DSE
1686
* 2) It will not find a subschemasubentry on the root DSE
1687
* If either of these things occur and the server is not v3, we
1688
* throw OperationNotSupported.
1689
*
1690
* the relative flag tells whether the given name is relative to this
1691
* context.
1692
*/
1693
private String getSchemaEntry(Name name, boolean relative)
1694
throws NamingException {
1695
1696
// Asks for operational attribute "subschemasubentry"
1697
SearchControls constraints = new SearchControls(SearchControls.OBJECT_SCOPE,
1698
0, 0, /* count and time limits */
1699
new String[]{"subschemasubentry"} /* attr to return */,
1700
false /* returning obj */,
1701
false /* deref link */);
1702
1703
NamingEnumeration<SearchResult> results;
1704
try {
1705
results = searchAux(name, "objectclass=*", constraints, relative,
1706
true, new Continuation());
1707
1708
} catch (NamingException ne) {
1709
if (!clnt.isLdapv3 && currentDN.length() == 0 && name.isEmpty()) {
1710
// we got an error looking for a root entry on an ldapv2
1711
// server. The server must not support schema.
1712
throw new OperationNotSupportedException(
1713
"Cannot get schema information from server");
1714
} else {
1715
throw ne;
1716
}
1717
}
1718
1719
if (!results.hasMoreElements()) {
1720
throw new ConfigurationException(
1721
"Requesting schema of nonexistent entry: " + name);
1722
}
1723
1724
SearchResult result = results.next();
1725
results.close();
1726
1727
Attribute schemaEntryAttr =
1728
result.getAttributes().get("subschemasubentry");
1729
//System.err.println("schema entry attrs: " + schemaEntryAttr);
1730
1731
if (schemaEntryAttr == null || schemaEntryAttr.size() < 0) {
1732
if (currentDN.length() == 0 && name.isEmpty()) {
1733
// the server doesn't have a subschemasubentry in its root DSE.
1734
// therefore, it doesn't support schema.
1735
throw new OperationNotSupportedException(
1736
"Cannot read subschemasubentry of root DSE");
1737
} else {
1738
return getSchemaEntry(new CompositeName(), false);
1739
}
1740
}
1741
1742
return (String)(schemaEntryAttr.get()); // return schema entry name
1743
}
1744
1745
// package-private; used by search enum.
1746
// Set attributes to point to this context in case some one
1747
// asked for their schema
1748
void setParents(Attributes attrs, Name name) throws NamingException {
1749
NamingEnumeration<? extends Attribute> ae = attrs.getAll();
1750
while(ae.hasMore()) {
1751
((LdapAttribute) ae.next()).setParent(this, name);
1752
}
1753
}
1754
1755
/*
1756
* Returns the URL associated with this context; used by LdapAttribute
1757
* after deserialization to get pointer to this context.
1758
*/
1759
String getURL() {
1760
if (url == null) {
1761
url = LdapURL.toUrlString(hostname, port_number, currentDN,
1762
hasLdapsScheme);
1763
}
1764
1765
return url;
1766
}
1767
1768
// --------------------- Searches -----------------------------
1769
protected NamingEnumeration<SearchResult> c_search(Name name,
1770
Attributes matchingAttributes,
1771
Continuation cont)
1772
throws NamingException {
1773
return c_search(name, matchingAttributes, null, cont);
1774
}
1775
1776
protected NamingEnumeration<SearchResult> c_search(Name name,
1777
Attributes matchingAttributes,
1778
String[] attributesToReturn,
1779
Continuation cont)
1780
throws NamingException {
1781
SearchControls cons = new SearchControls();
1782
cons.setReturningAttributes(attributesToReturn);
1783
String filter;
1784
try {
1785
filter = SearchFilter.format(matchingAttributes);
1786
} catch (NamingException e) {
1787
cont.setError(this, name);
1788
throw cont.fillInException(e);
1789
}
1790
return c_search(name, filter, cons, cont);
1791
}
1792
1793
protected NamingEnumeration<SearchResult> c_search(Name name,
1794
String filter,
1795
SearchControls cons,
1796
Continuation cont)
1797
throws NamingException {
1798
return searchAux(name, filter, cloneSearchControls(cons), true,
1799
waitForReply, cont);
1800
}
1801
1802
protected NamingEnumeration<SearchResult> c_search(Name name,
1803
String filterExpr,
1804
Object[] filterArgs,
1805
SearchControls cons,
1806
Continuation cont)
1807
throws NamingException {
1808
String strfilter;
1809
try {
1810
strfilter = SearchFilter.format(filterExpr, filterArgs);
1811
} catch (NamingException e) {
1812
cont.setError(this, name);
1813
throw cont.fillInException(e);
1814
}
1815
return c_search(name, strfilter, cons, cont);
1816
}
1817
1818
// Used by NamingNotifier
1819
NamingEnumeration<SearchResult> searchAux(Name name,
1820
String filter,
1821
SearchControls cons,
1822
boolean relative,
1823
boolean waitForReply, Continuation cont) throws NamingException {
1824
1825
LdapResult answer = null;
1826
String[] tokens = new String[2]; // stores ldap compare op. values
1827
String[] reqAttrs; // remember what was asked
1828
1829
if (cons == null) {
1830
cons = new SearchControls();
1831
}
1832
reqAttrs = cons.getReturningAttributes();
1833
1834
// if objects are requested then request the Java attributes too
1835
// so that the objects can be constructed
1836
if (cons.getReturningObjFlag()) {
1837
if (reqAttrs != null) {
1838
1839
// check for presence of "*" (user attributes wildcard)
1840
boolean hasWildcard = false;
1841
for (int i = reqAttrs.length - 1; i >= 0; i--) {
1842
if (reqAttrs[i].equals("*")) {
1843
hasWildcard = true;
1844
break;
1845
}
1846
}
1847
if (! hasWildcard) {
1848
String[] totalAttrs =
1849
new String[reqAttrs.length +Obj.JAVA_ATTRIBUTES.length];
1850
System.arraycopy(reqAttrs, 0, totalAttrs, 0,
1851
reqAttrs.length);
1852
System.arraycopy(Obj.JAVA_ATTRIBUTES, 0, totalAttrs,
1853
reqAttrs.length, Obj.JAVA_ATTRIBUTES.length);
1854
1855
cons.setReturningAttributes(totalAttrs);
1856
}
1857
}
1858
}
1859
1860
LdapCtx.SearchArgs args =
1861
new LdapCtx.SearchArgs(name, filter, cons, reqAttrs);
1862
1863
cont.setError(this, name);
1864
try {
1865
// see if this can be done as a compare, otherwise do a search
1866
if (searchToCompare(filter, cons, tokens)){
1867
//System.err.println("compare triggered");
1868
answer = compare(name, tokens[0], tokens[1]);
1869
if (! (answer.compareToSearchResult(fullyQualifiedName(name)))){
1870
processReturnCode(answer, name);
1871
}
1872
} else {
1873
answer = doSearch(name, filter, cons, relative, waitForReply);
1874
// search result may contain referrals
1875
processReturnCode(answer, name);
1876
}
1877
return new LdapSearchEnumeration(this, answer,
1878
fullyQualifiedName(name),
1879
args, cont);
1880
1881
} catch (LdapReferralException e) {
1882
if (handleReferrals == LdapClient.LDAP_REF_THROW)
1883
throw cont.fillInException(e);
1884
1885
// process the referrals sequentially
1886
while (true) {
1887
1888
@SuppressWarnings("unchecked")
1889
LdapReferralContext refCtx = (LdapReferralContext)
1890
e.getReferralContext(envprops, bindCtls);
1891
1892
// repeat the original operation at the new context
1893
try {
1894
1895
return refCtx.search(name, filter, cons);
1896
1897
} catch (LdapReferralException re) {
1898
e = re;
1899
continue;
1900
1901
} finally {
1902
// Make sure we close referral context
1903
refCtx.close();
1904
}
1905
}
1906
1907
} catch (LimitExceededException e) {
1908
LdapSearchEnumeration res =
1909
new LdapSearchEnumeration(this, answer, fullyQualifiedName(name),
1910
args, cont);
1911
res.setNamingException(e);
1912
return res;
1913
1914
} catch (PartialResultException e) {
1915
LdapSearchEnumeration res =
1916
new LdapSearchEnumeration(this, answer, fullyQualifiedName(name),
1917
args, cont);
1918
1919
res.setNamingException(e);
1920
return res;
1921
1922
} catch (IOException e) {
1923
NamingException e2 = new CommunicationException(e.getMessage());
1924
e2.setRootCause(e);
1925
throw cont.fillInException(e2);
1926
1927
} catch (NamingException e) {
1928
throw cont.fillInException(e);
1929
}
1930
}
1931
1932
1933
LdapResult getSearchReply(LdapClient eClnt, LdapResult res)
1934
throws NamingException {
1935
// ensureOpen() won't work here because
1936
// session was associated with previous connection
1937
1938
// %%% RL: we can actually allow the enumeration to continue
1939
// using the old handle but other weird things might happen
1940
// when we hit a referral
1941
if (clnt != eClnt) {
1942
throw new CommunicationException(
1943
"Context's connection changed; unable to continue enumeration");
1944
}
1945
1946
try {
1947
return eClnt.getSearchReply(batchSize, res, binaryAttrs);
1948
} catch (IOException e) {
1949
NamingException e2 = new CommunicationException(e.getMessage());
1950
e2.setRootCause(e);
1951
throw e2;
1952
}
1953
}
1954
1955
// Perform a search. Expect 1 SearchResultEntry and the SearchResultDone.
1956
private LdapResult doSearchOnce(Name name, String filter,
1957
SearchControls cons, boolean relative) throws NamingException {
1958
1959
int savedBatchSize = batchSize;
1960
batchSize = 2; // 2 protocol elements
1961
1962
LdapResult answer = doSearch(name, filter, cons, relative, true);
1963
1964
batchSize = savedBatchSize;
1965
return answer;
1966
}
1967
1968
private LdapResult doSearch(Name name, String filter, SearchControls cons,
1969
boolean relative, boolean waitForReply) throws NamingException {
1970
ensureOpen();
1971
try {
1972
int scope;
1973
1974
switch (cons.getSearchScope()) {
1975
case SearchControls.OBJECT_SCOPE:
1976
scope = LdapClient.SCOPE_BASE_OBJECT;
1977
break;
1978
default:
1979
case SearchControls.ONELEVEL_SCOPE:
1980
scope = LdapClient.SCOPE_ONE_LEVEL;
1981
break;
1982
case SearchControls.SUBTREE_SCOPE:
1983
scope = LdapClient.SCOPE_SUBTREE;
1984
break;
1985
}
1986
1987
// If cons.getReturningObjFlag() then caller should already
1988
// have make sure to request the appropriate attrs
1989
1990
String[] retattrs = cons.getReturningAttributes();
1991
if (retattrs != null && retattrs.length == 0) {
1992
// Ldap treats null and empty array the same
1993
// need to replace with single element array
1994
retattrs = new String[1];
1995
retattrs[0] = "1.1";
1996
}
1997
1998
String nm = (relative
1999
? fullyQualifiedName(name)
2000
: (name.isEmpty()
2001
? ""
2002
: name.get(0)));
2003
2004
// JNDI unit is milliseconds, LDAP unit is seconds.
2005
// Zero means no limit.
2006
int msecLimit = cons.getTimeLimit();
2007
int secLimit = 0;
2008
2009
if (msecLimit > 0) {
2010
secLimit = (msecLimit / 1000) + 1;
2011
}
2012
2013
LdapResult answer =
2014
clnt.search(nm,
2015
scope,
2016
derefAliases,
2017
(int)cons.getCountLimit(),
2018
secLimit,
2019
cons.getReturningObjFlag() ? false : typesOnly,
2020
retattrs,
2021
filter,
2022
batchSize,
2023
reqCtls,
2024
binaryAttrs,
2025
waitForReply,
2026
replyQueueSize);
2027
respCtls = answer.resControls; // retrieve response controls
2028
return answer;
2029
2030
} catch (IOException e) {
2031
NamingException e2 = new CommunicationException(e.getMessage());
2032
e2.setRootCause(e);
2033
throw e2;
2034
}
2035
}
2036
2037
2038
/*
2039
* Certain simple JNDI searches are automatically converted to
2040
* LDAP compare operations by the LDAP service provider. A search
2041
* is converted to a compare iff:
2042
*
2043
* - the scope is set to OBJECT_SCOPE
2044
* - the filter string contains a simple assertion: "<type>=<value>"
2045
* - the returning attributes list is present but empty
2046
*/
2047
2048
// returns true if a search can be carried out as a compare, and sets
2049
// tokens[0] and tokens[1] to the type and value respectively.
2050
// e.g. filter "cn=Jon Ruiz" becomes, type "cn" and value "Jon Ruiz"
2051
// This function uses the documents JNDI Compare example as a model
2052
// for when to turn a search into a compare.
2053
2054
private static boolean searchToCompare(
2055
String filter,
2056
SearchControls cons,
2057
String tokens[]) {
2058
2059
// if scope is not object-scope, it's really a search
2060
if (cons.getSearchScope() != SearchControls.OBJECT_SCOPE) {
2061
return false;
2062
}
2063
2064
// if attributes are to be returned, it's really a search
2065
String[] attrs = cons.getReturningAttributes();
2066
if (attrs == null || attrs.length != 0) {
2067
return false;
2068
}
2069
2070
// if the filter not a simple assertion, it's really a search
2071
if (! filterToAssertion(filter, tokens)) {
2072
return false;
2073
}
2074
2075
// it can be converted to a compare
2076
return true;
2077
}
2078
2079
// If the supplied filter is a simple assertion i.e. "<type>=<value>"
2080
// (enclosing parentheses are permitted) then
2081
// filterToAssertion will return true and pass the type and value as
2082
// the first and second elements of tokens respectively.
2083
// precondition: tokens[] must be initialized and be at least of size 2.
2084
2085
private static boolean filterToAssertion(String filter, String tokens[]) {
2086
2087
// find the left and right half of the assertion
2088
StringTokenizer assertionTokenizer = new StringTokenizer(filter, "=");
2089
2090
if (assertionTokenizer.countTokens() != 2) {
2091
return false;
2092
}
2093
2094
tokens[0] = assertionTokenizer.nextToken();
2095
tokens[1] = assertionTokenizer.nextToken();
2096
2097
// make sure the value does not contain a wildcard
2098
if (tokens[1].indexOf('*') != -1) {
2099
return false;
2100
}
2101
2102
// test for enclosing parenthesis
2103
boolean hasParens = false;
2104
int len = tokens[1].length();
2105
2106
if ((tokens[0].charAt(0) == '(') &&
2107
(tokens[1].charAt(len - 1) == ')')) {
2108
hasParens = true;
2109
2110
} else if ((tokens[0].charAt(0) == '(') ||
2111
(tokens[1].charAt(len - 1) == ')')) {
2112
return false; // unbalanced
2113
}
2114
2115
// make sure the left and right half are not expressions themselves
2116
StringTokenizer illegalCharsTokenizer =
2117
new StringTokenizer(tokens[0], "()&|!=~><*", true);
2118
2119
if (illegalCharsTokenizer.countTokens() != (hasParens ? 2 : 1)) {
2120
return false;
2121
}
2122
2123
illegalCharsTokenizer =
2124
new StringTokenizer(tokens[1], "()&|!=~><*", true);
2125
2126
if (illegalCharsTokenizer.countTokens() != (hasParens ? 2 : 1)) {
2127
return false;
2128
}
2129
2130
// strip off enclosing parenthesis, if present
2131
if (hasParens) {
2132
tokens[0] = tokens[0].substring(1);
2133
tokens[1] = tokens[1].substring(0, len - 1);
2134
}
2135
2136
return true;
2137
}
2138
2139
private LdapResult compare(Name name, String type, String value)
2140
throws IOException, NamingException {
2141
2142
ensureOpen();
2143
String nm = fullyQualifiedName(name);
2144
2145
LdapResult answer = clnt.compare(nm, type, value, reqCtls);
2146
respCtls = answer.resControls; // retrieve response controls
2147
2148
return answer;
2149
}
2150
2151
private static SearchControls cloneSearchControls(SearchControls cons) {
2152
if (cons == null) {
2153
return null;
2154
}
2155
String[] retAttrs = cons.getReturningAttributes();
2156
if (retAttrs != null) {
2157
String[] attrs = new String[retAttrs.length];
2158
System.arraycopy(retAttrs, 0, attrs, 0, retAttrs.length);
2159
retAttrs = attrs;
2160
}
2161
return new SearchControls(cons.getSearchScope(),
2162
cons.getCountLimit(),
2163
cons.getTimeLimit(),
2164
retAttrs,
2165
cons.getReturningObjFlag(),
2166
cons.getDerefLinkFlag());
2167
}
2168
2169
// -------------- Environment Properties ------------------
2170
2171
/**
2172
* Override with noncloning version.
2173
*/
2174
protected Hashtable<String, Object> p_getEnvironment() {
2175
return envprops;
2176
}
2177
2178
@SuppressWarnings("unchecked") // clone()
2179
public Hashtable<String, Object> getEnvironment() throws NamingException {
2180
return (envprops == null
2181
? new Hashtable<String, Object>(5, 0.75f)
2182
: (Hashtable<String, Object>)envprops.clone());
2183
}
2184
2185
@SuppressWarnings("unchecked") // clone()
2186
public Object removeFromEnvironment(String propName)
2187
throws NamingException {
2188
2189
// not there; just return
2190
if (envprops == null || envprops.get(propName) == null) {
2191
return null;
2192
}
2193
switch (propName) {
2194
case REF_SEPARATOR:
2195
addrEncodingSeparator = DEFAULT_REF_SEPARATOR;
2196
break;
2197
case TYPES_ONLY:
2198
typesOnly = DEFAULT_TYPES_ONLY;
2199
break;
2200
case DELETE_RDN:
2201
deleteRDN = DEFAULT_DELETE_RDN;
2202
break;
2203
case DEREF_ALIASES:
2204
derefAliases = DEFAULT_DEREF_ALIASES;
2205
break;
2206
case Context.BATCHSIZE:
2207
batchSize = DEFAULT_BATCH_SIZE;
2208
break;
2209
case REFERRAL_LIMIT:
2210
referralHopLimit = DEFAULT_REFERRAL_LIMIT;
2211
break;
2212
case Context.REFERRAL:
2213
setReferralMode(null, true);
2214
break;
2215
case BINARY_ATTRIBUTES:
2216
setBinaryAttributes(null);
2217
break;
2218
case CONNECT_TIMEOUT:
2219
connectTimeout = -1;
2220
break;
2221
case READ_TIMEOUT:
2222
readTimeout = -1;
2223
break;
2224
case WAIT_FOR_REPLY:
2225
waitForReply = true;
2226
break;
2227
case REPLY_QUEUE_SIZE:
2228
replyQueueSize = -1;
2229
break;
2230
2231
// The following properties affect the connection
2232
2233
case Context.SECURITY_PROTOCOL:
2234
closeConnection(SOFT_CLOSE);
2235
// De-activate SSL and reset the context's url and port number
2236
if (useSsl && !hasLdapsScheme) {
2237
useSsl = false;
2238
url = null;
2239
if (useDefaultPortNumber) {
2240
port_number = DEFAULT_PORT;
2241
}
2242
}
2243
break;
2244
case VERSION:
2245
case SOCKET_FACTORY:
2246
closeConnection(SOFT_CLOSE);
2247
break;
2248
case Context.SECURITY_AUTHENTICATION:
2249
case Context.SECURITY_PRINCIPAL:
2250
case Context.SECURITY_CREDENTIALS:
2251
sharable = false;
2252
break;
2253
}
2254
2255
// Update environment; reconnection will use new props
2256
envprops = (Hashtable<String, Object>)envprops.clone();
2257
return envprops.remove(propName);
2258
}
2259
2260
@SuppressWarnings("unchecked") // clone()
2261
public Object addToEnvironment(String propName, Object propVal)
2262
throws NamingException {
2263
2264
// If adding null, call remove
2265
if (propVal == null) {
2266
return removeFromEnvironment(propName);
2267
}
2268
switch (propName) {
2269
case REF_SEPARATOR:
2270
setRefSeparator((String)propVal);
2271
break;
2272
case TYPES_ONLY:
2273
setTypesOnly((String)propVal);
2274
break;
2275
case DELETE_RDN:
2276
setDeleteRDN((String)propVal);
2277
break;
2278
case DEREF_ALIASES:
2279
setDerefAliases((String)propVal);
2280
break;
2281
case Context.BATCHSIZE:
2282
setBatchSize((String)propVal);
2283
break;
2284
case REFERRAL_LIMIT:
2285
setReferralLimit((String)propVal);
2286
break;
2287
case Context.REFERRAL:
2288
setReferralMode((String)propVal, true);
2289
break;
2290
case BINARY_ATTRIBUTES:
2291
setBinaryAttributes((String)propVal);
2292
break;
2293
case CONNECT_TIMEOUT:
2294
setConnectTimeout((String)propVal);
2295
break;
2296
case READ_TIMEOUT:
2297
setReadTimeout((String)propVal);
2298
break;
2299
case WAIT_FOR_REPLY:
2300
setWaitForReply((String)propVal);
2301
break;
2302
case REPLY_QUEUE_SIZE:
2303
setReplyQueueSize((String)propVal);
2304
break;
2305
2306
// The following properties affect the connection
2307
2308
case Context.SECURITY_PROTOCOL:
2309
closeConnection(SOFT_CLOSE);
2310
// Activate SSL and reset the context's url and port number
2311
if ("ssl".equals(propVal)) {
2312
useSsl = true;
2313
url = null;
2314
if (useDefaultPortNumber) {
2315
port_number = DEFAULT_SSL_PORT;
2316
}
2317
}
2318
break;
2319
case VERSION:
2320
case SOCKET_FACTORY:
2321
closeConnection(SOFT_CLOSE);
2322
break;
2323
case Context.SECURITY_AUTHENTICATION:
2324
case Context.SECURITY_PRINCIPAL:
2325
case Context.SECURITY_CREDENTIALS:
2326
sharable = false;
2327
break;
2328
}
2329
2330
// Update environment; reconnection will use new props
2331
envprops = (envprops == null
2332
? new Hashtable<String, Object>(5, 0.75f)
2333
: (Hashtable<String, Object>)envprops.clone());
2334
return envprops.put(propName, propVal);
2335
}
2336
2337
/**
2338
* Sets the URL that created the context in the java.naming.provider.url
2339
* property.
2340
*/
2341
void setProviderUrl(String providerUrl) { // called by LdapCtxFactory
2342
if (envprops != null) {
2343
envprops.put(Context.PROVIDER_URL, providerUrl);
2344
}
2345
}
2346
2347
/**
2348
* Sets the domain name for the context in the com.sun.jndi.ldap.domainname
2349
* property.
2350
* Used for hostname verification by Start TLS
2351
*/
2352
void setDomainName(String domainName) { // called by LdapCtxFactory
2353
if (envprops != null) {
2354
envprops.put(DOMAIN_NAME, domainName);
2355
}
2356
}
2357
2358
private void initEnv() throws NamingException {
2359
if (envprops == null) {
2360
// Make sure that referrals are to their default
2361
setReferralMode(null, false);
2362
return;
2363
}
2364
2365
// Set batch size
2366
setBatchSize((String)envprops.get(Context.BATCHSIZE));
2367
2368
// Set separator used for encoding RefAddr
2369
setRefSeparator((String)envprops.get(REF_SEPARATOR));
2370
2371
// Set whether RDN is removed when renaming object
2372
setDeleteRDN((String)envprops.get(DELETE_RDN));
2373
2374
// Set whether types are returned only
2375
setTypesOnly((String)envprops.get(TYPES_ONLY));
2376
2377
// Set how aliases are dereferenced
2378
setDerefAliases((String)envprops.get(DEREF_ALIASES));
2379
2380
// Set the limit on referral chains
2381
setReferralLimit((String)envprops.get(REFERRAL_LIMIT));
2382
2383
setBinaryAttributes((String)envprops.get(BINARY_ATTRIBUTES));
2384
2385
bindCtls = cloneControls((Control[]) envprops.get(BIND_CONTROLS));
2386
2387
// set referral handling
2388
setReferralMode((String)envprops.get(Context.REFERRAL), false);
2389
2390
// Set the connect timeout
2391
setConnectTimeout((String)envprops.get(CONNECT_TIMEOUT));
2392
2393
// Set the read timeout
2394
setReadTimeout((String)envprops.get(READ_TIMEOUT));
2395
2396
// Set the flag that controls whether to block until the first reply
2397
// is received
2398
setWaitForReply((String)envprops.get(WAIT_FOR_REPLY));
2399
2400
// Set the size of the queue of unprocessed search replies
2401
setReplyQueueSize((String)envprops.get(REPLY_QUEUE_SIZE));
2402
2403
// When connection is created, it will use these and other
2404
// properties from the environment
2405
}
2406
2407
private void setDeleteRDN(String deleteRDNProp) {
2408
if ((deleteRDNProp != null) &&
2409
(deleteRDNProp.equalsIgnoreCase("false"))) {
2410
deleteRDN = false;
2411
} else {
2412
deleteRDN = DEFAULT_DELETE_RDN;
2413
}
2414
}
2415
2416
private void setTypesOnly(String typesOnlyProp) {
2417
if ((typesOnlyProp != null) &&
2418
(typesOnlyProp.equalsIgnoreCase("true"))) {
2419
typesOnly = true;
2420
} else {
2421
typesOnly = DEFAULT_TYPES_ONLY;
2422
}
2423
}
2424
2425
/**
2426
* Sets the batch size of this context;
2427
*/
2428
private void setBatchSize(String batchSizeProp) {
2429
// set batchsize
2430
if (batchSizeProp != null) {
2431
batchSize = Integer.parseInt(batchSizeProp);
2432
} else {
2433
batchSize = DEFAULT_BATCH_SIZE;
2434
}
2435
}
2436
2437
/**
2438
* Sets the referral mode of this context to 'follow', 'throw' or 'ignore'.
2439
* If referral mode is 'ignore' then activate the manageReferral control.
2440
*/
2441
private void setReferralMode(String ref, boolean update) {
2442
// First determine the referral mode
2443
if (ref != null) {
2444
switch (ref) {
2445
case "follow-scheme":
2446
handleReferrals = LdapClient.LDAP_REF_FOLLOW_SCHEME;
2447
break;
2448
case "follow":
2449
handleReferrals = LdapClient.LDAP_REF_FOLLOW;
2450
break;
2451
case "throw":
2452
handleReferrals = LdapClient.LDAP_REF_THROW;
2453
break;
2454
case "ignore":
2455
handleReferrals = LdapClient.LDAP_REF_IGNORE;
2456
break;
2457
default:
2458
throw new IllegalArgumentException(
2459
"Illegal value for " + Context.REFERRAL + " property.");
2460
}
2461
} else {
2462
handleReferrals = DEFAULT_REFERRAL_MODE;
2463
}
2464
2465
if (handleReferrals == LdapClient.LDAP_REF_IGNORE) {
2466
// If ignoring referrals, add manageReferralControl
2467
reqCtls = addControl(reqCtls, manageReferralControl);
2468
2469
} else if (update) {
2470
2471
// If we're update an existing context, remove the control
2472
reqCtls = removeControl(reqCtls, manageReferralControl);
2473
2474
} // else, leave alone; need not update
2475
}
2476
2477
/**
2478
* Set whether aliases are dereferenced during resolution and searches.
2479
*/
2480
private void setDerefAliases(String deref) {
2481
if (deref != null) {
2482
switch (deref) {
2483
case "never":
2484
derefAliases = 0; // never de-reference aliases
2485
break;
2486
case "searching":
2487
derefAliases = 1; // de-reference aliases during searching
2488
break;
2489
case "finding":
2490
derefAliases = 2; // de-reference during name resolution
2491
break;
2492
case "always":
2493
derefAliases = 3; // always de-reference aliases
2494
break;
2495
default:
2496
throw new IllegalArgumentException("Illegal value for " +
2497
DEREF_ALIASES + " property.");
2498
}
2499
} else {
2500
derefAliases = DEFAULT_DEREF_ALIASES;
2501
}
2502
}
2503
2504
private void setRefSeparator(String sepStr) throws NamingException {
2505
if (sepStr != null && sepStr.length() > 0) {
2506
addrEncodingSeparator = sepStr.charAt(0);
2507
} else {
2508
addrEncodingSeparator = DEFAULT_REF_SEPARATOR;
2509
}
2510
}
2511
2512
/**
2513
* Sets the limit on referral chains
2514
*/
2515
private void setReferralLimit(String referralLimitProp) {
2516
// set referral limit
2517
if (referralLimitProp != null) {
2518
referralHopLimit = Integer.parseInt(referralLimitProp);
2519
2520
// a zero setting indicates no limit
2521
if (referralHopLimit == 0)
2522
referralHopLimit = Integer.MAX_VALUE;
2523
} else {
2524
referralHopLimit = DEFAULT_REFERRAL_LIMIT;
2525
}
2526
}
2527
2528
// For counting referral hops
2529
void setHopCount(int hopCount) {
2530
this.hopCount = hopCount;
2531
}
2532
2533
/**
2534
* Sets the connect timeout value
2535
*/
2536
private void setConnectTimeout(String connectTimeoutProp) {
2537
if (connectTimeoutProp != null) {
2538
connectTimeout = Integer.parseInt(connectTimeoutProp);
2539
} else {
2540
connectTimeout = -1;
2541
}
2542
}
2543
2544
/**
2545
* Sets the size of the queue of unprocessed search replies
2546
*/
2547
private void setReplyQueueSize(String replyQueueSizeProp) {
2548
if (replyQueueSizeProp != null) {
2549
replyQueueSize = Integer.parseInt(replyQueueSizeProp);
2550
// disallow an empty queue
2551
if (replyQueueSize <= 0) {
2552
replyQueueSize = -1; // unlimited
2553
}
2554
} else {
2555
replyQueueSize = -1; // unlimited
2556
}
2557
}
2558
2559
/**
2560
* Sets the flag that controls whether to block until the first search
2561
* reply is received
2562
*/
2563
private void setWaitForReply(String waitForReplyProp) {
2564
if (waitForReplyProp != null &&
2565
(waitForReplyProp.equalsIgnoreCase("false"))) {
2566
waitForReply = false;
2567
} else {
2568
waitForReply = true;
2569
}
2570
}
2571
2572
/**
2573
* Sets the read timeout value
2574
*/
2575
private void setReadTimeout(String readTimeoutProp) {
2576
if (readTimeoutProp != null) {
2577
readTimeout = Integer.parseInt(readTimeoutProp);
2578
} else {
2579
readTimeout = -1;
2580
}
2581
}
2582
2583
/*
2584
* Extract URLs from a string. The format of the string is:
2585
*
2586
* <urlstring > ::= "Referral:" <ldapurls>
2587
* <ldapurls> ::= <separator> <ldapurl> | <ldapurls>
2588
* <separator> ::= ASCII linefeed character (0x0a)
2589
* <ldapurl> ::= LDAP URL format (RFC 1959)
2590
*
2591
* Returns a Vector of single-String Vectors.
2592
*/
2593
private static Vector<Vector<String>> extractURLs(String refString) {
2594
2595
int separator = 0;
2596
int urlCount = 0;
2597
2598
// count the number of URLs
2599
while ((separator = refString.indexOf('\n', separator)) >= 0) {
2600
separator++;
2601
urlCount++;
2602
}
2603
2604
Vector<Vector<String>> referrals = new Vector<>(urlCount);
2605
int iURL;
2606
int i = 0;
2607
2608
separator = refString.indexOf('\n');
2609
iURL = separator + 1;
2610
while ((separator = refString.indexOf('\n', iURL)) >= 0) {
2611
Vector<String> referral = new Vector<>(1);
2612
referral.addElement(refString.substring(iURL, separator));
2613
referrals.addElement(referral);
2614
iURL = separator + 1;
2615
}
2616
Vector<String> referral = new Vector<>(1);
2617
referral.addElement(refString.substring(iURL));
2618
referrals.addElement(referral);
2619
2620
return referrals;
2621
}
2622
2623
/*
2624
* Argument is a space-separated list of attribute IDs
2625
* Converts attribute IDs to lowercase before adding to built-in list.
2626
*/
2627
private void setBinaryAttributes(String attrIds) {
2628
if (attrIds == null) {
2629
binaryAttrs = null;
2630
} else {
2631
binaryAttrs = new Hashtable<>(11, 0.75f);
2632
StringTokenizer tokens =
2633
new StringTokenizer(attrIds.toLowerCase(Locale.ENGLISH), " ");
2634
2635
while (tokens.hasMoreTokens()) {
2636
binaryAttrs.put(tokens.nextToken(), Boolean.TRUE);
2637
}
2638
}
2639
}
2640
2641
// ----------------- Connection ---------------------
2642
2643
@SuppressWarnings("deprecation")
2644
protected void finalize() {
2645
try {
2646
close();
2647
} catch (NamingException e) {
2648
// ignore failures
2649
}
2650
}
2651
2652
synchronized public void close() throws NamingException {
2653
if (debug) {
2654
System.err.println("LdapCtx: close() called " + this);
2655
(new Throwable()).printStackTrace();
2656
}
2657
2658
// Event (normal and unsolicited)
2659
if (eventSupport != null) {
2660
eventSupport.cleanup(); // idempotent
2661
removeUnsolicited();
2662
}
2663
2664
// Enumerations that are keeping the connection alive
2665
if (enumCount > 0) {
2666
if (debug)
2667
System.err.println("LdapCtx: close deferred");
2668
closeRequested = true;
2669
return;
2670
}
2671
closeConnection(SOFT_CLOSE);
2672
2673
// %%%: RL: There is no need to set these to null, as they're just
2674
// variables whose contents and references will automatically
2675
// be cleaned up when they're no longer referenced.
2676
// Also, setting these to null creates problems for the attribute
2677
// schema-related methods, which need these to work.
2678
/*
2679
schemaTrees = null;
2680
envprops = null;
2681
*/
2682
}
2683
2684
@SuppressWarnings("unchecked") // clone()
2685
public void reconnect(Control[] connCtls) throws NamingException {
2686
// Update environment
2687
envprops = (envprops == null
2688
? new Hashtable<String, Object>(5, 0.75f)
2689
: (Hashtable<String, Object>)envprops.clone());
2690
2691
if (connCtls == null) {
2692
envprops.remove(BIND_CONTROLS);
2693
bindCtls = null;
2694
} else {
2695
envprops.put(BIND_CONTROLS, bindCtls = cloneControls(connCtls));
2696
}
2697
2698
sharable = false; // can't share with existing contexts
2699
ensureOpen(); // open or reauthenticated
2700
}
2701
2702
// Load 'mechsAllowedToSendCredentials' system property value
2703
@SuppressWarnings("removal")
2704
private static String getMechsAllowedToSendCredentials() {
2705
PrivilegedAction<String> pa = () -> System.getProperty(ALLOWED_MECHS_SP);
2706
return System.getSecurityManager() == null ? pa.run() : AccessController.doPrivileged(pa);
2707
}
2708
2709
// Get set of allowed authentication mechanism names from the property value
2710
private static Set<String> getMechsFromPropertyValue(String propValue) {
2711
if (propValue == null || propValue.isBlank()) {
2712
return Collections.emptySet();
2713
}
2714
return Arrays.stream(propValue.split(","))
2715
.map(String::trim)
2716
.filter(Predicate.not(String::isBlank))
2717
.collect(Collectors.toUnmodifiableSet());
2718
}
2719
2720
// Returns true if TLS connection opened using "ldaps" scheme, or using "ldap" and then upgraded with
2721
// startTLS extended operation, and startTLS is still active.
2722
private boolean isConnectionEncrypted() {
2723
return hasLdapsScheme || clnt.isUpgradedToStartTls();
2724
}
2725
2726
// Ensure connection and context are in a safe state to transmit credentials
2727
private void ensureCanTransmitCredentials(String authMechanism) throws NamingException {
2728
2729
// "none" and "anonumous" authentication mechanisms are allowed unconditionally
2730
if ("none".equalsIgnoreCase(authMechanism) || "anonymous".equalsIgnoreCase(authMechanism)) {
2731
return;
2732
}
2733
2734
// Check environment first
2735
String allowedMechanismsOrTrue = (String) envprops.get(ALLOWED_MECHS_SP);
2736
boolean useSpMechsCache = false;
2737
boolean anyPropertyIsSet = ALLOWED_MECHS_SP_VALUE != null || allowedMechanismsOrTrue != null;
2738
2739
// If current connection is not encrypted, and context seen to be secured with STARTTLS
2740
// or 'mechsAllowedToSendCredentials' is set to any value via system/context environment properties
2741
if (!isConnectionEncrypted() && (contextSeenStartTlsEnabled || anyPropertyIsSet)) {
2742
// First, check if security principal is provided in context environment for "simple"
2743
// authentication mechanism. There is no check for other SASL mechanisms since the credentials
2744
// can be specified via other properties
2745
if ("simple".equalsIgnoreCase(authMechanism) && !envprops.containsKey(SECURITY_PRINCIPAL)) {
2746
return;
2747
}
2748
2749
// If null - will use mechanism name cached from system property
2750
if (allowedMechanismsOrTrue == null) {
2751
useSpMechsCache = true;
2752
allowedMechanismsOrTrue = ALLOWED_MECHS_SP_VALUE;
2753
}
2754
2755
// If the property value (system or environment) is 'all':
2756
// any kind of authentication is allowed unconditionally - no check is needed
2757
if ("all".equalsIgnoreCase(allowedMechanismsOrTrue)) {
2758
return;
2759
}
2760
2761
// Get the set with allowed authentication mechanisms and check current mechanism
2762
Set<String> allowedAuthMechs = useSpMechsCache ?
2763
MECHS_ALLOWED_BY_SP : getMechsFromPropertyValue(allowedMechanismsOrTrue);
2764
if (!allowedAuthMechs.contains(authMechanism)) {
2765
throw new NamingException(UNSECURED_CRED_TRANSMIT_MSG);
2766
}
2767
}
2768
}
2769
2770
private void ensureOpen() throws NamingException {
2771
ensureOpen(false);
2772
}
2773
2774
private void ensureOpen(boolean startTLS) throws NamingException {
2775
2776
try {
2777
if (clnt == null) {
2778
if (debug) {
2779
System.err.println("LdapCtx: Reconnecting " + this);
2780
}
2781
2782
// reset the cache before a new connection is established
2783
schemaTrees = new Hashtable<>(11, 0.75f);
2784
connect(startTLS);
2785
2786
} else if (!sharable || startTLS) {
2787
2788
synchronized (clnt) {
2789
if (!clnt.isLdapv3
2790
|| clnt.referenceCount > 1
2791
|| clnt.usingSaslStreams()
2792
|| !clnt.conn.useable) {
2793
closeConnection(SOFT_CLOSE);
2794
}
2795
}
2796
// reset the cache before a new connection is established
2797
schemaTrees = new Hashtable<>(11, 0.75f);
2798
connect(startTLS);
2799
}
2800
2801
} finally {
2802
sharable = true; // connection is now either new or single-use
2803
// OK for others to start sharing again
2804
}
2805
}
2806
2807
private void connect(boolean startTLS) throws NamingException {
2808
if (debug) { System.err.println("LdapCtx: Connecting " + this); }
2809
2810
String user = null; // authenticating user
2811
Object passwd = null; // password for authenticating user
2812
String secProtocol = null; // security protocol (e.g. "ssl")
2813
String socketFactory = null; // socket factory
2814
String authMechanism = null; // authentication mechanism
2815
String ver = null;
2816
int ldapVersion; // LDAP protocol version
2817
boolean usePool = false; // enable connection pooling
2818
2819
if (envprops != null) {
2820
user = (String)envprops.get(Context.SECURITY_PRINCIPAL);
2821
passwd = envprops.get(Context.SECURITY_CREDENTIALS);
2822
ver = (String)envprops.get(VERSION);
2823
secProtocol =
2824
useSsl ? "ssl" : (String)envprops.get(Context.SECURITY_PROTOCOL);
2825
socketFactory = (String)envprops.get(SOCKET_FACTORY);
2826
authMechanism =
2827
(String)envprops.get(Context.SECURITY_AUTHENTICATION);
2828
2829
usePool = "true".equalsIgnoreCase((String)envprops.get(ENABLE_POOL));
2830
}
2831
2832
if (socketFactory == null) {
2833
socketFactory =
2834
"ssl".equals(secProtocol) ? DEFAULT_SSL_FACTORY : null;
2835
}
2836
2837
if (authMechanism == null) {
2838
authMechanism = (user == null) ? "none" : "simple";
2839
}
2840
2841
try {
2842
boolean initial = (clnt == null);
2843
2844
if (initial) {
2845
ldapVersion = (ver != null) ? Integer.parseInt(ver) :
2846
DEFAULT_LDAP_VERSION;
2847
2848
clnt = LdapClient.getInstance(
2849
usePool, // Whether to use connection pooling
2850
2851
// Required for LdapClient constructor
2852
hostname,
2853
port_number,
2854
socketFactory,
2855
connectTimeout,
2856
readTimeout,
2857
trace,
2858
2859
// Required for basic client identity
2860
ldapVersion,
2861
authMechanism,
2862
bindCtls,
2863
secProtocol,
2864
2865
// Required for simple client identity
2866
user,
2867
passwd,
2868
2869
// Required for SASL client identity
2870
envprops);
2871
2872
// Mark current context as secure if the connection is acquired
2873
// from the pool and it is secure.
2874
contextSeenStartTlsEnabled |= clnt.isUpgradedToStartTls();
2875
2876
/**
2877
* Pooled connections are preauthenticated;
2878
* newly created ones are not.
2879
*/
2880
if (clnt.authenticateCalled()) {
2881
return;
2882
}
2883
2884
} else if (sharable && startTLS) {
2885
return; // no authentication required
2886
2887
} else {
2888
// reauthenticating over existing connection;
2889
// only v3 supports this
2890
ldapVersion = LdapClient.LDAP_VERSION3;
2891
}
2892
2893
LdapResult answer;
2894
synchronized (clnt.conn.startTlsLock) {
2895
ensureCanTransmitCredentials(authMechanism);
2896
answer = clnt.authenticate(initial, user, passwd, ldapVersion,
2897
authMechanism, bindCtls, envprops);
2898
}
2899
2900
respCtls = answer.resControls; // retrieve (bind) response controls
2901
2902
if (answer.status != LdapClient.LDAP_SUCCESS) {
2903
if (initial) {
2904
closeConnection(HARD_CLOSE); // hard close
2905
}
2906
processReturnCode(answer);
2907
}
2908
2909
} catch (LdapReferralException e) {
2910
if (handleReferrals == LdapClient.LDAP_REF_THROW)
2911
throw e;
2912
2913
String referral;
2914
LdapURL url;
2915
NamingException saved_ex = null;
2916
2917
// Process the referrals sequentially (top level) and
2918
// recursively (per referral)
2919
while (true) {
2920
2921
if ((referral = e.getNextReferral()) == null) {
2922
// No more referrals to follow
2923
2924
if (saved_ex != null) {
2925
throw (NamingException)(saved_ex.fillInStackTrace());
2926
} else {
2927
// No saved exception, something must have gone wrong
2928
throw new NamingException(
2929
"Internal error processing referral during connection");
2930
}
2931
}
2932
2933
// Use host/port number from referral
2934
url = new LdapURL(referral);
2935
hostname = url.getHost();
2936
if ((hostname != null) && (hostname.charAt(0) == '[')) {
2937
hostname = hostname.substring(1, hostname.length() - 1);
2938
}
2939
port_number = url.getPort();
2940
2941
// Try to connect again using new host/port number
2942
try {
2943
connect(startTLS);
2944
break;
2945
2946
} catch (NamingException ne) {
2947
saved_ex = ne;
2948
continue; // follow another referral
2949
}
2950
}
2951
}
2952
}
2953
2954
private void closeConnection(boolean hardclose) {
2955
removeUnsolicited(); // idempotent
2956
2957
if (clnt != null) {
2958
if (debug) {
2959
System.err.println("LdapCtx: calling clnt.close() " + this);
2960
}
2961
clnt.close(reqCtls, hardclose);
2962
clnt = null;
2963
}
2964
}
2965
2966
// Used by Enum classes to track whether it still needs context
2967
private int enumCount = 0;
2968
private boolean closeRequested = false;
2969
2970
synchronized void incEnumCount() {
2971
++enumCount;
2972
if (debug) System.err.println("LdapCtx: " + this + " enum inc: " + enumCount);
2973
}
2974
2975
synchronized void decEnumCount() {
2976
--enumCount;
2977
if (debug) System.err.println("LdapCtx: " + this + " enum dec: " + enumCount);
2978
2979
if (enumCount == 0 && closeRequested) {
2980
try {
2981
close();
2982
} catch (NamingException e) {
2983
// ignore failures
2984
}
2985
}
2986
}
2987
2988
2989
// ------------ Return code and Error messages -----------------------
2990
2991
protected void processReturnCode(LdapResult answer) throws NamingException {
2992
processReturnCode(answer, null, this, null, envprops, null);
2993
}
2994
2995
void processReturnCode(LdapResult answer, Name remainName)
2996
throws NamingException {
2997
processReturnCode(answer,
2998
(new CompositeName()).add(currentDN),
2999
this,
3000
remainName,
3001
envprops,
3002
fullyQualifiedName(remainName));
3003
}
3004
3005
protected void processReturnCode(LdapResult res, Name resolvedName,
3006
Object resolvedObj, Name remainName, Hashtable<?,?> envprops, String fullDN)
3007
throws NamingException {
3008
3009
String msg = LdapClient.getErrorMessage(res.status, res.errorMessage);
3010
NamingException e;
3011
LdapReferralException r = null;
3012
3013
switch (res.status) {
3014
3015
case LdapClient.LDAP_SUCCESS:
3016
3017
// handle Search continuation references
3018
if (res.referrals != null) {
3019
3020
msg = "Unprocessed Continuation Reference(s)";
3021
3022
if (handleReferrals == LdapClient.LDAP_REF_IGNORE) {
3023
e = new PartialResultException(msg);
3024
break;
3025
}
3026
3027
// handle multiple sets of URLs
3028
int contRefCount = res.referrals.size();
3029
LdapReferralException head = null;
3030
LdapReferralException ptr = null;
3031
3032
msg = "Continuation Reference";
3033
3034
// make a chain of LdapReferralExceptions
3035
for (int i = 0; i < contRefCount; i++) {
3036
3037
r = new LdapReferralException(resolvedName, resolvedObj,
3038
remainName, msg, envprops, fullDN, handleReferrals,
3039
reqCtls);
3040
r.setReferralInfo(res.referrals.elementAt(i), true);
3041
3042
if (hopCount > 1) {
3043
r.setHopCount(hopCount);
3044
}
3045
3046
if (head == null) {
3047
head = ptr = r;
3048
} else {
3049
ptr.nextReferralEx = r; // append ex. to end of chain
3050
ptr = r;
3051
}
3052
}
3053
res.referrals = null; // reset
3054
3055
if (res.refEx == null) {
3056
res.refEx = head;
3057
3058
} else {
3059
ptr = res.refEx;
3060
3061
while (ptr.nextReferralEx != null) {
3062
ptr = ptr.nextReferralEx;
3063
}
3064
ptr.nextReferralEx = head;
3065
}
3066
3067
// check the hop limit
3068
if (hopCount > referralHopLimit) {
3069
NamingException lee =
3070
new LimitExceededException("Referral limit exceeded");
3071
lee.setRootCause(r);
3072
throw lee;
3073
}
3074
}
3075
return;
3076
3077
case LdapClient.LDAP_REFERRAL:
3078
3079
if (handleReferrals == LdapClient.LDAP_REF_IGNORE) {
3080
e = new PartialResultException(msg);
3081
break;
3082
}
3083
3084
r = new LdapReferralException(resolvedName, resolvedObj, remainName,
3085
msg, envprops, fullDN, handleReferrals, reqCtls);
3086
// only one set of URLs is present
3087
Vector<String> refs;
3088
if (res.referrals == null) {
3089
refs = null;
3090
} else if (handleReferrals == LdapClient.LDAP_REF_FOLLOW_SCHEME) {
3091
refs = new Vector<>();
3092
for (String s : res.referrals.elementAt(0)) {
3093
if (s.startsWith("ldap:")) {
3094
refs.add(s);
3095
}
3096
}
3097
if (refs.isEmpty()) {
3098
refs = null;
3099
}
3100
} else {
3101
refs = res.referrals.elementAt(0);
3102
}
3103
r.setReferralInfo(refs, false);
3104
3105
if (hopCount > 1) {
3106
r.setHopCount(hopCount);
3107
}
3108
3109
// check the hop limit
3110
if (hopCount > referralHopLimit) {
3111
NamingException lee =
3112
new LimitExceededException("Referral limit exceeded");
3113
lee.setRootCause(r);
3114
e = lee;
3115
3116
} else {
3117
e = r;
3118
}
3119
break;
3120
3121
/*
3122
* Handle SLAPD-style referrals.
3123
*
3124
* Referrals received during name resolution should be followed
3125
* until one succeeds - the target entry is located. An exception
3126
* is thrown now to handle these.
3127
*
3128
* Referrals received during a search operation point to unexplored
3129
* parts of the directory and each should be followed. An exception
3130
* is thrown later (during results enumeration) to handle these.
3131
*/
3132
3133
case LdapClient.LDAP_PARTIAL_RESULTS:
3134
3135
if (handleReferrals == LdapClient.LDAP_REF_IGNORE) {
3136
e = new PartialResultException(msg);
3137
break;
3138
}
3139
3140
// extract SLAPD-style referrals from errorMessage
3141
if ((res.errorMessage != null) && (!res.errorMessage.isEmpty())) {
3142
res.referrals = extractURLs(res.errorMessage);
3143
} else {
3144
e = new PartialResultException(msg);
3145
break;
3146
}
3147
3148
// build exception
3149
r = new LdapReferralException(resolvedName,
3150
resolvedObj,
3151
remainName,
3152
msg,
3153
envprops,
3154
fullDN,
3155
handleReferrals,
3156
reqCtls);
3157
3158
if (hopCount > 1) {
3159
r.setHopCount(hopCount);
3160
}
3161
/*
3162
* %%%
3163
* SLAPD-style referrals received during name resolution
3164
* cannot be distinguished from those received during a
3165
* search operation. Since both must be handled differently
3166
* the following rule is applied:
3167
*
3168
* If 1 referral and 0 entries is received then
3169
* assume name resolution has not yet completed.
3170
*/
3171
if (((res.entries == null) || (res.entries.isEmpty())) &&
3172
((res.referrals != null) && (res.referrals.size() == 1))) {
3173
3174
r.setReferralInfo(res.referrals, false);
3175
3176
// check the hop limit
3177
if (hopCount > referralHopLimit) {
3178
NamingException lee =
3179
new LimitExceededException("Referral limit exceeded");
3180
lee.setRootCause(r);
3181
e = lee;
3182
3183
} else {
3184
e = r;
3185
}
3186
3187
} else {
3188
r.setReferralInfo(res.referrals, true);
3189
res.refEx = r;
3190
return;
3191
}
3192
break;
3193
3194
case LdapClient.LDAP_INVALID_DN_SYNTAX:
3195
case LdapClient.LDAP_NAMING_VIOLATION:
3196
3197
if (remainName != null) {
3198
e = new
3199
InvalidNameException(remainName.toString() + ": " + msg);
3200
} else {
3201
e = new InvalidNameException(msg);
3202
}
3203
break;
3204
3205
default:
3206
e = mapErrorCode(res.status, res.errorMessage);
3207
break;
3208
}
3209
e.setResolvedName(resolvedName);
3210
e.setResolvedObj(resolvedObj);
3211
e.setRemainingName(remainName);
3212
throw e;
3213
}
3214
3215
/**
3216
* Maps an LDAP error code to an appropriate NamingException.
3217
* %%% public; used by controls
3218
*
3219
* @param errorCode numeric LDAP error code
3220
* @param errorMessage textual description of the LDAP error. May be null.
3221
*
3222
* @return A NamingException or null if the error code indicates success.
3223
*/
3224
public static NamingException mapErrorCode(int errorCode,
3225
String errorMessage) {
3226
3227
if (errorCode == LdapClient.LDAP_SUCCESS)
3228
return null;
3229
3230
NamingException e = null;
3231
String message = LdapClient.getErrorMessage(errorCode, errorMessage);
3232
3233
switch (errorCode) {
3234
3235
case LdapClient.LDAP_ALIAS_DEREFERENCING_PROBLEM:
3236
e = new NamingException(message);
3237
break;
3238
3239
case LdapClient.LDAP_ALIAS_PROBLEM:
3240
e = new NamingException(message);
3241
break;
3242
3243
case LdapClient.LDAP_ATTRIBUTE_OR_VALUE_EXISTS:
3244
e = new AttributeInUseException(message);
3245
break;
3246
3247
case LdapClient.LDAP_AUTH_METHOD_NOT_SUPPORTED:
3248
case LdapClient.LDAP_CONFIDENTIALITY_REQUIRED:
3249
case LdapClient.LDAP_STRONG_AUTH_REQUIRED:
3250
case LdapClient.LDAP_INAPPROPRIATE_AUTHENTICATION:
3251
e = new AuthenticationNotSupportedException(message);
3252
break;
3253
3254
case LdapClient.LDAP_ENTRY_ALREADY_EXISTS:
3255
e = new NameAlreadyBoundException(message);
3256
break;
3257
3258
case LdapClient.LDAP_INVALID_CREDENTIALS:
3259
case LdapClient.LDAP_SASL_BIND_IN_PROGRESS:
3260
e = new AuthenticationException(message);
3261
break;
3262
3263
case LdapClient.LDAP_INAPPROPRIATE_MATCHING:
3264
e = new InvalidSearchFilterException(message);
3265
break;
3266
3267
case LdapClient.LDAP_INSUFFICIENT_ACCESS_RIGHTS:
3268
e = new NoPermissionException(message);
3269
break;
3270
3271
case LdapClient.LDAP_INVALID_ATTRIBUTE_SYNTAX:
3272
case LdapClient.LDAP_CONSTRAINT_VIOLATION:
3273
e = new InvalidAttributeValueException(message);
3274
break;
3275
3276
case LdapClient.LDAP_LOOP_DETECT:
3277
e = new NamingException(message);
3278
break;
3279
3280
case LdapClient.LDAP_NO_SUCH_ATTRIBUTE:
3281
e = new NoSuchAttributeException(message);
3282
break;
3283
3284
case LdapClient.LDAP_NO_SUCH_OBJECT:
3285
e = new NameNotFoundException(message);
3286
break;
3287
3288
case LdapClient.LDAP_OBJECT_CLASS_MODS_PROHIBITED:
3289
case LdapClient.LDAP_OBJECT_CLASS_VIOLATION:
3290
case LdapClient.LDAP_NOT_ALLOWED_ON_RDN:
3291
e = new SchemaViolationException(message);
3292
break;
3293
3294
case LdapClient.LDAP_NOT_ALLOWED_ON_NON_LEAF:
3295
e = new ContextNotEmptyException(message);
3296
break;
3297
3298
case LdapClient.LDAP_OPERATIONS_ERROR:
3299
// %%% need new exception ?
3300
e = new NamingException(message);
3301
break;
3302
3303
case LdapClient.LDAP_OTHER:
3304
e = new NamingException(message);
3305
break;
3306
3307
case LdapClient.LDAP_PROTOCOL_ERROR:
3308
e = new CommunicationException(message);
3309
break;
3310
3311
case LdapClient.LDAP_SIZE_LIMIT_EXCEEDED:
3312
e = new SizeLimitExceededException(message);
3313
break;
3314
3315
case LdapClient.LDAP_TIME_LIMIT_EXCEEDED:
3316
e = new TimeLimitExceededException(message);
3317
break;
3318
3319
case LdapClient.LDAP_UNAVAILABLE_CRITICAL_EXTENSION:
3320
e = new OperationNotSupportedException(message);
3321
break;
3322
3323
case LdapClient.LDAP_UNAVAILABLE:
3324
case LdapClient.LDAP_BUSY:
3325
e = new ServiceUnavailableException(message);
3326
break;
3327
3328
case LdapClient.LDAP_UNDEFINED_ATTRIBUTE_TYPE:
3329
e = new InvalidAttributeIdentifierException(message);
3330
break;
3331
3332
case LdapClient.LDAP_UNWILLING_TO_PERFORM:
3333
e = new OperationNotSupportedException(message);
3334
break;
3335
3336
case LdapClient.LDAP_COMPARE_FALSE:
3337
case LdapClient.LDAP_COMPARE_TRUE:
3338
case LdapClient.LDAP_IS_LEAF:
3339
// these are really not exceptions and this code probably
3340
// never gets executed
3341
e = new NamingException(message);
3342
break;
3343
3344
case LdapClient.LDAP_ADMIN_LIMIT_EXCEEDED:
3345
e = new LimitExceededException(message);
3346
break;
3347
3348
case LdapClient.LDAP_REFERRAL:
3349
e = new NamingException(message);
3350
break;
3351
3352
case LdapClient.LDAP_PARTIAL_RESULTS:
3353
e = new NamingException(message);
3354
break;
3355
3356
case LdapClient.LDAP_INVALID_DN_SYNTAX:
3357
case LdapClient.LDAP_NAMING_VIOLATION:
3358
e = new InvalidNameException(message);
3359
break;
3360
3361
default:
3362
e = new NamingException(message);
3363
break;
3364
}
3365
3366
return e;
3367
}
3368
3369
// ----------------- Extensions and Controls -------------------
3370
3371
public ExtendedResponse extendedOperation(ExtendedRequest request)
3372
throws NamingException {
3373
3374
boolean startTLS = (request.getID().equals(STARTTLS_REQ_OID));
3375
ensureOpen(startTLS);
3376
3377
try {
3378
3379
LdapResult answer =
3380
clnt.extendedOp(request.getID(), request.getEncodedValue(),
3381
reqCtls, startTLS);
3382
respCtls = answer.resControls; // retrieve response controls
3383
3384
if (answer.status != LdapClient.LDAP_SUCCESS) {
3385
processReturnCode(answer, new CompositeName());
3386
}
3387
// %%% verify request.getID() == answer.extensionId
3388
3389
int len = (answer.extensionValue == null) ?
3390
0 :
3391
answer.extensionValue.length;
3392
3393
ExtendedResponse er =
3394
request.createExtendedResponse(answer.extensionId,
3395
answer.extensionValue, 0, len);
3396
3397
if (er instanceof StartTlsResponseImpl) {
3398
// Pass the connection handle to StartTlsResponseImpl
3399
String domainName = (String)
3400
(envprops != null ? envprops.get(DOMAIN_NAME) : null);
3401
((StartTlsResponseImpl)er).setConnection(clnt.conn, domainName);
3402
contextSeenStartTlsEnabled |= startTLS;
3403
}
3404
return er;
3405
3406
} catch (LdapReferralException e) {
3407
3408
if (handleReferrals == LdapClient.LDAP_REF_THROW)
3409
throw e;
3410
3411
// process the referrals sequentially
3412
while (true) {
3413
3414
LdapReferralContext refCtx =
3415
(LdapReferralContext)e.getReferralContext(envprops, bindCtls);
3416
3417
// repeat the original operation at the new context
3418
try {
3419
3420
return refCtx.extendedOperation(request);
3421
3422
} catch (LdapReferralException re) {
3423
e = re;
3424
continue;
3425
3426
} finally {
3427
// Make sure we close referral context
3428
refCtx.close();
3429
}
3430
}
3431
3432
} catch (IOException e) {
3433
NamingException e2 = new CommunicationException(e.getMessage());
3434
e2.setRootCause(e);
3435
throw e2;
3436
}
3437
}
3438
3439
public void setRequestControls(Control[] reqCtls) throws NamingException {
3440
if (handleReferrals == LdapClient.LDAP_REF_IGNORE) {
3441
this.reqCtls = addControl(reqCtls, manageReferralControl);
3442
} else {
3443
this.reqCtls = cloneControls(reqCtls);
3444
}
3445
}
3446
3447
public Control[] getRequestControls() throws NamingException {
3448
return cloneControls(reqCtls);
3449
}
3450
3451
public Control[] getConnectControls() throws NamingException {
3452
return cloneControls(bindCtls);
3453
}
3454
3455
public Control[] getResponseControls() throws NamingException {
3456
return (respCtls != null)? convertControls(respCtls) : null;
3457
}
3458
3459
/**
3460
* Narrow controls using own default factory and ControlFactory.
3461
* @param ctls A non-null Vector<Control>
3462
*/
3463
Control[] convertControls(Vector<Control> ctls) throws NamingException {
3464
int count = ctls.size();
3465
3466
if (count == 0) {
3467
return null;
3468
}
3469
3470
Control[] controls = new Control[count];
3471
3472
for (int i = 0; i < count; i++) {
3473
// Try own factory first
3474
controls[i] = myResponseControlFactory.getControlInstance(
3475
ctls.elementAt(i));
3476
3477
// Try assigned factories if own produced null
3478
if (controls[i] == null) {
3479
controls[i] = ControlFactory.getControlInstance(
3480
ctls.elementAt(i), this, envprops);
3481
}
3482
}
3483
return controls;
3484
}
3485
3486
private static Control[] addControl(Control[] prevCtls, Control addition) {
3487
if (prevCtls == null) {
3488
return new Control[]{addition};
3489
}
3490
3491
// Find it
3492
int found = findControl(prevCtls, addition);
3493
if (found != -1) {
3494
return prevCtls; // no need to do it again
3495
}
3496
3497
Control[] newCtls = new Control[prevCtls.length+1];
3498
System.arraycopy(prevCtls, 0, newCtls, 0, prevCtls.length);
3499
newCtls[prevCtls.length] = addition;
3500
return newCtls;
3501
}
3502
3503
private static int findControl(Control[] ctls, Control target) {
3504
for (int i = 0; i < ctls.length; i++) {
3505
if (ctls[i] == target) {
3506
return i;
3507
}
3508
}
3509
return -1;
3510
}
3511
3512
private static Control[] removeControl(Control[] prevCtls, Control target) {
3513
if (prevCtls == null) {
3514
return null;
3515
}
3516
3517
// Find it
3518
int found = findControl(prevCtls, target);
3519
if (found == -1) {
3520
return prevCtls; // not there
3521
}
3522
3523
// Remove it
3524
Control[] newCtls = new Control[prevCtls.length-1];
3525
System.arraycopy(prevCtls, 0, newCtls, 0, found);
3526
System.arraycopy(prevCtls, found+1, newCtls, found,
3527
prevCtls.length-found-1);
3528
return newCtls;
3529
}
3530
3531
private static Control[] cloneControls(Control[] ctls) {
3532
if (ctls == null) {
3533
return null;
3534
}
3535
Control[] copiedCtls = new Control[ctls.length];
3536
System.arraycopy(ctls, 0, copiedCtls, 0, ctls.length);
3537
return copiedCtls;
3538
}
3539
3540
// -------------------- Events ------------------------
3541
/*
3542
* Access to eventSupport need not be synchronized even though the
3543
* Connection thread can access it asynchronously. It is
3544
* impossible for a race condition to occur because
3545
* eventSupport.addNamingListener() must have been called before
3546
* the Connection thread can call back to this ctx.
3547
*/
3548
public void addNamingListener(Name nm, int scope, NamingListener l)
3549
throws NamingException {
3550
addNamingListener(getTargetName(nm), scope, l);
3551
}
3552
3553
public void addNamingListener(String nm, int scope, NamingListener l)
3554
throws NamingException {
3555
if (eventSupport == null)
3556
eventSupport = new EventSupport(this);
3557
eventSupport.addNamingListener(getTargetName(new CompositeName(nm)),
3558
scope, l);
3559
3560
// If first time asking for unsol
3561
if (l instanceof UnsolicitedNotificationListener && !unsolicited) {
3562
addUnsolicited();
3563
}
3564
}
3565
3566
public void removeNamingListener(NamingListener l) throws NamingException {
3567
if (eventSupport == null)
3568
return; // no activity before, so just return
3569
3570
eventSupport.removeNamingListener(l);
3571
3572
// If removing an Unsol listener and it is the last one, let clnt know
3573
if (l instanceof UnsolicitedNotificationListener &&
3574
!eventSupport.hasUnsolicited()) {
3575
removeUnsolicited();
3576
}
3577
}
3578
3579
public void addNamingListener(String nm, String filter, SearchControls ctls,
3580
NamingListener l) throws NamingException {
3581
if (eventSupport == null)
3582
eventSupport = new EventSupport(this);
3583
eventSupport.addNamingListener(getTargetName(new CompositeName(nm)),
3584
filter, cloneSearchControls(ctls), l);
3585
3586
// If first time asking for unsol
3587
if (l instanceof UnsolicitedNotificationListener && !unsolicited) {
3588
addUnsolicited();
3589
}
3590
}
3591
3592
public void addNamingListener(Name nm, String filter, SearchControls ctls,
3593
NamingListener l) throws NamingException {
3594
addNamingListener(getTargetName(nm), filter, ctls, l);
3595
}
3596
3597
public void addNamingListener(Name nm, String filter, Object[] filterArgs,
3598
SearchControls ctls, NamingListener l) throws NamingException {
3599
addNamingListener(getTargetName(nm), filter, filterArgs, ctls, l);
3600
}
3601
3602
public void addNamingListener(String nm, String filterExpr, Object[] filterArgs,
3603
SearchControls ctls, NamingListener l) throws NamingException {
3604
String strfilter = SearchFilter.format(filterExpr, filterArgs);
3605
addNamingListener(getTargetName(new CompositeName(nm)), strfilter, ctls, l);
3606
}
3607
3608
public boolean targetMustExist() {
3609
return true;
3610
}
3611
3612
/**
3613
* Retrieves the target name for which the listener is registering.
3614
* If nm is a CompositeName, use its first and only component. It
3615
* cannot have more than one components because a target be outside of
3616
* this namespace. If nm is not a CompositeName, then treat it as a
3617
* compound name.
3618
* @param nm The non-null target name.
3619
*/
3620
private static String getTargetName(Name nm) throws NamingException {
3621
if (nm instanceof CompositeName) {
3622
if (nm.size() > 1) {
3623
throw new InvalidNameException(
3624
"Target cannot span multiple namespaces: " + nm);
3625
} else if (nm.isEmpty()) {
3626
return "";
3627
} else {
3628
return nm.get(0);
3629
}
3630
} else {
3631
// treat as compound name
3632
return nm.toString();
3633
}
3634
}
3635
3636
// ------------------ Unsolicited Notification ---------------
3637
// package private methods for handling unsolicited notification
3638
3639
/**
3640
* Registers this context with the underlying LdapClient.
3641
* When the underlying LdapClient receives an unsolicited notification,
3642
* it will invoke LdapCtx.fireUnsolicited() so that this context
3643
* can (using EventSupport) notified any registered listeners.
3644
* This method is called by EventSupport when an unsolicited listener
3645
* first registers with this context (should be called just once).
3646
* @see #removeUnsolicited
3647
* @see #fireUnsolicited
3648
*/
3649
private void addUnsolicited() throws NamingException {
3650
if (debug) {
3651
System.out.println("LdapCtx.addUnsolicited: " + this);
3652
}
3653
3654
// addNamingListener must have created EventSupport already
3655
ensureOpen();
3656
synchronized (eventSupport) {
3657
clnt.addUnsolicited(this);
3658
unsolicited = true;
3659
}
3660
}
3661
3662
/**
3663
* Removes this context from registering interest in unsolicited
3664
* notifications from the underlying LdapClient. This method is called
3665
* under any one of the following conditions:
3666
* <ul>
3667
* <li>All unsolicited listeners have been removed. (see removingNamingListener)
3668
* <li>This context is closed.
3669
* <li>This context's underlying LdapClient changes.
3670
*</ul>
3671
* After this method has been called, this context will not pass
3672
* on any events related to unsolicited notifications to EventSupport and
3673
* and its listeners.
3674
*/
3675
3676
private void removeUnsolicited() {
3677
if (debug) {
3678
System.out.println("LdapCtx.removeUnsolicited: " + unsolicited);
3679
}
3680
if (eventSupport == null) {
3681
return;
3682
}
3683
3684
// addNamingListener must have created EventSupport already
3685
synchronized(eventSupport) {
3686
if (unsolicited && clnt != null) {
3687
clnt.removeUnsolicited(this);
3688
}
3689
unsolicited = false;
3690
}
3691
}
3692
3693
/**
3694
* Uses EventSupport to fire an event related to an unsolicited notification.
3695
* Called by LdapClient when LdapClient receives an unsolicited notification.
3696
*/
3697
void fireUnsolicited(Object obj) {
3698
if (debug) {
3699
System.out.println("LdapCtx.fireUnsolicited: " + obj);
3700
}
3701
// addNamingListener must have created EventSupport already
3702
synchronized(eventSupport) {
3703
if (unsolicited) {
3704
eventSupport.fireUnsolicited(obj);
3705
3706
if (obj instanceof NamingException) {
3707
unsolicited = false;
3708
// No need to notify clnt because clnt is the
3709
// only one that can fire a NamingException to
3710
// unsol listeners and it will handle its own cleanup
3711
}
3712
}
3713
}
3714
}
3715
}
3716
3717