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