From cfb091ced5278520f07f8664ec7bef93705e3b1c Mon Sep 17 00:00:00 2001 From: "miodrag%netscape.com" Date: Sat, 5 Jun 1999 01:05:59 +0000 Subject: [PATCH] JNDI LDAP Service Provider git-svn-id: svn://10.0.0.236/trunk@33884 18797224-902f-48f8-a5cc-f745e15eee43 --- mozilla/directory/java-sdk/ldapsp.mk | 118 ++++ mozilla/directory/java-sdk/ldapsp/Readme.html | 541 ++++++++++++++++ .../netscape/jndi/ldap/AttributesImpl.java | 312 +++++++++ .../com/netscape/jndi/ldap/BindingEnum.java | 95 +++ .../com/netscape/jndi/ldap/ContextEnv.java | 535 ++++++++++++++++ .../com/netscape/jndi/ldap/EventService.java | 391 ++++++++++++ .../jndi/ldap/LdapContextFactory.java | 47 ++ .../netscape/jndi/ldap/LdapContextImpl.java | 604 ++++++++++++++++++ .../netscape/jndi/ldap/LdapNameParser.java | 164 +++++ .../jndi/ldap/LdapReferralException.java | 80 +++ .../com/netscape/jndi/ldap/LdapService.java | 515 +++++++++++++++ .../netscape/jndi/ldap/NameClassPairEnum.java | 95 +++ .../com/netscape/jndi/ldap/ObjectMapper.java | 473 ++++++++++++++ .../com/netscape/jndi/ldap/ProviderUtils.java | 279 ++++++++ .../netscape/jndi/ldap/SearchResultEnum.java | 122 ++++ .../jndi/ldap/SearchResultWithControls.java | 63 ++ .../com/netscape/jndi/ldap/common/Debug.java | 63 ++ .../jndi/ldap/common/DirContextAdapter.java | 251 ++++++++ .../jndi/ldap/common/ExceptionMapper.java | 220 +++++++ .../jndi/ldap/common/LdapContextAdapter.java | 86 +++ .../jndi/ldap/common/ShareableEnv.java | 404 ++++++++++++ .../ldap/controls/LdapEntryChangeControl.java | 172 +++++ .../controls/LdapPasswordExpiredControl.java | 56 ++ .../controls/LdapPasswordExpiringControl.java | 59 ++ .../controls/LdapPersistSearchControl.java | 194 ++++++ .../ldap/controls/LdapProxiedAuthControl.java | 89 +++ .../jndi/ldap/controls/LdapSortControl.java | 257 ++++++++ .../jndi/ldap/controls/LdapSortKey.java | 124 ++++ .../controls/LdapSortResponseControl.java | 223 +++++++ .../jndi/ldap/controls/LdapStringControl.java | 1 + .../ldap/controls/LdapVirtualListControl.java | 147 +++++ .../LdapVirtualListResponseControl.java | 90 +++ .../ldap/controls/NetscapeControlFactory.java | 142 ++++ .../jndi/ldap/schema/SchemaAttribute.java | 209 ++++++ .../ldap/schema/SchemaAttributeContainer.java | 97 +++ .../jndi/ldap/schema/SchemaDirContext.java | 160 +++++ .../jndi/ldap/schema/SchemaElement.java | 213 ++++++ .../ldap/schema/SchemaElementBindingEnum.java | 73 +++ .../ldap/schema/SchemaElementContainer.java | 235 +++++++ .../ldap/schema/SchemaElementNameEnum.java | 60 ++ .../jndi/ldap/schema/SchemaManager.java | 256 ++++++++ .../jndi/ldap/schema/SchemaMatchingRule.java | 208 ++++++ .../schema/SchemaMatchingRuleContainer.java | 97 +++ .../jndi/ldap/schema/SchemaNameParser.java | 53 ++ .../jndi/ldap/schema/SchemaObjectClass.java | 214 +++++++ .../schema/SchemaObjectClassContainer.java | 97 +++ .../netscape/jndi/ldap/schema/SchemaRoot.java | 360 +++++++++++ .../java-sdk/ldapsp/jndi-object-schema.conf | 47 ++ .../directory/java-sdk/ldapsp/lib/jndi.jar | Bin 0 -> 96941 bytes mozilla/directory/java-sdk/ldapsp/manifest.mf | 104 +++ 50 files changed, 9495 insertions(+) create mode 100644 mozilla/directory/java-sdk/ldapsp.mk create mode 100644 mozilla/directory/java-sdk/ldapsp/Readme.html create mode 100644 mozilla/directory/java-sdk/ldapsp/com/netscape/jndi/ldap/AttributesImpl.java create mode 100644 mozilla/directory/java-sdk/ldapsp/com/netscape/jndi/ldap/BindingEnum.java create mode 100644 mozilla/directory/java-sdk/ldapsp/com/netscape/jndi/ldap/ContextEnv.java create mode 100644 mozilla/directory/java-sdk/ldapsp/com/netscape/jndi/ldap/EventService.java create mode 100644 mozilla/directory/java-sdk/ldapsp/com/netscape/jndi/ldap/LdapContextFactory.java create mode 100644 mozilla/directory/java-sdk/ldapsp/com/netscape/jndi/ldap/LdapContextImpl.java create mode 100644 mozilla/directory/java-sdk/ldapsp/com/netscape/jndi/ldap/LdapNameParser.java create mode 100644 mozilla/directory/java-sdk/ldapsp/com/netscape/jndi/ldap/LdapReferralException.java create mode 100644 mozilla/directory/java-sdk/ldapsp/com/netscape/jndi/ldap/LdapService.java create mode 100644 mozilla/directory/java-sdk/ldapsp/com/netscape/jndi/ldap/NameClassPairEnum.java create mode 100644 mozilla/directory/java-sdk/ldapsp/com/netscape/jndi/ldap/ObjectMapper.java create mode 100644 mozilla/directory/java-sdk/ldapsp/com/netscape/jndi/ldap/ProviderUtils.java create mode 100644 mozilla/directory/java-sdk/ldapsp/com/netscape/jndi/ldap/SearchResultEnum.java create mode 100644 mozilla/directory/java-sdk/ldapsp/com/netscape/jndi/ldap/SearchResultWithControls.java create mode 100644 mozilla/directory/java-sdk/ldapsp/com/netscape/jndi/ldap/common/Debug.java create mode 100644 mozilla/directory/java-sdk/ldapsp/com/netscape/jndi/ldap/common/DirContextAdapter.java create mode 100644 mozilla/directory/java-sdk/ldapsp/com/netscape/jndi/ldap/common/ExceptionMapper.java create mode 100644 mozilla/directory/java-sdk/ldapsp/com/netscape/jndi/ldap/common/LdapContextAdapter.java create mode 100644 mozilla/directory/java-sdk/ldapsp/com/netscape/jndi/ldap/common/ShareableEnv.java create mode 100644 mozilla/directory/java-sdk/ldapsp/com/netscape/jndi/ldap/controls/LdapEntryChangeControl.java create mode 100644 mozilla/directory/java-sdk/ldapsp/com/netscape/jndi/ldap/controls/LdapPasswordExpiredControl.java create mode 100644 mozilla/directory/java-sdk/ldapsp/com/netscape/jndi/ldap/controls/LdapPasswordExpiringControl.java create mode 100644 mozilla/directory/java-sdk/ldapsp/com/netscape/jndi/ldap/controls/LdapPersistSearchControl.java create mode 100644 mozilla/directory/java-sdk/ldapsp/com/netscape/jndi/ldap/controls/LdapProxiedAuthControl.java create mode 100644 mozilla/directory/java-sdk/ldapsp/com/netscape/jndi/ldap/controls/LdapSortControl.java create mode 100644 mozilla/directory/java-sdk/ldapsp/com/netscape/jndi/ldap/controls/LdapSortKey.java create mode 100644 mozilla/directory/java-sdk/ldapsp/com/netscape/jndi/ldap/controls/LdapSortResponseControl.java create mode 100644 mozilla/directory/java-sdk/ldapsp/com/netscape/jndi/ldap/controls/LdapStringControl.java create mode 100644 mozilla/directory/java-sdk/ldapsp/com/netscape/jndi/ldap/controls/LdapVirtualListControl.java create mode 100644 mozilla/directory/java-sdk/ldapsp/com/netscape/jndi/ldap/controls/LdapVirtualListResponseControl.java create mode 100644 mozilla/directory/java-sdk/ldapsp/com/netscape/jndi/ldap/controls/NetscapeControlFactory.java create mode 100644 mozilla/directory/java-sdk/ldapsp/com/netscape/jndi/ldap/schema/SchemaAttribute.java create mode 100644 mozilla/directory/java-sdk/ldapsp/com/netscape/jndi/ldap/schema/SchemaAttributeContainer.java create mode 100644 mozilla/directory/java-sdk/ldapsp/com/netscape/jndi/ldap/schema/SchemaDirContext.java create mode 100644 mozilla/directory/java-sdk/ldapsp/com/netscape/jndi/ldap/schema/SchemaElement.java create mode 100644 mozilla/directory/java-sdk/ldapsp/com/netscape/jndi/ldap/schema/SchemaElementBindingEnum.java create mode 100644 mozilla/directory/java-sdk/ldapsp/com/netscape/jndi/ldap/schema/SchemaElementContainer.java create mode 100644 mozilla/directory/java-sdk/ldapsp/com/netscape/jndi/ldap/schema/SchemaElementNameEnum.java create mode 100644 mozilla/directory/java-sdk/ldapsp/com/netscape/jndi/ldap/schema/SchemaManager.java create mode 100644 mozilla/directory/java-sdk/ldapsp/com/netscape/jndi/ldap/schema/SchemaMatchingRule.java create mode 100644 mozilla/directory/java-sdk/ldapsp/com/netscape/jndi/ldap/schema/SchemaMatchingRuleContainer.java create mode 100644 mozilla/directory/java-sdk/ldapsp/com/netscape/jndi/ldap/schema/SchemaNameParser.java create mode 100644 mozilla/directory/java-sdk/ldapsp/com/netscape/jndi/ldap/schema/SchemaObjectClass.java create mode 100644 mozilla/directory/java-sdk/ldapsp/com/netscape/jndi/ldap/schema/SchemaObjectClassContainer.java create mode 100644 mozilla/directory/java-sdk/ldapsp/com/netscape/jndi/ldap/schema/SchemaRoot.java create mode 100644 mozilla/directory/java-sdk/ldapsp/jndi-object-schema.conf create mode 100644 mozilla/directory/java-sdk/ldapsp/lib/jndi.jar create mode 100644 mozilla/directory/java-sdk/ldapsp/manifest.mf diff --git a/mozilla/directory/java-sdk/ldapsp.mk b/mozilla/directory/java-sdk/ldapsp.mk new file mode 100644 index 00000000000..fbd03c0dbad --- /dev/null +++ b/mozilla/directory/java-sdk/ldapsp.mk @@ -0,0 +1,118 @@ +# -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- +# +# The contents of this file are subject to the Netscape Public License +# Version 1.0 (the "NPL"); you may not use this file except in +# compliance with the NPL. You may obtain a copy of the NPL at +# http://www.mozilla.org/NPL/ +# +# Software distributed under the NPL is distributed on an "AS IS" basis, +# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL +# for the specific language governing rights and limitations under the +# NPL. +# +# The Initial Developer of this code under the NPL is Netscape +# Communications Corporation. Portions created by Netscape are +# Copyright (C) 1998 Netscape Communications Corporation. All Rights +# Reserved. +# +# Makefile for the LDAP classes +# +# An optimized compile is done by default. You can specify "DEBUG=1" on +# the make line to generate debug symbols in the bytecode. +# +ARCH := $(shell uname -s) + +MCOM_ROOT=. +ifeq ($(ARCH), WINNT) + MOZ_DIR:=$(subst \,/,$(MOZ_SRC)) + BASEDIR:=$(MOZ_DIR)/mozilla/directory/java-sdk +else + ifeq ($(ARCH), WIN95) + MOZ_DIR:=$(subst \,/,$(MOZ_SRC)) + BASEDIR:=$(MOZ_DIR)/mozilla/directory/java-sdk + else + BASEDIR := $(shell cd $(MCOM_ROOT); pwd) + endif +endif +# Destination for class files and packages +CLASS_DEST=$(BASEDIR)/dist/classes + +# Set up the CLASSPATH automatically, +ifeq ($(ARCH), WINNT) + JDK := $(subst \,/,$(JAVA_HOME)) + JAR:=$(JDK)/bin/jar + SEP=; +else + ifeq ($(ARCH), WIN95) + JDK := $(subst \,/,$(JAVA_HOME)) + JAR:=$(JDK)/bin/jar + SEP=; + else + JDK := $(JAVA_HOME) + JAR:=$(JAVA_HOME)/bin/jar + SEP=: + endif +endif +JNDILIB:=$(BASEDIR)/ldapsp/lib/jndi.jar +JAVACLASSPATH:=$(CLASS_DEST)$(SEP)$(BASEDIR)/ldapsp$(SEP)$(JDKLIB)$(SEP)$(JNDILIB)$(SEP)$(CLASSPATH) + +SRCDIR=com/netscape/jndi/ldap +DISTDIR=$(MCOM_ROOT)/dist +CLASSDIR=$(MCOM_ROOT)/dist/classes +CLASSPACKAGEDIR=$(DISTDIR)/packages +#PACKAGENAME=jndi.zip +ifeq ($(DEBUG), full) +BASEPACKAGENAME=ldapsp_debug.jar +else +BASEPACKAGENAME=ldapsp.jar +endif +CLASSPACKAGE=$(CLASSPACKAGEDIR)/$(PACKAGENAME) +MANIFEST=$(BASEDIR)/ldapsp/manifest.mf + +ifndef JAVAC + ifdef JAVA_HOME + JDKBIN=$(JDK)/bin/ + endif + ifeq ($(DEBUG), 1) + JAVAC=$(JDKBIN)javac -g -classpath "$(JAVACLASSPATH)" + else + JAVAC=$(JDKBIN)javac -O -classpath "$(JAVACLASSPATH)" + endif +endif + +all: classes + +basics: $(DISTDIR) $(CLASSDIR) + +classes: JNDICLASSES + +basepackage: $(CLASSPACKAGEDIR) + cd $(DISTDIR)/classes; rm -f ../packages/$(BASEPACKAGENAME); $(JAR) cvfm ../packages/$(BASEPACKAGENAME) $(MANIFEST) com/netscape/jndi/ldap/*.class com/netscape/jndi/ldap/common/*.class com/netscape/jndi/ldap/schema/*.class com/netscape/jndi/ldap/controls/*.class + +MAIN: basics + cd ldapsp/$(SRCDIR); $(JAVAC) -d $(CLASS_DEST) *.java + +SCHEMA: basics + cd ldapsp/$(SRCDIR)/schema; $(JAVAC) -d $(CLASS_DEST) *.java + +COMMON: basics + cd ldapsp/$(SRCDIR)/common; $(JAVAC) -d $(CLASS_DEST) *.java + +CONTROLS: basics + cd ldapsp/$(SRCDIR)/controls; $(JAVAC) -d $(CLASS_DEST) *.java + + +JNDICLASSES: COMMON CONTROLS SCHEMA MAIN + +clean: + rm -rf $(DISTDIR)/classes/com/netscape/jndi/ldap + +$(CLASSPACKAGEDIR): + mkdir -p $@ + +$(DISTDIR): + mkdir -p $@ + +$(CLASSDIR): + mkdir -p $@ + diff --git a/mozilla/directory/java-sdk/ldapsp/Readme.html b/mozilla/directory/java-sdk/ldapsp/Readme.html new file mode 100644 index 00000000000..e4bdcc6842c --- /dev/null +++ b/mozilla/directory/java-sdk/ldapsp/Readme.html @@ -0,0 +1,541 @@ + + + + + + + Netscape LDAP Service Provider - Readme + + + +

+                +Netscape LDAP Service Provider for JNDI (06-04-99)

+The Netscape LDAP service provider for JNDI implements the JNDI DirContext +interface. It is implemented as a layer on top of the Netscape Directory +SDK for Java (ldapjdk.jar). While the ldapjdk uses the LDAP connection +as the primary abstraction enabling the access to the directory services, +the JNDI provider uses the concept of  a  Directory Context (the +DirContext interface) to achieve the same functionality. A DirContext as +an equivalent of a directory entry in the ldapjdk. +

The following sections are available in this document: +

Using Netscape Ldap Service Provider +
Environment Properties +
Working With Controls +
What's Not Implemented +
  +

+Using Netscape LDAP Service Provider

+The current implementation  is based on the JNDI 1.2 beta1. In addition +to the DirContext interface implementation, the Netscape LDAP provider +implements the new JNDI event service (javax.naming.event package) +and support for controls (javax.naming.ldap package) which were +introduced with the JNDI 1.2. +

To use the service provider, you'll need to: +

(1) Add the provider and the jars it depends on in the classpath. For +example, on Windows NT the classpath should be set as follows: +

set classpath=%classpath%;ldapsp.jar;ldapjdk.jar;jndi.jar; +

Assuming that all the jars are available in the current directory. The +listed jar files are: +
  + + + + + + + + + + + + + + + + + + +
ldapsp.jar Netscape LDAP Service Provider for JNDI
ldapjdk.jarNetscape Directory SDK for Java 3.1
jndi.jarJNDI 1.2 beta1
+ +

(2) Specify the Netscape LDAP provider as the provider in the context +environment created for the initial context; +
       Hashtable env = new Hashtable(); +
   env.put(Context.INITIAL_CONTEXT_FACTORY, +
          "com.netscape.jndi.ldap.LdapContextFactory"); +
    env.put(... +
    ... +
    DirContext ctx = new InitialDirContext(env); +

(3) For storing of  Java objects in a LDAP Directory, the JNDI +java object schema must be added to the directory. To enable the JNDI schema +copy the file jndi-object-schema.conf  to your <server-root>/slapd-<id>/config +directory, and include the file into your  <server-root>/slapd-<id>/config/ns-schema.conf +schema configuration file. +

For examples of using JNDI please go to the official JNDI site. +

+Environment Properties

+The environment properties can be passed directly to the initial context +as a hash table, or specified as system properties. For compatibility reasons, +for those environment properties that are relevant to LDAP protocol but +are not defined in the JNDI, the Netscape LDAP provider is using the same +property names as the SUN LDAP service provided, if a property with the +same semantics is defined by the SUN provider. +

Note: If a new property is added to the context environment, or an existing +property is changed after the initial context is created, the change will +be immediately visible unless the changed property pertains to the connection. +For changes related to connection, in order to take effect you'll need +to invoke LdapContext.reconnect(). +

The following table contains JNDI environment properties are relevant +for the Netscape LDAP service provider. Properties not found in this table +are silently ignored. +
  + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Environment PropertyDescription
java.naming.factory.initial +
This environment property is used to select the LDAP provider. To select +the Netscape LDAP provider "com.netscape.jndi.ldap.LdapContextFactory" +should be specified. +

 env.put(Context.INITIAL_CONTEXT_FACTORY, "com.netscape.jndi.ldap.LdapContextFactory");

java.naming.provider.url +
Specifies LDAP server information. For example: +

env.put(Context.PROVIDER_URL, "ldap://dilly.mcom.com:389"); +

If it has not been set then the provider will attempt to access an LDAP +server at port 389 on the local host.

java.naming.ldap.version +
Specifies the protocol version for the provider. Two values are +
possible: +
    +
  • +2 - selects LDAP Version 2 (LDAPv2)
  • + +
  • +3 - selects LDAP Version 3 (LDAPv3)
  • +
+ For example,  env.put("java.naming.ldap.version", "3"); +

If this environment property has not been set then the provider will +
attempt to use LDAPv3.

java.naming.security.authentication +
Specifies the authentication mechanism for the provider to use. +
The following values are permitted for this property: +
    +
  • +   none     - use no authentication +(anonymous)
  • + +
  • +   simple   - use weak authentication (clear +text password)
  • +
+If this environment property has not been set but the     +java.naming.security.principal environment property has been +
set then the provider will use 'simple'. If neither have been set then +the provider will use anonymous bind.
java.naming.security.principal +
Specifies the DN of the principal to be authenticated. For example: +

env.put(Context.SECURITY_PRINCIPAL, "cn=Directory manager"); +

If this environment property has not been set then the provider +
will use anonymous bind.

java.naming.security.credentials +
Specifies the password of the principal to be authenticated. For example: +

env.put(Context.SECURITY_CREDENTIALS, "secret");

java.naming.security.protocol +
 Specifies the security protocol for the provider to use. One +possible value is defined:  ssl - use Secure Socket Layer +

 env.put(Context.SECURITY_PROTOCOL, "ssl"); +

When this environment property has been set and the +
 java.naming.ldap.factory.socket property has not been +set, then +
the ldapjdk default socket factory  netscape.net.SSLSocket +is used. This class is provided with Netscape Communicator 4.05 and higher. +If java.naming.ldap.factory.socket property has been set, the +
socket factory specified therein is used.

java.naming.ldap.factory.socketSpecifies the class name of a socket factory. This environment +
property is used to override the default socket factory. For example: +

env.put("java.naming.ldap.factory.socket", "crysec.SSL.SSLSocket"); +

If  the security protocol environment property has been set but +this property has not been set, then this property's value is set to netscape.net.SSLSocket. +See ldapjdk documentation for more information for connecting over SSL. +
 

java.naming.ldap.ssl.ciphersSpecifies the suite of ciphers used for SSL connections made through +sockets created by the factory specified with java.naming.ldap.factory.socket. +The value of this property is of type java.lang.Object. For example: +

try { +
    Class c = Class.forName("crysec.SSL.SSLParams"); +
    java.lang.reflect.Method m =  +
         +getMethod("getCipherSuite",new Class[0]); +
    Object cipherSuite = m.invoke(null,null); +
    if (cipherSuite != null) { +
       env.put("java.naming.ldap.ssl.ciphers", +cipherSuite); +
    } +
} +
catch (Exception e) {} +
 

java.naming.batchsizeSpecifies that search results are to be returned in batches. A setting +of zero indicates that the provider should block until all results have +been received. If this environment property has not been set then search  +results are returned in batches of one.
java.naming.ldap.maxresults +
The default  maximum number of search results to be returned for +a search request. 0 means no limit. If not specified, the ldapjdk default +is 1000. This value can be overridden per request with the parameter SearchConstraints +in the DirContex.search() method.
java.naming.ldap.timelimitThe default maximum number of milliseconds to wait for a search operation +to complete. If 0, which is the ldapjdk default, there is no maximum time +limit for a search operation. This value can be overridden per request +with the parameter SearchConstraints in the DirContex.search() +method.
java.naming.referral +
 Specifies how referrals shall be handled by the provider. Three  +possible values are defined: +
    +
  • + follow - automatically follow any referrals
  • + +
  • throw  - throw a ReferralException for each referral
  • + +
  • ignore - ignore referrals if they appear in results and treat +them like ordinary attributes if they appear in entries.
  • +
+If this environment property has not been set then the LDAP provider by +default follows referrals.
java.naming.ldap.referral.limit +
Specifies the maximum number of referrals to follow in a chain of +
referrals. A setting of zero indicates that there is no limit. The +default limit is 10.
java.naming.ldap.deleteRDN Specifies whether the old RDN is removed during rename(). +
 If the value is "true", the old RDN is removed; otherwise, +
 the RDN is not removed.  The default value is true.
java.naming.ldap.derefAliases +
 Specifies how aliases are dereferenced during search operations. +
 The possible values are: +
    +
  • +always        always dereference +aliases
  • + +
  • +never         never dereference +aliases
  • + +
  • +finding       dereference aliases +only during name resolution
  • + +
  • +searching   dereference aliases only after name resolution
  • +
+ NOTE: Netscape LDAP Server 3.x and 4.x do not support aliases
java.naming.ldap.typesOnly +
 Specifies whether only attribute types are to be returned during +
 searches and getAttributes(). Its possible values are "true" +or "false". The default is false.
java.naming.ldap.conntrol.connectAn array of Controls to be set on the LDAPConnection before +a connection attempt is made to the server. 
java.naming.ldap.attributes.binarySpecifies attributes that have binary syntax. It extends the provider's +list of known binary attributes. Its value is a space separated list of +attribute names. +

env.put("java.naming.ldap.attributes.binary", "mpegVideo"); +

In contrast to ldapjdk, JNDI does not provide for reading of attribute +values as either Strings or byte arrays. All attributes are returned as +Strings unless recognized as having binary syntax. The values of attributes +that have binary syntax are returned as byte arrays instead of Strings.  +

If this environment property has not been set then, by default, only +the following attributes are considered to have binary syntax: +

    +
  • +attribute names containing ';binary'
  • + +
  • +photo                      +(0.9.2342.19200300.100.1.7)
  • + +
  • +personalSignature  (0.9.2342.19200300.100.1.53)
  • + +
  • +audio                      +(0.9.2342.19200300.100.1.55)
  • + +
  • +jpegPhoto               +(0.9.2342.19200300.100.1.60)
  • + +
  • +javaSerializedData   (1.3.6.1.4.1.42.2.27.4.1.7)
  • + +
  • +thumbnailPhoto         (1.3.6.1.4.1.1466.101.120.35)
  • + +
  • +thumbnailLogo          (1.3.6.1.4.1.1466.101.120.36)
  • + +
  • +userPassword           +(2.5.4.35)
  • + +
  • +userCertificate          (2.5.4.36)
  • + +
  • +cACertificate            +(2.5.4.37)
  • + +
  • +authorityRevocationList    (2.5.4.38)
  • + +
  • +certificateRevocationList  (2.5.4.39)
  • + +
  • +crossCertificatePair          +(2.5.4.40)
  • + +
  • +x500UniqueIdentifier        (2.5.4.45)
  • +
+
java.naming.ldap.ref.separatorSpecifies the character to use when encoding a RefAddr object in +
the javaReferenceAddress attribute. This environment property should +be used to avoid a conflict in  the case where the default separator +character appears in the components of a RefAddr object. +

 If unspecified, the default separator is the hash character '#'.

+ +

+Working with Controls

+JNDI 1.2 adds support for Controls which are fully implemented with the +Netscape LDAP provider. However, JNDI 1.2 does not define interfaces for +any of the standard controls, like for example the sort control. Instead, +the task of defining particular controls and their interfaces is left to +service providers. Therefore, if using controls, you will also need to +import the  com.netscape.jndi.ldap.controls package in your +souce in addition to the JNDI packages. +

Being implemented on the top of ldapjdk, the Netscape LDAP provider +simply exposes all of the ldapjdk controls as JNDI controls. Thus, the +control APIs are exactly the same as in ldapjdk. The only difference is +that for the LDAP provider controls class names start with  "Ldap" +while in ldapjdk the class names start with "LDAP". For instance, the ldapjdk +control LDAPSortControl is LdapSortControl in the Netscape LDAP provider. +

Here is an example of how to use the LdapSortControl. Notice that you'll +need to obtain a LdapContext object as an initial context, because controls +are not part of the directory context (DirContext). That means that instead +of calling getInitialDirContext() you 'll need to call getInitialLdapContext(). +

import java.util.Hashtable; +
import javax.naming.*; +
import javax.naming.directory.*; +
import javax.naming.ldap.*; +
import com.netscape.jndi.ldap.controls.*; +

public class SortReverseOrder { +

public static void main(String[] args) { +

    Hashtable env = new Hashtable(5, 0.75f); +
    /* +
     * Specify the initial context implementation +to use. +
     */ +
    env.put(Context.INITIAL_CONTEXT_FACTORY, +
        "com.netscape.jndi.ldap.LdapContextFactory"); +

    /* Specify host and port to use for directory +service */ +
    //env.put(Context.PROVIDER_URL, "ldap://localhost:389"); +

    LdapContext ctx = null; +
    try { +
        /* get a handle to an +Initial DirContext */ +
        ctx = new InitialLdapContext(env, +null); +

        /* specify search constraints +to search subtree */ +
        SearchControls cons += new SearchControls(); +
        cons.setSearchScope(SearchControls.SUBTREE_SCOPE); +
        cons.setReturningAttributes(new +String[] { "sn" }); +

        // specify sort control +
        ctx.setRequestControls( +
           +new +Control[] {new LdapSortControl( +
               + +new LdapSortKey[]{ +
                  + +new LdapSortKey("sn", true,null)},Control.CRITICAL)}); +

        /* search for all entries +with surname of Jensen */ +
        NamingEnumeration results +
            += ctx.search("o=mcom.com", "(objectclass=person)", cons); +

        /* for each entry print +out name + all attrs and values */ +
        while (results != null +&& results.hasMore()) { +
            +SearchResult si = (SearchResult)results.next(); +

            +Attributes attrs = si.getAttributes(); +
            +/* print each attribute */ +
            +for (NamingEnumeration ae = attrs.getAll(); ae.hasMoreElements();) { +
                +Attribute attr = (Attribute)ae.next(); +
                +String attrId = attr.getID(); +

                    +/* print each value */ +
                +for (NamingEnumeration vals = attr.getAll(); vals.hasMore(); +
                    +System.out.println(attrId + ": " + vals.next())); +
                +} +
            +System.out.println(); +
        } +
    } +
    catch (NamingException e) { +
        System.err.println("Search +example failed."); +
        e.printStackTrace(); +
    } +
    finally { +
       // cleanup +
       if (ctx != null) { +
           try +{ ctx.close(); } catch (Exception e) {} +
       } +
    } +
} +
} +

For full documenation on available controls and their interfaces, please +check the ldapjdk documentation. +

+What's Not Implemented

+Currently, the following JNDI features are not implemented by the Netscape +JNDI provider: + + + + diff --git a/mozilla/directory/java-sdk/ldapsp/com/netscape/jndi/ldap/AttributesImpl.java b/mozilla/directory/java-sdk/ldapsp/com/netscape/jndi/ldap/AttributesImpl.java new file mode 100644 index 00000000000..bf5cddbed7b --- /dev/null +++ b/mozilla/directory/java-sdk/ldapsp/com/netscape/jndi/ldap/AttributesImpl.java @@ -0,0 +1,312 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * + * The contents of this file are subject to the Netscape Public License + * Version 1.0 (the "NPL"); you may not use this file except in + * compliance with the NPL. You may obtain a copy of the NPL at + * http://www.mozilla.org/NPL/ + * + * Software distributed under the NPL is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL + * for the specific language governing rights and limitations under the + * NPL. + * + * The Initial Developer of this code under the NPL is Netscape + * Communications Corporation. Portions created by Netscape are + * Copyright (C) 1999 Netscape Communications Corporation. All Rights + * Reserved. + */ +package com.netscape.jndi.ldap; + +import javax.naming.*; +import javax.naming.directory.*; + +import netscape.ldap.*; + +import java.util.*; + +/** + * Wrapper for LDAPAttributes which implements JNDI Attribute interface + */ +class AttributesImpl implements Attributes { + + // TODO Create JndiAttribute class so that getAttributeDefinition and + // getAttributeSyntaxDefinition can be implemented + + LDAPAttributeSet m_attrSet; + + /** + * A list of predefined binary attribute name + */ + static String[] m_binaryAttrs = { + "photo", "userpassword", "jpegphoto", "audio", "thumbnailphoto", "thumbnaillogo", + "usercertificate", "cacertificate", "certificaterevocationlist", + "authorityrevocationlist", "crosscertificatepair", "personalsignature", + "x500uniqueidentifier", "javaserializeddata"}; + + /** + * A list of user defined binary attributes specified with the environment + * property java.naming.ldap.attributes.binary + */ + static String[] m_userBinaryAttrs = null; + + public AttributesImpl(LDAPAttributeSet attrSet, String[] userBinaryAttrs) { + m_attrSet = attrSet; + m_userBinaryAttrs = userBinaryAttrs; + } + + public Object clone() { + return new AttributesImpl((LDAPAttributeSet)m_attrSet.clone(), m_userBinaryAttrs); + } + + public Attribute get(String attrID) { + LDAPAttribute attr = m_attrSet.getAttribute(attrID); + return (attr == null) ? null : ldapAttrToJndiAttr(attr); + } + + public NamingEnumeration getAll() { + return new AttributeEnum(m_attrSet.getAttributes()); + } + + public NamingEnumeration getIDs() { + return new AttributeIDEnum(m_attrSet.getAttributes()); + } + + public boolean isCaseIgnored() { + return false; + } + + public Attribute put(String attrID, Object val) { + LDAPAttribute attr = m_attrSet.getAttribute(attrID); + if (val == null) { // no Value + m_attrSet.add(new LDAPAttribute(attrID)); + } + else if (val instanceof byte[]) { + m_attrSet.add(new LDAPAttribute(attrID, (byte[])val)); + } + else { + m_attrSet.add(new LDAPAttribute(attrID, val.toString())); + } + return (attr == null) ? null : ldapAttrToJndiAttr(attr); + } + + public Attribute put(Attribute jndiAttr) { + try { + LDAPAttribute oldAttr = m_attrSet.getAttribute(jndiAttr.getID()); + m_attrSet.add(jndiAttrToLdapAttr(jndiAttr)); + return (oldAttr == null) ? null : ldapAttrToJndiAttr(oldAttr); + } + catch (NamingException e) { + System.err.println( "Error in AttributesImpl.put(): " + e.toString() ); + e.printStackTrace(System.err); + } + return null; + } + + public Attribute remove(String attrID) { + Attribute attr = get(attrID); + m_attrSet.remove(attrID); + return attr; + } + + public int size() { + return m_attrSet.size(); + } + + + /** + * Check if an attribute is a binary one + */ + static boolean isBinaryAttribute(String attrID) { + + // attr name contains ";binary" + if (attrID.indexOf(";binary") >=0) { + return true; + } + + attrID = attrID.toLowerCase(); + + // check the predefined binary attr table + for (int i=0; i < m_binaryAttrs.length; i++) { + if (m_binaryAttrs[i].equals(attrID)) { + return true; + } + } + + // check user specified binary attrs with + for (int i=0; m_userBinaryAttrs != null && i < m_userBinaryAttrs.length; i++) { + if (m_userBinaryAttrs[i].equals(attrID)) { + return true; + } + } + + return false; + } + + /** + * Convert a JNDI Attributes object into a LDAPAttributeSet + */ + static LDAPAttributeSet jndiAttrsToLdapAttrSet(Attributes jndiAttrs) throws NamingException{ + LDAPAttributeSet attrs = new LDAPAttributeSet(); + for (Enumeration enum = jndiAttrs.getAll(); enum.hasMoreElements();) { + attrs.add(jndiAttrToLdapAttr((Attribute) enum.nextElement())); + } + return attrs; + } + + /** + * Convert a JNDI Attribute to a LDAPAttribute + */ + static LDAPAttribute jndiAttrToLdapAttr(Attribute jndiAttr) throws NamingException{ + LDAPAttribute attr = new LDAPAttribute(jndiAttr.getID()); + + for (NamingEnumeration vals = jndiAttr.getAll(); vals.hasMoreElements();) { + Object val = vals.nextElement(); + if (val == null) { + ; // no value + } + else if (val instanceof byte[]) { + attr.addValue((byte[])val); + } + else { + attr.addValue(val.toString()); + } + } + return attr; + } + + /** + * Convert a LDAPAttribute to a JNDI Attribute + */ + static Attribute ldapAttrToJndiAttr(LDAPAttribute attr) { + BasicAttribute jndiAttr = new BasicAttribute(attr.getName()); + Enumeration enumVals = null; + if (isBinaryAttribute(attr.getName())) { + enumVals = attr.getByteValues(); + } + else { + enumVals = attr.getStringValues(); + } + if (enumVals != null) { + while ( enumVals.hasMoreElements() ) { + jndiAttr.add(enumVals.nextElement()); + } + } + return jndiAttr; + } + + /** + * Convert and array of JNDI ModificationItem to a LDAPModificationSet + */ + static LDAPModificationSet jndiModsToLdapModSet(ModificationItem[] jndiMods) throws NamingException{ + LDAPModificationSet mods = new LDAPModificationSet(); + for (int i=0; i < jndiMods.length; i++) { + int modop = jndiMods[i].getModificationOp(); + LDAPAttribute attr = jndiAttrToLdapAttr(jndiMods[i].getAttribute()); + if (modop == DirContext.ADD_ATTRIBUTE) { + mods.add(LDAPModification.ADD, attr); + } + else if (modop == DirContext.REPLACE_ATTRIBUTE) { + mods.add(LDAPModification.REPLACE, attr); + } + else if (modop == DirContext.REMOVE_ATTRIBUTE) { + mods.add(LDAPModification.DELETE, attr); + } + else { + // Should never come here. ModificationItem can not + // be constructed with a wrong value + } + } + return mods; + } + + /** + * Create a LDAPModificationSet from a JNDI mod operation and JNDI Attributes + */ + static LDAPModificationSet jndiAttrsToLdapModSet(int modop, Attributes jndiAttrs) throws NamingException{ + LDAPModificationSet mods = new LDAPModificationSet(); + for (NamingEnumeration attrEnum = jndiAttrs.getAll(); attrEnum.hasMore();) { + LDAPAttribute attr = jndiAttrToLdapAttr((Attribute)attrEnum.next()); + if (modop == DirContext.ADD_ATTRIBUTE) { + mods.add(LDAPModification.ADD, attr); + } + else if (modop == DirContext.REPLACE_ATTRIBUTE) { + mods.add(LDAPModification.REPLACE, attr); + } + else if (modop == DirContext.REMOVE_ATTRIBUTE) { + mods.add(LDAPModification.DELETE, attr); + } + else { + throw new IllegalArgumentException("Illegal Attribute Modification Operation"); + } + } + return mods; + } +} + +/** + * Wrapper for enumeration of LDAPAttrubute-s. Convert each LDAPAttribute + * into a JNDI Attribute accessed through the NamingEnumeration + */ +class AttributeEnum implements NamingEnumeration { + + Enumeration _attrEnum; + + public AttributeEnum(Enumeration attrEnum) { + _attrEnum = attrEnum; + } + + public Object next() throws NamingException{ + LDAPAttribute attr = (LDAPAttribute) _attrEnum.nextElement(); + return AttributesImpl.ldapAttrToJndiAttr(attr); + } + + public Object nextElement() { + LDAPAttribute attr = (LDAPAttribute) _attrEnum.nextElement(); + return AttributesImpl.ldapAttrToJndiAttr(attr); + } + + public boolean hasMore() throws NamingException{ + return _attrEnum.hasMoreElements(); + } + + public boolean hasMoreElements() { + return _attrEnum.hasMoreElements(); + } + + public void close() { + } +} + +/** + * Wrapper for enumeration of LDAPAttrubute-s. Return the name for each + * LDAPAttribute accessed through the NamingEnumeration + */ +class AttributeIDEnum implements NamingEnumeration { + + Enumeration _attrEnum; + + public AttributeIDEnum(Enumeration attrEnum) { + _attrEnum = attrEnum; + } + + public Object next() throws NamingException{ + LDAPAttribute attr = (LDAPAttribute) _attrEnum.nextElement(); + return attr.getName(); + } + + public Object nextElement() { + LDAPAttribute attr = (LDAPAttribute) _attrEnum.nextElement(); + return attr.getName(); + } + + public boolean hasMore() throws NamingException{ + return _attrEnum.hasMoreElements(); + } + + public boolean hasMoreElements() { + return _attrEnum.hasMoreElements(); + } + + public void close() { + } +} diff --git a/mozilla/directory/java-sdk/ldapsp/com/netscape/jndi/ldap/BindingEnum.java b/mozilla/directory/java-sdk/ldapsp/com/netscape/jndi/ldap/BindingEnum.java new file mode 100644 index 00000000000..47d82efbf6e --- /dev/null +++ b/mozilla/directory/java-sdk/ldapsp/com/netscape/jndi/ldap/BindingEnum.java @@ -0,0 +1,95 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * + * The contents of this file are subject to the Netscape Public License + * Version 1.0 (the "NPL"); you may not use this file except in + * compliance with the NPL. You may obtain a copy of the NPL at + * http://www.mozilla.org/NPL/ + * + * Software distributed under the NPL is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL + * for the specific language governing rights and limitations under the + * NPL. + * + * The Initial Developer of this code under the NPL is Netscape + * Communications Corporation. Portions created by Netscape are + * Copyright (C) 1999 Netscape Communications Corporation. All Rights + * Reserved. + */ +package com.netscape.jndi.ldap; + +import javax.naming.*; +import javax.naming.directory.*; +import javax.naming.ldap.*; +import com.netscape.jndi.ldap.common.ExceptionMapper; + +import netscape.ldap.*; + +import java.util.*; + +/** + * Wrapper for the LDAPSearchResult. Convert each LDAPEntry entry into + * a JNDI Binding. Bindings are accessed through the NamingEnumeration + * interface + */ +class BindingEnum implements NamingEnumeration { + + LDAPSearchResults m_res; + LdapContextImpl m_ctx; + Name m_ctxName; + + public BindingEnum(LDAPSearchResults res, LdapContextImpl ctx) throws NamingException { + m_res = res; + m_ctx = ctx; + try { + m_ctxName = LdapNameParser.getParser().parse(m_ctx.m_ctxDN); + } + catch ( NamingException e ) { + throw ExceptionMapper.getNamingException(e); + } + } + + public Object next() throws NamingException{ + try { + LDAPEntry entry = m_res.next(); + String name = LdapNameParser.getRelativeName(m_ctxName, entry.getDN()); + Object obj = ObjectMapper.entryToObject(entry, m_ctx); + String className = obj.getClass().getName(); + return new Binding(name, className, obj, /*isRelative=*/true); + + } + catch (LDAPReferralException e) { + throw new LdapReferralException(m_ctx, e); + } + catch ( LDAPException e ) { + throw ExceptionMapper.getNamingException(e); + } + } + + public Object nextElement() { + try { + return next(); + } + catch ( Exception e ) { + System.err.println( "Error in BindingEnum.nextElement(): " + e.toString() ); + e.printStackTrace(System.err); + return null; + } + } + + public boolean hasMore() throws NamingException{ + return m_res.hasMoreElements(); + } + + public boolean hasMoreElements() { + return m_res.hasMoreElements(); + } + + public void close() throws NamingException{ + try { + m_ctx.m_ldapSvc.getConnection().abandon(m_res); + } + catch (LDAPException e) { + throw ExceptionMapper.getNamingException(e); + } + } +} \ No newline at end of file diff --git a/mozilla/directory/java-sdk/ldapsp/com/netscape/jndi/ldap/ContextEnv.java b/mozilla/directory/java-sdk/ldapsp/com/netscape/jndi/ldap/ContextEnv.java new file mode 100644 index 00000000000..cb455ea524a --- /dev/null +++ b/mozilla/directory/java-sdk/ldapsp/com/netscape/jndi/ldap/ContextEnv.java @@ -0,0 +1,535 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * + * The contents of this file are subject to the Netscape Public License + * Version 1.0 (the "NPL"); you may not use this file except in + * compliance with the NPL. You may obtain a copy of the NPL at + * http://www.mozilla.org/NPL/ + * + * Software distributed under the NPL is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL + * for the specific language governing rights and limitations under the + * NPL. + * + * The Initial Developer of this code under the NPL is Netscape + * Communications Corporation. Portions created by Netscape are + * Copyright (C) 1999 Netscape Communications Corporation. All Rights + * Reserved. + */ +package com.netscape.jndi.ldap; + +import javax.naming.*; +import javax.naming.directory.*; +import javax.naming.ldap.*; + +import netscape.ldap.*; +import netscape.ldap.controls.*; + +import com.netscape.jndi.ldap.common.*; + +import java.util.*; + +/** + * Context Environment + */ +class ContextEnv extends ShareableEnv { + + public static final String DEFAULT_HOST = "localhost"; + public static final int DEFAULT_PORT = LDAPv2.DEFAULT_PORT; + public static final int DEFAULT_SSL_PORT = 636; + public static final int DEFAULT_LDAP_VERSION = 3; + + // JNDI defined environment propertiies + public static final String P_PROVIDER_URL = Context.PROVIDER_URL; + public static final String P_SECURITY_PROTOCOL = Context.SECURITY_PROTOCOL; + public static final String P_SECURITY_AUTHMODE = Context.SECURITY_AUTHENTICATION; + public static final String P_USER_DN = Context.SECURITY_PRINCIPAL; + public static final String P_USER_PASSWORD = Context.SECURITY_CREDENTIALS; + public static final String P_APPLET = Context.APPLET; + public static final String P_AUTHORITATIVE = Context.AUTHORITATIVE; + public static final String P_LANGUAGE = Context.LANGUAGE; + public static final String P_BATCHSIZE = Context.BATCHSIZE; + public static final String P_REFERRAL_MODE = Context.REFERRAL; + + // Custom properties + public static final String P_CONNECT_CTRLS = "java.naming.ldap.control.connect"; + public static final String P_BINARY_ATTRS = "java.naming.ldap.attributes.binary"; + public static final String P_ATTRS_ONLY = "java.naming.ldap.typesOnly"; + public static final String P_DELETE_OLDRDN = "java.naming.ldap.deleteRDN"; + public static final String P_SOCKET_FACTORY = "java.naming.ldap.factory.socket"; + public static final String P_CIPHER_SUITE = "java.naming.ldap.ssl.ciphers"; // new + public static final String P_TIME_LIMIT = "java.naming.ldap.timelimit"; // new + public static final String P_MAX_RESULTS = "java.naming.ldap.maxresults"; // new + public static final String P_DEREF_ALIASES = "java.naming.ldap.derefAliases"; + public static final String P_REFERRAL_HOPLIMIT = "java.naming.referral.limit"; + public static final String P_LDAP_VERSION = "java.naming.ldap.version"; + public static final String P_JNDIREF_SEPARATOR = "java.naming.ref.separator"; + + // Possible values for the Context.REFERRAL env property + private static final String V_REFERRAL_FOLLOW = "follow"; + private static final String V_REFERRAL_IGNORE = "ignore"; + private static final String V_REFERRAL_THROW_EXCEPTION = "throw"; + + // Possible values for the java.naming.ldap.derefAliases env property + private static final String V_DEREF_NEVER = "never"; + + private static final String V_DEREF_SEARCHING = "searching"; + private static final String V_DEREF_FINDING = "finding"; + private static final String V_DEREF_ALWAYS = "always"; + + // Possible values for the java.naming... env property + private static final String V_AUTH_NONE = "none"; + private static final String V_AUTH_SIMPLE = "simple"; + private static final String V_AUTH_STRONG = "strong"; + + /** + * Constructor for non root Contexts + * + * @param parent A reference to the parent context environment + * @param parentSharedEnvIdx index into parent's shared environemnt list + */ + public ContextEnv(ShareableEnv parent, int parentSharedEnvIdx) { + super(parent, parentSharedEnvIdx); + } + + /** + * Constructor for the root context + * + * @param initialEnv a hashtable with environemnt properties + */ + public ContextEnv(Hashtable initialEnv) { + super(initialEnv); + } + + /** + * Clone ContextEnv + * + * @return A "clone" of the current context environment + */ + /** + * Clone ShareableEnv + * The code is the same as in the superclass (ShareableEnv) except that + * a ContextEnv instance is returned + * + * @return A "clone" of the current context environment + */ + public Object clone() { + + // First freeze updates for this context + freezeUpdates(); + + // If the context has been modified, then it is the parent of the clone + if (m_sharedEnv != null) { + return new ContextEnv(this, m_sharedEnv.size()-1); + } + + // No changes has been done to the inherited parent context. Pass the parent + // context to the clone + else { + return new ContextEnv(m_parentEnv, m_parentSharedEnvIdx); + } + } + + /** + * Update property value. Properties that pertain to LDAPSearchConstraints + * are immediately propagated. To take effect of properties that are connection + * related, (like user name/password, ssl mode) the context mujst be reconnected + * after the change of environment + */ + Object updateProperty(String name, Object val, LDAPSearchConstraints cons) throws NamingException{ + Object oldVal = getProperty(name); + setProperty(name,val); + try { + if (name.equalsIgnoreCase(P_BATCHSIZE)) { + updateBatchSize(cons); + } + else if (name.equalsIgnoreCase(P_TIME_LIMIT)) { + updateTimeLimit(cons); + } + else if (name.equalsIgnoreCase(P_MAX_RESULTS)) { + updateMaxResults(cons); + } + else if (name.equalsIgnoreCase(P_DEREF_ALIASES)) { + updateDerefAliases(cons); + } + else if (name.equalsIgnoreCase(P_REFERRAL_MODE)) { + updateReferralMode(cons); + } + else if (name.equalsIgnoreCase(P_REFERRAL_HOPLIMIT)) { + updateReferralHopLimit(cons); + } + } + catch (IllegalArgumentException e) { + if (oldVal == null) { + removeProperty(name); + } + else { + setProperty(name, oldVal); + } + throw e; + } + return oldVal; + } + + /** + * Initialize LDAPSearchConstraints with environment properties + */ + void updateSearchCons(LDAPSearchConstraints cons) throws NamingException{ + updateBatchSize(cons); + updateTimeLimit(cons); + updateMaxResults(cons); + updateDerefAliases(cons); + updateReferralMode(cons); + updateReferralHopLimit(cons); + } + + /** + * Set the suggested number of result to return at a time during search in the + * default SearchConstraints for the connection. + * Specified with the env property java.naming.batchsize + */ + void updateBatchSize(LDAPSearchConstraints cons) { + String size = (String)getProperty(P_BATCHSIZE); + if (size != null) { + int n = -1; + try { + n = Integer.parseInt(size); + } + catch (Exception e) { + throw new IllegalArgumentException("Illegal value for " + P_BATCHSIZE); + } + cons.setBatchSize(n); + } + } + + /** + * Set the maximum number of miliseconds to wait for any operation under default + * SearchConstraints for the connection. + * Specified with the env property java.naming.ldap.timelimit + * Note: sun ldap does not have this property + */ + void updateTimeLimit(LDAPSearchConstraints cons) { + String millis = (String)getProperty(P_TIME_LIMIT); + if (millis != null) { + int n = -1; + try { + n = Integer.parseInt(millis); + } + catch (Exception e) { + throw new IllegalArgumentException("Illegal value for " + P_TIME_LIMIT); + } + cons.setTimeLimit(n); + } + } + + /** + * Set the maximum number of search results to be returned under default + * SearchConstraints for the connection. + * Specified with the env property java.naming.ldap.maxresults + * Note: sun ldap does not have this property + */ + void updateMaxResults(LDAPSearchConstraints cons) { + String max = (String)getProperty(P_MAX_RESULTS); + if (max != null) { + int n = -1; + try { + n = Integer.parseInt(max); + } + catch (Exception e) { + throw new IllegalArgumentException( + "Illegal value for " + P_MAX_RESULTS); + } + cons.setMaxResults(n); + } + } + + /** + * Set how aliases should be dereferenced under default SearchConstraints for the + * connection. + * Specified with the env property java.naming.ldap.derefAliases + */ + final void updateDerefAliases(LDAPSearchConstraints cons) throws IllegalArgumentException{ + String deref = (String)getProperty(P_DEREF_ALIASES); + if(deref != null) { + if(deref.equalsIgnoreCase(V_DEREF_NEVER)) { + cons.setDereference(LDAPv2.DEREF_NEVER); + } + else if(deref.equalsIgnoreCase(V_DEREF_SEARCHING)) { + cons.setDereference(LDAPv2.DEREF_SEARCHING); + } + else if(deref.equalsIgnoreCase(V_DEREF_FINDING)) { + cons.setDereference(LDAPv2.DEREF_FINDING); + } + else if(deref.equalsIgnoreCase(V_DEREF_ALWAYS)) { + cons.setDereference(LDAPv2.DEREF_ALWAYS); + } + else { + throw new IllegalArgumentException("Illegal value for " + P_DEREF_ALIASES); + } + } + } + + /** + * Set referral parameters for the default SearchConstraints for the connection. + * Specified with the env property java.naming.referral + */ + void updateReferralMode(LDAPSearchConstraints cons) { + String mode = (String)getProperty(P_REFERRAL_MODE); + if(mode != null) { + if(mode.equalsIgnoreCase(V_REFERRAL_FOLLOW)) { + cons.setReferrals(true); + String user = getUserDN(), passwd = getUserPassword(); + if (user != null && passwd != null) { + cons.setRebindProc(new ReferralRebindProc(user, passwd)); + } + } + else if(mode.equalsIgnoreCase(V_REFERRAL_THROW_EXCEPTION)) { + cons.setReferrals(false); + } + else if(mode.equalsIgnoreCase(V_REFERRAL_IGNORE)) { + cons.setServerControls(new LDAPControl( + LDAPControl.MANAGEDSAIT, true, null)); + } + else { + throw new IllegalArgumentException("Illegal value for " + P_REFERRAL_MODE); + } + } + } + + /** + * Implementation for LDAPRebind interface. Provide user name, password + * to autenticate with the referred to directory server. + */ + static class ReferralRebindProc implements LDAPRebind { + LDAPRebindAuth auth; + + public ReferralRebindProc(String user, String passwd) { + auth = new LDAPRebindAuth(user, passwd); + } + + public LDAPRebindAuth getRebindAuthentication(String host, int port) { + return auth; + } + } + + /** + * Set maximal number of referral hops under default SearchConstraints for the + * connection. + * Specified with the env property java.naming.referral.limit + */ + void updateReferralHopLimit(LDAPSearchConstraints cons) throws IllegalArgumentException{ + String limit = (String)getProperty(P_REFERRAL_HOPLIMIT); + if(limit != null) { + int n = -1; + try { + n = Integer.parseInt(limit); + } + catch (Exception e) { + throw new IllegalArgumentException("Illegal value for " + P_REFERRAL_HOPLIMIT); + } + cons.setHopLimit(n); + } + } + + + /** + * Check if simple auth mode is explicitly requested. If that's the case + * user DN and password must be specified as well + */ + boolean useSimpleAuth() throws NamingException { + String authMode = (String)getProperty(P_SECURITY_AUTHMODE); + if(authMode != null) { + if(authMode.equalsIgnoreCase(V_AUTH_NONE)) { + return false; + } + else if (authMode.equalsIgnoreCase(V_AUTH_SIMPLE)) { + return true; + } + else { + throw new AuthenticationNotSupportedException( + "Unsupported value for " + P_SECURITY_AUTHMODE); + } + } + return false; + } + + /** + * Check if SSL mode is enabled + */ + boolean isSSLEnabled() throws NamingException { + String secMode = (String)getProperty(P_SECURITY_PROTOCOL); + if(secMode != null) { + if(secMode.equalsIgnoreCase("ssl")) { + return true; + } + else { + throw new AuthenticationNotSupportedException( + "Unsupported value for " + P_SECURITY_PROTOCOL); + } + } + return false; + } + + /** + * Get the Directory Server URL + */ + LDAPUrl getDirectoryServerURL() throws NamingException{ + String url = (String) getProperty(Context.PROVIDER_URL); + try { + return (url == null) ? null : new LDAPUrl(url); + } + catch (java.net.MalformedURLException e) { + throw new IllegalArgumentException( + "Illegal value for " + Context.PROVIDER_URL); + } + } + + /** + * Get Ldap Version. If not specified the default is version 3 + */ + int getLdapVersion() throws NamingException{ + String version = (String) getProperty(P_LDAP_VERSION); + if (version != null) { + int v = -1; + try { + v = Integer.parseInt(version); + } + catch (Exception e) { + throw new IllegalArgumentException( + "Illegal value for java.naming.ldap.version property."); + } + /*if ( v !=2 && v !=3) { + throw new IllegalArgumentException( + "Illegal value for + java.naming.ldap.version property."); + }BLITS*/ + return v; + } + return DEFAULT_LDAP_VERSION; + } + + /** + * Get user authenticate name + */ + String getUserDN() { + return (String) getProperty(Context.SECURITY_PRINCIPAL); + } + + /** + * Get user authenticate password + */ + String getUserPassword() { + return (String) getProperty(Context.SECURITY_CREDENTIALS); + } + + /** + * Get full qualified socket factory class name + */ + String getSocketFactory() { + return (String)getProperty(P_SOCKET_FACTORY); + } + + /** + * Get cipher suite for the socket factory + */ + Object getCipherSuite() { + return getProperty(P_CIPHER_SUITE); + } + + /** + * Get controls to be used during a connection request like ProxyAuth + */ + LDAPControl[] getConnectControls() throws NamingException{ + Control[] reqCtls = (Control[])getProperty(P_CONNECT_CTRLS); + if (reqCtls != null) { + LDAPControl[] ldapCtls = new LDAPControl[reqCtls.length]; + for (int i=0; i < reqCtls.length; i++) { + try { + ldapCtls[i] = (LDAPControl) reqCtls[i]; + } + catch (ClassCastException ex) { + throw new NamingException( + "Unsupported control type " + reqCtls[i].getClass().getName()); + } + } + return ldapCtls; + } + return null; + } + + /** + * Flag whether search operation should return attribute names only + * (no values). Read environment property P_ATTRS_ONLY. If not defined + * FALSE is returned (return attribute values by default) + */ + boolean getAttrsOnlyFlag() { + String flag = (String)getProperty(P_ATTRS_ONLY); + if (flag == null) { + return false; //default + } + else if (flag.equalsIgnoreCase("true")) { + return true; + } + else if (flag.equalsIgnoreCase("false")) { + return false; + } + else { + throw new IllegalArgumentException("Illegal value for " + P_ATTRS_ONLY); + } + } + + /** + * Flag whether rename operation should delete old RDN + * Read environment property P_ATTRS_ONLY. If not defined + * TRUE is returned (delete old RDN by default) + */ + boolean getDeleteOldRDNFlag() { + String flag = (String)getProperty(P_DELETE_OLDRDN); + if (flag == null) { + return true; //default + } + else if (flag.equalsIgnoreCase("true")) { + return true; + } + else if (flag.equalsIgnoreCase("false")) { + return false; + } + else { + throw new IllegalArgumentException("Illegal value for " + P_DELETE_OLDRDN); + } + } + + /** + * A user defined value for the separator for JNDI References. + * The default value is '#'. + * Read environment property P_JNDIREF_SEPARATOR. + */ + char getRefSeparator() throws NamingException{ + String sep = (String)getProperty(P_JNDIREF_SEPARATOR ); + if(sep != null) { + if (sep.length() !=1) { + throw new IllegalArgumentException("Illegal value for " + P_JNDIREF_SEPARATOR); + } + return sep.charAt(0); + } + return '#'; + } + + /** + * A user defined list of names of binary attributes. This list augments the + * default list of well-known binary attributes. List entries are space separated + * Read environment property P_BINARY_ATTRS. + */ + String[] getUserDefBinaryAttrs() { + String binAttrList = (String)getProperty(P_BINARY_ATTRS); + if (binAttrList == null) { + return null; + } + + StringTokenizer tok = new StringTokenizer(binAttrList); + String[] binAttrs = new String[tok.countTokens()]; + for (int i=0; tok.hasMoreTokens(); i++) { + binAttrs[i] = tok.nextToken(); + } + return binAttrs; + } + +} \ No newline at end of file diff --git a/mozilla/directory/java-sdk/ldapsp/com/netscape/jndi/ldap/EventService.java b/mozilla/directory/java-sdk/ldapsp/com/netscape/jndi/ldap/EventService.java new file mode 100644 index 00000000000..8c86118da71 --- /dev/null +++ b/mozilla/directory/java-sdk/ldapsp/com/netscape/jndi/ldap/EventService.java @@ -0,0 +1,391 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * + * The contents of this file are subject to the Netscape Public License + * Version 1.0 (the "NPL"); you may not use this file except in + * compliance with the NPL. You may obtain a copy of the NPL at + * http://www.mozilla.org/NPL/ + * + * Software distributed under the NPL is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL + * for the specific language governing rights and limitations under the + * NPL. + * + * The Initial Developer of this code under the NPL is Netscape + * Communications Corporation. Portions created by Netscape are + * Copyright (C) 1999 Netscape Communications Corporation. All Rights + * Reserved. + */ +package com.netscape.jndi.ldap; + +import javax.naming.*; +import javax.naming.directory.*; +import javax.naming.event.*; +import netscape.ldap.*; +import netscape.ldap.controls.*; +import java.util.*; +import com.netscape.jndi.ldap.common.*; + +/** + * Event Service. Use Persistant Search control to monitor changes on the + * server. Because public ldapjdk APIs do not provide for multiplexing of + * multiple requests over the same connection, a separate thread is created + * for each monitored event + * + */ +class EventService { + + LdapService m_ldapSvc; + Vector m_eventThreads = new Vector(); + + public EventService(LdapService ldapSvc) { + m_ldapSvc = ldapSvc; + } + + /** + * Create a persistent search control. + */ + private LDAPPersistSearchControl createSrchCtrl(NamingListener listener) throws NamingException{ + + int op = 0; + + if (listener instanceof ObjectChangeListener) { + op = LDAPPersistSearchControl.MODIFY; + } + if (listener instanceof NamespaceChangeListener) { + op |= LDAPPersistSearchControl.ADD | + LDAPPersistSearchControl.DELETE | + LDAPPersistSearchControl.MODDN; + } + if (op == 0) { + throw new NamingException("Non supported listener type " + listener.getClass().getName()); + } + return new LDAPPersistSearchControl( op, /*changesOnly=*/true, + /*returnControls=*/true, /*isCritical=*/true ); + } + + /** + * Remove change event listener + */ + synchronized void removeListener(NamingListener listener) throws NamingException { + boolean removed = false; + int i=0; + while(i < m_eventThreads.size()) { + EventThread et = (EventThread)m_eventThreads.elementAt(i); + if (et.removeListener(listener)) { + removed = true; + + // If no listeners left, stop the thread. + if (et.isEmpty()) { + et.abandonRequest(); + et.stop(); + if (m_eventThreads.removeElement(et)) { + continue; // do not advance counter i + } + } + } + i++; + } + + if (!removed) { + throw new NamingException("Listener not found"); + } + } + + /** + * Add change event listener + */ + synchronized void addListener (LdapContextImpl ctx, String name, String filter, SearchControls jndiCtrls, NamingListener l) throws NamingException{ + Debug.println(1, "ADD LISTENER"); + + // Create DN by appending the name to the current context + String base = ctx.getDN(); + if (name.length() > 0) { + if (base.length() > 0) { + base = name + "," + base; + } + else { + base = name; + } + } + + //Create search constraints + LDAPConnection ld = (LDAPConnection) m_ldapSvc.getConnection().clone(); + LDAPSearchConstraints cons=ld.getSearchConstraints(); + LDAPPersistSearchControl psearchCtrl = createSrchCtrl(l); + cons.setServerControls(psearchCtrl); + + // return obj flag is ignored in this implementation + boolean returnObjs = jndiCtrls.getReturningObjFlag(); + + // Because we are not returning objects in the NamingEvent's + // oldBinding and newBinding variables, there is no way for + // for the listener to read attributes from the event. Thus + // jndiCtrls.getReturningAttributes() are ignored. Request only + // javaClassName to be able to determine object type + String[] attrs = new String[] { "javaclassname" }; + + // Search scope + int scope = ProviderUtils.jndiSearchScopeToLdap(jndiCtrls.getSearchScope()); + + // Check if such change is already monitored + EventThread et = null; + for (int i=0; i < m_eventThreads.size(); i++) { + EventThread activeET = (EventThread) m_eventThreads.elementAt(i); + if (activeET.isEqualEvent(ctx, base, scope, filter, attrs, cons)) { + et = activeET; + et.addListener(l); + break; + } + } + + // Create new event if reqested change is not already monitored + if (et == null) { + et = new EventThread(ctx, ld, base, scope, filter, attrs, cons); + m_eventThreads.addElement(et); + et.addListener(l); + et.setDaemon(true); + et.start(); + } + } + + + /** + * Change Monitoring Thread + */ + static private class EventThread extends Thread { + + String base, filter, attrs[]; + int scope; + LDAPSearchConstraints cons; + LDAPConnection ld; + EventContext src; + LDAPSearchResults res = null; + LdapContextImpl ctx; + boolean doRun; + Vector listeners = new Vector(); // vector of NamingListener + + EventThread(LdapContextImpl ctx, LDAPConnection ld, String base, int scope, String filter, + String[] attrs, LDAPSearchConstraints cons) { + + this.ctx = ctx; + this.ld = ld; + this.base = base; + this.scope = scope; + this.filter = filter; + this.attrs = attrs; + this.cons = cons; + } + + /** + * Add Listsner + */ + void addListener(NamingListener l) { + synchronized(listeners) { + listeners.addElement(l); + } + } + + /** + * Remove listener + */ + boolean removeListener(NamingListener l) { + synchronized(listeners) { + return listeners.removeElement(l); + } + } + + /** + * Abandon request. Called when no listeners left. + * Unfortunatlly, ldapjdk does not allows us the interrupt a waiting + * thread and do proper cleanup. + * TODO Need improvment here!! + */ + void abandonRequest() throws NamingException { + try { + if (res != null) { + ld.abandon(res); + } + ld.disconnect(); + } + catch (Exception ex) { + throw ExceptionMapper.getNamingException(ex); + } + } + + /** + * Chech whether there are any listeners + */ + boolean isEmpty() { + return listeners.size() == 0; + } + + /** + * Check whether this event paramaters are matched + */ + boolean isEqualEvent(LdapContextImpl ctx, String base, int scope, String filter, + String[] attrs, LDAPSearchConstraints cons) { + + if (this.ctx != ctx || !this.base.equals(base) || + this.scope != scope || !this.filter.equals(filter)) { + + return false; + } + // attrs[] + if (this.attrs == null) { + if (attrs != null) { + return false; + } + } + else if (attrs == null) { + return false; + } + else if (this.attrs.length != attrs.length) { + return false; + } + else { + for (int i=0; i < this.attrs.length; i++) { + // TODO not 100% ok. Arrays may contain the same names + // but at different positions + if (!this.attrs[i].equals(attrs[i])) { + return false; + } + } + } + + // Check if persistant search is for the same change type + LDAPPersistSearchControl + psearch1 = (LDAPPersistSearchControl)this.cons.getServerControls()[0], + psearch2 = (LDAPPersistSearchControl)cons.getServerControls()[0]; + int types1 = psearch1.getChangeTypes(), + types2 = psearch2.getChangeTypes(); + return (types1 == types2); + } + + /** + * Issue persistent search request. Loop waiting for change notifications + */ + public void run() { + doRun = true; + try { + Debug.println(1, "Do persistent search for " + base); + res = ld.search( base, scope, filter, attrs, false, cons); + + /* Loop through the results until finished. */ + while ( res.hasMoreElements() && doRun) { + + /* Get the next modified directory entry. */ + LDAPEntry modEntry = res.next(); + + Debug.println(1, "Changed " + modEntry.getDN()); + + /* Get any entry change controls. */ + LDAPControl[] ctrls = ld.getResponseControls(); + + // Can not create event without change control + if (ctrls == null) { + throw new NamingException( + "Can not create NamingEvent, no change control info"); + } + + LDAPEntryChangeControl changeCtrl = + LDAPPersistSearchControl.parseResponse(ctrls); + + // Can not create event without change control + if (changeCtrl == null || changeCtrl.getChangeType() == -1) { + throw new NamingException( + "Can not create NamingEvent, no change control info"); + } + + NamingEvent event = createNamingEvent(modEntry, changeCtrl); + dispatchEvent(event); + } + } + catch(Exception ex) { + NamingException nameEx = ExceptionMapper.getNamingException(ex); + dispatchEvent(new NamingExceptionEvent(ctx, nameEx)); + } + } + + /** + * Dispatch naming event to all listeners + */ + void dispatchEvent(EventObject event) { + NamingListener[] dispatchList = null; + + // Copy listeners so that list can be modifed during dispatching + synchronized (listeners) { + dispatchList = new NamingListener[listeners.size()]; + for (int i=0; i < dispatchList.length; i++) { + dispatchList[i] = (NamingListener)listeners.elementAt(i); + } + } + + // dispatch to all listeners + for (int i=0; i < dispatchList.length; i++) { + if (event instanceof NamingEvent) { + ((NamingEvent)event).dispatch(dispatchList[i]); + } + else { + ((NamingExceptionEvent)event).dispatch(dispatchList[i]); + } + } + } + + /** + * Create naming event from the ldap entry and change control + */ + NamingEvent createNamingEvent(LDAPEntry entry, LDAPEntryChangeControl changeCtrl) throws NamingException{ + + Binding oldBd = null, newBd = null; + int eventType = -1; + Object changeInfo = null; + String oldName = null, newName = null; + + // Get the class name from the entry + String className = ObjectMapper.getClassName(entry); + + // Get information on the type of change made + int changeType = changeCtrl.getChangeType(); + switch ( changeType ) { + case LDAPPersistSearchControl.ADD: + eventType = NamingEvent.OBJECT_ADDED; + newName = LdapNameParser.getRelativeName(ctx.m_ctxDN, entry.getDN()); + break; + case LDAPPersistSearchControl.DELETE: + eventType = NamingEvent.OBJECT_REMOVED; + oldName = LdapNameParser.getRelativeName(ctx.m_ctxDN, entry.getDN()); + break; + case LDAPPersistSearchControl.MODIFY: + eventType = NamingEvent.OBJECT_CHANGED; + oldName = newName = LdapNameParser.getRelativeName(ctx.m_ctxDN, entry.getDN()); + break; + case LDAPPersistSearchControl.MODDN: + eventType = NamingEvent.OBJECT_RENAMED; + // Get the previous DN of the entry + String oldDN = changeCtrl.getPreviousDN(); + if ( oldDN != null ) { + oldName = LdapNameParser.getRelativeName(ctx.m_ctxDN, oldDN); + } + // newName might be outside the context for which the listener has registred + try { + newName = LdapNameParser.getRelativeName(ctx.m_ctxDN, entry.getDN()); + } + catch (NamingException ex) {} + break; + } + + // Pass the change log number as event's change info + // If the change log number is not present the value is -1 + changeInfo = new Integer(changeCtrl.getChangeNumber()); + + + if (oldName != null) { + oldBd = new Binding(oldName, className, /*obj=*/null, /*isRelative=*/true); + } + if (newName!= null) { + newBd = new Binding(newName, className, /*obj=*/null, /*isRelative=*/true); + } + return new NamingEvent(ctx, eventType, newBd, oldBd, changeInfo); + } + } +} diff --git a/mozilla/directory/java-sdk/ldapsp/com/netscape/jndi/ldap/LdapContextFactory.java b/mozilla/directory/java-sdk/ldapsp/com/netscape/jndi/ldap/LdapContextFactory.java new file mode 100644 index 00000000000..8375ede8925 --- /dev/null +++ b/mozilla/directory/java-sdk/ldapsp/com/netscape/jndi/ldap/LdapContextFactory.java @@ -0,0 +1,47 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * + * The contents of this file are subject to the Netscape Public License + * Version 1.0 (the "NPL"); you may not use this file except in + * compliance with the NPL. You may obtain a copy of the NPL at + * http://www.mozilla.org/NPL/ + * + * Software distributed under the NPL is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL + * for the specific language governing rights and limitations under the + * NPL. + * + * The Initial Developer of this code under the NPL is Netscape + * Communications Corporation. Portions created by Netscape are + * Copyright (C) 1999 Netscape Communications Corporation. All Rights + * Reserved. + */ +package com.netscape.jndi.ldap; + +import javax.naming.*; +import javax.naming.directory.*; +import javax.naming.event.*; +import javax.naming.spi.*; +import java.util.*; + +public class LdapContextFactory implements InitialContextFactory { + + public Context getInitialContext(Hashtable env) throws NamingException { + Hashtable ctxEnv = (Hashtable)env.clone(); + + // Read system properties as well. Add a system property to the + // env if it's name start with "java.naming." and it is not already + // present in the env (env has precedence over the System properties) + for (Enumeration e = System.getProperties().keys(); e.hasMoreElements();) { + String key = (String) e.nextElement(); + if (key.startsWith("java.naming.")) { + if (ctxEnv.get(key) == null) { + ctxEnv.put(key,System.getProperty(key)); + } + } + } + + EventDirContext ctx = new LdapContextImpl(ctxEnv); + return ctx; + } +} + diff --git a/mozilla/directory/java-sdk/ldapsp/com/netscape/jndi/ldap/LdapContextImpl.java b/mozilla/directory/java-sdk/ldapsp/com/netscape/jndi/ldap/LdapContextImpl.java new file mode 100644 index 00000000000..53bc6d19119 --- /dev/null +++ b/mozilla/directory/java-sdk/ldapsp/com/netscape/jndi/ldap/LdapContextImpl.java @@ -0,0 +1,604 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * + * The contents of this file are subject to the Netscape Public License + * Version 1.0 (the "NPL"); you may not use this file except in + * compliance with the NPL. You may obtain a copy of the NPL at + * http://www.mozilla.org/NPL/ + * + * Software distributed under the NPL is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL + * for the specific language governing rights and limitations under the + * NPL. + * + * The Initial Developer of this code under the NPL is Netscape + * Communications Corporation. Portions created by Netscape are + * Copyright (C) 1999 Netscape Communications Corporation. All Rights + * Reserved. + */ +package com.netscape.jndi.ldap; + +import javax.naming.*; +import javax.naming.directory.*; +import javax.naming.ldap.*; +import javax.naming.event.*; + +import netscape.ldap.*; +import netscape.ldap.controls.*; + +import com.netscape.jndi.ldap.controls.NetscapeControlFactory; +import com.netscape.jndi.ldap.common.Debug; + +import java.util.*; + +/** + * Implementation for the DirContext. The context also supports controls + * through the implementation of LdapContext interface and events through + * the implementaion of EventDirContext. + * Semantically, the LdapContextImpl corresponds to a directory entry. + * Thus a context is associated with a DN (m_ctxDN). Multiple contexts share + * the same LDAPConnection which is wrapped into a LdapService object + * (m_ldapSvc). Each context also maintains a set of environment properties + * (m_ctxEnv). A context environment is shared among mutiple contexts using a + * variation of copy-on-write algorithm (see common.ShareableEnv class). + * + * Each context also maintains a set of LDAPSearchConstraints, as search + * constrainsts like e.g. server controls, or max number of returned search + * search results, are context specific. The LdapService reads the + * LDAPSearchConstraints from a context that makes a service request. + */ +public class LdapContextImpl implements EventDirContext, LdapContext { + + /** + * Context environment setting + */ + protected ContextEnv m_ctxEnv; + + /** + * DN associated with this context + * The default value is the root DSE ("") + */ + protected String m_ctxDN; + + /** + * Ldap Connection/Service + */ + protected LdapService m_ldapSvc; + + + /** + * Ldap Connection Search Constraints + */ + protected LDAPSearchConstraints m_searchCons; + + // TODO Should have a constructor that accepts attributes + + /** + * Constructor + */ + public LdapContextImpl(Hashtable env) throws NamingException{ + m_ctxEnv = new ContextEnv(env); // no need to clone (Hashtable)env.clone()); + m_ldapSvc = new LdapService(this); + m_ldapSvc.connect(); // BLITS but to be removed, hurts lazy resource usage + getDN(); + getSearchConstraints(); + } + + /** + * Copy Constructor + */ + public LdapContextImpl(String ctxDN, LdapContextImpl cloneCtx) throws NamingException{ + + m_ctxEnv = (ContextEnv)cloneCtx.m_ctxEnv.clone(); + + // An instance of ldapService is shared among multiple contexts. + // Increment the client reference count + m_ldapSvc = cloneCtx.m_ldapSvc; + cloneCtx.m_ldapSvc.incrementClientCount(); + + if (cloneCtx.getSearchConstraints().getServerControls() == null) { + m_searchCons = cloneCtx.getSearchConstraints(); + } + else { + // In LdapContext Context Controls are not inherited by derived contexts + m_searchCons = (LDAPSearchConstraints) cloneCtx.getSearchConstraints().clone(); + m_searchCons.setServerControls((LDAPControl[])null); + } + + m_ctxDN = ctxDN; + } + + + /** + * Close the context when finalized + */ + protected void finalize() { + Debug.println(1, "finalize ctx"); + try { + close(); + } + catch (Exception e) {} + } + + /** + * Disconnect the Ldap Connection if close is requested + * LDAP operations can not be performed any more ones + * the context is closed + */ + public void close() throws NamingException { + m_ldapSvc.disconnect(); + m_ldapSvc = null; + } + + /** + * Return LdapJdk search constraints for this context + */ + LDAPSearchConstraints getSearchConstraints() throws NamingException{ + if (m_searchCons == null) { + LDAPSearchConstraints cons = new LDAPSearchConstraints(); + m_ctxEnv.updateSearchCons(cons); + m_searchCons = cons; + } + return m_searchCons; + } + + /** + * Return DN for this context + */ + String getDN() throws NamingException{ + if (m_ctxDN == null) { + LDAPUrl url = m_ctxEnv.getDirectoryServerURL(); + if (url != null && url.getDN() != null) { + m_ctxDN = url.getDN(); + } + else { + m_ctxDN = ""; + } + } + return m_ctxDN; + } + + /** + * Return reference to the context environment + */ + ContextEnv getEnv() { + return m_ctxEnv; + } + + /** + * Conver object to String + */ + public String toString() { + return this.getClass().getName() + ": " + m_ctxDN; + } + + /** + * Check if LdapURL is passed as the name paremetr to a method + * If that's the case, craete environment for the ldap url + */ + String checkLdapUrlAsName(String name) throws NamingException{ + if (name.startsWith("ldap://")) { + m_ctxEnv.setProperty(ContextEnv.P_PROVIDER_URL, name); + close(); // Force reconnect + m_ldapSvc = new LdapService(this); + // Return New name relative to the context + return ""; + } + return name; + } + + /** + * Environment operatins (javax.naming.Context interface) + */ + + public Hashtable getEnvironment() throws NamingException { + return m_ctxEnv.getAllProperties(); + } + + public Object addToEnvironment(String propName, Object propValue) throws NamingException { + return m_ctxEnv.updateProperty(propName, propValue, getSearchConstraints()); + } + + public Object removeFromEnvironment(String propName) throws NamingException { + return m_ctxEnv.removeProperty(propName); + } + + /** + * Name operations (javax.naming.Context interface) + */ + + public String composeName(String name, String prefix) throws NamingException { + return name + "," + prefix; + } + + public Name composeName(Name name, Name prefix) throws NamingException { + String compoundName = composeName(name.toString(), prefix.toString()); + return LdapNameParser.getParser().parse(compoundName); + } + + public String getNameInNamespace() throws NamingException { + return new String(m_ctxDN); + } + + public NameParser getNameParser(String name) throws NamingException { + return LdapNameParser.getParser(); + } + + public NameParser getNameParser(Name name) throws NamingException { + return LdapNameParser.getParser(); + } + + /** + * Search operations (javax.naming.DirContext interface) + */ + + public NamingEnumeration search(String name, String filter, SearchControls cons) throws NamingException { + name = checkLdapUrlAsName(name); + return m_ldapSvc.search(this, name, filter, /*attrs=*/null, cons); + } + + public NamingEnumeration search(String name, String filterExpr, Object[] filterArgs, SearchControls cons) throws NamingException { + name = checkLdapUrlAsName(name); + String filter = ProviderUtils.expandFilterExpr(filterExpr, filterArgs); + return m_ldapSvc.search(this, name, filter, /*attrs=*/null, cons); + } + + public NamingEnumeration search(String name, Attributes matchingAttributes) throws NamingException { + name = checkLdapUrlAsName(name); + String filter = ProviderUtils.attributesToFilter(matchingAttributes); + return m_ldapSvc.search(this, name, filter, /*attrs=*/null, /*jndiCons=*/null); + } + + public NamingEnumeration search(String name, Attributes matchingAttributes, String[] attributesToReturn) throws NamingException { + name = checkLdapUrlAsName(name); + String filter = ProviderUtils.attributesToFilter(matchingAttributes); + return m_ldapSvc.search(this, name, filter, attributesToReturn, /*jndiCons=*/null); + } + + public NamingEnumeration search(Name name, String filter, SearchControls cons) throws NamingException { + return m_ldapSvc.search(this, name.toString(), filter, /*attrs=*/null, cons); + } + + public NamingEnumeration search(Name name, String filterExpr, Object[] filterArgs, SearchControls cons) throws NamingException { + String filter = ProviderUtils.expandFilterExpr(filterExpr, filterArgs); + return m_ldapSvc.search(this, name.toString(), filter, /*attrs=*/null, cons); + } + + public NamingEnumeration search(Name name, Attributes attrs) throws NamingException { + String filter = ProviderUtils.attributesToFilter(attrs); + return m_ldapSvc.search(this, name.toString(), filter, /*attr=*/null, /*jndiCons=*/null); + } + + public NamingEnumeration search(Name name, Attributes matchingAttributes, String[] attributesToReturn) throws NamingException { + String filter = ProviderUtils.attributesToFilter(matchingAttributes); + return m_ldapSvc.search(this, name.toString(), filter, attributesToReturn, /*jndiCons=*/null); + } + + /** + * Attribute Operations (javax.naming.DirContext interface) + */ + + public Attributes getAttributes(String name) throws NamingException { + name = checkLdapUrlAsName(name); + return m_ldapSvc.readAttrs(this, name, null); + } + + public Attributes getAttributes(String name, String[] attrIds) throws NamingException { + name = checkLdapUrlAsName(name); + return m_ldapSvc.readAttrs(this, name, attrIds); + } + + public Attributes getAttributes(Name name) throws NamingException { + return m_ldapSvc.readAttrs(this, name.toString(), null); + + } + + public Attributes getAttributes(Name name, String[] attrIds) throws NamingException { + return m_ldapSvc.readAttrs(this, name.toString(), attrIds); + + } + + public void modifyAttributes(String name, int mod_op, Attributes attrs) throws NamingException { + name = checkLdapUrlAsName(name); + m_ldapSvc.modifyEntry(this, name, AttributesImpl.jndiAttrsToLdapModSet(mod_op, attrs)); + } + + public void modifyAttributes(String name, ModificationItem[] mods) throws NamingException { + name = checkLdapUrlAsName(name); + m_ldapSvc.modifyEntry(this, name, AttributesImpl.jndiModsToLdapModSet(mods)); + } + + public void modifyAttributes(Name name, int mod_op, Attributes attrs) throws NamingException { + m_ldapSvc.modifyEntry(this, name.toString(), AttributesImpl.jndiAttrsToLdapModSet(mod_op, attrs)); + + } + + public void modifyAttributes(Name name, ModificationItem[] mods) throws NamingException { + m_ldapSvc.modifyEntry(this, name.toString(), AttributesImpl.jndiModsToLdapModSet(mods)); + } + + /** + * Ldap entry operations (javax.naming.DirContext interface) + */ + + public Context createSubcontext(String name) throws NamingException { + // Directory entry must have attributes + throw new OperationNotSupportedException(); + } + + public Context createSubcontext(Name name) throws NamingException { + // Directory entry must have attributes + throw new OperationNotSupportedException(); + } + + public DirContext createSubcontext(String name, Attributes attrs) throws NamingException { + name = checkLdapUrlAsName(name); + return m_ldapSvc.addEntry(this, name, AttributesImpl.jndiAttrsToLdapAttrSet(attrs)); + } + + public DirContext createSubcontext(Name name, Attributes attrs) throws NamingException { + return m_ldapSvc.addEntry(this, name.toString(), AttributesImpl.jndiAttrsToLdapAttrSet(attrs)); + + } + + public void destroySubcontext(String name) throws NamingException { + name = checkLdapUrlAsName(name); + m_ldapSvc.delEntry(this, name); + } + + public void destroySubcontext(Name name) throws NamingException { + m_ldapSvc.delEntry(this, name.toString()); + + } + + /** + * Naming Bind/Rename operations + * (javax.naming.Context, javax.naming.DirContext interface) + */ + + public void bind(String name, Object obj) throws NamingException { + name = checkLdapUrlAsName(name); + m_ldapSvc.addEntry(this, name.toString(), + ObjectMapper.objectToAttrSet(obj, name, this, /*attrs=*/null)); + } + + public void bind(Name name, Object obj) throws NamingException { + bind(name.toString(), obj); + } + + public void bind(String name, Object obj, Attributes attrs) throws NamingException { + name = checkLdapUrlAsName(name); + m_ldapSvc.addEntry(this, name.toString(), + ObjectMapper.objectToAttrSet(obj, name, this, attrs)); + } + + public void bind(Name name, Object obj, Attributes attrs) throws NamingException { + bind(name.toString(), obj, attrs); + } + + public void rebind(String name, Object obj) throws NamingException { + rebind(name, obj, /*attrs=*/null); + } + + public void rebind(Name name, Object obj) throws NamingException { + rebind(name.toString(), obj, null); + } + + public void rebind(String name, Object obj, Attributes attrs) throws NamingException { + name = checkLdapUrlAsName(name); + try { + bind(name, obj, attrs); + } + catch (NameAlreadyBoundException ex) { + unbind(name); + bind(name, obj, attrs); + } + } + + public void rebind(Name name, Object obj, Attributes attrs) throws NamingException { + rebind(name.toString(), obj, attrs); + } + + public void rename(String oldName, String newName) throws NamingException { + oldName = checkLdapUrlAsName(oldName); + LdapNameParser parser = LdapNameParser.getParser(); + Name oldNameObj = parser.parse(oldName); + Name newNameObj = parser.parse(newName); + rename(oldNameObj, newNameObj); + } + + public void rename(Name oldName, Name newName) throws NamingException { + // Can rename only RDN + if (newName.size() != oldName.size()) { + throw new InvalidNameException("Invalid name " + newName); + } + Name oldPrefix = oldName.getPrefix(oldName.size() -1); + Name newPrefix = newName.getPrefix(oldName.size() -1); + if (!newPrefix.equals(oldPrefix)) { + throw new InvalidNameException("Invalid name " + newName); + } + m_ldapSvc.changeRDN(this, oldName.toString(), newName.get(newName.size()-1)); + } + + public void unbind(String name) throws NamingException { + name = checkLdapUrlAsName(name); + // In ldap every entry is naming context + destroySubcontext(name); + } + + public void unbind(Name name) throws NamingException { + // In ldap every entry is naming context + destroySubcontext(name); + } + + /** + * List Operations (javax.naming.Context interface) + */ + + public NamingEnumeration list(String name) throws NamingException { + name = checkLdapUrlAsName(name); + return m_ldapSvc.listEntries(this, name, /*returnBindings=*/false); + } + + public NamingEnumeration list(Name name) throws NamingException { + return m_ldapSvc.listEntries(this, name.toString(), /*returnBindings=*/false); + } + + public NamingEnumeration listBindings(String name) throws NamingException { + name = checkLdapUrlAsName(name); + return m_ldapSvc.listEntries(this, name, /*returnBindings=*/true); + } + + public NamingEnumeration listBindings(Name name) throws NamingException { + return m_ldapSvc.listEntries(this, name.toString(), /*returnBindings=*/true); + } + + /** + * Lookup Operations (javax.naming.Context interface) + */ + + public Object lookup(String name) throws NamingException { + name = checkLdapUrlAsName(name); + return m_ldapSvc.lookup(this, name); + } + + public Object lookup(Name name) throws NamingException { + return m_ldapSvc.lookup(this, name.toString()); + } + + public Object lookupLink(String name) throws NamingException { + throw new OperationNotSupportedException(); + } + + public Object lookupLink(Name name) throws NamingException { + throw new OperationNotSupportedException(); + } + + + /** + * Schema Operations (javax.naming.DirContext interface) + */ + public DirContext getSchema(String name) throws NamingException { + name = checkLdapUrlAsName(name); + return m_ldapSvc.getSchema(name); + } + + public DirContext getSchema(Name name) throws NamingException { + return m_ldapSvc.getSchema(name.toString()); + } + + public DirContext getSchemaClassDefinition(String name) throws NamingException { + name = checkLdapUrlAsName(name); + throw new OperationNotSupportedException(); + } + + public DirContext getSchemaClassDefinition(Name name) throws NamingException { + return getSchemaClassDefinition(name.toString()); + } + + /** + * Naming Event methods javax.naming.event.EventDirContext interface) + */ + public void addNamingListener(String target, int scope, NamingListener l) throws NamingException { + EventService eventSvc = m_ldapSvc.getEventService(); + String filter = LdapService.DEFAULT_FILTER; + SearchControls ctls = new SearchControls(); + ctls.setSearchScope(scope); + eventSvc.addListener(this, target, filter, ctls, l); + } + + public void addNamingListener(Name target, int scope, NamingListener l) throws NamingException { + addNamingListener(target.toString(), scope, l); + } + + public void addNamingListener(String target, String filter, SearchControls ctls, NamingListener l)throws NamingException { + EventService eventSvc = m_ldapSvc.getEventService(); + eventSvc.addListener(this, target, filter, ctls, l); + + } + + public void addNamingListener(Name target, String filter, SearchControls ctls, NamingListener l)throws NamingException { + addNamingListener(target.toString(), filter, ctls, l); + } + + public void addNamingListener(String target, String filterExpr, Object[] filterArgs, SearchControls ctls, NamingListener l)throws NamingException { + EventService eventSvc = m_ldapSvc.getEventService(); + String filter = ProviderUtils.expandFilterExpr(filterExpr, filterArgs); + eventSvc.addListener(this, target, filter, ctls, l); + } + + public void addNamingListener(Name target, String filterExpr, Object[] filterArgs, SearchControls ctls, NamingListener l)throws NamingException { + addNamingListener(target.toString(), filterExpr, filterArgs, ctls, l); + } + + public void removeNamingListener(NamingListener l) throws NamingException { + EventService eventSvc = m_ldapSvc.getEventService(); + eventSvc.removeListener(l); + } + + public boolean targetMustExist() { + return true; + } + + /** + * LdapContext methods (javax.naming.ldap.LdapContext interface) + */ + public ExtendedResponse extendedOperation(ExtendedRequest req)throws NamingException { + throw new OperationNotSupportedException(); + } + + public Control[] getRequestControls() throws NamingException { + LDAPControl[] ldapCtls = m_searchCons.getServerControls(); + if (ldapCtls == null) { + return null; + } + Control[] ctls = new Control[ldapCtls.length]; + for (int i=0; i < ldapCtls.length; i++) { + ctls[i] = (Control) ldapCtls[i]; + } + return ctls; + } + + public void setRequestControls(Control[] reqCtls) throws NamingException { + LDAPControl[] ldapCtls = new LDAPControl[reqCtls.length]; + for (int i=0; i < reqCtls.length; i++) { + try { + ldapCtls[i] = (LDAPControl) reqCtls[i]; + } + catch (ClassCastException ex) { + throw new NamingException( + "Unsupported control type " + reqCtls[i].getClass().getName()); + } + } + + getSearchConstraints().setServerControls(ldapCtls); + } + + public Control[] getResponseControls() throws NamingException { + LDAPControl[] ldapCtls = m_ldapSvc.getConnection().getResponseControls(); + if (ldapCtls == null) { + return null; + } + // Parse raw controls + Control[] ctls = new Control[ldapCtls.length]; + for (int i=0; i < ldapCtls.length; i++) { + ctls[i] = NetscapeControlFactory.getControlInstance(ldapCtls[i]); + if (ctls[i] == null) { + throw new NamingException("Unsupported control " + ldapCtls[i].getID()); + } + } + return ctls; + } + + public LdapContext newInstance(Control[] reqCtls) throws NamingException { + LdapContextImpl clone = new LdapContextImpl(m_ctxDN, this); + // This controls are to be set on the the LDAPConnection + clone.m_ctxEnv.setProperty(ContextEnv.P_CONNECT_CTRLS, reqCtls); + return clone; + } + + public void reconnect() throws NamingException { + close(); + m_ldapSvc = new LdapService(this); + m_ldapSvc.connect(); + } +} diff --git a/mozilla/directory/java-sdk/ldapsp/com/netscape/jndi/ldap/LdapNameParser.java b/mozilla/directory/java-sdk/ldapsp/com/netscape/jndi/ldap/LdapNameParser.java new file mode 100644 index 00000000000..8dad5eb6e1a --- /dev/null +++ b/mozilla/directory/java-sdk/ldapsp/com/netscape/jndi/ldap/LdapNameParser.java @@ -0,0 +1,164 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * + * The contents of this file are subject to the Netscape Public License + * Version 1.0 (the "NPL"); you may not use this file except in + * compliance with the NPL. You may obtain a copy of the NPL at + * http://www.mozilla.org/NPL/ + * + * Software distributed under the NPL is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL + * for the specific language governing rights and limitations under the + * NPL. + * + * The Initial Developer of this code under the NPL is Netscape + * Communications Corporation. Portions created by Netscape are + * Copyright (C) 1999 Netscape Communications Corporation. All Rights + * Reserved. + */ +package com.netscape.jndi.ldap; + +import java.util.Hashtable; +import java.util.Properties; +import javax.naming.*; + +class LdapNameParser implements NameParser { + + private static LdapNameParser m_parser; + + // A table with compound name syntax properties + static Properties nameSyntax; + static { + nameSyntax = new Properties(); + nameSyntax.put("jndi.syntax.direction", "right_to_left"); + nameSyntax.put("jndi.syntax.separator", ","); + nameSyntax.put("jndi.syntax.ignorecase", "true"); + nameSyntax.put("jndi.syntax.escape", "\\"); + nameSyntax.put("jndi.syntax.beginquote", "\""); + nameSyntax.put("jndi.syntax.trimblanks", "true"); + nameSyntax.put("jndi.syntax.separator.ava", "+"); + nameSyntax.put("jndi.syntax.separator.typeval", "="); + } + + // Can not be constructed + private LdapNameParser() {} + + // Shared instance must be used + public static LdapNameParser getParser() { + if (m_parser == null) { + m_parser = new LdapNameParser(); + } + return m_parser; + } + + // implements parse + public Name parse(String name) throws NamingException { + return new CompoundName(name, nameSyntax); + } + + /** + * A convenience method for extracting RDN + * @return RDN for the DN + * @param dn Ldap Distinguished name + * @throw NamingException Name parsing failed + */ + static String getRDN(String dn) throws NamingException { + Name parsedName = getParser().parse(dn); + if (parsedName.size() > 0) { + return parsedName.get(parsedName.size()-1); + } + return ""; + } + + /** + * A convenience method for extracting attribute name from name=val + * @return attribute name or null if "=" not found + * @param nameEqVal name=value + */ + static String getAttrName(String nameEqVal) throws NamingException { + int eq = nameEqVal.indexOf("="); + return (eq >= 0) ? nameEqVal.substring(0,eq).trim() : null; + } + + /** + * A convenience method for extracting attribute value from name=val + * @return attribute value or null if "=" not found + * @param nameEqVal name=value + */ + static String getAttrValue(String nameEqVal) throws NamingException { + int eq = nameEqVal.indexOf("="); + return (eq >= 0) ? nameEqVal.substring(eq+1).trim() : null; + } + + /** + * A convenience method for extracting relative name from the ancestor context + * @return name relative to an ancestor context + * @param ctx ancestor context distinguished name + * @param entry distinguished name + */ + static String getRelativeName(String ctx, String entry) throws NamingException{ + if (entry==null) { + entry = ""; + } + Name contextName = getParser().parse(ctx); + Name entryName = getParser().parse(entry); + if (!entryName.startsWith(contextName)) { + throw new NamingException("Name not in context"); + } + return entryName.getSuffix(contextName.size()).toString(); + } + + /** + * A convenience method for extracting relative name from the ancestor context + * @return name relative to an ancestor context + * @param ctx ancestor context distinguished name + * @param entry distinguished name + */ + static String getRelativeName(Name contextName, String entry) throws NamingException{ + if (entry==null) { + entry = ""; + } + Name entryName = getParser().parse(entry); + if (!entryName.startsWith(contextName)) { + throw new NamingException("Name not in context"); + } + return entryName.getSuffix(contextName.size()).toString(); + } + + /** + * Unit test + */ + public static void main0(String[] args) { + if (args.length != 1) { + System.out.println("Usage LdapNameParser "); + System.exit(1); + } + try { + Name name = getParser().parse(args[0]); + System.out.println(name); + System.out.println("rdn: " + getParser().getRDN(args[0])); + name.add("attr=val"); + System.out.println(name); + System.out.println(name.get(0)); + System.out.println("in name=val name:<" + getAttrName("name=val ") + + "> val:<" + getAttrValue("name=val ") + ">"); + } + catch (Exception e) { + System.err.println(e); + } + } + + // Relative name test + public static void main(String[] args) { + if (args.length != 2) { + System.out.println("Usage LdapNameParser "); + System.exit(1); + } + try { + System.out.println("relativeName: " + getParser().getRelativeName(args[0], args[1])); + } + catch (Exception e) { + System.err.println(e); + } + } + +} diff --git a/mozilla/directory/java-sdk/ldapsp/com/netscape/jndi/ldap/LdapReferralException.java b/mozilla/directory/java-sdk/ldapsp/com/netscape/jndi/ldap/LdapReferralException.java new file mode 100644 index 00000000000..406641245e1 --- /dev/null +++ b/mozilla/directory/java-sdk/ldapsp/com/netscape/jndi/ldap/LdapReferralException.java @@ -0,0 +1,80 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * + * The contents of this file are subject to the Netscape Public License + * Version 1.0 (the "NPL"); you may not use this file except in + * compliance with the NPL. You may obtain a copy of the NPL at + * http://www.mozilla.org/NPL/ + * + * Software distributed under the NPL is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL + * for the specific language governing rights and limitations under the + * NPL. + * + * The Initial Developer of this code under the NPL is Netscape + * Communications Corporation. Portions created by Netscape are + * Copyright (C) 1999 Netscape Communications Corporation. All Rights + * Reserved. + */ +package com.netscape.jndi.ldap; + +import javax.naming.*; +import javax.naming.directory.*; +import javax.naming.ldap.*; + +import netscape.ldap.*; + +import java.util.*; + +/** + * A wrapper for the ldapjdk LDAPReferralException + * + */ +class LdapReferralException extends javax.naming.ldap.LdapReferralException { + + LDAPReferralException m_ldapEx; + LdapContextImpl m_srcCtx; + int m_referralIdx = 0; + + public LdapReferralException(LdapContextImpl srcCtx, LDAPReferralException ldapEx) { + m_srcCtx = srcCtx; + m_ldapEx = ldapEx; + } + + public Object getReferralInfo() { + return m_ldapEx.getURLs(); + } + + public Context getReferralContext() throws NamingException{ + Hashtable env = m_srcCtx.getEnv().getAllProperties(); + env.put(ContextEnv.P_PROVIDER_URL, m_ldapEx.getURLs()[m_referralIdx]); + return new LdapContextImpl(env); + } + + public Context getReferralContext(Hashtable env) throws NamingException{ + return getReferralContext(env, null); + } + + public Context getReferralContext(Hashtable env, Control[] reqCtls) throws NamingException{ + env.put(ContextEnv.P_PROVIDER_URL, m_ldapEx.getURLs()[m_referralIdx]); + if (reqCtls != null) { + env.put(ContextEnv.P_CONNECT_CTRLS, reqCtls); + } + return new LdapContextImpl(env); + } + + + /** + * Skip the referral to be processed + */ + public boolean skipReferral() { + int maxIdx = m_ldapEx.getURLs().length - 1; + if (m_referralIdx < maxIdx) { + m_referralIdx++; + return true; + } + return false; + } + + public void retryReferral() { + } +} diff --git a/mozilla/directory/java-sdk/ldapsp/com/netscape/jndi/ldap/LdapService.java b/mozilla/directory/java-sdk/ldapsp/com/netscape/jndi/ldap/LdapService.java new file mode 100644 index 00000000000..659458e67f6 --- /dev/null +++ b/mozilla/directory/java-sdk/ldapsp/com/netscape/jndi/ldap/LdapService.java @@ -0,0 +1,515 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * + * The contents of this file are subject to the Netscape Public License + * Version 1.0 (the "NPL"); you may not use this file except in + * compliance with the NPL. You may obtain a copy of the NPL at + * http://www.mozilla.org/NPL/ + * + * Software distributed under the NPL is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL + * for the specific language governing rights and limitations under the + * NPL. + * + * The Initial Developer of this code under the NPL is Netscape + * Communications Corporation. Portions created by Netscape are + * Copyright (C) 1999 Netscape Communications Corporation. All Rights + * Reserved. + */ +package com.netscape.jndi.ldap; + +import javax.naming.*; +import javax.naming.directory.*; +import javax.naming.ldap.*; +import netscape.ldap.*; +import java.util.*; +import com.netscape.jndi.ldap.common.*; +import com.netscape.jndi.ldap.schema.SchemaRoot; + +/** + * Ldap Service encapsulates a Ldap connection and Ldap operations over the + * connection. The connection is established in a lazy manner, first time a + * Ldap operation is initiated. A Ldap Service object is shared by multiple + * contexts. The object maintains a reference counter which is incremented + * when a context is cloned, and decremeneted when a context is closed. The + * associated Ldap Connection is relased when the reference counter reaches + * zero. + * + * LDAPsearchConstraints are always read from a context, because ldap service + * is a shared object and each context may request different search constraints. + * + */ +class LdapService { + + public static final String DEFAULT_FILTER = "(objectclass=*)"; + public static final int DEFAULT_SCOPE = LDAPv3.SCOPE_SUB; + public static final String DEFAULT_HOST = "localhost"; + public static final int DEFAULT_PORT = LDAPv2.DEFAULT_PORT; + public static final int DEFAULT_SSL_PORT = 636; + + private LdapContextImpl m_initialCtx; + private LDAPConnection m_ld; + private EventService m_eventSvc; + + /** + * The number of contexts that are currently sharing this LdapService. + * Essentially, a reference counter. Incremented in the LdapContextImpl + * copy constructor. Decremented in the LdapService.disconnect(). + * When the count reaches zero, the LDAPConnection is released. + */ + private int m_clientCount; + + + public LdapService(LdapContextImpl initialCtx) { + m_initialCtx = initialCtx; + m_ld = new LDAPConnection(); + m_clientCount = 1; + } + + LDAPConnection getConnection() { + return m_ld; + } + + /** + * Connect to the server and send bind request to authenticate the user + */ + void connect() throws NamingException{ + + if (m_ld.isConnected()) { + return; //already connected + } + + LDAPUrl url = m_initialCtx.m_ctxEnv.getDirectoryServerURL(); + String host = (url != null) ? url.getHost() : DEFAULT_HOST; + int port = (url != null) ? url.getPort() : DEFAULT_PORT; + String user = m_initialCtx.m_ctxEnv.getUserDN(); + String passwd = m_initialCtx.m_ctxEnv.getUserPassword(); + String socketFactory = m_initialCtx.m_ctxEnv.getSocketFactory(); + Object cipherSuite = m_initialCtx.m_ctxEnv.getCipherSuite(); + int ldapVersion = m_initialCtx.m_ctxEnv.getLdapVersion(); + boolean isSSLEnabled = m_initialCtx.m_ctxEnv.isSSLEnabled(); + boolean useSimpleAuth= m_initialCtx.m_ctxEnv.useSimpleAuth(); + LDAPControl[] ldCtrls= m_initialCtx.m_ctxEnv.getConnectControls(); + + // Set default ssl port if DS not specifed + if (isSSLEnabled && url == null) { + port = DEFAULT_SSL_PORT; + } + + // SSL is enabled but no Socket factory specified. Use the + // ldapjdk default one + if (isSSLEnabled && socketFactory == null) { + m_ld = new LDAPConnection(new LDAPSSLSocketFactory()); + } + + // Set the socket factory and cipher suite if cpecified + if (socketFactory != null) { + try { + LDAPSSLSocketFactory sf = null; + if (cipherSuite != null) { + Debug.println(1, "CIPHERS=" + cipherSuite); + sf = new LDAPSSLSocketFactory(socketFactory, cipherSuite); + } + else { + sf = new LDAPSSLSocketFactory(socketFactory); + } + m_ld = new LDAPConnection(sf); + Debug.println(1, "SSL CONNECTION"); + } + catch (Exception e) { + throw new IllegalArgumentException( + "Illegal value for java.naming.ldap.factory.socket: " + e); + } + } + + try { + if (ldCtrls != null) { + m_ld.setOption(LDAPv3.SERVERCONTROLS, ldCtrls); + } + m_ld.connect(ldapVersion, host, port, user, passwd); + } + catch (Exception e) { + throw ExceptionMapper.getNamingException(e); + } + } + + protected void finalize() { + try { + m_ld.disconnect(); + } + catch (Exception e) {} + } + + boolean isConnected() { + return (m_ld.isConnected()); + } + + + /** + * Physically disconect only if the client count is zero + */ + void disconnect() { + try { + if (m_clientCount > 0) { + m_clientCount--; + } + if (m_clientCount == 0 && isConnected()) { + m_ld.disconnect(); + } + } + catch (Exception e) {} + } + + /** + * Increment client count + */ + void incrementClientCount() { + m_clientCount++; + } + + /** + * LDAP search operation + */ + NamingEnumeration search (LdapContextImpl ctx, String name, String filter, String[] attrs, SearchControls jndiCtrls) throws NamingException{ + Debug.println(1, "SEARCH"); + String base = ctx.getDN(); + int scope = LDAPConnection.SCOPE_SUB; + LDAPSearchConstraints cons=ctx.getSearchConstraints(); + boolean returnObjs = false; + + connect(); // Lazy Connect + + // Create DN by appending the name to the current context + if (name.length() > 0) { + if (base.length() > 0) { + base = name + "," + base; + } + else { + base = name; + } + } + + // Map JNDI SearchControls to ldapjdk LDAPSearchConstraints + if (jndiCtrls != null) { + int maxResults = (int)jndiCtrls.getCountLimit(); + int timeLimitInMsec = jndiCtrls.getTimeLimit(); + // Convert timeLimit in msec to sec + int timeLimit = timeLimitInMsec/1000; + if (timeLimitInMsec > 0 && timeLimitInMsec < 1000) { + timeLimit = 1; //sec + } + + // Clone cons if maxResults or timeLimit is different from the default one + if (cons.getServerTimeLimit() != timeLimit || cons.getMaxResults() != maxResults) { + cons = (LDAPSearchConstraints)cons.clone(); + cons.setMaxResults(maxResults); + cons.setServerTimeLimit(timeLimit); + } + + // TODO The concept of links is not well described in JNDI docs. + // We can only specify dereferencing of Aliases, but Links and + // Aliases are not the same; IGNORE jndiCtrls.getDerefLinkFlag() + + // Attributes to return + attrs = jndiCtrls.getReturningAttributes(); + if (attrs != null && attrs.length==0) { + //return no attributes + attrs = new String[] { "1.1" }; + + // TODO check whether ldap compare operation should be performed + // That's the case if: (1) scope is OBJECT_SCOPE, (2) no attrs + // are requested to return (3) filter has the form (name==value) + // where no wildcards ()&|!=~><* are used. + + } + + // Search scope + scope = ProviderUtils.jndiSearchScopeToLdap(jndiCtrls.getSearchScope()); + + // return obj flag + returnObjs = jndiCtrls.getReturningObjFlag(); + } + + try { + // Perform Search + boolean attrsOnly = ctx.m_ctxEnv.getAttrsOnlyFlag(); + LDAPSearchResults res = m_ld.search( base, scope, filter, attrs, attrsOnly, cons ); + return new SearchResultEnum(res, returnObjs, ctx); + } + catch (LDAPReferralException e) { + throw new LdapReferralException(ctx, e); + } + catch (LDAPException e) { + throw ExceptionMapper.getNamingException(e); + } + } + + /** + * List child entries using LDAP lookup operation + */ + NamingEnumeration listEntries(LdapContextImpl ctx, String name, boolean returnBindings) throws NamingException{ + Debug.println(1, "LIST " + (returnBindings ? "BINDINGS" : "")); + String base = ctx.getDN(); + + connect(); // Lazy Connect + + // Create DN by appending the name to the current context + if (name.length() > 0) { + if (base.length() > 0) { + base = name + "," + base; + } + else { + base = name; + } + } + + try { + // Perform One Level Search + String[] attrs = null; // return all attributes if Bindings are requested + if (!returnBindings) { // for ClassNamePairs + attrs = new String[]{"javaclassname"}; //attr names must be in lowercase + } + LDAPSearchConstraints cons=ctx.getSearchConstraints(); + LDAPSearchResults res = m_ld.search( base, LDAPConnection.SCOPE_ONE, DEFAULT_FILTER, attrs, /*attrsOnly=*/false, cons); + if (returnBindings) { + return new BindingEnum(res, ctx); + } + else { + return new NameClassPairEnum(res, ctx); + } + } + catch (LDAPReferralException e) { + throw new LdapReferralException(ctx, e); + } + catch (LDAPException e) { + throw ExceptionMapper.getNamingException(e); + } + } + + /** + * Lookup an entry using LDAP search operation + */ + Object lookup(LdapContextImpl ctx, String name) throws NamingException{ + Debug.println(1, "LOOKUP"); + String base = ctx.getDN(); + + connect(); // Lazy Connect + + // Create DN by appending the name to the current context + if (name.length() > 0) { + if (base.length() > 0) { + base = name + "," + base; + } + else { + base = name; + } + } + + try { + // Perform Base Search + String[] attrs = null; // return all attrs + LDAPSearchConstraints cons=ctx.getSearchConstraints(); + LDAPSearchResults res = m_ld.search( base, LDAPConnection.SCOPE_BASE, DEFAULT_FILTER, attrs, /*attrsOnly=*/false, cons); + if (res.hasMoreElements()) { + LDAPEntry entry = res.next(); + return ObjectMapper.entryToObject(entry, ctx); + } + return null; // should never get here + + } + catch (LDAPReferralException e) { + throw new LdapReferralException(ctx, e); + } + catch (LDAPException e) { + throw ExceptionMapper.getNamingException(e); + } + } + + /** + * Read LDAP entry attributes + */ + Attributes readAttrs (LdapContextImpl ctx, String name, String[] attrs) throws NamingException{ + Debug.println(1, "READ ATTRS"); + String base = ctx.getDN(); + int scope = LDAPConnection.SCOPE_BASE; + + connect(); // Lazy Connect + + // Create DN by appending the name to the current context + if (name.length() > 0) { + if (base.length() > 0) { + base = name + "," + base; + } + else { + base = name; + } + } + + try { + // Perform Search + LDAPSearchConstraints cons=ctx.getSearchConstraints(); + LDAPSearchResults res = m_ld.search(base, scope, DEFAULT_FILTER, attrs, /*attrsOnly=*/false, cons); + while (res.hasMoreElements()) { + LDAPEntry entry = res.next(); + return new AttributesImpl(entry.getAttributeSet(), + ctx.m_ctxEnv.getUserDefBinaryAttrs()); + } + return null; + } + catch (LDAPReferralException e) { + throw new LdapReferralException(ctx, e); + } + catch (LDAPException e) { + throw ExceptionMapper.getNamingException(e); + } + } + + /** + * Modify LDAP entry attributes + */ + void modifyEntry(LdapContextImpl ctx, String name, LDAPModificationSet mods) throws NamingException{ + Debug.println(1, "MODIFY"); + String base = ctx.getDN(); + + if (mods.size() == 0) { + return; + } + connect(); // Lazy Connect + + // Create DN by appending the name to the current context + if (name.length() > 0) { + if (base.length() > 0) { + base = name + "," + base; + } + else { + base = name; + } + } + + try { + // Perform Modify + m_ld.modify(base, mods); + } + catch (LDAPReferralException e) { + throw new LdapReferralException(ctx, e); + } + catch (LDAPException e) { + throw ExceptionMapper.getNamingException(e); + } + } + + /** + * Create a new LDAP entry + */ + LdapContextImpl addEntry(LdapContextImpl ctx, String name, LDAPAttributeSet attrs) throws NamingException{ + Debug.println(1, "ADD"); + String dn = ctx.getDN(); + + connect(); // Lazy Connect + + // Create DN by appending the name to the current context + if (name.length() == 0) { + throw new IllegalArgumentException("Name can not be empty"); + } + if (dn.length() > 0) { + dn = name + "," + dn; + } + else { + dn = name; + } + + try { + // Add a new entry + m_ld.add(new LDAPEntry(dn, attrs)); + return new LdapContextImpl(dn, ctx); + } + catch (LDAPReferralException e) { + throw new LdapReferralException(ctx, e); + } + catch (LDAPException e) { + throw ExceptionMapper.getNamingException(e); + } + } + + /** + * Delete a LDAP entry + */ + void delEntry(LdapContextImpl ctx, String name) throws NamingException{ + Debug.println(1, "DELETE"); + String dn = ctx.getDN(); + + connect(); // Lazy Connect + + // Create DN by appending the name to the current context + if (name.length() == 0) { + throw new IllegalArgumentException("Name can not be empty"); + } + if (dn.length() > 0) { + dn = name + "," + dn; + } + else { + dn = name; + } + + try { + // Perform Delete + m_ld.delete(dn); + } + catch (LDAPReferralException e) { + throw new LdapReferralException(ctx, e); + } + catch (LDAPException e) { + throw ExceptionMapper.getNamingException(e); + } + } + + /** + * Chanage RDN for a LDAP entry + */ + void changeRDN(LdapContextImpl ctx, String name, String newRDN) throws NamingException{ + Debug.println(1, "RENAME"); + String dn = ctx.getDN(); + + connect(); // Lazy Connect + + // Create DN by appending the name to the current context + if (name.length() == 0 || newRDN.length() == 0) { + throw new IllegalArgumentException("Name can not be empty"); + } + if (dn.length() > 0) { + dn = name + "," + dn; + } + else { + dn = name; + } + + try { + // Rename + m_ld.rename(dn, newRDN, ctx.m_ctxEnv.getDeleteOldRDNFlag()); + } + catch (LDAPReferralException e) { + throw new LdapReferralException(ctx, e); + } + catch (LDAPException e) { + throw ExceptionMapper.getNamingException(e); + } + } + + /** + * Schema Operations + */ + DirContext getSchema(String name) throws NamingException { + connect(); // Lazy Connect + return new SchemaRoot(m_ld); + } + + /** + * Return the event service + */ + EventService getEventService() throws NamingException { + connect(); // Lazy Connect + if (m_eventSvc == null) { + m_eventSvc = new EventService(this); + } + return m_eventSvc; + } +} diff --git a/mozilla/directory/java-sdk/ldapsp/com/netscape/jndi/ldap/NameClassPairEnum.java b/mozilla/directory/java-sdk/ldapsp/com/netscape/jndi/ldap/NameClassPairEnum.java new file mode 100644 index 00000000000..cf96dd3d228 --- /dev/null +++ b/mozilla/directory/java-sdk/ldapsp/com/netscape/jndi/ldap/NameClassPairEnum.java @@ -0,0 +1,95 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * + * The contents of this file are subject to the Netscape Public License + * Version 1.0 (the "NPL"); you may not use this file except in + * compliance with the NPL. You may obtain a copy of the NPL at + * http://www.mozilla.org/NPL/ + * + * Software distributed under the NPL is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL + * for the specific language governing rights and limitations under the + * NPL. + * + * The Initial Developer of this code under the NPL is Netscape + * Communications Corporation. Portions created by Netscape are + * Copyright (C) 1999 Netscape Communications Corporation. All Rights + * Reserved. + */ +package com.netscape.jndi.ldap; + +import javax.naming.*; +import javax.naming.directory.*; +import javax.naming.ldap.*; +import com.netscape.jndi.ldap.common.ExceptionMapper; + +import netscape.ldap.*; + +import java.util.*; + +/** + * Wrapper for the LDAPSearchResult. Convert each LDAPEntry entry into + * a JNDI NameClassPair. NameClassPairs are accessed through the NamingEnumeration + * interface + */ +class NameClassPairEnum implements NamingEnumeration { + + LDAPSearchResults m_res; + LdapContextImpl m_ctx; + Name m_ctxName; + + public NameClassPairEnum(LDAPSearchResults res, LdapContextImpl ctx) throws NamingException{ + m_res = res; + m_ctx = ctx; + try { + m_ctxName = LdapNameParser.getParser().parse(m_ctx.m_ctxDN); + } + catch ( NamingException e ) { + throw ExceptionMapper.getNamingException(e); + } + } + + public Object next() throws NamingException{ + try { + LDAPEntry entry = m_res.next(); + String name = LdapNameParser.getRelativeName(m_ctxName, entry.getDN()); + String className = ObjectMapper.getClassName(entry); + return new NameClassPair(name, className, /*isRelative=*/true); + + } + catch (LDAPReferralException e) { + throw new LdapReferralException(m_ctx, e); + } + catch ( LDAPException e ) { + throw ExceptionMapper.getNamingException(e); + } + } + + public Object nextElement() { + try { + return next(); + } + catch ( Exception e ) { + System.err.println( "Error in NameClassPairEnum.nextElement(): " + e.toString() ); + e.printStackTrace(System.err); + return null; + } + } + + public boolean hasMore() throws NamingException{ + return m_res.hasMoreElements(); + } + + public boolean hasMoreElements() { + return m_res.hasMoreElements(); + } + + public void close() throws NamingException{ + try { + m_ctx.m_ldapSvc.getConnection().abandon(m_res); + } + catch (LDAPException e) { + throw ExceptionMapper.getNamingException(e); + } + } +} + diff --git a/mozilla/directory/java-sdk/ldapsp/com/netscape/jndi/ldap/ObjectMapper.java b/mozilla/directory/java-sdk/ldapsp/com/netscape/jndi/ldap/ObjectMapper.java new file mode 100644 index 00000000000..50257f390c7 --- /dev/null +++ b/mozilla/directory/java-sdk/ldapsp/com/netscape/jndi/ldap/ObjectMapper.java @@ -0,0 +1,473 @@ +package com.netscape.jndi.ldap; + +import javax.naming.*; +import javax.naming.directory.*; +import javax.naming.spi.*; +import netscape.ldap.*; +import netscape.ldap.util.*; +import java.io.*; +import java.util.*; +import com.netscape.jndi.ldap.common.Debug; + +/** + * Class used to map Java objects to ldap entries back and forth +*/ +public class ObjectMapper { + + /** + * Schema object classes for mapping java objects to ldap entries + */ + static final String OC_JAVAOBJECT = "javaObject"; // abstract oc + static final String OC_SERIALOBJ = "javaSerializedObject";//aux oc + static final String OC_REFERENCE = "javaNamingReference"; //aux oc + static final String OC_REMOTEOBJ = "javaRemoteObject"; //aux oc + static final String OC_CONTAINER = "javaContainer"; //structural oc + + /** + * Schema attributes for mapping java objects to ldap entries + */ + static final String AT_CLASSNAME = "javaClassName"; //required + static final String AT_CODEBASE = "javaCodeBase"; //optional + static final String AT_SERIALDATA = "javaSerializedData"; //required + static final String AT_REFADDR = "javaReferenceAddress"; //optional + static final String AT_OBJFACTORY = "javaFactory"; //optional + static final String AT_REMOTELOC = "javaRemoteLocation"; //required + + //Default Object class for NameClassPair + static final String DEFAULT_OBJCLASS = "javax.naming.directory.DirContext"; + + static Object entryToObject(LDAPEntry entry, LdapContextImpl ctx) throws NamingException { + Object obj = entryToObject(entry); + if (obj == null) { + obj = new LdapContextImpl(entry.getDN(), ctx); + } + + // SP is required to contact the NamingManager first to obtain an object + try { + String relName = LdapNameParser.getRelativeName(ctx.m_ctxDN, entry.getDN()); + Name nameObj = LdapNameParser.getParser().parse(relName); + obj = NamingManager.getObjectInstance(obj, nameObj, ctx, ctx.getEnvironment()); + } + catch (Exception ex) { + if (ex instanceof NamingException) { + throw (NamingException)ex; + } + NamingException nameEx = new NamingException("NamingManager.getObjectInstance failed"); + nameEx.setRootCause(ex); + throw nameEx; + } + + return obj; + + } + + /** + * Convert a Ldap entry into a java object + */ + static Object entryToObject(LDAPEntry entry) throws NamingException { + + try { + LDAPAttributeSet attrs = entry.getAttributeSet(); + + // TODO javaCodeBase to be processed + + LDAPAttribute attr = null; + if ((attr = attrs.getAttribute(AT_SERIALDATA)) != null) { + byte[] serdata = (byte[])attr.getByteValues().nextElement(); + return deserializeObject(serdata); + } + + // Reference + else if ((attr = attrs.getAttribute(AT_REFADDR)) != null) { + return decodeRefObj(attrs); + } + + // RMI Object + else if ((attr = attrs.getAttribute(AT_REMOTELOC)) != null) { + String rmiURL = (String)attr.getByteValues().nextElement(); + return java.rmi.Naming.lookup(rmiURL); + } + + return null; + } + catch (Exception ex) { + if (ex instanceof NamingException) { + throw (NamingException)ex; + } + NamingException nameEx = new NamingException("NamingManager.getStateToStore failed"); + nameEx.setRootCause(ex); + throw nameEx; + } + } + + /** + * Get the className for NameClassPair from an entry + */ + static String getClassName(LDAPEntry entry) { + LDAPAttributeSet attrs = entry.getAttributeSet(); + LDAPAttribute attrClass = attrs.getAttribute(AT_CLASSNAME); + if (attrClass != null) { + String className = (String)attrClass.getStringValues().nextElement(); + return className; + } + return DEFAULT_OBJCLASS; + } + + + /** + * Convert a java object with an optional set of attributes into a LDAP entry + */ + static LDAPAttributeSet objectToAttrSet(Object obj, String name, LdapContextImpl ctx, Attributes attrs) throws NamingException { + + // SP is required to contact the NamingManager first to obtain a state object + try { + Name nameObj = LdapNameParser.getParser().parse(name); + obj = NamingManager.getStateToBind(obj, nameObj, ctx, ctx.getEnvironment()); + } + catch (Exception ex) { + if (ex instanceof NamingException) { + throw (NamingException)ex; + } + NamingException nameEx = new NamingException("NamingManager.getStateToStore failed"); + nameEx.setRootCause(ex); + throw nameEx; + } + + if (attrs == null) { + attrs = new BasicAttributes(/*ignoreCase=*/true); + } + + Attribute objectclass = attrs.get("objectClass"); + if (objectclass == null) { + objectclass = new BasicAttribute("objectClass", "top"); + attrs.put(objectclass); + } + + // Root object class + objectclass.add(OC_JAVAOBJECT); + + if (obj instanceof Reference) { + objectclass.add(OC_REFERENCE); + char delimChar = ctx.m_ctxEnv.getRefSeparator(); + attrs = encodeRefObj(delimChar, (Reference)obj, attrs); + } + + else if (obj instanceof Referenceable) { + objectclass.add(OC_REFERENCE); + char delimChar = ctx.m_ctxEnv.getRefSeparator(); + attrs = encodeRefObj(delimChar, ((Referenceable)obj).getReference(), attrs); + } + + else if (obj instanceof java.rmi.Remote) { + objectclass.add(OC_REMOTEOBJ); + attrs = encodeRMIObj((java.rmi.Remote)obj, attrs); + } + + else if (obj instanceof Serializable) { + objectclass.add(OC_SERIALOBJ); + attrs = encodeSerialObj((Serializable)obj , attrs); + } + + else if (obj instanceof DirContext) { + attrs = encodeDirCtxObj((DirContext)obj , attrs); + } + + else { + throw new NamingException("Can not bind object of type " + + obj.getClass().getName()); + } + + return AttributesImpl.jndiAttrsToLdapAttrSet(attrs); + } + + /** + * Deserialized object, create object from a stream of bytes + */ + private static Object deserializeObject(byte[] byteBuf) throws NamingException { + + ByteArrayInputStream bis = null; + ObjectInputStream objis = null; + + try { + bis = new ByteArrayInputStream(byteBuf); + objis = new ObjectInputStream(bis); + return objis.readObject(); + } + catch(Exception ex) { + NamingException nameEx = new NamingException("Failed to deserialize object"); + nameEx.setRootCause(ex); + throw nameEx; + } + finally { + try { + if (objis != null) { + objis.close(); + } + if (bis != null) { + bis.close(); + } + } + catch (Exception ex) {} + } + } + + + /** + * Serialize object, convert object to a stream of bytes + */ + private static byte[] serializeObject(Object obj) throws NamingException { + + ByteArrayOutputStream bos = null; + ObjectOutputStream objos = null; + + try { + bos = new ByteArrayOutputStream(); + objos = new ObjectOutputStream(bos); + objos.writeObject(obj); + objos.flush(); + return bos.toByteArray(); + } + catch(Exception ex) { + NamingException nameEx = new NamingException("Failed to serialize object"); + nameEx.setRootCause(ex); + throw nameEx; + } + finally { + try { + if (objos != null) { + objos.close(); + } + if (bos != null) { + bos.close(); + } + } + catch (Exception ex) {} + } + } + + /** + * Decode Jndi Reference Object + */ + private static Reference decodeRefObj(LDAPAttributeSet attrs) throws NamingException { + try { + + LDAPAttribute attr = null; + String className=null, factory = null, factoryLoc = null; + if ((attr = attrs.getAttribute(AT_CLASSNAME)) == null ) { + throw new NamingException("Bad Reference encoding, no attribute " + AT_CLASSNAME); + } + className = (String)attr.getStringValues().nextElement(); + + if ((attr = attrs.getAttribute(AT_OBJFACTORY)) != null ) { + factory = (String)attr.getStringValues().nextElement(); + } + if ((attr = attrs.getAttribute(AT_CODEBASE)) != null ) { + factoryLoc = (String)attr.getStringValues().nextElement(); + } + + Reference ref = new Reference(className, factory, factoryLoc); + + if ((attr = attrs.getAttribute(AT_REFADDR)) == null ) { + return ref; // no refAddr + } + + for (Enumeration e = attr.getStringValues(); e.hasMoreElements();) { + decodeRefAddr((String)e.nextElement(), ref); + } + + return ref; + } + catch (Exception ex) { + if (ex instanceof NamingException) { + throw (NamingException)ex; + } + NamingException nameEx = new NamingException("NamingManager.getStateToStore failed"); + nameEx.setRootCause(ex); + throw nameEx; + } + } + + /** + * Decode RefAddr according to the + * StringRefAddr are encoded as #posNo#refType#refValue where posNo is the + * refAddr position (index) inside a rerference. BynaryRefAddr is encoded + * as #posNo#refType##data where data is the base64 encoding of the serialized + * form of the entire BinaryRefAddr instance. + */ + private static void decodeRefAddr(String enc, Reference ref) throws NamingException{ + + if (enc.length() == 0) { + throw new NamingException("Bad Reference encoding, empty refAddr"); + } + + String delimChar = enc.substring(0,1); + + StringTokenizer tok = new StringTokenizer(enc, delimChar); + + int tokCount = tok.countTokens(); + if (tokCount != 3 && tokCount != 4) { + Debug.println(3, "enc=" + enc + " delimChar="+delimChar + " tokCount="+tokCount); + throw new NamingException("Bad Reference encoding"); + } + + String type = null; + int posn = -1; + for (int i = 0; i < tokCount; i++) { + String s = tok.nextToken(); + + if (i == 0) { // position + try { + posn = Integer.parseInt(s); + } + catch (Exception e) { + throw new NamingException("Bad Reference encoding, refAddr position not an initger"); + } + } + + else if (i == 1) { // type + if (s.length() == 0) { + throw new NamingException("Bad Reference encoding, empty refAddr type"); + } + type = s; + } + + else if (i == 2) { // value for StringRefAddr, empty for BinaryRefAddr + if (s.length() == 0 && tokCount != 4) { // should be empty for binary refs + throw new NamingException("Bad Reference encoding, empty refAddr string value"); + } + ref.add(posn, new StringRefAddr(type, s)); + } + + else { // base64 encoding of the serialized BinaryRefAddr + if (s.length() == 0) { + throw new NamingException("Bad Reference encoding, empty refAddr binary value"); + } + MimeBase64Decoder base64Dec = new MimeBase64Decoder(); + ByteBuf in = new ByteBuf(s), out = new ByteBuf(); + base64Dec.translate(in, out); + base64Dec.eof(out); + ref.add(posn, (RefAddr) deserializeObject(out.toBytes())); + } + } + } + + + /** + * Encode Jndi Reference Object + */ + private static Attributes encodeRefObj(char delimChar, Reference ref, Attributes attrs) throws NamingException { + + if (ref.getClassName() != null) { + attrs.put(new BasicAttribute(AT_CLASSNAME, ref.getClassName())); + } + if (ref.getFactoryClassName() != null) { + attrs.put(new BasicAttribute(AT_OBJFACTORY, ref.getFactoryClassName())); + } + if (ref.getFactoryClassLocation() != null) { + attrs.put(new BasicAttribute(AT_CODEBASE, ref.getFactoryClassLocation())); + } + + if(ref.size() > 0) { + BasicAttribute refAttr = new BasicAttribute(AT_REFADDR); + for(int i = 0; i < ref.size(); i++) { + refAttr.add(encodeRefAddr(delimChar, i, ref.get(i))); + } + attrs.put(refAttr); + } + return attrs; + } + + /** + * Encode RefAddr according to the + * StringRefAddr are encoded as #posNo#refType#refValue where posNo is the + * refAddr position (index) inside a rerference. BynaryRefAddr is encoded + * as #posNo#refType##data where data is the base64 encoding of the serialized + * form of the entire BinaryRefAddr instance. + + */ + private static String encodeRefAddr(char delimChar, int idx, RefAddr refAddr) throws NamingException{ + + if(refAddr instanceof StringRefAddr) { + + String content = (String) refAddr.getContent(); + if (content != null && content.length() > 0 && content.charAt(0) == delimChar) { + throw new NamingException( + "Can not encode StringRefAddr, value starts with the delimiter character " + delimChar); + } + return delimChar + idx + + delimChar + refAddr.getType() + + delimChar + content; + } + + else { // BinaryRefAdd + + byte[] serialRefAddr = serializeObject(refAddr); + MimeBase64Encoder base64 = new MimeBase64Encoder(); + ByteBuf in = new ByteBuf(), out = new ByteBuf(); + in.append(serialRefAddr); + base64.translate(in, out); + base64.eof(out); + return delimChar + idx + + delimChar + refAddr.getType() + + delimChar + delimChar + out.toString(); + } + } + + + /** + * Encode Serializable object + */ + + static Attributes encodeSerialObj(Serializable obj, Attributes attrs) throws NamingException{ + if (attrs.get(AT_CLASSNAME) == null) { + attrs.put(new BasicAttribute(AT_CLASSNAME, obj.getClass().getName())); + } + attrs.put(new BasicAttribute(AT_SERIALDATA, serializeObject(obj))); + return attrs; + } + + /** + * Encode RMI Object + */ + static Attributes encodeRMIObj(java.rmi.Remote obj, Attributes attrs) throws NamingException{ + if (attrs.get(AT_REMOTELOC) == null) { + throw new NamingException( + "Can not bind Remote object, " + AT_REMOTELOC + " not specified"); + } + if (attrs.get(AT_CLASSNAME) == null) { + attrs.put(new BasicAttribute(AT_CLASSNAME, obj.getClass().getName())); + } + return attrs; + } + + /** + * Encode DirContext object (merege attributes) + */ + static Attributes encodeDirCtxObj(DirContext obj, Attributes attrs) throws NamingException{ + Attributes ctxAttrs = obj.getAttributes(""); + for (NamingEnumeration enum = ctxAttrs.getAll(); enum.hasMore();) { + attrs.put((Attribute)enum.next()); + } + return attrs; + } + + public static void main(String[] args) { + byte[] serialRefAddr = { 'a', '0', 'A', (byte)0x10, (byte)0x7f, (byte)0xaa }; + MimeBase64Encoder base64 = new MimeBase64Encoder(); + MimeBase64Decoder base64Dec = new MimeBase64Decoder(); + ByteBuf in = new ByteBuf(), out = new ByteBuf(); + in.append(serialRefAddr); + base64.translate(in, out); + base64.eof(out); + System.err.println("in="+in); + System.err.println("out="+out); + in = new ByteBuf(out.toString()); + out = new ByteBuf(); + base64Dec.translate(in, out); + base64Dec.eof(out); + System.err.println("in="+in); + System.err.println("out="+out); + + } +} + diff --git a/mozilla/directory/java-sdk/ldapsp/com/netscape/jndi/ldap/ProviderUtils.java b/mozilla/directory/java-sdk/ldapsp/com/netscape/jndi/ldap/ProviderUtils.java new file mode 100644 index 00000000000..91f769e1c8f --- /dev/null +++ b/mozilla/directory/java-sdk/ldapsp/com/netscape/jndi/ldap/ProviderUtils.java @@ -0,0 +1,279 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * + * The contents of this file are subject to the Netscape Public License + * Version 1.0 (the "NPL"); you may not use this file except in + * compliance with the NPL. You may obtain a copy of the NPL at + * http://www.mozilla.org/NPL/ + * + * Software distributed under the NPL is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL + * for the specific language governing rights and limitations under the + * NPL. + * + * The Initial Developer of this code under the NPL is Netscape + * Communications Corporation. Portions created by Netscape are + * Copyright (C) 1999 Netscape Communications Corporation. All Rights + * Reserved. + */ +package com.netscape.jndi.ldap; + +import javax.naming.*; +import javax.naming.directory.*; +import javax.naming.ldap.*; + +import netscape.ldap.*; + +import java.util.*; + +/** + * Common utility methods + * + */ +class ProviderUtils { + + public static final String DEFAULT_FILTER = "(objectclass=*)"; + + static int jndiSearchScopeToLdap(int jndiScope) throws NamingException { + int scope = -1; + if (jndiScope == SearchControls.SUBTREE_SCOPE) { + scope = LDAPConnection.SCOPE_SUB; + } + else if (jndiScope == SearchControls.ONELEVEL_SCOPE) { + scope = LDAPConnection.SCOPE_ONE; + } + else if (jndiScope == SearchControls.OBJECT_SCOPE) { + scope = LDAPConnection.SCOPE_BASE; + } + else { + throw new InvalidSearchControlsException("Illegal value for the search scope"); + } + return scope; + } + + + /** + * Convert Attribute List to a LDAP filter + * + * @return LDAP Filter + * @throw NamingException + * @param attrs An Attribute List + */ + static String attributesToFilter(Attributes attrs) throws NamingException{ + + if (attrs == null || attrs.size() == 0) { + return DEFAULT_FILTER; + } + + String filter = ""; + + for (NamingEnumeration attrEnum = attrs.getAll(); attrEnum.hasMore();) { + Attribute attrib = (Attribute) attrEnum.next(); + + //Has attributes any values + if ( attrib.size() == 0) { + // test only for presence of the attribute + filter += "(" + attrib.getID() + "=*)"; + continue; + } + + // Add attribute values to the filter, ecsaping if necessary + String attrValues = ""; + for (NamingEnumeration valEnum = attrib.getAll(); valEnum.hasMore();) { + Object val = valEnum.next(); + if (val instanceof String) { + attrValues += "(" + attrib.getID() + "=" + escapeString((String)val) +")"; + } + else if (val instanceof byte[]) { + attrValues += "(" + attrib.getID() + "=" + escapeBytes((byte[])val) +")"; + } + else if (val == null) { + //null is allowed value in Attribute.add(Object), accept it just in case + attrValues += "(" + attrib.getID() + "=*)"; + } + else { + throw new NamingException( + "Wrong Attribute value, expecting String or byte[]"); + } + } + filter += (attrib.size() > 1) ? ("(|" + attrValues + ")") : attrValues; + } + + return (attrs.size() > 1) ? ("(&" + filter + ")") : filter; + } + + + /** + * Expand filterExpr. Each occurrence of a variable "{n}", where n is a non-negative + * integer, is replaced with a variable from the filterArgs array indexed by the 'n'. + * FilterArgs can be Strings or byte[] and they are escaped according to the RFC2254 + */ + static String expandFilterExpr(String filterExpr, Object[] filterArgs) throws InvalidSearchFilterException{ + StringTokenizer tok = new StringTokenizer(filterExpr, "{}", /*returnTokens=*/true); + + if (tok.countTokens() == 1) { + return filterExpr; // No escape characters + } + + StringBuffer out= new StringBuffer(); + boolean expectVarIdx = false, expectVarOff = false; + Object arg= null; + while (tok.hasMoreTokens()) { + String s = tok.nextToken(); + + if (expectVarIdx) { + expectVarIdx = false; + try { + int idx = Integer.parseInt(s); + arg = filterArgs[idx]; + expectVarOff = true; + } + catch (IndexOutOfBoundsException e) { + throw new InvalidSearchFilterException("Filter expression variable index out of bounds"); + } + + catch (Exception e) { + throw new InvalidSearchFilterException("Invalid filter expression"); + } + } + + else if (expectVarOff) { + expectVarOff = false; + if (!s.equals("}")) { + throw new InvalidSearchFilterException("Invalid filter expression"); + } + if (arg instanceof String) { + out.append(escapeString((String)arg)); + } + else if (arg instanceof byte[]) { + out.append(escapeBytes((byte[])arg)); + } + else { + throw new InvalidSearchFilterException("Invalid filter argument type"); + } + arg = null; + } + + else if (s.equals("{")) { + expectVarIdx = true; + } + else { + out.append(s); + } + } + if (expectVarIdx || expectVarOff) { + throw new InvalidSearchFilterException("Invalid filter expression"); + } + return out.toString(); + } + + + /** + * Escape a string according to the RFC 2254 + */ + static String escapeString(String str) { + String charToEscape = "\\*()\000"; + StringTokenizer tok = new StringTokenizer(str, charToEscape, /*returnTokens=*/true); + + if (tok.countTokens() == 1) { + return str; // No escape characters + } + + StringBuffer out= new StringBuffer(); + while (tok.hasMoreTokens()) { + String s = tok.nextToken(); + + if (s.equals("*")) { + out.append("\\2a"); + } + else if (s.equals("(")) { + out.append("\\28"); + } + else if (s.equals(")")) { + out.append("\\29"); + } + else if (s.equals("\\")) { + out.append("\\5c"); + } + else if (s.equals("\000")) { + out.append("\\00"); + } + else { + out.append(s); + } + } + return out.toString(); + } + + + /** + * Escape a byte array according to the RFC 2254 + */ + static final String hexDigits="0123456789abcdef"; + + static String escapeBytes(byte[] bytes) { + StringBuffer out = new StringBuffer(""); + for (int i=0; i < bytes.length; i++) { + + int low = bytes[i] & 0x0f; + int high = (bytes[i] & 0xf0) >> 4; + out.append("\\"); + out.append(hexDigits.charAt(high)); + out.append(hexDigits.charAt(low)); + } + return out.toString(); + } + + /** + * A method used only for testing + */ + private static void testAttributesToFilter() { + try { + System.out.println(attributesToFilter(null)); + + BasicAttributes attrs = new BasicAttributes(true); + + System.out.println(attrs + " = " + attributesToFilter(attrs)); + + attrs.put (new BasicAttribute("attr1", "val1")); + attrs.put (new BasicAttribute("attr2", "(val2)\\*x")); + attrs.put (new BasicAttribute("attr3")); + BasicAttribute attr4 = new BasicAttribute("attr4", "val41"); + attr4.add("val42"); + attrs.put(attr4); + attrs.put("attr5", new byte[] { (byte)0x23, (byte)0x3, (byte)0x0, (byte)0xab, (byte)0xff}); + System.out.println(attrs + " = " +attributesToFilter(attrs)); + } + catch (Exception e) { + System.err.println(e); + } + } + + /** + * A method used only for testing + */ + private static void testFilterExpr() { + try { + String filterExpr = "(&(attr0={0})(attr1={1}))"; + Object [] args = new Object[] { "val*0", new byte[] { (byte)0xf0, (byte) 0x3a}}; + String filter = null; + filter = expandFilterExpr(filterExpr, args); + System.out.println(filterExpr + " -> " + filter); + } + catch (Exception e) { + System.err.println(e); + } + } + + /** + * Test + */ + public static void main(String[] args) { + /*testAttributesToFilter(); + String x = "012\0003"; + byte[] b = x.getBytes(); + for (int i=0; i < b.length; i++) { + System.out.println(i+"="+b[i]); + }*/ + testFilterExpr(); + } +} diff --git a/mozilla/directory/java-sdk/ldapsp/com/netscape/jndi/ldap/SearchResultEnum.java b/mozilla/directory/java-sdk/ldapsp/com/netscape/jndi/ldap/SearchResultEnum.java new file mode 100644 index 00000000000..a344d843164 --- /dev/null +++ b/mozilla/directory/java-sdk/ldapsp/com/netscape/jndi/ldap/SearchResultEnum.java @@ -0,0 +1,122 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * + * The contents of this file are subject to the Netscape Public License + * Version 1.0 (the "NPL"); you may not use this file except in + * compliance with the NPL. You may obtain a copy of the NPL at + * http://www.mozilla.org/NPL/ + * + * Software distributed under the NPL is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL + * for the specific language governing rights and limitations under the + * NPL. + * + * The Initial Developer of this code under the NPL is Netscape + * Communications Corporation. Portions created by Netscape are + * Copyright (C) 1999 Netscape Communications Corporation. All Rights + * Reserved. + */ +package com.netscape.jndi.ldap; + +import javax.naming.*; +import javax.naming.directory.*; +import javax.naming.ldap.*; +import com.netscape.jndi.ldap.controls.NetscapeControlFactory; +import com.netscape.jndi.ldap.common.ExceptionMapper; +import netscape.ldap.*; +import java.util.*; + +/** + * A wrapper for the LDAPSeatchResults. Convert a LDAPSearchResults enumeration + * of LDAPEntries into a JNDI NamingEnumeration of JNDI SearchResults. + */ +class SearchResultEnum implements NamingEnumeration { + + LDAPSearchResults m_res; + boolean m_returnObjs; // ReturningObjFlag in SearchControls + LdapContextImpl m_ctx; + Name m_ctxName; + String[] m_userBinaryAttrs; + LDAPConnection m_ld; + + public SearchResultEnum(LDAPSearchResults res, boolean returnObjs, LdapContextImpl ctx) throws NamingException{ + m_res = res; + m_returnObjs = returnObjs; + m_ctx = ctx; + m_userBinaryAttrs = ctx.m_ctxEnv.getUserDefBinaryAttrs(); + + m_ld = m_ctx.m_ldapSvc.getConnection(); + try { + m_ctxName = LdapNameParser.getParser().parse(m_ctx.m_ctxDN); + } + catch ( NamingException e ) { + throw ExceptionMapper.getNamingException(e); + } + } + + public Object next() throws NamingException{ + try { + LDAPEntry entry = m_res.next(); + String name = LdapNameParser.getRelativeName(m_ctxName, entry.getDN()); + Object obj = (m_returnObjs) ? ObjectMapper.entryToObject(entry, m_ctx) : null; + Attributes attrs = new AttributesImpl(entry.getAttributeSet(), m_userBinaryAttrs); + + // check for response controls + LDAPControl[] ldapCtls = m_ld.getResponseControls(); + if (ldapCtls != null) { + // Parse raw controls + Control[] ctls = new Control[ldapCtls.length]; + for (int i=0; i < ldapCtls.length; i++) { + ctls[i] = NetscapeControlFactory.getControlInstance(ldapCtls[i]); + if (ctls[i] == null) { + throw new NamingException("Unsupported control " + ldapCtls[i].getID()); + } + } + + SearchResultWithControls searchRes = + new SearchResultWithControls(name, obj, attrs); + searchRes.setControls(ctls); + + return searchRes; + } + else { // no controls + return new SearchResult(name, obj, attrs); + } + + } + catch (LDAPReferralException e) { + throw new LdapReferralException(m_ctx, e); + } + catch ( LDAPException e ) { + throw ExceptionMapper.getNamingException(e); + } + } + + public Object nextElement() { + try { + return next(); + } + catch ( Exception e ) { + System.err.println( "Error in SearchResultEnum.nextElement(): " + e.toString() ); + e.printStackTrace(System.err); + return null; + } + } + + public boolean hasMore() throws NamingException { + return m_res.hasMoreElements(); + } + + public boolean hasMoreElements() { + return m_res.hasMoreElements(); + } + + public void close() throws NamingException{ + try { + m_ld.abandon(m_res); + } + catch (LDAPException e) { + throw ExceptionMapper.getNamingException(e); + } + } +} + diff --git a/mozilla/directory/java-sdk/ldapsp/com/netscape/jndi/ldap/SearchResultWithControls.java b/mozilla/directory/java-sdk/ldapsp/com/netscape/jndi/ldap/SearchResultWithControls.java new file mode 100644 index 00000000000..eeedcb406aa --- /dev/null +++ b/mozilla/directory/java-sdk/ldapsp/com/netscape/jndi/ldap/SearchResultWithControls.java @@ -0,0 +1,63 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * + * The contents of this file are subject to the Netscape Public License + * Version 1.0 (the "NPL"); you may not use this file except in + * compliance with the NPL. You may obtain a copy of the NPL at + * http://www.mozilla.org/NPL/ + * + * Software distributed under the NPL is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL + * for the specific language governing rights and limitations under the + * NPL. + * + * The Initial Developer of this code under the NPL is Netscape + * Communications Corporation. Portions created by Netscape are + * Copyright (C) 1999 Netscape Communications Corporation. All Rights + * Reserved. + */ +package com.netscape.jndi.ldap; + +import javax.naming.ldap.*; +import javax.naming.directory.*; + +/** + * An extension of SearchResult that allows access to controls sent + * back with the results of a search + */ +class SearchResultWithControls extends SearchResult implements HasControls { + + Control[] m_ctrls; + + /** + * Enable constructors + */ + public SearchResultWithControls(String name, Object obj, Attributes attrs) { + super(name, obj, attrs); + } + + public SearchResultWithControls(String name, Object obj, Attributes attrs, boolean isRelative) { + super(name, obj, attrs, isRelative); + } + + public SearchResultWithControls(String name, String className, Object obj, Attributes attrs) { + super(name, className, obj, attrs); + } + + public SearchResultWithControls(String name, String className, Object obj, Attributes attrs, boolean isRelative) { + super(name, className, obj, attrs, isRelative); + } + + /** + * Implements HasControls interface + */ + public Control[] getControls() { + return m_ctrls; + } + + /** + * Set controls array + */ + public void setControls(Control[] ctrls) { + m_ctrls = ctrls; + } +} diff --git a/mozilla/directory/java-sdk/ldapsp/com/netscape/jndi/ldap/common/Debug.java b/mozilla/directory/java-sdk/ldapsp/com/netscape/jndi/ldap/common/Debug.java new file mode 100644 index 00000000000..a5b435bfb72 --- /dev/null +++ b/mozilla/directory/java-sdk/ldapsp/com/netscape/jndi/ldap/common/Debug.java @@ -0,0 +1,63 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * + * The contents of this file are subject to the Netscape Public License + * Version 1.0 (the "NPL"); you may not use this file except in + * compliance with the NPL. You may obtain a copy of the NPL at + * http://www.mozilla.org/NPL/ + * + * Software distributed under the NPL is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL + * for the specific language governing rights and limitations under the + * NPL. + * + * The Initial Developer of this code under the NPL is Netscape + * Communications Corporation. Portions created by Netscape are + * Copyright (C) 1999 Netscape Communications Corporation. All Rights + * Reserved. + */ +package com.netscape.jndi.ldap.common; + + +/** + * Class used to selectivly enable debug statements + */ +public class Debug { + + // lower number is a higher priority message. Level 0 is the + // highest priority to be used ONLY for errors + private static int m_level = 0; + + static { + try { + String level = System.getProperty("jndi.netscape.debug"); + if (level != null) { + m_level = Integer.parseInt(level); + } + } + catch (Exception e) {} + } + + + /** + * Set the debug level. To disable debugging set the level to -1 + */ + public static void setDebugLevel(int level) { + m_level = level; + } + + /** + * Get the debug level. If -1 is returned, then debugging is disabled + */ + public static int getDebugLevel() { + return m_level; + } + + /** + * Print the message if its debug level is enabled + */ + public static void println(int level, String msg) { + if (m_level >= 0 && level <= m_level) { + System.err.println(msg); + } + } +} diff --git a/mozilla/directory/java-sdk/ldapsp/com/netscape/jndi/ldap/common/DirContextAdapter.java b/mozilla/directory/java-sdk/ldapsp/com/netscape/jndi/ldap/common/DirContextAdapter.java new file mode 100644 index 00000000000..215c2558cf8 --- /dev/null +++ b/mozilla/directory/java-sdk/ldapsp/com/netscape/jndi/ldap/common/DirContextAdapter.java @@ -0,0 +1,251 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * + * The contents of this file are subject to the Netscape Public License + * Version 1.0 (the "NPL"); you may not use this file except in + * compliance with the NPL. You may obtain a copy of the NPL at + * http://www.mozilla.org/NPL/ + * + * Software distributed under the NPL is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL + * for the specific language governing rights and limitations under the + * NPL. + * + * The Initial Developer of this code under the NPL is Netscape + * Communications Corporation. Portions created by Netscape are + * Copyright (C) 1999 Netscape Communications Corporation. All Rights + * Reserved. + */ +package com.netscape.jndi.ldap.common; + +import javax.naming.*; +import javax.naming.directory.*; + +import java.util.Hashtable; + +public class DirContextAdapter implements DirContext { + +/* Context Methods */ + + public Object addToEnvironment(String propName, Object propValue) throws NamingException { + throw new OperationNotSupportedException(); + } + + public void bind(String name, Object obj) throws NamingException { + throw new OperationNotSupportedException(); + } + + public void bind(Name name, Object obj) throws NamingException { + throw new OperationNotSupportedException(); + } + + public void close() throws NamingException { + throw new OperationNotSupportedException(); + } + + public String composeName(String name, String prefix) throws NamingException { + throw new OperationNotSupportedException(); + } + + public Name composeName(Name name, Name prefix) throws NamingException { + throw new OperationNotSupportedException(); + } + + public Context createSubcontext(String name) throws NamingException { + throw new OperationNotSupportedException(); + } + + public Context createSubcontext(Name name) throws NamingException { + throw new OperationNotSupportedException(); + } + + public void destroySubcontext(String name) throws NamingException { + throw new OperationNotSupportedException(); + } + + public void destroySubcontext(Name name) throws NamingException { + throw new OperationNotSupportedException(); + } + + public Hashtable getEnvironment() throws NamingException { + throw new OperationNotSupportedException(); + } + + public String getNameInNamespace() throws NamingException { + throw new OperationNotSupportedException(); + } + + public NameParser getNameParser(String name) throws NamingException { + throw new OperationNotSupportedException(); + } + + public NameParser getNameParser(Name name) throws NamingException { + throw new OperationNotSupportedException(); + } + + public NamingEnumeration list(String name) throws NamingException { + throw new OperationNotSupportedException(); + } + + public NamingEnumeration list(Name name) throws NamingException { + throw new OperationNotSupportedException(); + } + + public NamingEnumeration listBindings(String name) throws NamingException { + throw new OperationNotSupportedException(); + } + + public NamingEnumeration listBindings(Name name) throws NamingException { + throw new OperationNotSupportedException(); + } + + public Object lookup(String name) throws NamingException { + throw new OperationNotSupportedException(); + } + + public Object lookup(Name name) throws NamingException { + throw new OperationNotSupportedException(); + } + + public Object lookupLink(String name) throws NamingException { + throw new OperationNotSupportedException(); + } + + public Object lookupLink(Name name) throws NamingException { + throw new OperationNotSupportedException(); + } + + public void rebind(String name, Object obj) throws NamingException { + throw new OperationNotSupportedException(); + } + + public void rebind(Name name, Object obj) throws NamingException { + throw new OperationNotSupportedException(); + } + + public Object removeFromEnvironment(String propName) throws NamingException { + throw new OperationNotSupportedException(); + } + + public void rename(String oldName, String newName) throws NamingException { + throw new OperationNotSupportedException(); + } + + public void rename(Name oldName, Name newName) throws NamingException { + throw new OperationNotSupportedException(); + } + + public void unbind(String name) throws NamingException { + throw new OperationNotSupportedException(); + } + + public void unbind(Name name) throws NamingException { + throw new OperationNotSupportedException(); + } + + +/* DirContext Methods */ + + public void bind(String name, Object obj, Attributes attrs) throws NamingException { + throw new OperationNotSupportedException(); + } + + public void bind(Name name, Object obj, Attributes attrs) throws NamingException { + throw new OperationNotSupportedException(); + } + + public DirContext createSubcontext(String name, Attributes attrs) throws NamingException { + throw new OperationNotSupportedException(); + } + + public DirContext createSubcontext(Name name, Attributes attrs) throws NamingException { + throw new OperationNotSupportedException(); + } + + public Attributes getAttributes(String name) throws NamingException { + throw new OperationNotSupportedException(); + } + + public Attributes getAttributes(String name, String[] attrIds) throws NamingException { + throw new OperationNotSupportedException(); + } + + public Attributes getAttributes(Name name) throws NamingException { + throw new OperationNotSupportedException(); + } + + public Attributes getAttributes(Name name, String[] attrIds) throws NamingException { + throw new OperationNotSupportedException(); + } + + public DirContext getSchema(String name) throws NamingException { + throw new OperationNotSupportedException(); + } + + public DirContext getSchema(Name name) throws NamingException { + throw new OperationNotSupportedException(); + } + + public DirContext getSchemaClassDefinition(String name) throws NamingException { + throw new OperationNotSupportedException(); + } + + public DirContext getSchemaClassDefinition(Name name) throws NamingException { + throw new OperationNotSupportedException(); + } + + public void modifyAttributes(String name, int mod_op, Attributes attrs) throws NamingException { + throw new OperationNotSupportedException(); + } + + public void modifyAttributes(String name, ModificationItem[] mods) throws NamingException { + throw new OperationNotSupportedException(); + } + + public void modifyAttributes(Name name, int mod_op, Attributes attrs) throws NamingException { + throw new OperationNotSupportedException(); + } + + public void modifyAttributes(Name name, ModificationItem[] mods) throws NamingException { + throw new OperationNotSupportedException(); + } + + public void rebind(String name, Object obj, Attributes attrs) throws NamingException { + throw new OperationNotSupportedException(); + } + + public void rebind(Name name, Object obj, Attributes attrs) throws NamingException { + throw new OperationNotSupportedException(); + } + + public NamingEnumeration search(String name, String filter, SearchControls cons) throws NamingException { + throw new OperationNotSupportedException(); + } + + public NamingEnumeration search(String name, String filterExpr, Object[] filterArgs, SearchControls cons) throws NamingException { + throw new OperationNotSupportedException(); + } + + public NamingEnumeration search(String name, Attributes matchingAttributes) throws NamingException { + throw new OperationNotSupportedException(); + } + + public NamingEnumeration search(String name, Attributes matchingAttributes, String[] attributesToReturn) throws NamingException { + throw new OperationNotSupportedException(); + } + + public NamingEnumeration search(Name name, String filter, SearchControls cons) throws NamingException { + throw new OperationNotSupportedException(); + } + + public NamingEnumeration search(Name name, String filterExpr, Object[] filterArgs, SearchControls cons) throws NamingException { + throw new OperationNotSupportedException(); + } + + public NamingEnumeration search(Name name, Attributes attrs) throws NamingException { + throw new OperationNotSupportedException(); + } + + public NamingEnumeration search(Name name, Attributes matchingAttributes, String[] attributesToReturn) throws NamingException { + throw new OperationNotSupportedException(); + } +} diff --git a/mozilla/directory/java-sdk/ldapsp/com/netscape/jndi/ldap/common/ExceptionMapper.java b/mozilla/directory/java-sdk/ldapsp/com/netscape/jndi/ldap/common/ExceptionMapper.java new file mode 100644 index 00000000000..275435c7f23 --- /dev/null +++ b/mozilla/directory/java-sdk/ldapsp/com/netscape/jndi/ldap/common/ExceptionMapper.java @@ -0,0 +1,220 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * + * The contents of this file are subject to the Netscape Public License + * Version 1.0 (the "NPL"); you may not use this file except in + * compliance with the NPL. You may obtain a copy of the NPL at + * http://www.mozilla.org/NPL/ + * + * Software distributed under the NPL is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL + * for the specific language governing rights and limitations under the + * NPL. + * + * The Initial Developer of this code under the NPL is Netscape + * Communications Corporation. Portions created by Netscape are + * Copyright (C) 1999 Netscape Communications Corporation. All Rights + * Reserved. + */ +package com.netscape.jndi.ldap.common; + +import javax.naming.*; +import javax.naming.directory.*; +import javax.naming.ldap.*; + +import netscape.ldap.*; + +import java.util.Hashtable; + +/** + * Class used to map ldapjdk exceptions to JNDI NamingException +*/ +public class ExceptionMapper { + + public static NamingException getNamingException(Exception origException) { + + if (origException instanceof NamingException) { + return (NamingException)origException; + } + + /** + * LdapJDK exceptions + */ + else if (origException instanceof LDAPReferralException) { + // Should never get here. The ldapjdk referral exceptions + // should be explicitly captured by the ecode and converted + // into jndi LdapReferralException instances. + return new NamingException("Provider internal error, LDAPReferralException not captured"); + } + + else if (origException instanceof LDAPException) { + LDAPException ldapException = (LDAPException) origException; + int resCode = ldapException.getLDAPResultCode(); + + if (resCode == LDAPException.OPERATION_ERROR) { + NamingException nameEx = new NamingException(ldapException.toString()); + nameEx.setRootCause(ldapException); + return nameEx; + } + else if (resCode == LDAPException.PROTOCOL_ERROR) { + return new CommunicationException(ldapException.toString()); + } + else if (resCode == LDAPException.TIME_LIMIT_EXCEEDED) { + return new TimeLimitExceededException(ldapException.toString()); + } + else if (resCode == LDAPException.SIZE_LIMIT_EXCEEDED) { + return new SizeLimitExceededException(ldapException.toString()); + } + // COMPARE_FALSE should never happen, but is included here for completeness + else if (resCode == LDAPException.COMPARE_FALSE) { + NamingException nameEx = new NamingException(ldapException.toString()); + nameEx.setRootCause(ldapException); + return nameEx; + } + // COMPARE_TRUE should never happen, but is included here for completeness + else if (resCode == LDAPException.COMPARE_TRUE) { + NamingException nameEx = new NamingException(ldapException.toString()); + nameEx.setRootCause(ldapException); + return nameEx; + } + else if (resCode == LDAPException.AUTH_METHOD_NOT_SUPPORTED) { + return new AuthenticationNotSupportedException(ldapException.toString()); + } + else if (resCode == LDAPException.STRONG_AUTH_REQUIRED) { + return new AuthenticationNotSupportedException(ldapException.toString()); + } + else if (resCode == LDAPException.LDAP_PARTIAL_RESULTS) { + return new PartialResultException(ldapException.toString()); + } + // REFERRAL code should be in LDAPReferralException + else if (resCode == LDAPException.REFERRAL) { + LDAPReferralException referralEx = (LDAPReferralException)ldapException; + // TODO create jndi LdapReferralException + } + else if (resCode == LDAPException.ADMIN_LIMIT_EXCEEDED) { + return new LimitExceededException(ldapException.toString()); + } + else if(resCode == LDAPException.UNAVAILABLE_CRITICAL_EXTENSION) { + return new OperationNotSupportedException(ldapException.toString()); + } + else if (resCode == LDAPException.CONFIDENTIALITY_REQUIRED) { + return new AuthenticationNotSupportedException( + "A secure connection is required for this operation"); + } + // This shoud never propagate to the caller + else if (resCode == LDAPException.SASL_BIND_IN_PROGRESS) { + NamingException nameEx = new NamingException(ldapException.toString()); + nameEx.setRootCause(ldapException); + return nameEx; + } + else if (resCode == LDAPException.NO_SUCH_ATTRIBUTE) { + return new NoSuchAttributeException(ldapException.toString()); + } + else if (resCode == LDAPException.UNDEFINED_ATTRIBUTE_TYPE) { + return new InvalidAttributeIdentifierException(ldapException.toString()); + } + else if (resCode == LDAPException.INAPPROPRIATE_MATCHING) { + return new InvalidSearchFilterException(ldapException.toString()); + } + else if (resCode == LDAPException.CONSTRAINT_VIOLATION) { + return new InvalidSearchControlsException(ldapException.toString()); + } + else if (resCode == LDAPException.ATTRIBUTE_OR_VALUE_EXISTS) { + return new AttributeInUseException(ldapException.toString()); + } + else if (resCode == LDAPException.INVALID_ATTRIBUTE_SYNTAX) { + return new InvalidAttributeValueException(ldapException.toString()); + } + else if (resCode == LDAPException.NO_SUCH_OBJECT) { + return new NameNotFoundException(ldapException.toString()); + } + else if (resCode == LDAPException.ALIAS_PROBLEM) { + return new NamingException(ldapException.toString()); + } + else if (resCode == LDAPException.INVALID_DN_SYNTAX) { + return new InvalidNameException(ldapException.toString()); + } + else if (resCode == LDAPException.IS_LEAF) { + NamingException nameEx = new NamingException(ldapException.toString()); + nameEx.setRootCause(ldapException); + return nameEx; + } + else if (resCode == LDAPException.ALIAS_DEREFERENCING_PROBLEM) { + NamingException nameEx = new NamingException(ldapException.toString()); + nameEx.setRootCause(ldapException); + return nameEx; + } + else if (resCode == LDAPException.INAPPROPRIATE_AUTHENTICATION) { + //return new AuthenticationNotSupportedException(ldapException.toString()); + return new AuthenticationException(ldapException.toString()); + } + else if (resCode == LDAPException.INVALID_CREDENTIALS) { + return new AuthenticationException(ldapException.toString()); + } + else if (resCode == LDAPException.INSUFFICIENT_ACCESS_RIGHTS) { + return new NoPermissionException(ldapException.toString()); + } + else if (resCode == LDAPException.BUSY) { + return new ServiceUnavailableException(ldapException.toString()); + } + else if (resCode == LDAPException.UNAVAILABLE) { + return new ServiceUnavailableException(ldapException.toString()); + } + else if (resCode == LDAPException.UNWILLING_TO_PERFORM) { + return new OperationNotSupportedException(ldapException.toString()); + } + else if (resCode == LDAPException.LOOP_DETECT) { + NamingException nameEx = new NamingException(ldapException.toString()); + nameEx.setRootCause(ldapException); + return nameEx; + } + else if (resCode == LDAPException.NAMING_VIOLATION) { + return new InvalidNameException(ldapException.toString()); + } + else if (resCode == LDAPException.OBJECT_CLASS_VIOLATION) { + return new SchemaViolationException(ldapException.toString()); + } + else if (resCode == LDAPException.NOT_ALLOWED_ON_NONLEAF) { + return new ContextNotEmptyException(ldapException.toString()); + } + else if (resCode == LDAPException.NOT_ALLOWED_ON_RDN) { + return new SchemaViolationException(ldapException.toString()); + } + else if (resCode == LDAPException.ENTRY_ALREADY_EXISTS) { + return new NameAlreadyBoundException(ldapException.toString()); + } + else if (resCode == LDAPException.OBJECT_CLASS_MODS_PROHIBITED) { + return new SchemaViolationException(ldapException.toString()); + } + else if (resCode == LDAPException.SERVER_DOWN) { + return new CommunicationException(ldapException.toString()); + } + else if (resCode == LDAPException.CONNECT_ERROR) { + return new CommunicationException(ldapException.toString()); + } + + else { + // All other result codes + // 71 AFFECTS_MULTIPLE_DSAS + // 80 OTHER + // 89 PARAM_ERROR + // 92 LDAP_NOT_SUPPORTED + // 93 CONTROL_NOT_FOUND + // 94 NO_RESULTS_RETURNED + // 95 MORE_RESULTS_TO_RETURN + // 96 CLIENT_LOOP referral + // 97 REFERRAL_LIMIT_EXCEEDED referral + + NamingException nameEx = new NamingException(ldapException.toString()); + nameEx.setRootCause(ldapException); + return nameEx; + } + } + + /** + * All other Exceptions + */ + NamingException nameEx = new NamingException(origException.toString()); + nameEx.setRootCause(origException); + return nameEx; + } +} diff --git a/mozilla/directory/java-sdk/ldapsp/com/netscape/jndi/ldap/common/LdapContextAdapter.java b/mozilla/directory/java-sdk/ldapsp/com/netscape/jndi/ldap/common/LdapContextAdapter.java new file mode 100644 index 00000000000..5828b2672a7 --- /dev/null +++ b/mozilla/directory/java-sdk/ldapsp/com/netscape/jndi/ldap/common/LdapContextAdapter.java @@ -0,0 +1,86 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * + * The contents of this file are subject to the Netscape Public License + * Version 1.0 (the "NPL"); you may not use this file except in + * compliance with the NPL. You may obtain a copy of the NPL at + * http://www.mozilla.org/NPL/ + * + * Software distributed under the NPL is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL + * for the specific language governing rights and limitations under the + * NPL. + * + * The Initial Developer of this code under the NPL is Netscape + * Communications Corporation. Portions created by Netscape are + * Copyright (C) 1999 Netscape Communications Corporation. All Rights + * Reserved. + */ +package com.netscape.jndi.ldap.common; + +import javax.naming.*; +import javax.naming.directory.*; +import javax.naming.ldap.*; +import javax.naming.event.*; +import java.util.Hashtable; + +public class LdapContextAdapter extends DirContextAdapter implements EventDirContext, LdapContext { + +/* LdapContext methods */ + public ExtendedResponse extendedOperation(ExtendedRequest req)throws NamingException { + throw new OperationNotSupportedException(); + } + + public Control[] getRequestControls() throws NamingException { + throw new OperationNotSupportedException(); + } + + public Control[] getResponseControls() throws NamingException { + throw new OperationNotSupportedException(); + } + + public LdapContext newInstance(Control[] reqCtls) throws NamingException { + throw new OperationNotSupportedException(); + } + + public void reconnect() throws NamingException { + throw new OperationNotSupportedException(); + } + + public void setRequestControls(Control[] reqCtls) throws NamingException { + throw new OperationNotSupportedException(); + } + /** + * Naming Event methods + */ + public void addNamingListener(String target, int scope, NamingListener l) throws NamingException { + throw new OperationNotSupportedException(); + } + + public void addNamingListener(Name target, int scope, NamingListener l) throws NamingException { + throw new OperationNotSupportedException(); + } + + public void addNamingListener(String target, String filter, SearchControls ctls, NamingListener l)throws NamingException { + throw new OperationNotSupportedException(); + } + + public void addNamingListener(Name target, String filter, SearchControls ctls, NamingListener l)throws NamingException { + throw new OperationNotSupportedException(); + } + + public void addNamingListener(String target, String filter, Object[] filtrArgs, SearchControls ctls, NamingListener l)throws NamingException { + throw new OperationNotSupportedException(); + } + + public void addNamingListener(Name target, String filter, Object[] filtrArgs, SearchControls ctls, NamingListener l)throws NamingException { + throw new OperationNotSupportedException(); + } + + public void removeNamingListener(NamingListener l) throws NamingException { + throw new OperationNotSupportedException(); + } + + public boolean targetMustExist() { + return true; + } +} diff --git a/mozilla/directory/java-sdk/ldapsp/com/netscape/jndi/ldap/common/ShareableEnv.java b/mozilla/directory/java-sdk/ldapsp/com/netscape/jndi/ldap/common/ShareableEnv.java new file mode 100644 index 00000000000..06340a9b21b --- /dev/null +++ b/mozilla/directory/java-sdk/ldapsp/com/netscape/jndi/ldap/common/ShareableEnv.java @@ -0,0 +1,404 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * + * The contents of this file are subject to the Netscape Public License + * Version 1.0 (the "NPL"); you may not use this file except in + * compliance with the NPL. You may obtain a copy of the NPL at + * http://www.mozilla.org/NPL/ + * + * Software distributed under the NPL is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL + * for the specific language governing rights and limitations under the + * NPL. + * + * The Initial Developer of this code under the NPL is Netscape + * Communications Corporation. Portions created by Netscape are + * Copyright (C) 1999 Netscape Communications Corporation. All Rights + * Reserved. + */ +package com.netscape.jndi.ldap.common; + +import java.util.*; + +/** + * ShareableEnv manages a set of environment properties. The class enables a memory + * efficient sharing of the environment between multiple contexts, while preserving + * the semantics that each context has its own environment. If the environment for a + * context is changed, the change is not visible to other contexts. + * + * The efficiency is achieved by implementing inheritance and override of environment + * properties ("subclass-on-write"). A read-only table of properties + * is shared among multiple contexts (_sharedEnv. If a context wants to + * modified a shared property, it will create a separate table (_privateEnv) + * to make the modifications. This table overrides the values in the shared table. + * + * Note1: The class is not thread safe, it requires external synchronization + * Note2: The class does not provide enumaration. Call getAllProperties() and then + * use the standard Hashtable enumaration techniques. + */ +public class ShareableEnv implements Cloneable{ + + /** + * A special value that denotes a removed propety. Because shared property tables + * are read-only, a shared property is deleted in the curent context by assigning + * the REMOVED_PROPERTY value in the _modEnv table. + */ + private static final Object REMOVED_PROPERTY = new Object(); + + /** + * A table of most recent environment modifications. These modifications are not + * shared until the current context is cloned, which moves _privateEnv to + * _sharedEnv + */ + protected Hashtable m_privateEnv; + + /** + * A set of environment propeties that have been changed in this Context and are + * shared with one or more child contexts. Read-only access. + */ + protected Vector m_sharedEnv; // A vector of Hashtables + + /** + * Shared environment inherited from the parent context + */ + protected ShareableEnv m_parentEnv; + + /** + * Index into parent _sharedEnv list. Designates all environment chnages + * in the parent context that are visible to this child context + */ + protected int m_parentSharedEnvIdx = -1; + + /** + * No-arg constructor is private + */ + private ShareableEnv() {} + + /** + * Constructor for non root Contexts + * + * @param parent A reference to the parent context environment + * @param parentSharedEnvIdx index into parent's shared environemnt list + */ + public ShareableEnv(ShareableEnv parent, int parentSharedEnvIdx) { + m_parentEnv = parent; + m_parentSharedEnvIdx = parentSharedEnvIdx; + } + + /** + * Constructor for the root context + * + * @param initialEnv a hashtable with environemnt properties + */ + public ShareableEnv(Hashtable initialEnv) { + m_privateEnv = initialEnv; + } + + /** + * Set the property value. + * + * @return the previous value of the specified property, + * or null if it did not exist before. + * @param prop property name + * @param val property value + */ + public Object setProperty(String prop, Object val) { + + // Need the current value to be returned + Object oldVal = getProperty(prop); + + // Lazy create _privateEnv table + if (m_privateEnv == null) { + m_privateEnv = new Hashtable(5); + } + + // Add/Modify property + m_privateEnv.put(prop, val); + + return oldVal; + } + + /** + * Get the property value. + * + * @return the object associated with the property name + * or null if property is not found + * @param prop property name + */ + public Object getProperty(String prop) { + + // First check most recent modifications + if (m_privateEnv != null) { + Object val = m_privateEnv.get(prop); + if (val != null) { + return (val == REMOVED_PROPERTY) ? null : val; + } + } + + // Then try shared properties + if (m_sharedEnv != null) { + return getSharedProperty(m_sharedEnv.size()-1, prop); + } + else { + return getSharedProperty(-1, prop); + } + } + + /** + * Get the property value for the specified _sharedEnv index + * + * @return the object associated with the property name + * @param envIdx start index in the shared environment list + * @param prop property name + */ + private Object getSharedProperty(int envIdx, String prop) { + + // Check first the frozen updtates list + if (envIdx >= 0) { + for (int i= envIdx; i >= 0; i--) { + Hashtable tab = (Hashtable) m_sharedEnv.elementAt(i); + Object val = tab.get(prop); + if (val != null) { + return (val == REMOVED_PROPERTY) ? null : val; + } + } + } + + // Check the parent env context + if (m_parentSharedEnvIdx >= 0) { + return m_parentEnv.getSharedProperty(m_parentSharedEnvIdx, prop); + } + + return null; + } + + /** + * Remove property + * + * @return the previous value of the specified property, + * or null if it did not exist before. + * @param prop property name + */ + public Object removeProperty(String prop) { + + Object val = null; + // Is this a shared property ? + if (m_sharedEnv != null) { + val = getSharedProperty(m_sharedEnv.size()-1, prop); + } + else { + val = getSharedProperty(-1, prop); + } + + if (val == null) { // Not a shared property, remove if physically + if (m_privateEnv != null) { + return m_privateEnv.remove(prop); + } + } + else { //A shared property, hide it + setProperty(prop, REMOVED_PROPERTY); + } + return val; + } + + /** + * Create a table of all properties. First read all properties from the parent env, + * then merge the local updates. Notice that the data is processed in reverse order + * than in the getProperty method. + * + * @return a hashtable containing all properties visible in this context + */ + public Hashtable getAllProperties() { + Hashtable res = null; + + // First copy shared properties + if (m_sharedEnv != null) { + res = getAllSharedProperties(m_sharedEnv.size()-1); + } + else { + res = getAllSharedProperties(-1); + } + + if (res == null) { + res = new Hashtable(51); + } + + // Then apply private env + if (m_privateEnv != null) { + Hashtable tab = m_privateEnv; + for (Enumeration e = tab.keys(); e.hasMoreElements();) { + Object key = e.nextElement(); + Object val = tab.get(key); + if (val != REMOVED_PROPERTY) { + res.put(key, val); + } + else { + res.remove(key); + } + } + } + + return res; + } + + /** + * Create a table of all shared properties for the given envIdx. First read all + * properties from the parent env, then merge the local environment. + * + * @return a hashtable containing all properties visible for the shared environment index + * @param envIdx start index in the shared environment list + */ + private Hashtable getAllSharedProperties(int envIdx) { + Hashtable res = null; + + // First copy parent env + if (m_parentEnv != null) { + res = m_parentEnv.getAllSharedProperties(m_parentSharedEnvIdx); + } + + if (res == null) { + res = new Hashtable(51); + } + + // Then copy _sharedEnv (older entries are processed before newer ones) + if (envIdx >= 0) { + for (int i= 0; i <= envIdx; i++) { + Hashtable tab = (Hashtable) m_sharedEnv.elementAt(i); + for (Enumeration e = tab.keys(); e.hasMoreElements();) { + Object key = e.nextElement(); + Object val = tab.get(key); + if (val != REMOVED_PROPERTY) { + res.put(key, val); + } + else { + res.remove(key); + } + } + } + } + return res; + } + + /** + * Freeze all environment changes changes in the current context. The "Freeze" is + * done by moving the _privateEnv table to _sharedEnv vector. + */ + protected void freezeUpdates() { + if (m_privateEnv != null) { + // Lazy create _sharedEnv vector + if (m_sharedEnv == null) { + m_sharedEnv = new Vector(); + } + m_sharedEnv.addElement(m_privateEnv); + m_privateEnv = null; + } + } + + /** + * Clone ShareableEnv + * + * @return A "clone" of the current context environment + */ + public Object clone() { + + // First freeze updates for this context + freezeUpdates(); + + // If the context has been modified, then it is the parent of the clone + if (m_sharedEnv != null) { + return new ShareableEnv(this, m_sharedEnv.size()-1); + } + + // No changes has been done to the inherited parent context. Pass the parent + // context to the clone + else { + return new ShareableEnv(m_parentEnv, m_parentSharedEnvIdx); + } + } + + /** + * Return string representation of the object + * + * @return a string representation of the object + */ + public String toString() { + StringBuffer buf = new StringBuffer(); + buf.append("ShareableEnv private="); + if (m_privateEnv != null) { + buf.append("("); + buf.append(m_privateEnv.size()); + buf.append(")"); + } + buf.append(" shared="); + if (m_sharedEnv != null) { + for (int i=0; i < m_sharedEnv.size(); i++) { + Hashtable tab = (Hashtable) m_sharedEnv.elementAt(i); + buf.append("("); + buf.append(tab.size()); + buf.append(")"); + } + } + buf.append(" parentIdx="); + buf.append(m_parentSharedEnvIdx); + + return buf.toString(); + } + + /** + * Test program + */ + public static void main(String[] args) { + + ShareableEnv c0 = new ShareableEnv(null, -1); // c=context 0=level 0=index + System.err.println("c0.getProperty(p1)=" + c0.getProperty("p1")); + System.err.println("c0.getAllProperties()=" + c0.getAllProperties()); + System.err.println("c0.setProperty(p1,vxxx)=" + c0.setProperty("p1", "vxxx")); + System.err.println("c0.setProperty(p2,v2)=" + c0.setProperty("p2", "v2")); + System.err.println("c0.setProperty(p3,v3)=" + c0.setProperty("p3", "v3")); + System.err.println("c0.setProperty(p1,v1)=" + c0.setProperty("p1", "v1")); + System.err.println("c0.getAllProperties()=" + c0.getAllProperties()); + + System.err.println("---"); + ShareableEnv c01 = (ShareableEnv)c0.clone(); + System.err.println("c01.getProperty(p1)=" + c01.getProperty("p1")); + System.err.println("c01.getAllProperties()=" + c01.getAllProperties()); + System.err.println("c01.setProperty(p1,v1a)=" + c01.setProperty("p1", "v1a")); + System.err.println("c01.getProperty(p1)=" + c01.getProperty("p1")); + System.err.println("c01.removeProperty(p2)=" + c01.removeProperty("p2")); + System.err.println("c01.getProperty(p2)=" + c01.getProperty("p2")); + System.err.println("c01.setProperty(p11,v11a)=" + c01.setProperty("p11", "v11a")); + System.err.println("c01.getAllProperties()=" + c01.getAllProperties()); + + System.err.println("---"); + ShareableEnv c02 = (ShareableEnv)c0.clone(); + System.err.println("c02.getProperty(p1)=" + c02.getProperty("p1")); + System.err.println("c02.getAllProperties()=" + c02.getAllProperties()); + + System.err.println("---"); + ShareableEnv c011 = (ShareableEnv)c01.clone(); + System.err.println("c011.getProperty(p1)=" + c011.getProperty("p1")); + System.err.println("c011.getAllProperties()=" + c011.getAllProperties()); + System.err.println("c011.setProperty(p1,v11b)=" + c011.setProperty("p1", "v11b")); + System.err.println("c011.getProperty(p1)=" + c011.getProperty("p1")); + System.err.println("c011.getAllProperties()=" + c011.getAllProperties()); + + System.err.println("---"); + System.err.println("c01.getAllProperties()=" + c01.getAllProperties()); + System.err.println("c01.removeProperty(p11)=" + c01.removeProperty("p11")); + System.err.println("c01.getAllProperties()=" + c01.getAllProperties()); + System.err.println("c011.getAllProperties()=" + c011.getAllProperties()); + + System.err.println("---"); + ShareableEnv c012 = (ShareableEnv)c01.clone(); + System.err.println("c012.getAllProperties()=" + c012.getAllProperties()); + System.err.println("c012.getProperty(p1)=" + c012.getProperty("p1")); + + System.err.println("---"); + System.err.println("c0="+c0); + System.err.println("c01="+c01); + System.err.println("c02="+c02); + System.err.println("c011="+c011); + System.err.println("c012="+c012); + + } +} + \ No newline at end of file diff --git a/mozilla/directory/java-sdk/ldapsp/com/netscape/jndi/ldap/controls/LdapEntryChangeControl.java b/mozilla/directory/java-sdk/ldapsp/com/netscape/jndi/ldap/controls/LdapEntryChangeControl.java new file mode 100644 index 00000000000..b07db150125 --- /dev/null +++ b/mozilla/directory/java-sdk/ldapsp/com/netscape/jndi/ldap/controls/LdapEntryChangeControl.java @@ -0,0 +1,172 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * + * The contents of this file are subject to the Netscape Public License + * Version 1.0 (the "NPL"); you may not use this file except in + * compliance with the NPL. You may obtain a copy of the NPL at + * http://www.mozilla.org/NPL/ + * + * Software distributed under the NPL is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL + * for the specific language governing rights and limitations under the + * NPL. + * + * The Initial Developer of this code under the NPL is Netscape + * Communications Corporation. Portions created by Netscape are + * Copyright (C) 1999 Netscape Communications Corporation. All Rights + * Reserved. + */ +package com.netscape.jndi.ldap.controls; + +/** + * Represents an LDAP v3 server control that specifies information + * about a change to an entry in the directory. (The OID for this + * control is 2.16.840.1.113730.3.4.7.) You need to use this control in + * conjunction with a "persistent search" control (represented + * by LdapPersistentSearchControl object. + *

+ * + * To use persistent searching for change notification, you create a + * "persistent search" control that specifies the types of changes that + * you want to track. When an entry is changed, the server sends that + * entry back to your client and may include an "entry change notification" + * control that specifies additional information about the change. + *

+ * + * Typically, you use the getResponseControls method of + * the LDAPConnection object and the parseResponse + * method of the LdapPersistSearchControl object to get + * an LdapEntryChangeControl object. + *

+ * + * Once you retrieve an LdapEntryChangeControl object from + * the server, you can get the following additional information about + * the change made to the entry: + *

+ * + *

    + *
  • The type of change made (add, modify, delete, or modify DN) + *
  • The change number identifying the record of the change in the + * change log (if the server supports change logs) + *
  • If the entry was renamed, the old DN of the entry + *
+ *

+ * + * @see com.netscape.jndi.ldap.controls.LdapPersistSearchControl + * @see com.netscape.jndi.ldap.LDAPConnection#getResponseControls + */ + +import javax.naming.ldap.Control; +import netscape.ldap.controls.*; + +public class LdapEntryChangeControl extends LDAPEntryChangeControl implements Control { + + /** + * Constructs a new LdapEntryChangeControl object. + * This constructor is used by the NetscapeControlFactory + * + * @see com.netscape.jndi.ldap.LdapControl + * @see com.netscape.jndi.ldap.controls.LdapPersistSearchControl + */ + LdapEntryChangeControl(boolean critical, byte[] value) { + m_critical = critical; + m_value = value; + } + + /** + * Sets the change number (which identifies the record of the change + * in the server's change log) in this "entry change notification" + * control. + * @param num Change number that you want to set. + * @see com.netscape.jndi.ldap.controls.LdapEntryChangeControl#getChangeNumber + */ + public void setChangeNumber(int num) { + super.setChangeNumber(num); + } + + /** + * Sets the change type (which identifies the type of change + * that occurred) in this "entry change notification" control. + * @param num Change type that you want to set. This can be one of + * the following values: + *

+ * + *

    + *
  • LdapPersistSearchControl.ADD (a new entry was + * added to the directory) + *
  • LdapPersistSearchControl.DELETE (an entry was + * removed from the directory) + *
  • LdapPersistSearchControl.MODIFY (an entry was + * modified) + *
  • LdapPersistSearchControl.MODDN (an entry was + * renamed) + *
+ *

+ * + * @see com.netscape.jndi.ldap.controls.LdapEntryChangeControl#getChangeType + */ + public void setChangeType(int num) { + super.setChangeType(num); + } + + /** + * Sets the previous DN of the entry (if the entry was renamed) + * in the "entry change notification control". + * @param dn The previous distinguished name of the entry. + * @see com.netscape.jndi.ldap.controls.LdapEntryChangeControl#getPreviousDN + */ + public void setPreviousDN(String dn) { + super.setPreviousDN(dn); + } + + /** + * Gets the change number, which identifies the record of the change + * in the server's change log. + * @return Change number identifying the change made. + * @see com.netscape.jndi.ldap.controls.LdapEntryChangeControl#setChangeNumber + */ + public int getChangeNumber() { + return super.getChangeNumber(); + } + + /** + * Gets the change type, which identifies the type of change + * that occurred. + * @returns Change type identifying the type of change that + * occurred. This can be one of the following values: + *

+ * + *

    + *
  • LdapPersistSearchControl.ADD (a new entry was + * added to the directory) + *
  • LdapPersistSearchControl.DELETE (an entry was + * removed from the directory) + *
  • LdapPersistSearchControl.MODIFY (an entry was + * modified) + *
  • LdapPersistSearchControl.MODDN (an entry was + * renamed) + *
+ *

+ * + * @see com.netscape.jndi.ldap.controls.LdapEntryChangeControl#setChangeType + */ + public int getChangeType() { + return super.getChangeType(); + } + + /** + * Gets the previous DN of the entry (if the entry was renamed). + * @returns The previous distinguished name of the entry. + * @see com.netscape.jndi.ldap.controls.LdapEntryChangeControl#setPreviousDN + */ + public String getPreviousDN() { + return super.getPreviousDN(); + } + + /** + * Implements Control interface + */ + public byte[] getEncodedValue() { + return getValue(); + } +} + diff --git a/mozilla/directory/java-sdk/ldapsp/com/netscape/jndi/ldap/controls/LdapPasswordExpiredControl.java b/mozilla/directory/java-sdk/ldapsp/com/netscape/jndi/ldap/controls/LdapPasswordExpiredControl.java new file mode 100644 index 00000000000..569c4113e3f --- /dev/null +++ b/mozilla/directory/java-sdk/ldapsp/com/netscape/jndi/ldap/controls/LdapPasswordExpiredControl.java @@ -0,0 +1,56 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * + * The contents of this file are subject to the Netscape Public License + * Version 1.0 (the "NPL"); you may not use this file except in + * compliance with the NPL. You may obtain a copy of the NPL at + * http://www.mozilla.org/NPL/ + * + * Software distributed under the NPL is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL + * for the specific language governing rights and limitations under the + * NPL. + * + * The Initial Developer of this code under the NPL is Netscape + * Communications Corporation. Portions created by Netscape are + * Copyright (C) 1998 Netscape Communications Corporation. All Rights + * Reserved. + */ +package com.netscape.jndi.ldap.controls; + +import javax.naming.ldap.Control; +import netscape.ldap.controls.*; + +/** + * Represents an LDAP v3 server control that may be returned if a + * password has expired, and password policy is enabled on the server. + * The OID for this control is 2.16.840.1.113730.3.4.4. + *

+ */ + +public class LdapPasswordExpiredControl extends LDAPPasswordExpiredControl implements Control{ + + private String m_message; + /** + * This constractor is used by the NetscapeControlFactory + */ + LdapPasswordExpiredControl(boolean critical, byte[] value) throws Exception{ + m_value = value; + m_critical = critical; + m_message = new String(m_value, "UTF8"); + } + + /** + * Return string message passed in the control + * TODO what is the information in this string value ? + */ + public String getMessage() { + return m_message; + } + + /** + * Implements Control interface + */ + public byte[] getEncodedValue() { + return getValue(); + } +} diff --git a/mozilla/directory/java-sdk/ldapsp/com/netscape/jndi/ldap/controls/LdapPasswordExpiringControl.java b/mozilla/directory/java-sdk/ldapsp/com/netscape/jndi/ldap/controls/LdapPasswordExpiringControl.java new file mode 100644 index 00000000000..2a573a0a255 --- /dev/null +++ b/mozilla/directory/java-sdk/ldapsp/com/netscape/jndi/ldap/controls/LdapPasswordExpiringControl.java @@ -0,0 +1,59 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * + * The contents of this file are subject to the Netscape Public License + * Version 1.0 (the "NPL"); you may not use this file except in + * compliance with the NPL. You may obtain a copy of the NPL at + * http://www.mozilla.org/NPL/ + * + * Software distributed under the NPL is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL + * for the specific language governing rights and limitations under the + * NPL. + * + * The Initial Developer of this code under the NPL is Netscape + * Communications Corporation. Portions created by Netscape are + * Copyright (C) 1998 Netscape Communications Corporation. All Rights + * Reserved. + */ +package com.netscape.jndi.ldap.controls; + +import javax.naming.ldap.Control; +import netscape.ldap.controls.*; + +/** + * Represents an LDAP v3 server control that may be returned if a + * password is about to expire, and password policy is enabled on the server. + * The OID for this control is 2.16.840.1.113730.3.4.5. + *

+ */ +public class LdapPasswordExpiringControl extends LDAPPasswordExpiringControl implements Control { + + //number of seconds before password expiration + int m_secondsToExpire = -1; + + /** + * This constractor is used by the NetscapeControlFactory + */ + LdapPasswordExpiringControl(boolean critical, byte[] value) throws Exception{ + m_value = value; + m_critical = critical; + String msg = new String(m_value, "UTF8"); + if (msg != null) { + m_secondsToExpire = Integer.parseInt(msg); + } + } + + /** + * Return parsed number of seconds before password expires + */ + public int getSecondsToExipre() { + return m_secondsToExpire; + } + + /** + * Implements Control interface + */ + public byte[] getEncodedValue() { + return getValue(); + } +} diff --git a/mozilla/directory/java-sdk/ldapsp/com/netscape/jndi/ldap/controls/LdapPersistSearchControl.java b/mozilla/directory/java-sdk/ldapsp/com/netscape/jndi/ldap/controls/LdapPersistSearchControl.java new file mode 100644 index 00000000000..32af3fa882a --- /dev/null +++ b/mozilla/directory/java-sdk/ldapsp/com/netscape/jndi/ldap/controls/LdapPersistSearchControl.java @@ -0,0 +1,194 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * + * The contents of this file are subject to the Netscape Public License + * Version 1.0 (the "NPL"); you may not use this file except in + * compliance with the NPL. You may obtain a copy of the NPL at + * http://www.mozilla.org/NPL/ + * + * Software distributed under the NPL is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL + * for the specific language governing rights and limitations under the + * NPL. + * + * The Initial Developer of this code under the NPL is Netscape + * Communications Corporation. Portions created by Netscape are + * Copyright (C) 1999 Netscape Communications Corporation. All Rights + * Reserved. + */ +package com.netscape.jndi.ldap.controls; + +import javax.naming.ldap.Control; +import netscape.ldap.controls.*; + +/** + * Represents an LDAP v3 server control that specifies a persistent + * search (an ongoing search operation), which allows your LDAP client + * to get notification of changes to the directory. (The OID for this + * control is 2.16.840.1.113730.3.4.3.) You can use this control in + * conjunction with an "entry change notification" control (represented + * by LDAPEntryChangeControl object. + *

+ * + * To use persistent searching for change notification, you create a + * "persistent search" control that specifies the types of changes that + * you want to track. You include the control in a search request. + * If an entry in the directory is changed, the server determines if + * the entry matches the search criteria in your request and if the + * change is the type of change that you are tracking. If both of + * these are true, the server sends the entry to your client. + *

+ * + * The server can also include an "entry change notification" control + * with the entry. (The OID for this control is 2.16.840.1.113730.3.4.7.) + * This control contains additional information about the + * change made to the entry, including the type of change made, + * the change number (which corresponds to an item in the server's + * change log, if the server supports a change log), and, if the + * entry was renamed, the old DN of the entry. + *

+ * + * When constructing an LDAPPersistSearchControl object, + * you can specify the following information: + *

+ * + *

    + *
  • the type of change you want to track (added, modified, deleted, + * or renamed entries) + *
  • a preference indicating whether or not you want the server to + * return all entries that initially matched the search criteria + * (rather than only the entries that change) + *
  • a preference indicating whether or not you want entry change + * notification controls included with every entry returned by the + * server + *
+ *

+ * @see com.netscape.jndi.ldap.controls.LDAPEntryChangeControl + */ + +public class LdapPersistSearchControl extends LDAPPersistSearchControl implements Control { + + /** + * Default constructor + */ + public LdapPersistSearchControl() { + } + + /** + * Constructs an LdapPersistSearchControl object + * that specifies a persistent search. + * + * @param changeTypes The change types to be monitored. You can perform + * a bitwise OR on any of the following values and specify the result as + * the change types: + *

    + *
  • LdapPersistSearchControl.ADD (to track new entries + * added to the directory) + *
  • LdapPersistSearchControl.DELETE (to track entries + * removed from the directory) + *
  • LdapPersistSearchControl.MODIFY (to track entries + * that have been modified) + *
  • LdapPersistSearchControl.MODDN (to track entries + * that have been renamed) + *
+ * @param changesOnly true if you do not want the server + * to return all existing entries in the directory that match the + * search criteria. (You just want the changed entries to be returned.) + * @param returnControls true you want the server to return + * entry change controls with each entry in the search results. + * @param isCritical true if this control is critical to + * the search operation (for example, if the server does not support + * this control, you may not want the server to perform the search + * at all.) + * @see com.netscape.jndi.ldap.controls.LdapEntryChangeControl + */ + public LdapPersistSearchControl(int changeTypes, boolean changesOnly, + boolean returnControls, boolean isCritical) { + super(changeTypes, changesOnly, returnControls, isCritical); + } + + /** + * Gets the change types monitored by this control. + * @return Integer representing the change types that you want monitored. + * This value can be the bitwise OR of ADD, DELETE, MODIFY, + * and/or MODDN. If the change type is unknown, + * this method returns -1. + * @see com.netscape.jndi.ldap.controls.LdapPersistSearchControl#setChangeTypes + */ + public int getChangeTypes() { + return super.getChangeTypes(); + } + + /** + * Indicates whether you want the server to send any existing + * entries that already match the search criteria or only the + * entries that have changed. + * @return If true, the server returns only the + * entries that have changed. If false, the server + * also returns any existing entries that match the search criteria + * but have not changed. + * @see com.netscape.jndi.ldap.controls.LdapPersistSearchControl#setChangesOnly + */ + public boolean getChangesOnly() { + return super.getChangesOnly(); + } + + /** + * Indicates whether or not the server includes an "entry change + * notification" control with each entry it sends back to the client + * during the persistent search. + * @return true if the server includes "entry change + * notification" controls with the entries it sends during the + * persistent search. + * @see com.netscape.jndi.ldap.controls.LdapEntryChangeControl + * @see com.netscape.jndi.ldap.controls.LdapPersistSearchControl#setReturnControls + */ + public boolean getReturnControls() { + return super.getReturnControls(); + } + + /** + * Sets the change types that you want monitored by this control. + * @param types Integer representing the change types that you want monitored. + * This value can be the bitwise OR of ADD, DELETE, MODIFY, + * and/or MODDN. + * @see com.netscape.jndi.ldap.controls.LdapPersistSearchControl#getChangeTypes + */ + public void setChangeTypes(int types) { + super.setChangeTypes(types); + } + + /** + * Specifies whether you want the server to send any existing + * entries that already match the search criteria or only the + * entries that have changed. + * @param changesOnly If true, the server returns only the + * entries that have changed. If false, the server + * also returns any existing entries that match the search criteria + * but have not changed. + * @see com.netscape.jndi.ldap.controls.LdapPersistSearchControl#getChangesOnly + */ + public void setChangesOnly(boolean changesOnly) { + super.setChangesOnly(changesOnly); + } + + /** + * Specifies whether you want the server to include an "entry change + * notification" control with each entry it sends back to the client + * during the persistent search. + * @param returnControls If true, the server includes + * "entry change notification" controls with the entries it sends + * during the persistent search. + * @see com.netscape.jndi.ldap.controls.LdapEntryChangeControl + * @see com.netscape.jndi.ldap.controls.LdapPersistSearchControl#setReturnControls + */ + public void setReturnControls(boolean returnControls) { + super.setReturnControls(returnControls); + } + + /** + * Implements Control interface + */ + public byte[] getEncodedValue() { + return getValue(); + } +} diff --git a/mozilla/directory/java-sdk/ldapsp/com/netscape/jndi/ldap/controls/LdapProxiedAuthControl.java b/mozilla/directory/java-sdk/ldapsp/com/netscape/jndi/ldap/controls/LdapProxiedAuthControl.java new file mode 100644 index 00000000000..ff5ed80b868 --- /dev/null +++ b/mozilla/directory/java-sdk/ldapsp/com/netscape/jndi/ldap/controls/LdapProxiedAuthControl.java @@ -0,0 +1,89 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * + * The contents of this file are subject to the Netscape Public License + * Version 1.0 (the "NPL"); you may not use this file except in + * compliance with the NPL. You may obtain a copy of the NPL at + * http://www.mozilla.org/NPL/ + * + * Software distributed under the NPL is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL + * for the specific language governing rights and limitations under the + * NPL. + * + * The Initial Developer of this code under the NPL is Netscape + * Communications Corporation. Portions created by Netscape are + * Copyright (C) 1999 Netscape Communications Corporation. All Rights + * Reserved. + */ +package com.netscape.jndi.ldap.controls; + +import javax.naming.ldap.Control; +import netscape.ldap.controls.*; + +/** + * Represents an LDAP v3 server control that specifies that you want + * the server to use the specified DN's identity for this operation. + * (The OID for this control is 2.16.840.1.113730.3.4.12.) + *

+ * + * You can include the control in any request by constructing + * an LDAPSearchConstraints object and calling the + * setServerControls method. You can then pass this + * LDAPSearchConstraints object to the search + * or other request method of an LDAPConnection object. + *

+ * + * For example: + *

+ * ...
+ * LDAPConnection ld = new LDAPConnection();
+ * try {
+ *     // Connect to server.
+ *     ld.connect( 3, hostname, portnumber, "", "" );
+ *
+ *     // Create a "critical" proxied auth server control using
+ *     // the DN "uid=charlie,ou=people,o=acme.com".
+ *     LDAPProxiedAuthControl ctrl =
+ *         new LDAPProxiedAuthControl( "uid=charlie,ou=people,o=acme.com",
+ *                                     true );
+ *
+ *     // Create search constraints to use that control.
+ *     LDAPSearchConstraints cons = new LDAPSearchConstraints();
+ *     cons.setServerControls( sortCtrl );
+ *
+ *     // Send the search request.
+ *     LDAPSearchResults res = ld.search( "o=Airius.com",
+ *        LDAPv3.SCOPE_SUB, "(cn=Barbara*)", null, false, cons );
+ *
+ *     ...
+ *
+ * 
+ * + *

+ * @see com.netscape.jndi.ldap.LdapSearchConstraints + * @see com.netscape.jndi.ldap.LdapSearchConstraints#setServerControls(LDAPControl) + */ +public class LdapProxiedAuthControl extends LDAPProxiedAuthControl implements Control { + + /** + * Constructs an LdapProxiedAuthControl object with a + * DN to use as identity. + * @param dn DN to use as identity for execution of a request. + * @param critical true if the LDAP operation should be + * discarded when the server does not support this control (in other + * words, this control is critical to the LDAP operation). + */ + public LdapProxiedAuthControl( String dn, + boolean critical) { + super( dn, critical); + } + + /** + * Implements Control interface + */ + public byte[] getEncodedValue() { + return getValue(); + } + +} + diff --git a/mozilla/directory/java-sdk/ldapsp/com/netscape/jndi/ldap/controls/LdapSortControl.java b/mozilla/directory/java-sdk/ldapsp/com/netscape/jndi/ldap/controls/LdapSortControl.java new file mode 100644 index 00000000000..23157a5fcc4 --- /dev/null +++ b/mozilla/directory/java-sdk/ldapsp/com/netscape/jndi/ldap/controls/LdapSortControl.java @@ -0,0 +1,257 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * + * The contents of this file are subject to the Netscape Public License + * Version 1.0 (the "NPL"); you may not use this file except in + * compliance with the NPL. You may obtain a copy of the NPL at + * http://www.mozilla.org/NPL/ + * + * Software distributed under the NPL is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL + * for the specific language governing rights and limitations under the + * NPL. + * + * The Initial Developer of this code under the NPL is Netscape + * Communications Corporation. Portions created by Netscape are + * Copyright (C) 1998 Netscape Communications Corporation. All Rights + * Reserved. + */ +package com.netscape.jndi.ldap.controls; + +import javax.naming.ldap.Control; +import netscape.ldap.controls.*; + +/** + * Represents an LDAP v3 server control that specifies that you want + * the server to return sorted search results. (The OID for this + * control is 1.2.840.113556.1.4.473.) + *

+ * + * When constructing an LDAPSortControl object, you can + * specify the order in which you want the results sorted. + * You can also specify whether or not this control is critical + * to the search operation. + *

+ * + * To specify the sort order, you construct an LDAPSortKey + * object and pass it to the LDAPSortControl constructor. + * The LDAPSortKey object represents a list of the attribute + * types used for sorting (a "sort key list"). + *

+ * + * You can include the control in a search request by constructing + * an LDAPSearchConstraints object and calling the + * setServerControls method. You can then pass this + * LDAPSearchConstraints object to the search + * method of an LDAPConnection object. + *

+ * + * For example: + *

+ * ...
+ * LDAPConnection ld = new LDAPConnection();
+ * try {
+ *     // Connect to server.
+ *     ld.connect( 3, hostname, portnumber, "", "" );
+ *
+ *     // Create a sort key that specifies the sort order.
+ *     LDAPSortKey sortOrder = new LDAPSortKey( attrname );
+ *
+ *     // Create a "critical" server control using that sort key.
+ *     LDAPSortControl sortCtrl = new LDAPSortControl(sortOrder,true);
+ *
+ *     // Create search constraints to use that control.
+ *     LDAPSearchConstraints cons = new LDAPSearchConstraints();
+ *     cons.setServerControls( sortCtrl );
+ *
+ *     // Send the search request.
+ *     LDAPSearchResults res = ld.search( "o=Airius.com",
+ *        LDAPv3.SCOPE_SUB, "(cn=Barbara*)", null, false, cons );
+ *
+ *     ...
+ *
+ * 
+ * + * The LDAP server sends back a sort response control to indicate + * the result of the sorting operation. (The OID for this control + * is 1.2.840.113556.1.4.474.) + *

+ * + * This control contains: + *

+ * + *

    + *
  • the result code from the sorting operation + *
  • optionally, the first attribute type in the sort key list + * that resulted in an error (for example, if the attribute does + * not exist) + *
+ *

+ * + * To parse this control, use the parseResponse method. + *

+ * + * The following table lists what kinds of results to expect from the + * LDAP server under different situations: + *

+ * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + *
Does the Server Support the Sorting Control?Is the Sorting Control Marked As Critical?Other ConditionsResults from LDAP Server
+ * No + * + * Yes + * + * None + * + *
    + *
  • The server does not send back any entries. + *
  • An LDAPException.UNAVAILABLE_CRITICAL_EXTENSION + * exception is thrown. + *
+ *
+ * No + * + * None + * + *
    + *
  • The server ignores the sorting control and + * returns the entries unsorted. + *
+ *

+ *

+ * Yes + * + * Yes + * + * The server cannot sort the results using the specified + * sort key list. + * + *
    + *
  • The server does not send back any entries. + *
  • An LDAPException.UNAVAILABLE_CRITICAL_EXTENSION + * exception is thrown. + *
  • The server sends back the sorting response control, which + * specifies the result code of the sort attempt and (optionally) + * the attribute type that caused the error. + *
+ *
+ * No + * + *
    + *
  • The server returns the entries unsorted. + *
  • The server sends back the sorting response control, which + * specifies the result code of the sort attempt and (optionally) + * the attribute type that caused the error. + *
+ *
+ * N/A (could either be marked as critical or not) + * + * The server successfully sorted the entries. + * + *
    + *
  • The server sends back the sorted entries. + *
  • The server sends back the sorting response control, which + * specifies the result code of the sort attempt + * (LDAPException.SUCCESS). + *
+ *
+ * The search itself failed (for any reason). + * + *
    + *
  • The server sends back a result code for the search + * operation. + *
  • The server does not send back the sorting response control. + *
+ *
+ *

+ * @see com.netscape.jndi.ldap.LdapSortKey + * @see com.netscape.jndi.ldap.LdapSearchConstraints + * @see com.netscape.jndi.ldap.LdapSearchConstraints#setServerControls(LDAPControl) + */ +public class LdapSortControl extends LDAPSortControl implements Control{ + /** + * Constructs an LDAPSortControl object with a single + * sorting key. + * @param key A single attribute to sort by. + * @param critical true if the LDAP operation should be + * discarded when the server does not support this control (in other + * words, this control is critical to the LDAP operation). + * @see com.netscape.jndi.ldap.LdapSortKey + */ + public LdapSortControl(LdapSortKey key, + boolean critical) { + super (key, critical); + } + + /** + * Constructs an LDAPSortControl object with an array of + * sorting keys. + * @param keys The attributes to sort by. + * @param critical true if the LDAP operation should be + * discarded when the server does not support this control (in other + * words, this control is critical to the LDAP operation). + * @see com.netscape.jndi.ldap.LdapSortKey + */ + public LdapSortControl(LdapSortKey[] keys, + boolean critical) { + super(keys, critical); + } + + + static LdapSortKey[] toSortKey(String[] keysIn) { + LdapSortKey[] keysOut = new LdapSortKey[keysIn.length]; + for (int i=0; i < keysIn.length; i++) { + keysOut[i] = new LdapSortKey(keysIn[i]); + } + return keysOut; + } + + public LdapSortControl(String[] keys, + boolean critical) { + super(toSortKey(keys), critical); + } + + + /** + * Implements Control interface + */ + public byte[] getEncodedValue() { + return getValue(); + } +} + diff --git a/mozilla/directory/java-sdk/ldapsp/com/netscape/jndi/ldap/controls/LdapSortKey.java b/mozilla/directory/java-sdk/ldapsp/com/netscape/jndi/ldap/controls/LdapSortKey.java new file mode 100644 index 00000000000..2adabe6f97e --- /dev/null +++ b/mozilla/directory/java-sdk/ldapsp/com/netscape/jndi/ldap/controls/LdapSortKey.java @@ -0,0 +1,124 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * + * The contents of this file are subject to the Netscape Public License + * Version 1.0 (the "NPL"); you may not use this file except in + * compliance with the NPL. You may obtain a copy of the NPL at + * http://www.mozilla.org/NPL/ + * + * Software distributed under the NPL is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL + * for the specific language governing rights and limitations under the + * NPL. + * + * The Initial Developer of this code under the NPL is Netscape + * Communications Corporation. Portions created by Netscape are + * Copyright (C) 1999 Netscape Communications Corporation. All Rights + * Reserved. + */ +package com.netscape.jndi.ldap.controls; + +import netscape.ldap.LDAPSortKey; + +/** + * Represents sorting instructions for a particular attribute. + * + */ +public class LdapSortKey extends LDAPSortKey{ + + /** + * Constructs a new LdapSortKey object that will + * sort based on the specified instructions. + * @param keyDescription A single attribute specification to sort by. + * If preceded by a hyphen ("-"), the attribute is sorted in reverse order. + * You can also specify the object ID (OID) of a matching rule after + * a colon (":"). For example: + *

+ *

    + *
  • "cn" (sort by the cn attribute)

    + *

  • "-cn" (sort by the cn attribute in + * reverse order)

    + *

  • "-cn:1.2.3.4" (sort by the cn + * attribute in reverse order and use the matching rule identified + * by the OID 1.2.3.4)

    + *

+ * @see com.netscape.jndi.ldap.controls.LdapPagedControl + * @see com.netscape.jndi.ldap.controls.LdapSortControl + * @see com.netscape.jndi.ldap.controls.LdapVirtualListControl + */ + public LdapSortKey( String keyDescription ) { + super(keyDescription); + } + + /** + * Constructs a new LdapSortKey object that will + * sort based on the specified attribute and sort order. + * @param key A single attribute to sort by. For example: + *

+ *

    + *
  • "cn" (sort by the cn attribute) + *
  • "givenname" (sort by the givenname + * attribute) + *
+ * @param reverse If true, the sorting is done in + * descending order. + * @see com.netscape.jndi.ldap.controls.LdapPagedControl + * @see com.netscape.jndi.ldap.controls.LdapSortControl + * @see com.netscape.jndi.ldap.controls.LdapVirtualListControl + */ + public LdapSortKey( String key, + boolean reverse) { + super(key,reverse); + } + + /** + * Constructs a new LdapSortKey object that will + * sort based on the specified attribute, sort order, and matching + * rule. + * @param key A single attribute to sort by. For example: + *

+ *

    + *
  • "cn" (sort by the cn attribute) + *
  • "givenname" (sort by the givenname + * attribute) + *
+ * @param reverse If true, the sorting is done in + * descending order. + * @param matchRule Object ID (OID) of the matching rule for + * the attribute (for example, 1.2.3.4). + * @see com.netscape.jndi.ldap.controls.LdapPagedControl + * @see com.netscape.jndi.ldap.controls.LdapSortControl + * @see com.netscape.jndi.ldap.controls.LdapVirtualListControl + */ + public LdapSortKey( String key, + boolean reverse, + String matchRule) { + super(key, reverse, matchRule); + } + + /** + * Returns the attribute to sort by. + * @return A single attribute to sort by. + */ + public String getKey() { + return super.getKey(); + } + + /** + * Returns true if sorting is to be done in descending order. + * @return true if sorting is to be done in descending order. + */ + public boolean getReverse() { + return super.getReverse(); + } + + /** + * Returns the object ID (OID) of the matching rule used for sorting. + * If no matching rule is specified, null is returned. + * @return The object ID (OID) of the matching rule, or null + * if the sorting instructions specify no matching rule. + */ + public String getMatchRule() { + return super.getMatchRule(); + } +} + diff --git a/mozilla/directory/java-sdk/ldapsp/com/netscape/jndi/ldap/controls/LdapSortResponseControl.java b/mozilla/directory/java-sdk/ldapsp/com/netscape/jndi/ldap/controls/LdapSortResponseControl.java new file mode 100644 index 00000000000..5ad9d9e796e --- /dev/null +++ b/mozilla/directory/java-sdk/ldapsp/com/netscape/jndi/ldap/controls/LdapSortResponseControl.java @@ -0,0 +1,223 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * + * The contents of this file are subject to the Netscape Public License + * Version 1.0 (the "NPL"); you may not use this file except in + * compliance with the NPL. You may obtain a copy of the NPL at + * http://www.mozilla.org/NPL/ + * + * Software distributed under the NPL is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL + * for the specific language governing rights and limitations under the + * NPL. + * + * The Initial Developer of this code under the NPL is Netscape + * Communications Corporation. Portions created by Netscape are + * Copyright (C) 1999 Netscape Communications Corporation. All Rights + * Reserved. + */ +package com.netscape.jndi.ldap.controls; + +import javax.naming.NamingException; +import javax.naming.ldap.Control; +import netscape.ldap.LDAPControl; +import netscape.ldap.LDAPException; +import netscape.ldap.controls.*; +import com.netscape.jndi.ldap.common.ExceptionMapper; + +/** + * The LDAP server sends back a sort response control to indicate + * the result of the sorting operation. (The OID for this control + * is 1.2.840.113556.1.4.474.) + *

+ * + * This control contains: + *

+ * + *

    + *
  • the result code from the sorting operation + *
  • optionally, the first attribute type in the sort key list + * that resulted in an error (for example, if the attribute does + * not exist) + *
+ *

+ * + * To parse this control, use the parseResponse method. + *

+ * + * The following table lists what kinds of results to expect from the + * LDAP server under different situations: + *

+ * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + *
Does the Server Support the Sorting Control?Is the Sorting Control Marked As Critical?Other ConditionsResults from LDAP Server
+ * No + * + * Yes + * + * None + * + *
    + *
  • The server does not send back any entries. + *
  • An LDAPException.UNAVAILABLE_CRITICAL_EXTENSION + * exception is thrown. + *
+ *
+ * No + * + * None + * + *
    + *
  • The server ignores the sorting control and + * returns the entries unsorted. + *
+ *

+ *

+ * Yes + * + * Yes + * + * The server cannot sort the results using the specified + * sort key list. + * + *
    + *
  • The server does not send back any entries. + *
  • An LDAPException.UNAVAILABLE_CRITICAL_EXTENSION + * exception is thrown. + *
  • The server sends back the sorting response control, which + * specifies the result code of the sort attempt and (optionally) + * the attribute type that caused the error. + *
+ *
+ * No + * + *
    + *
  • The server returns the entries unsorted. + *
  • The server sends back the sorting response control, which + * specifies the result code of the sort attempt and (optionally) + * the attribute type that caused the error. + *
+ *
+ * N/A (could either be marked as critical or not) + * + * The server successfully sorted the entries. + * + *
    + *
  • The server sends back the sorted entries. + *
  • The server sends back the sorting response control, which + * specifies the result code of the sort attempt + * (LDAPException.SUCCESS). + *
+ *
+ * The search itself failed (for any reason). + * + *
    + *
  • The server sends back a result code for the search + * operation. + *
  • The server does not send back the sorting response control. + *
+ *
+ *

+ * @see com.netscape.jndi.ldap.LdapSortKey + * @see com.netscape.jndi.ldap.LdapSearchConstraints + * @see com.netscape.jndi.ldap.LdapSearchConstraints#setServerControls(LDAPControl) + */ +public class LdapSortResponseControl extends LDAPControl implements Control { + + String m_failedAttribute; + int m_resultCode; + /** + * Constructs a new LdapEntryChangeControl object. + * This constructor is used by the NetscapeControlFactory + * + * @see com.netscape.jndi.ldap.controls.LdapPersistSearchControl + */ + LdapSortResponseControl(boolean critical, byte[] value) { + super(LDAPSortControl.SORTRESPONSE, critical, value); + } + + /** + * Set the first attribute type from the sort key list that + * resulted in an error + * This method is used by the NetscapeControlFactory + */ + void setFailedAttribute(String attr) { + m_failedAttribute = attr; + } + + /** + * Set the sort result code + * This method is used by the NetscapeControlFactory + */ + void setResultCode(int code) { + m_resultCode = code; + } + + /** + * Get the first attribute type from the sort key list that + * resulted in an error + */ + public String getFailedAttribute() { + return m_failedAttribute; + } + + /** + * Return the sort result code + */ + public int getResultCode() { + return m_resultCode; + } + + /** + * Return corresponding NamingException for the sort error code + */ + public NamingException getSortException() { + if (m_resultCode == 0) { // success + return null; + } + return ExceptionMapper.getNamingException( + new LDAPException("Server Sort Failed", m_resultCode)); + } + /** + * Implements Control interface + */ + public byte[] getEncodedValue() { + return getValue(); + } +} + diff --git a/mozilla/directory/java-sdk/ldapsp/com/netscape/jndi/ldap/controls/LdapStringControl.java b/mozilla/directory/java-sdk/ldapsp/com/netscape/jndi/ldap/controls/LdapStringControl.java new file mode 100644 index 00000000000..8b137891791 --- /dev/null +++ b/mozilla/directory/java-sdk/ldapsp/com/netscape/jndi/ldap/controls/LdapStringControl.java @@ -0,0 +1 @@ + diff --git a/mozilla/directory/java-sdk/ldapsp/com/netscape/jndi/ldap/controls/LdapVirtualListControl.java b/mozilla/directory/java-sdk/ldapsp/com/netscape/jndi/ldap/controls/LdapVirtualListControl.java new file mode 100644 index 00000000000..17f40f6ef2a --- /dev/null +++ b/mozilla/directory/java-sdk/ldapsp/com/netscape/jndi/ldap/controls/LdapVirtualListControl.java @@ -0,0 +1,147 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * + * The contents of this file are subject to the Netscape Public License + * Version 1.0 (the "NPL"); you may not use this file except in + * compliance with the NPL. You may obtain a copy of the NPL at + * http://www.mozilla.org/NPL/ + * + * Software distributed under the NPL is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL + * for the specific language governing rights and limitations under the + * NPL. + * + * The Initial Developer of this code under the NPL is Netscape + * Communications Corporation. Portions created by Netscape are + * Copyright (C) 1998 Netscape Communications Corporation. All Rights + * Reserved. + */ +package com.netscape.jndi.ldap.controls; + +import javax.naming.ldap.Control; +import netscape.ldap.controls.*; + +/** + * Represents control data for returning paged results from a search. + * + *

+ *      VirtualListViewRequest ::= SEQUENCE {
+ *                      beforeCount    INTEGER,
+ *                      afterCount     INTEGER,
+ *                      CHOICE {
+ *                      byIndex [0] SEQUENCE {
+ *                          index           INTEGER,
+ *                          contentCount    INTEGER }
+ *                      byFilter [1] jumpTo    Substring }
+ * 
+ * + */ + +public class LdapVirtualListControl extends LDAPVirtualListControl implements Control { + + /** + * Constructs a new LDAPVirtualListControl object. Use this + * constructor on an initial search operation, specifying the first + * entry to be matched, or the initial part of it. + * @param jumpTo An LDAP search expression defining the result set. + * @param beforeCount The number of results before the top/center to + * return per page. + * @param afterCount The number of results after the top/center to + * return per page. + */ + public LdapVirtualListControl( String jumpTo, int beforeCount, + int afterCount ) { + super( jumpTo, beforeCount, afterCount); + } + + /** + * Constructs a new LDAPVirtualListControl object. Use this + * constructor on a subsquent search operation, after we know the + * size of the virtual list, to fetch a subset. + * @param startIndex The index into the virtual list of an entry to + * return. + * @param beforeCount The number of results before the top/center to + * return per page. + * @param afterCount The number of results after the top/center to + * return per page. + */ + public LdapVirtualListControl( int startIndex, int beforeCount, + int afterCount, int contentCount ) { + super( startIndex, beforeCount, afterCount, contentCount ); + } + + /** + * Sets the starting index, and the number of entries before and after + * to return. Apply this method to a control returned from a previous + * search, to specify what result range to return on the next search. + * @param startIndex The index into the virtual list of an entry to + * return. + * @param beforeCount The number of results before startIndex to + * return per page. + * @param afterCount The number of results after startIndex to + * return per page. + */ + public void setRange( int startIndex, int beforeCount, int afterCount ) { + super.setRange(startIndex, beforeCount, afterCount); + } + + /** + * Sets the search expression, and the number of entries before and after + * to return. + * @param jumpTo An LDAP search expression defining the result set. + * return. + * @param beforeCount The number of results before startIndex to + * return per page. + * @param afterCount The number of results after startIndex to + * return per page. + */ + public void setRange( String jumpTo, int beforeCount, int afterCount ) { + super.setRange(jumpTo, beforeCount, afterCount); + } + + /** + * Gets the size of the virtual result set. + * @return The size of the virtual result set, or -1 if not known. + */ + public int getIndex() { + return super.getIndex(); + } + + /** + * Gets the size of the virtual result set. + * @return The size of the virtual result set, or -1 if not known. + */ + public int getListSize() { + return super.getListSize(); + } + + /** + * Sets the size of the virtual result set. + * @param listSize The virtual result set size. + */ + public void setListSize( int listSize ) { + super.setListSize(listSize); + } + + /** + * Gets the number of results before the top/center to return per page. + * @return The number of results before the top/center to return per page. + */ + public int getBeforeCount() { + return super.getBeforeCount(); + } + + /** + * Gets the number of results after the top/center to return per page. + * @return The number of results after the top/center to return per page. + */ + public int getAfterCount() { + return super.getAfterCount(); + } + + /** + * Implements Control interface + */ + public byte[] getEncodedValue() { + return getValue(); + } +} diff --git a/mozilla/directory/java-sdk/ldapsp/com/netscape/jndi/ldap/controls/LdapVirtualListResponseControl.java b/mozilla/directory/java-sdk/ldapsp/com/netscape/jndi/ldap/controls/LdapVirtualListResponseControl.java new file mode 100644 index 00000000000..15c8d86877d --- /dev/null +++ b/mozilla/directory/java-sdk/ldapsp/com/netscape/jndi/ldap/controls/LdapVirtualListResponseControl.java @@ -0,0 +1,90 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * + * The contents of this file are subject to the Netscape Public License + * Version 1.0 (the "NPL"); you may not use this file except in + * compliance with the NPL. You may obtain a copy of the NPL at + * http://www.mozilla.org/NPL/ + * + * Software distributed under the NPL is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL + * for the specific language governing rights and limitations under the + * NPL. + * + * The Initial Developer of this code under the NPL is Netscape + * Communications Corporation. Portions created by Netscape are + * Copyright (C) 1999 Netscape Communications Corporation. All Rights + * Reserved. + */ +package com.netscape.jndi.ldap.controls; + +import javax.naming.ldap.Control; +import netscape.ldap.controls.*; + + +/** + * Represents control data for returning paged results from a search. + * + * + *
+ *      VirtualListViewResponse ::= SEQUENCE {
+ *               firstPosition    INTEGER,
+ *               contentCount     INTEGER,
+ *               virtualListViewResult ENUMERATED {
+ *                 success                  (0),
+ *                 unwillingToPerform       (53),
+ *                 insufficientAccessRights (50),
+ *                 operationsError          (1),
+ *                 busy                     (51),
+ *                 timeLimitExceeded        (3),
+ *                 adminLimitExceeded       (11),
+ *                 sortControlMissing       (60),
+ *                 indexRangeError          (?),
+ *               }
+ *     }
+ *
+ */ + +public class LdapVirtualListResponseControl extends LDAPVirtualListResponse implements Control{ + + /** + * Constructs a new LDAPVirtualListResponse object. + * @param value A BER encoded byte array. + * This constructor is used by the NetscapeControlFactory + */ + LdapVirtualListResponseControl( byte[] value ) { + // The superclass constractor parses the byte[] value + // automatically. No need to call parseResponse + super(value); + } + + /** + * Gets the size of the virtual result set. + * @return The size of the virtual result set, or -1 if not known. + */ + public int getContentCount() { + return super.getContentCount(); + } + + /** + * Gets the index of the first entry returned. + * @return The index of the first entry returned. + */ + public int getFirstPosition() { + return super.getFirstPosition(); + } + + /** + * Gets the result code. + * @return The result code. + */ + public int getResultCode() { + return super.getResultCode(); + } + + /** + * Implements Control interface + */ + public byte[] getEncodedValue() { + return getValue(); + } +} diff --git a/mozilla/directory/java-sdk/ldapsp/com/netscape/jndi/ldap/controls/NetscapeControlFactory.java b/mozilla/directory/java-sdk/ldapsp/com/netscape/jndi/ldap/controls/NetscapeControlFactory.java new file mode 100644 index 00000000000..6bb1c82fc4b --- /dev/null +++ b/mozilla/directory/java-sdk/ldapsp/com/netscape/jndi/ldap/controls/NetscapeControlFactory.java @@ -0,0 +1,142 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * + * The contents of this file are subject to the Netscape Public License + * Version 1.0 (the "NPL"); you may not use this file except in + * compliance with the NPL. You may obtain a copy of the NPL at + * http://www.mozilla.org/NPL/ + * + * Software distributed under the NPL is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL + * for the specific language governing rights and limitations under the + * NPL. + * + * The Initial Developer of this code under the NPL is Netscape + * Communications Corporation. Portions created by Netscape are + * Copyright (C) 1999 Netscape Communications Corporation. All Rights + * Reserved. + */ +package com.netscape.jndi.ldap.controls; + + +/** + * Factory for creating controls. Only controls send by the direcory server + * are processed. + */ + +import javax.naming.*; +import javax.naming.ldap.*; +import netscape.ldap.LDAPControl; +import netscape.ldap.controls.*; +import com.netscape.jndi.ldap.common.ExceptionMapper; + +public class NetscapeControlFactory extends ControlFactory { + + // "1.2.840.113556.1.4.473" Sort Control (Request) + final static String REQ_SORT = LDAPSortControl.SORTREQUEST; + + // "1.2.840.113556.1.4.474" Sort Control (Response) + final static String RSP_SORT = LDAPSortControl.SORTRESPONSE; + + // "2.16.840.1.113730.3.4.2" ManageDSAIT Control + final static String REQ_MANAGEDSAIT = LDAPControl.MANAGEDSAIT; + + // "2.16.840.1.113730.3.4.3" PersistentSearch Control + final static String REQ_PERSISTENTSEARCH = LDAPPersistSearchControl.PERSISTENTSEARCH; + + // "2.16.840.1.113730.3.4.4" PasswordExpired Control + final static String RSP_PWDEXPIRED = LDAPPasswordExpiredControl.EXPIRED; + + // "2.16.840.1.113730.3.4.5" PasswordExpiring Control + final static String RSP_PWDEXPIRING = LDAPPasswordExpiringControl.EXPIRING; + + // "2.16.840.1.113730.3.4.7" EntryChanged Controle + final static String RSP_ENTRYCHANGED = LDAPEntryChangeControl.ENTRYCHANGED; + + // "2.16.840.1.113730.3.4.9" Virtual List (Request) + final static String REQ_VIRTUALLIST = LDAPVirtualListControl.VIRTUALLIST; + + // "2.16.840.1.113730.3.4.10" Virtual List (Response) + final static String RSP_VIRTUALLIST = LDAPVirtualListResponse.VIRTUALLISTRESPONSE; + + // "2.16.840.1.113730.3.4.12" Proxed Authentication + final static String REQ_PROXIEDAUTH = LDAPProxiedAuthControl.PROXIEDAUTHREQUEST; + + + /** + * Implements abstract getControlInstance() from ConrolFactory + */ + public Control getControlInstance(Control ctrl) throws NamingException { + if (ctrl == null) { + return null; + } + LDAPControl rawCtrl = new LDAPControl( + ctrl.getID(), ctrl.isCritical(), ctrl.getEncodedValue()); + return getControlInstance(rawCtrl); + } + + /** + * Create control using parseResponse() methods in ldapjdk controls + */ + public static Control getControlInstance(LDAPControl rawCtrl) throws NamingException { + if (rawCtrl == null) { + return null; + } + + try { + String ctrlID = rawCtrl.getID(); + + // Entry changed control is parsed by LDAPPersistSearchControl + if (ctrlID.equals(RSP_ENTRYCHANGED)) { + // First get ldapjdk object + LDAPEntryChangeControl ldapjdkCtrl = + LDAPPersistSearchControl.parseResponse(new LDAPControl[] {rawCtrl}); + // then map it to a jndi object + if (ldapjdkCtrl != null) { + LdapEntryChangeControl ctrl = new LdapEntryChangeControl( + rawCtrl.isCritical(), rawCtrl.getValue()); + ctrl.setChangeNumber(ldapjdkCtrl.getChangeNumber()); + ctrl.setChangeType(ldapjdkCtrl.getChangeType()); + ctrl.setPreviousDN(ldapjdkCtrl.getPreviousDN()); + return ctrl; + } + } + + // Password Expired control + else if(ctrlID.equals(RSP_PWDEXPIRED)) { + LdapPasswordExpiredControl ctrl = new LdapPasswordExpiredControl( + rawCtrl.isCritical(), rawCtrl.getValue()); + } + + // Password Expiring control + else if(ctrlID.equals(RSP_PWDEXPIRING)) { + LdapPasswordExpiringControl ctrl = new LdapPasswordExpiringControl( + rawCtrl.isCritical(), rawCtrl.getValue()); + } + + // Sort Response control + else if(ctrlID.equals(RSP_SORT)) { + // First parse the control + int[] resultCodes = new int[1]; + String failedAttr = LDAPSortControl.parseResponse( + new LDAPControl[] {rawCtrl}, resultCodes); + + LdapSortResponseControl ctrl = new LdapSortResponseControl( + rawCtrl.isCritical(), rawCtrl.getValue()); + ctrl.setResultCode(resultCodes[0]); + ctrl.setFailedAttribute(failedAttr); + return ctrl; + } + + // Virtual List Response control + else if(ctrlID.equals(RSP_VIRTUALLIST)) { + return new LdapVirtualListResponseControl(rawCtrl.getValue()); + } + + // No match try another ControlFactory + return null; + } + catch (Exception ex) { + throw ExceptionMapper.getNamingException(ex); + } + } +} diff --git a/mozilla/directory/java-sdk/ldapsp/com/netscape/jndi/ldap/schema/SchemaAttribute.java b/mozilla/directory/java-sdk/ldapsp/com/netscape/jndi/ldap/schema/SchemaAttribute.java new file mode 100644 index 00000000000..af8ea990e82 --- /dev/null +++ b/mozilla/directory/java-sdk/ldapsp/com/netscape/jndi/ldap/schema/SchemaAttribute.java @@ -0,0 +1,209 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * + * The contents of this file are subject to the Netscape Public License + * Version 1.0 (the "NPL"); you may not use this file except in + * compliance with the NPL. You may obtain a copy of the NPL at + * http://www.mozilla.org/NPL/ + * + * Software distributed under the NPL is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL + * for the specific language governing rights and limitations under the + * NPL. + * + * The Initial Developer of this code under the NPL is Netscape + * Communications Corporation. Portions created by Netscape are + * Copyright (C) 1999 Netscape Communications Corporation. All Rights + * Reserved. + */ +package com.netscape.jndi.ldap.schema; + +import javax.naming.*; +import javax.naming.directory.*; +import javax.naming.ldap.*; + +import netscape.ldap.*; +import netscape.ldap.controls.*; + +import java.util.*; + +public class SchemaAttribute extends SchemaElement { + + LDAPAttributeSchema m_ldapAttribute; + + // Attribute IDs that are exposed through Netscape LdapJDK + private static String[] m_allAttrIds = {NUMERICOID, NAME, DESC, SYNTAX, SINGLEVALUE }; + + public SchemaAttribute(LDAPAttributeSchema ldapAttribute, SchemaManager schemaManager) { + super(schemaManager); + m_ldapAttribute = ldapAttribute; + m_path = ATTRDEF + "/" + m_ldapAttribute.getName(); + } + + public SchemaAttribute(Attributes attrs, SchemaManager schemaManager) throws NamingException { + super(schemaManager); + m_ldapAttribute = parseDefAttributes(attrs); + m_path = ATTRDEF + "/" + m_ldapAttribute.getName(); + } + + /** + * Parse Definition Attributes for a LDAP attribute + */ + static LDAPAttributeSchema parseDefAttributes(Attributes attrs) throws NamingException { + String name="", oid="", desc=""; + int syntax=-1; + boolean singleValued = false; + + for (Enumeration attrEnum = attrs.getAll(); attrEnum.hasMoreElements(); ) { + Attribute attr = (Attribute) attrEnum.nextElement(); + String attrName = attr.getID(); + if (attrName.equals(NAME)) { + for (Enumeration valEnum = attr.getAll(); valEnum.hasMoreElements(); ) { + name = (String)valEnum.nextElement(); + break; + } + } + else if (attrName.equals(NUMERICOID)) { + for (Enumeration valEnum = attr.getAll(); valEnum.hasMoreElements(); ) { + oid = (String)valEnum.nextElement(); + break; + } + } + else if (attrName.equals(SYNTAX)) { + for (Enumeration valEnum = attr.getAll(); valEnum.hasMoreElements(); ) { + String syntaxString = (String)valEnum.nextElement(); + syntax = syntaxStringToInt(syntaxString); + break; + } + } + else if (attrName.equals(DESC)) { + for (Enumeration valEnum = attr.getAll(); valEnum.hasMoreElements(); ) { + desc = (String)valEnum.nextElement(); + break; + } + } + else if (attrName.equals(SINGLEVALUE)) { + for (Enumeration valEnum = attr.getAll(); valEnum.hasMoreElements(); ) { + String flag = (String)valEnum.nextElement(); + if (flag.equals("true")) { + singleValued = true; + } + else if (flag.equals("false")) { + singleValued = false; + } + else { + throw new InvalidAttributeValueException(attrName); + } + break; + } + } + else if (attrName.equals(OBSOLETE) || attrName.equals(SUP) || attrName.equals(USAGE) || + attrName.equals(EQUALITY) || attrName.equals(ORDERING) || + attrName.equals(COLLECTIVE) || attrName.equals(NOUSERMOD)) { + ; //ignore + } + else { + throw new NamingException("Invalid schema attribute type for attribute definition " + attrName); + } + } + + if (name.length() == 0 || oid.length() == 0 || syntax == -1) { + throw new NamingException("Incomplete schema attribute definition"); + } + + return new LDAPAttributeSchema(name, oid, desc, syntax, singleValued); + } + + + /** + * Exctract specified attributes from the ldapObjectClass + */ + Attributes extractAttributeIds(String[] attrIds) throws NamingException{ + Attributes attrs = new BasicAttributes(); + for (int i = 0; i < attrIds.length; i++) { + if (attrIds[i].equals(NUMERICOID)) { + attrs.put(new BasicAttribute(NUMERICOID, m_ldapAttribute.getOID())); + } + else if (attrIds[i].equals(NAME)) { + attrs.put(new BasicAttribute(NAME, m_ldapAttribute.getName())); + } + else if (attrIds[i].equals(DESC)) { + attrs.put(new BasicAttribute(DESC, m_ldapAttribute.getDescription())); + } + else if (attrIds[i].equals(SYNTAX)) { + attrs.put(new BasicAttribute(SYNTAX, syntaxIntToString(m_ldapAttribute.getSyntax()))); + } + else if (attrIds[i].equals(SINGLEVALUE)) { + attrs.put(new BasicAttribute(SINGLEVALUE, + (m_ldapAttribute.isSingleValued() ? "true" : "false"))); + } + else { + // ignore other attrIds as not supported by Netscape LdapJDK APIs + } + } + return attrs; + } + + + /** + * DirContext Attribute Operations + */ + + public Attributes getAttributes(String name) throws NamingException { + if (name.length() != 0) { // no subcontexts + throw new NameNotFoundException(name); // no such object + } + return extractAttributeIds(m_allAttrIds); + } + + public Attributes getAttributes(String name, String[] attrIds) throws NamingException { + if (name.length() != 0) { // no subcontexts + throw new NameNotFoundException(name); // no such object + } + return extractAttributeIds(attrIds); + } + + public Attributes getAttributes(Name name) throws NamingException { + return getAttributes(name.toString()); + } + + public Attributes getAttributes(Name name, String[] attrIds) throws NamingException { + return getAttributes(name.toString(), attrIds); + } + + public void modifyAttributes(String name, int mod_op, Attributes attrs) throws NamingException { + if (name.length() != 0) { // no subcontexts + throw new NameNotFoundException(name); // no such object + } + Attributes modAttrs = extractAttributeIds(m_allAttrIds); + modifySchemaElementAttrs(modAttrs, mod_op, attrs); + LDAPAttributeSchema modLdapAttribute = parseDefAttributes(modAttrs); + m_schemaMgr.modifyAttribute(m_ldapAttribute, modLdapAttribute); + m_ldapAttribute = modLdapAttribute; + } + + public void modifyAttributes(String name, ModificationItem[] mods) throws NamingException { + if (name.length() != 0) { // no subcontexts + throw new NameNotFoundException(name); // no such object + } + Attributes modAttrs = extractAttributeIds(m_allAttrIds); + modifySchemaElementAttrs(modAttrs, mods); + LDAPAttributeSchema modLdapAttribute = parseDefAttributes(modAttrs); + m_schemaMgr.modifyAttribute(m_ldapAttribute, modLdapAttribute); + m_ldapAttribute = modLdapAttribute; + } + + public void modifyAttributes(Name name, int mod_op, Attributes attrs) throws NamingException { + modifyAttributes(name.toString(), mod_op, attrs); + } + + public void modifyAttributes(Name name, ModificationItem[] mods) throws NamingException { + modifyAttributes(name.toString(), mods); + } + + /** + * Search operations are not implemented because of complexity. Ir will require + * to implement the full LDAP search filter sematics in the client. (The search + * filter sematics is implemented by the Directory server). + */ + +} diff --git a/mozilla/directory/java-sdk/ldapsp/com/netscape/jndi/ldap/schema/SchemaAttributeContainer.java b/mozilla/directory/java-sdk/ldapsp/com/netscape/jndi/ldap/schema/SchemaAttributeContainer.java new file mode 100644 index 00000000000..a61bf8bc681 --- /dev/null +++ b/mozilla/directory/java-sdk/ldapsp/com/netscape/jndi/ldap/schema/SchemaAttributeContainer.java @@ -0,0 +1,97 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * + * The contents of this file are subject to the Netscape Public License + * Version 1.0 (the "NPL"); you may not use this file except in + * compliance with the NPL. You may obtain a copy of the NPL at + * http://www.mozilla.org/NPL/ + * + * Software distributed under the NPL is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL + * for the specific language governing rights and limitations under the + * NPL. + * + * The Initial Developer of this code under the NPL is Netscape + * Communications Corporation. Portions created by Netscape are + * Copyright (C) 1999 Netscape Communications Corporation. All Rights + * Reserved. + */ +package com.netscape.jndi.ldap.schema; + +import javax.naming.*; +import javax.naming.directory.*; +import javax.naming.ldap.*; + +import netscape.ldap.*; +import netscape.ldap.controls.*; + +import java.util.*; + +public class SchemaAttributeContainer extends SchemaElementContainer { + + public SchemaAttributeContainer(SchemaManager schemaMgr) throws NamingException{ + super(schemaMgr, ATTRDEF); + } + + /** + * Ldap entry operations + */ + + public DirContext createSchemaElement(String name, Attributes attrs) throws NamingException { + if (name.length() == 0) { + throw new NamingException("Empty name for schema objectclass"); + } + LDAPAttributeSchema attr = SchemaAttribute.parseDefAttributes(attrs); + m_schemaMgr.createAttribute(attr); + return new SchemaAttribute(attr, m_schemaMgr); + + } + + public void removeSchemaElement(String name) throws NamingException { + if (name.length() == 0) { + throw new NamingException("Can not delete schema object container"); + } + m_schemaMgr.removeAttribute(name); + } + + /** + * List Operations + */ + + public NamingEnumeration getNameList(String name) throws NamingException { + SchemaDirContext schemaObj = (SchemaDirContext)lookup(name); + if (schemaObj == this) { + return new SchemaElementNameEnum(m_schemaMgr.getAttributeNames()); + } + else { + throw new NotContextException(name); + } + } + + public NamingEnumeration getBindingsList(String name) throws NamingException { + SchemaDirContext schemaObj = (SchemaDirContext)lookup(name); + if (schemaObj == this) { + return new SchemaElementBindingEnum(m_schemaMgr.getAttributes(), m_schemaMgr); + } + else { + throw new NotContextException(name); + } + } + + /** + * Lookup Operations + */ + + public Object lookupSchemaElement(String name) throws NamingException { + if (name.length() == 0) { + return this; + } + + // No caching; Always create a new object + LDAPAttributeSchema attr = m_schemaMgr.getAttribute(name); + if (attr == null) { + throw new NameNotFoundException(name); + } + return new SchemaAttribute(attr, m_schemaMgr); + + } +} diff --git a/mozilla/directory/java-sdk/ldapsp/com/netscape/jndi/ldap/schema/SchemaDirContext.java b/mozilla/directory/java-sdk/ldapsp/com/netscape/jndi/ldap/schema/SchemaDirContext.java new file mode 100644 index 00000000000..03ad4ce6c95 --- /dev/null +++ b/mozilla/directory/java-sdk/ldapsp/com/netscape/jndi/ldap/schema/SchemaDirContext.java @@ -0,0 +1,160 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * + * The contents of this file are subject to the Netscape Public License + * Version 1.0 (the "NPL"); you may not use this file except in + * compliance with the NPL. You may obtain a copy of the NPL at + * http://www.mozilla.org/NPL/ + * + * Software distributed under the NPL is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL + * for the specific language governing rights and limitations under the + * NPL. + * + * The Initial Developer of this code under the NPL is Netscape + * Communications Corporation. Portions created by Netscape are + * Copyright (C) 1999 Netscape Communications Corporation. All Rights + * Reserved. + */ +package com.netscape.jndi.ldap.schema; + +import javax.naming.*; +import javax.naming.directory.*; +import javax.naming.ldap.*; + +import netscape.ldap.*; +import netscape.ldap.controls.*; +import com.netscape.jndi.ldap.common.DirContextAdapter; + +import java.util.*; + +public class SchemaDirContext extends DirContextAdapter { + + public static final String CLASSDEF = "ClassDefinition"; + public static final String ATTRDEF = "AttributeDefinition"; + public static final String MRULEDEF = "MatchingRule"; + + + String m_path; + + public void close() throws NamingException { + ; //NOP + } + + /** + * Name operations + */ + + public String composeName(String name, String prefix) throws NamingException { + return name + "," + prefix; + } + + public Name composeName(Name name, Name prefix) throws NamingException { + String compoundName = composeName(name.toString(), prefix.toString()); + return SchemaNameParser.getParser().parse(compoundName); + } + + public String getNameInNamespace() throws NamingException { + return new String(m_path); + } + + public NameParser getNameParser(String name) throws NamingException { + return SchemaNameParser.getParser(); + } + + public NameParser getNameParser(Name name) throws NamingException { + return SchemaNameParser.getParser(); + } + + + /** + * Naming Bind operations + */ + + public void bind(String name, Object obj) throws NamingException { + if (obj instanceof DirContext) { + createSubcontext(name, ((DirContext)obj).getAttributes("")); + } + else { + throw new IllegalArgumentException("Can not bind this type of object"); + } + } + + public void bind(Name name, Object obj) throws NamingException { + bind(name.toString(), obj); + } + + public void rebind(String name, Object obj) throws NamingException { + try { + bind(name, obj); + } + catch (NameAlreadyBoundException ex) { + unbind(name); + bind(name, obj); + } + } + + public void rebind(Name name, Object obj) throws NamingException { + rebind(name.toString(), obj); + } + + public void rename(String oldName, String newName) throws NamingException { + throw new OperationNotSupportedException(); + } + + public void rename(Name oldName, Name newName) throws NamingException { + rename(oldName.toString(), newName.toString()); + } + + public void unbind(String name) throws NamingException { + // In ldap every entry is naming context + destroySubcontext(name); + } + + public void unbind(Name name) throws NamingException { + // In ldap every entry is naming context + destroySubcontext(name); + } + + /** + * Empty enumeration for list operations + */ + class EmptyNamingEnumeration implements NamingEnumeration { + + public Object next() throws NamingException{ + throw new NoSuchElementException("EmptyNamingEnumeration"); + } + + public Object nextElement() { + throw new NoSuchElementException("EmptyNamingEnumeration"); + } + + public boolean hasMore() throws NamingException{ + return false; + } + + public boolean hasMoreElements() { + return false; + } + + public void close() {} + } + + static class SchemaObjectSubordinateNamePair { + SchemaDirContext schemaObj; + String subordinateName; + + public SchemaObjectSubordinateNamePair(SchemaDirContext object, String subordinateName) { + this.schemaObj = object; + this.subordinateName = subordinateName; + } + + public String toString() { + StringBuffer str = new StringBuffer("SchemaObjectSubordinateNamePair{obj:"); + str.append(((schemaObj == null) ? "null" : schemaObj.toString())); + str.append(" name:"); + str.append(subordinateName); + str.append("}"); + return str.toString(); + } + } +} diff --git a/mozilla/directory/java-sdk/ldapsp/com/netscape/jndi/ldap/schema/SchemaElement.java b/mozilla/directory/java-sdk/ldapsp/com/netscape/jndi/ldap/schema/SchemaElement.java new file mode 100644 index 00000000000..66c5f5c0b6a --- /dev/null +++ b/mozilla/directory/java-sdk/ldapsp/com/netscape/jndi/ldap/schema/SchemaElement.java @@ -0,0 +1,213 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * + * The contents of this file are subject to the Netscape Public License + * Version 1.0 (the "NPL"); you may not use this file except in + * compliance with the NPL. You may obtain a copy of the NPL at + * http://www.mozilla.org/NPL/ + * + * Software distributed under the NPL is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL + * for the specific language governing rights and limitations under the + * NPL. + * + * The Initial Developer of this code under the NPL is Netscape + * Communications Corporation. Portions created by Netscape are + * Copyright (C) 1999 Netscape Communications Corporation. All Rights + * Reserved. + */ +package com.netscape.jndi.ldap.schema; + +import javax.naming.*; +import javax.naming.directory.*; +import javax.naming.ldap.*; + +import netscape.ldap.*; +import netscape.ldap.controls.*; + +import java.util.*; + +public class SchemaElement extends SchemaDirContext { + + SchemaManager m_schemaMgr; + + // Attributes used to define schema elements + static final String NUMERICOID = "NUMERICOID"; + static final String NAME = "NAME"; + static final String DESC = "DESC"; + static final String SYNTAX = "SYNTAX"; + static final String SUP = "SUP"; + static final String MUST = "MUST"; + static final String MAY = "MAY"; + static final String SINGLEVALUE = "SINGLE-VALUE"; + + // These attribute definition properties are not supported by LdapJDK and DS + // Accept them for the appropriate schema element, but ignore them + static final String OBSOLETE = "OBSOLETE"; + static final String EQUALITY = "EQUALITY"; + static final String ORDERING = "ORDERING"; + static final String COLLECTIVE = "COLLECTIVE"; + static final String NOUSERMOD = "NO-USER-MODIFICATION"; + static final String USAGE = "USAGE"; + static final String ABSTRACT = "ABSTRACT"; + static final String STRUCTURAL = "STRUCTURAL"; + static final String AUXILIARY = "AUXILIARY"; + + + // Syntax string recognized by Netscape LdapJDK + static final String cisString = "1.3.6.1.4.1.1466.115.121.1.15"; + static final String binaryString = "1.3.6.1.4.1.1466.115.121.1.5"; + static final String telephoneString = "1.3.6.1.4.1.1466.115.121.1.50"; + static final String cesString = "1.3.6.1.4.1.1466.115.121.1.26"; + static final String intString = "1.3.6.1.4.1.1466.115.121.1.27"; + static final String dnString = "1.3.6.1.4.1.1466.115.121.1.12"; + + SchemaElement(SchemaManager schemaMgr) { + m_schemaMgr = schemaMgr; + } + + /** + * Map a syntax oid string to a constant recognized by LdapJDK + */ + static int syntaxStringToInt(String syntax) throws NamingException{ + if (syntax.equals(cisString)) { + return LDAPSchemaElement.cis; + } + else if (syntax.equals(cesString)) { + return LDAPSchemaElement.ces; + } + else if (syntax.equals(telephoneString)) { + return LDAPSchemaElement.telephone; + } + else if (syntax.equals(intString)) { + return LDAPSchemaElement.integer; + } + else if (syntax.equals(dnString)) { + return LDAPSchemaElement.dn; + } + else if (syntax.equals(binaryString)) { + return LDAPSchemaElement.binary; + } + else { + throw new InvalidAttributeValueException(syntax); + } + } + + /** + * Map a syntax identifier to a oid string + */ + static String syntaxIntToString(int syntax) throws NamingException{ + if (syntax == LDAPSchemaElement.cis) { + return cisString; + } + else if (syntax == LDAPSchemaElement.ces) { + return cesString; + } + else if (syntax == LDAPSchemaElement.telephone) { + return telephoneString; + } + else if (syntax == LDAPSchemaElement.integer) { + return intString; + } + else if (syntax == LDAPSchemaElement.dn) { + return dnString; + } + else if (syntax == LDAPSchemaElement.binary) { + return binaryString; + } + else { + throw new InvalidAttributeValueException("Interanal error, unexpected syntax value " + syntax); + } + } + + /** + * Convert string vector to an array + */ + static String[] vectorToStringAry(Vector v) { + String[] ary = new String[v.size()]; + for(int i=0; i 0) { + BasicAttribute applies = new BasicAttribute(APPLIES); + for (int a=0; a < appliesToAttrs.length; a++) { + applies.add(appliesToAttrs[a]); + } + attrs.put(applies); + } + } + else { + // ignore other attrIds as not supported by Netscape LdapJDK APIs + } + } + return attrs; + } + + + /** + * DirContext Attribute Operations + */ + + public Attributes getAttributes(String name) throws NamingException { + if (name.length() != 0) { // no subcontexts + throw new NameNotFoundException(name); // no such object + } + return extractAttributeIds(m_allAttrIds); + } + + public Attributes getAttributes(String name, String[] attrIds) throws NamingException { + if (name.length() != 0) { // no subcontexts + throw new NameNotFoundException(name); // no such object + } + return extractAttributeIds(attrIds); + } + + public Attributes getAttributes(Name name) throws NamingException { + return getAttributes(name.toString()); + } + + public Attributes getAttributes(Name name, String[] attrIds) throws NamingException { + return getAttributes(name.toString(), attrIds); + } + + public void modifyAttributes(String name, int mod_op, Attributes attrs) throws NamingException { + if (name.length() != 0) { // no subcontexts + throw new NameNotFoundException(name); // no such object + } + Attributes modAttrs = extractAttributeIds(m_allAttrIds); + modifySchemaElementAttrs(modAttrs, mod_op, attrs); + LDAPMatchingRuleSchema modLdapMatchingRule = parseDefAttributes(modAttrs); + m_schemaMgr.modifyMatchingRule(m_ldapMatchingRule, modLdapMatchingRule); + m_ldapMatchingRule = modLdapMatchingRule; + } + + public void modifyAttributes(String name, ModificationItem[] mods) throws NamingException { + if (name.length() != 0) { // no subcontexts + throw new NameNotFoundException(name); // no such object + } + Attributes modAttrs = extractAttributeIds(m_allAttrIds); + modifySchemaElementAttrs(modAttrs, mods); + LDAPMatchingRuleSchema modLdapMatchingRule = parseDefAttributes(modAttrs); + m_schemaMgr.modifyMatchingRule(m_ldapMatchingRule, modLdapMatchingRule); + m_ldapMatchingRule = modLdapMatchingRule; + } + + public void modifyAttributes(Name name, int mod_op, Attributes attrs) throws NamingException { + modifyAttributes(name.toString(), mod_op, attrs); + } + + public void modifyAttributes(Name name, ModificationItem[] mods) throws NamingException { + modifyAttributes(name.toString(), mods); + } + + /** + * Search operations are not implemented because of complexity. Ir will require + * to implement the full LDAP search filter sematics in the client. (The search + * filter sematics is implemented by the Directory server). + */ + +} diff --git a/mozilla/directory/java-sdk/ldapsp/com/netscape/jndi/ldap/schema/SchemaMatchingRuleContainer.java b/mozilla/directory/java-sdk/ldapsp/com/netscape/jndi/ldap/schema/SchemaMatchingRuleContainer.java new file mode 100644 index 00000000000..ca2f85ba892 --- /dev/null +++ b/mozilla/directory/java-sdk/ldapsp/com/netscape/jndi/ldap/schema/SchemaMatchingRuleContainer.java @@ -0,0 +1,97 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * + * The contents of this file are subject to the Netscape Public License + * Version 1.0 (the "NPL"); you may not use this file except in + * compliance with the NPL. You may obtain a copy of the NPL at + * http://www.mozilla.org/NPL/ + * + * Software distributed under the NPL is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL + * for the specific language governing rights and limitations under the + * NPL. + * + * The Initial Developer of this code under the NPL is Netscape + * Communications Corporation. Portions created by Netscape are + * Copyright (C) 1999 Netscape Communications Corporation. All Rights + * Reserved. + */ +package com.netscape.jndi.ldap.schema; + +import javax.naming.*; +import javax.naming.directory.*; +import javax.naming.ldap.*; + +import netscape.ldap.*; +import netscape.ldap.controls.*; + +import java.util.*; + +public class SchemaMatchingRuleContainer extends SchemaElementContainer { + + public SchemaMatchingRuleContainer(SchemaManager schemaMgr) throws NamingException{ + super(schemaMgr, MRULEDEF); + } + + /** + * Ldap entry operations + */ + + public DirContext createSchemaElement(String name, Attributes attrs) throws NamingException { + if (name.length() == 0) { + throw new NamingException("Empty name for schema objectclass"); + } + LDAPMatchingRuleSchema mrule = SchemaMatchingRule.parseDefAttributes(attrs); + m_schemaMgr.createMatchingRule(mrule); + return new SchemaMatchingRule(mrule, m_schemaMgr); + + } + + public void removeSchemaElement(String name) throws NamingException { + if (name.length() == 0) { + throw new NamingException("Can not delete schema object container"); + } + m_schemaMgr.removeMatchingRule(name); + } + + /** + * List Operations + */ + + public NamingEnumeration getNameList(String name) throws NamingException { + SchemaDirContext schemaObj = (SchemaDirContext)lookup(name); + if (schemaObj == this) { + return new SchemaElementNameEnum(m_schemaMgr.getMatchingRuleNames()); + } + else { + throw new NotContextException(name); + } + } + + public NamingEnumeration getBindingsList(String name) throws NamingException { + SchemaDirContext schemaObj = (SchemaDirContext)lookup(name); + if (schemaObj == this) { + return new SchemaElementBindingEnum(m_schemaMgr.getMatchingRules(), m_schemaMgr); + } + else { + throw new NotContextException(name); + } + } + + /** + * Lookup Operations + */ + + public Object lookupSchemaElement(String name) throws NamingException { + if (name.length() == 0) { + return this; + } + + // No caching; Always create a new object + LDAPAttributeSchema mrule = m_schemaMgr.getMatchingRule(name); + if (mrule == null) { + throw new NameNotFoundException(name); + } + return new SchemaAttribute(mrule, m_schemaMgr); + + } +} diff --git a/mozilla/directory/java-sdk/ldapsp/com/netscape/jndi/ldap/schema/SchemaNameParser.java b/mozilla/directory/java-sdk/ldapsp/com/netscape/jndi/ldap/schema/SchemaNameParser.java new file mode 100644 index 00000000000..470c4a477fd --- /dev/null +++ b/mozilla/directory/java-sdk/ldapsp/com/netscape/jndi/ldap/schema/SchemaNameParser.java @@ -0,0 +1,53 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * + * The contents of this file are subject to the Netscape Public License + * Version 1.0 (the "NPL"); you may not use this file except in + * compliance with the NPL. You may obtain a copy of the NPL at + * http://www.mozilla.org/NPL/ + * + * Software distributed under the NPL is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL + * for the specific language governing rights and limitations under the + * NPL. + * + * The Initial Developer of this code under the NPL is Netscape + * Communications Corporation. Portions created by Netscape are + * Copyright (C) 1999 Netscape Communications Corporation. All Rights + * Reserved. + */ +package com.netscape.jndi.ldap.schema; + +import java.util.Hashtable; +import java.util.Properties; +import javax.naming.*; + +class SchemaNameParser implements NameParser { + + private static SchemaNameParser m_parser; + + // A table with compound name syntax properties + static Properties nameSyntax; + static { + nameSyntax = new Properties(); + nameSyntax.put("jndi.syntax.direction", "left_to_right"); + nameSyntax.put("jndi.syntax.separator", "/"); + nameSyntax.put("jndi.syntax.ignorecase", "true"); + } + + // Can not be constructed + private SchemaNameParser() {} + + // Shared instance must be used + public static SchemaNameParser getParser() { + if (m_parser == null) { + m_parser = new SchemaNameParser(); + } + return m_parser; + } + + // implements parse + public Name parse(String name) throws NamingException { + return new CompoundName(name, nameSyntax); + } + +} diff --git a/mozilla/directory/java-sdk/ldapsp/com/netscape/jndi/ldap/schema/SchemaObjectClass.java b/mozilla/directory/java-sdk/ldapsp/com/netscape/jndi/ldap/schema/SchemaObjectClass.java new file mode 100644 index 00000000000..9e8a685c312 --- /dev/null +++ b/mozilla/directory/java-sdk/ldapsp/com/netscape/jndi/ldap/schema/SchemaObjectClass.java @@ -0,0 +1,214 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * + * The contents of this file are subject to the Netscape Public License + * Version 1.0 (the "NPL"); you may not use this file except in + * compliance with the NPL. You may obtain a copy of the NPL at + * http://www.mozilla.org/NPL/ + * + * Software distributed under the NPL is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL + * for the specific language governing rights and limitations under the + * NPL. + * + * The Initial Developer of this code under the NPL is Netscape + * Communications Corporation. Portions created by Netscape are + * Copyright (C) 1999 Netscape Communications Corporation. All Rights + * Reserved. + */ +package com.netscape.jndi.ldap.schema; + +import javax.naming.*; +import javax.naming.directory.*; +import javax.naming.ldap.*; + +import netscape.ldap.*; +import netscape.ldap.controls.*; + +import java.util.*; + +public class SchemaObjectClass extends SchemaElement { + + LDAPObjectClassSchema m_ldapObjectClass; + + // Attribute IDs that are exposed through Netscape LdapJDK + private static String[] m_allAttrIds = {NUMERICOID, NAME, DESC, SUP, MUST, MAY }; + + public SchemaObjectClass(LDAPObjectClassSchema ldapObjectClass, SchemaManager schemaManager) { + super(schemaManager); + m_ldapObjectClass = ldapObjectClass; + m_path = CLASSDEF + "/" + m_ldapObjectClass.getName(); + } + + public SchemaObjectClass(Attributes attrs, SchemaManager schemaManager) throws NamingException { + super(schemaManager); + m_ldapObjectClass = parseDefAttributes(attrs); + m_path = CLASSDEF + "/" + m_ldapObjectClass.getName(); + } + + /** + * Parse Definition Attributes for a LDAP objectcalss + */ + static LDAPObjectClassSchema parseDefAttributes(Attributes attrs) throws NamingException { + String name="", oid="", desc="", sup=""; + Vector must=new Vector(), may=new Vector(); + + for (Enumeration attrEnum = attrs.getAll(); attrEnum.hasMoreElements(); ) { + Attribute attr = (Attribute) attrEnum.nextElement(); + String attrName = attr.getID(); + if (attrName.equals(NAME)) { + for (Enumeration valEnum = attr.getAll(); valEnum.hasMoreElements(); ) { + name = (String)valEnum.nextElement(); + break; + } + } + else if (attrName.equals(NUMERICOID)) { + for (Enumeration valEnum = attr.getAll(); valEnum.hasMoreElements(); ) { + oid = (String)valEnum.nextElement(); + break; + } + } + else if (attrName.equals(SUP)) { + for (Enumeration valEnum = attr.getAll(); valEnum.hasMoreElements(); ) { + sup = (String)valEnum.nextElement(); + break; + } + } + else if (attrName.equals(DESC)) { + for (Enumeration valEnum = attr.getAll(); valEnum.hasMoreElements(); ) { + desc = (String)valEnum.nextElement(); + break; + } + } + else if (attrName.equals(MAY)) { + for (Enumeration valEnum = attr.getAll(); valEnum.hasMoreElements(); ) { + may.addElement((String)valEnum.nextElement()); + } + } + else if (attrName.equals(MUST)) { + for (Enumeration valEnum = attr.getAll(); valEnum.hasMoreElements(); ) { + must.addElement((String)valEnum.nextElement()); + } + } + else if (attrName.equals(OBSOLETE) || attrName.equals(ABSTRACT) || + attrName.equals(STRUCTURAL) || attrName.equals(AUXILIARY)) { + ; //ignore + } + else { + throw new NamingException("Invalid schema attribute type for object class definition " + attrName); + } + } + + if (name.length() == 0 || oid.length() == 0 || sup.length() == 0 || + (must.size() == 0 && may.size() == 0)) { + + throw new NamingException("Incomplete schema object class definition"); + } + + return new LDAPObjectClassSchema(name, oid, sup, desc, + vectorToStringAry(must), + vectorToStringAry(may)); + } + + /** + * Exctract specified attributes from the ldapObjectClass + */ + Attributes extractAttributeIds(String[] attrIds) { + Attributes attrs = new BasicAttributes(); + for (int i = 0; i < attrIds.length; i++) { + if (attrIds[i].equals(NUMERICOID)) { + attrs.put(new BasicAttribute(NUMERICOID, m_ldapObjectClass.getOID())); + } + else if (attrIds[i].equals(NAME)) { + attrs.put(new BasicAttribute(NAME, m_ldapObjectClass.getName())); + } + else if (attrIds[i].equals(DESC)) { + attrs.put(new BasicAttribute(DESC, m_ldapObjectClass.getDescription())); + } + else if (attrIds[i].equals(SUP)) { + attrs.put(new BasicAttribute(SUP, m_ldapObjectClass.getSuperior())); + } + else if (attrIds[i].equals(MAY)) { + BasicAttribute optional = new BasicAttribute(MAY); + for (Enumeration e = m_ldapObjectClass.getOptionalAttributes(); e.hasMoreElements();) { + optional.add(e.nextElement()); + } + attrs.put(optional); + } + else if (attrIds[i].equals(MUST)) { + BasicAttribute required = new BasicAttribute(MUST); + for (Enumeration e = m_ldapObjectClass.getRequiredAttributes(); e.hasMoreElements();) { + required.add(e.nextElement()); + } + attrs.put(required); + } + else { + // ignore other attrIds as not supported by Netscape LdapJDK APIs + } + } + return attrs; + } + + + /** + * DirContext Attribute Operations + */ + + public Attributes getAttributes(String name) throws NamingException { + if (name.length() != 0) { // no subcontexts + throw new NameNotFoundException(name); // no such object + } + return extractAttributeIds(m_allAttrIds); + } + + public Attributes getAttributes(String name, String[] attrIds) throws NamingException { + if (name.length() != 0) { // no subcontexts + throw new NameNotFoundException(name); // no such object + } + return extractAttributeIds(attrIds); + } + + public Attributes getAttributes(Name name) throws NamingException { + return getAttributes(name.toString()); + } + + public Attributes getAttributes(Name name, String[] attrIds) throws NamingException { + return getAttributes(name.toString(), attrIds); + } + + public void modifyAttributes(String name, int mod_op, Attributes attrs) throws NamingException { + if (name.length() != 0) { // no subcontexts + throw new NameNotFoundException(name); // no such object + } + Attributes modAttrs = extractAttributeIds(m_allAttrIds); + modifySchemaElementAttrs(modAttrs, mod_op, attrs); + LDAPObjectClassSchema modLdapObjectClass = parseDefAttributes(modAttrs); + m_schemaMgr.modifyObjectClass(m_ldapObjectClass, modLdapObjectClass); + m_ldapObjectClass = modLdapObjectClass; + } + + public void modifyAttributes(String name, ModificationItem[] mods) throws NamingException { + if (name.length() != 0) { // no subcontexts + throw new NameNotFoundException(name); // no such object + } + Attributes modAttrs = extractAttributeIds(m_allAttrIds); + modifySchemaElementAttrs(modAttrs, mods); + LDAPObjectClassSchema modLdapObjectClass = parseDefAttributes(modAttrs); + m_schemaMgr.modifyObjectClass(m_ldapObjectClass, modLdapObjectClass); + m_ldapObjectClass = modLdapObjectClass; + } + + public void modifyAttributes(Name name, int mod_op, Attributes attrs) throws NamingException { + modifyAttributes(name.toString(), mod_op, attrs); + } + + public void modifyAttributes(Name name, ModificationItem[] mods) throws NamingException { + modifyAttributes(name.toString(), mods); + } + + /** + * Search operations are not implemented because of complexity. Ir will require + * to implement the full LDAP search filter sematics in the client. (The search + * filter sematics is implemented by the Directory server). + */ + +} diff --git a/mozilla/directory/java-sdk/ldapsp/com/netscape/jndi/ldap/schema/SchemaObjectClassContainer.java b/mozilla/directory/java-sdk/ldapsp/com/netscape/jndi/ldap/schema/SchemaObjectClassContainer.java new file mode 100644 index 00000000000..2140bd440c2 --- /dev/null +++ b/mozilla/directory/java-sdk/ldapsp/com/netscape/jndi/ldap/schema/SchemaObjectClassContainer.java @@ -0,0 +1,97 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * + * The contents of this file are subject to the Netscape Public License + * Version 1.0 (the "NPL"); you may not use this file except in + * compliance with the NPL. You may obtain a copy of the NPL at + * http://www.mozilla.org/NPL/ + * + * Software distributed under the NPL is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL + * for the specific language governing rights and limitations under the + * NPL. + * + * The Initial Developer of this code under the NPL is Netscape + * Communications Corporation. Portions created by Netscape are + * Copyright (C) 1999 Netscape Communications Corporation. All Rights + * Reserved. + */ +package com.netscape.jndi.ldap.schema; + +import javax.naming.*; +import javax.naming.directory.*; +import javax.naming.ldap.*; + +import netscape.ldap.*; +import netscape.ldap.controls.*; + +import java.util.*; + +public class SchemaObjectClassContainer extends SchemaElementContainer { + + public SchemaObjectClassContainer(SchemaManager schemaMgr) throws NamingException{ + super(schemaMgr, CLASSDEF); + } + + /** + * Ldap entry operations + */ + + public DirContext createSchemaElement(String name, Attributes attrs) throws NamingException { + if (name.length() == 0) { + throw new NamingException("Empty name for schema objectclass"); + } + LDAPObjectClassSchema objclass = SchemaObjectClass.parseDefAttributes(attrs); + m_schemaMgr.createObjectClass(objclass); + return new SchemaObjectClass(objclass, m_schemaMgr); + + } + + public void removeSchemaElement(String name) throws NamingException { + if (name.length() == 0) { + throw new NamingException("Can not delete schema object container"); + } + m_schemaMgr.removeObjectClass(name); + } + + /** + * List Operations + */ + + public NamingEnumeration getNameList(String name) throws NamingException { + SchemaDirContext schemaObj = (SchemaDirContext)lookup(name); + if (schemaObj == this) { + return new SchemaElementNameEnum(m_schemaMgr.getObjectClassNames()); + } + else { + throw new NotContextException(name); + } + } + + public NamingEnumeration getBindingsList(String name) throws NamingException { + SchemaDirContext schemaObj = (SchemaDirContext)lookup(name); + if (schemaObj == this) { + return new SchemaElementBindingEnum(m_schemaMgr.getObjectClasses(), m_schemaMgr); + } + else { + throw new NotContextException(name); + } + } + + /** + * Lookup Operations + */ + + public Object lookupSchemaElement(String name) throws NamingException { + if (name.length() == 0) { + return this; + } + + // No caching; Always create a new object + LDAPObjectClassSchema objclass = m_schemaMgr.getObjectClass(name); + if (objclass == null) { + throw new NameNotFoundException(name); + } + return new SchemaObjectClass(objclass, m_schemaMgr); + + } +} diff --git a/mozilla/directory/java-sdk/ldapsp/com/netscape/jndi/ldap/schema/SchemaRoot.java b/mozilla/directory/java-sdk/ldapsp/com/netscape/jndi/ldap/schema/SchemaRoot.java new file mode 100644 index 00000000000..f2fac29a934 --- /dev/null +++ b/mozilla/directory/java-sdk/ldapsp/com/netscape/jndi/ldap/schema/SchemaRoot.java @@ -0,0 +1,360 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * + * The contents of this file are subject to the Netscape Public License + * Version 1.0 (the "NPL"); you may not use this file except in + * compliance with the NPL. You may obtain a copy of the NPL at + * http://www.mozilla.org/NPL/ + * + * Software distributed under the NPL is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL + * for the specific language governing rights and limitations under the + * NPL. + * + * The Initial Developer of this code under the NPL is Netscape + * Communications Corporation. Portions created by Netscape are + * Copyright (C) 1999 Netscape Communications Corporation. All Rights + * Reserved. + */ +package com.netscape.jndi.ldap.schema; + +import javax.naming.*; +import javax.naming.directory.*; +import javax.naming.ldap.*; + +import netscape.ldap.*; +import netscape.ldap.controls.*; + +import java.util.*; + +public class SchemaRoot extends SchemaDirContext { + + static final String m_className = "javax.naming.directory.DirContext"; // for class name is bindings + + SchemaDirContext m_classContainer, m_attrContainer, m_matchRuleContainer; + + SchemaManager m_schemaMgr; + + public SchemaRoot(LDAPConnection ld) throws NamingException{ + m_path = ""; + m_schemaMgr = new SchemaManager(ld); + m_classContainer = new SchemaObjectClassContainer(m_schemaMgr); + m_attrContainer = new SchemaAttributeContainer(m_schemaMgr); + m_matchRuleContainer = new SchemaMatchingRuleContainer(m_schemaMgr); + } + + SchemaObjectSubordinateNamePair resolveSchemaObject(String name) throws NamingException{ + + SchemaDirContext obj = null; + + if (name.length() == 0) { + obj = this; + } + else if (name.startsWith(CLASSDEF) || name.startsWith(CLASSDEF.toLowerCase())) { + name = name.substring(CLASSDEF.length()); + obj = m_classContainer; + } + else if (name.startsWith(ATTRDEF) || name.startsWith(ATTRDEF.toLowerCase())) { + name = name.substring(ATTRDEF.length()); + obj = m_attrContainer; + } + else if (name.startsWith(MRULEDEF) || name.startsWith(MRULEDEF.toLowerCase())) { + name = name.substring(MRULEDEF.length()); + obj = m_matchRuleContainer; + + } + else { + throw new NameNotFoundException(name); + } + + if (name.length() > 1 && name.startsWith("/")) { + name = name.substring(1); + } + return new SchemaObjectSubordinateNamePair(obj, name); + } + + + /** + * Attribute Operations + */ + + public Attributes getAttributes(String name) throws NamingException { + SchemaObjectSubordinateNamePair objNamePair = resolveSchemaObject(name); + if (objNamePair.schemaObj == this) { + throw new OperationNotSupportedException(); + } + else { + return objNamePair.schemaObj.getAttributes(objNamePair.subordinateName); + } + } + + public Attributes getAttributes(String name, String[] attrIds) throws NamingException { + SchemaObjectSubordinateNamePair objNamePair = resolveSchemaObject(name); + if (objNamePair.schemaObj == this) { + throw new OperationNotSupportedException(); + } + else { + return objNamePair.schemaObj.getAttributes(objNamePair.subordinateName, attrIds); + } + } + + public Attributes getAttributes(Name name) throws NamingException { + return getAttributes(name.toString()); + } + + public Attributes getAttributes(Name name, String[] attrIds) throws NamingException { + return getAttributes(name.toString(), attrIds); + } + + public void modifyAttributes(String name, int mod_op, Attributes attrs) throws NamingException { + throw new OperationNotSupportedException(); + } + + public void modifyAttributes(String name, ModificationItem[] mods) throws NamingException { + throw new OperationNotSupportedException(); + } + + public void modifyAttributes(Name name, int mod_op, Attributes attrs) throws NamingException { + throw new OperationNotSupportedException(); + } + + public void modifyAttributes(Name name, ModificationItem[] mods) throws NamingException { + throw new OperationNotSupportedException(); + } + + /** + * Ldap entry operations + */ + + public Context createSubcontext(String name) throws NamingException { + // Directory entry must have attributes + throw new OperationNotSupportedException(); + } + + public Context createSubcontext(Name name) throws NamingException { + // Directory entry must have attributes + throw new OperationNotSupportedException(); + } + + public DirContext createSubcontext(String name, Attributes attrs) throws NamingException { + SchemaObjectSubordinateNamePair objNamePair = resolveSchemaObject(name); + if (objNamePair.schemaObj == this) { + throw new OperationNotSupportedException(); + } + else { + return objNamePair.schemaObj.createSubcontext(objNamePair.subordinateName, attrs); + } + } + + public DirContext createSubcontext(Name name, Attributes attrs) throws NamingException { + return createSubcontext(name.toString(), attrs); + } + + public void destroySubcontext(String name) throws NamingException { + SchemaObjectSubordinateNamePair objNamePair = resolveSchemaObject(name); + if (objNamePair.schemaObj == this) { + throw new OperationNotSupportedException(); + } + else { + objNamePair.schemaObj.destroySubcontext(objNamePair.subordinateName); + } + } + + public void destroySubcontext(Name name) throws NamingException { + destroySubcontext(name.toString()); + } + + /** + * Naming Bind operations + */ + + public void bind(String name, Object obj) throws NamingException { + if (obj instanceof DirContext) { + createSubcontext(name, ((DirContext)obj).getAttributes("")); + } + else { + throw new IllegalArgumentException("Can not bind this type of object"); + } + } + + public void bind(Name name, Object obj) throws NamingException { + bind(name.toString(), obj); + } + + public void rebind(String name, Object obj) throws NamingException { + unbind(name); + bind(name, obj); + } + + public void rebind(Name name, Object obj) throws NamingException { + rebind(name.toString(), obj); + } + + public void rename(String oldName, String newName) throws NamingException { + throw new OperationNotSupportedException(); + } + + public void rename(Name oldName, Name newName) throws NamingException { + rename(oldName.toString(), newName.toString()); + } + + public void unbind(String name) throws NamingException { + // In ldap every entry is naming context + destroySubcontext(name); + } + + public void unbind(Name name) throws NamingException { + // In ldap every entry is naming context + destroySubcontext(name); + } + + /** + * List Operations + */ + + public NamingEnumeration list(String name) throws NamingException { + SchemaObjectSubordinateNamePair objNamePair = resolveSchemaObject(name); + if (objNamePair.schemaObj == this) { + return new SchemaRootNameClassPairEnum(); + } + else { + return objNamePair.schemaObj.list(objNamePair.subordinateName); + } + } + + public NamingEnumeration list(Name name) throws NamingException { + return list(name.toString()); + } + + public NamingEnumeration listBindings(String name) throws NamingException { + SchemaObjectSubordinateNamePair objNamePair = resolveSchemaObject(name); + if (objNamePair.schemaObj == this) { + return new SchemaRootBindingEnum(); + } + else { + return objNamePair.schemaObj.listBindings(objNamePair.subordinateName); + } + + } + + public NamingEnumeration listBindings(Name name) throws NamingException { + return listBindings(name.toString()); + } + + /** + * Lookup Operations + */ + + public Object lookup(String name) throws NamingException { + SchemaObjectSubordinateNamePair objNamePair = resolveSchemaObject(name); + if (objNamePair.schemaObj == this) { + return this; + } + else { + return objNamePair.schemaObj.lookup(objNamePair.subordinateName); + } + + } + + public Object lookup(Name name) throws NamingException { + return lookup(name.toString()); + } + + public Object lookupLink(String name) throws NamingException { + throw new OperationNotSupportedException(); + } + + public Object lookupLink(Name name) throws NamingException { + throw new OperationNotSupportedException(); + } + + /** + * Test program + */ + public static void main(String args[]) { + try { + String name = args[0]; + System.out.println((new SchemaRoot(null)).resolveSchemaObject(name)); + } + catch (Exception e) { + System.err.println(e); + } + } + + /** + * NamigEnumeration of NameClassPairs + */ + class SchemaRootNameClassPairEnum implements NamingEnumeration { + + private int m_idx = -1; + + public Object next() { + return nextElement(); + } + + public Object nextElement() { + m_idx++; + if (m_idx == 0 ) { + return new NameClassPair(CLASSDEF, m_className); + } + else if (m_idx == 1) { + return new NameClassPair(ATTRDEF, m_className); + } + else if (m_idx == 2) { + return new NameClassPair(MRULEDEF, m_className); + } + else { + throw new NoSuchElementException("SchemaRootEnumerator"); + } + + } + + public boolean hasMore() { + return hasMoreElements(); + } + + public boolean hasMoreElements() { + return m_idx < 2; + } + + public void close() {} + } + + /** + * NamingEnumeration of Bindings + */ + class SchemaRootBindingEnum implements NamingEnumeration { + + private int m_idx = -1; + + public Object next() { + return nextElement(); + } + + public Object nextElement() { + m_idx++; + if (m_idx == 0 ) { + return new Binding(CLASSDEF, m_className, m_classContainer); + } + else if (m_idx == 1) { + return new Binding(ATTRDEF, m_className, m_attrContainer); + } + else if (m_idx == 2) { + return new Binding(MRULEDEF, m_className, m_matchRuleContainer); + } + else { + throw new NoSuchElementException("SchemaRootEnumerator"); + } + + } + + public boolean hasMore() { + return hasMoreElements(); + } + + public boolean hasMoreElements() { + return m_idx < 2; + } + + public void close() {} + } +} diff --git a/mozilla/directory/java-sdk/ldapsp/jndi-object-schema.conf b/mozilla/directory/java-sdk/ldapsp/jndi-object-schema.conf new file mode 100644 index 00000000000..f901d90f3ae --- /dev/null +++ b/mozilla/directory/java-sdk/ldapsp/jndi-object-schema.conf @@ -0,0 +1,47 @@ +# +# -- Attribute types -- +# +attribute javaClassName 1.3.6.1.4.1.42.2.27.4.1.6 ces single +attribute javaCodebase 1.3.6.1.4.1.42.2.27.4.1.7 ces +attribute javaSerializedData 1.3.6.1.4.1.42.2.27.4.1.8 bin single +attribute javaRemoteLocation 1.3.6.1.4.1.42.2.27.4.1.9 ces single +attribute javaFactory 1.3.6.1.4.1.42.2.27.4.1.10 ces single +attribute javaReferenceAddress 1.3.6.1.4.1.42.2.27.4.1.11 ces + +# +# -- Object classes -- +# +objectclass javaContainer + oid 1.3.6.1.4.1.42.2.27.4.2.1 + superior top + requires + cn + +objectclass javaObject + oid 1.3.6.1.4.1.42.2.27.4.2.4 + superior top + requires + javaClassName + allows + javaCodebase + +objectclass javaSerializedObject + oid 1.3.6.1.4.1.42.2.27.4.2.5 + superior javaObject + requires + javaSerializedData + + +objectclass javaRemoteObject + oid 1.3.6.1.4.1.42.2.27.4.2.6 + superior javaObject + requires + javaRemoteLocation + +objectclass javaNamingReference + oid 1.3.6.1.4.1.42.2.27.4.2.7 + superior javaObject + allows + javaReferenceAddress, + javaFactory + diff --git a/mozilla/directory/java-sdk/ldapsp/lib/jndi.jar b/mozilla/directory/java-sdk/ldapsp/lib/jndi.jar new file mode 100644 index 0000000000000000000000000000000000000000..54dcb6527921bc426dc75cb0e648b0508e7825b4 GIT binary patch literal 96941 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

X(_m%_FosppSafJ+3S^5#{Uv0`dY0T2ahe677%@ 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 literal 0 HcmV?d00001 diff --git a/mozilla/directory/java-sdk/ldapsp/manifest.mf b/mozilla/directory/java-sdk/ldapsp/manifest.mf new file mode 100644 index 00000000000..99b9c1baf2b --- /dev/null +++ b/mozilla/directory/java-sdk/ldapsp/manifest.mf @@ -0,0 +1,104 @@ +Name: com/netscape/jndi/ldap/AttributeEnum.class + +Name: com/netscape/jndi/ldap/AttributeIDEnum.class + +Name: com/netscape/jndi/ldap/AttributesImpl.class + +Name: com/netscape/jndi/ldap/BindingEnum.class + +Name: com/netscape/jndi/ldap/ContextEnv$ReferralRebindProc.class + +Name: com/netscape/jndi/ldap/ContextEnv.class + +Name: com/netscape/jndi/ldap/EventService$EventThread.class + +Name: com/netscape/jndi/ldap/EventService.class + +Name: com/netscape/jndi/ldap/LdapContextFactory.class + +Name: com/netscape/jndi/ldap/LdapContextImpl.class + +Name: com/netscape/jndi/ldap/LdapNameParser.class + +Name: com/netscape/jndi/ldap/LdapReferralException.class + +Name: com/netscape/jndi/ldap/LdapService.class + +Name: com/netscape/jndi/ldap/NameClassPairEnum.class + +Name: com/netscape/jndi/ldap/ObjectMapper.class + +Name: com/netscape/jndi/ldap/ProviderUtils.class + +Name: com/netscape/jndi/ldap/SearchResultEnum.class + +Name: com/netscape/jndi/ldap/SearchResultWithControls.class + +Name: com/netscape/jndi/ldap/common/Debug.class + +Name: com/netscape/jndi/ldap/common/DirContextAdapter.class + +Name: com/netscape/jndi/ldap/common/ExceptionMapper.class + +Name: com/netscape/jndi/ldap/common/LdapContextAdapter.class + +Name: com/netscape/jndi/ldap/common/ShareableEnv.class + +Name: com/netscape/jndi/ldap/schema/SchemaAttribute.class + +Name: com/netscape/jndi/ldap/schema/SchemaAttributeContainer.class + +Name: com/netscape/jndi/ldap/schema/SchemaDirContext$EmptyNamingEnumeration.class + +Name: com/netscape/jndi/ldap/schema/SchemaDirContext$SchemaObjectSubordinateNamePair.class + +Name: com/netscape/jndi/ldap/schema/SchemaDirContext.class + +Name: com/netscape/jndi/ldap/schema/SchemaElement.class + +Name: com/netscape/jndi/ldap/schema/SchemaElementBindingEnum.class + +Name: com/netscape/jndi/ldap/schema/SchemaElementContainer.class + +Name: com/netscape/jndi/ldap/schema/SchemaElementNameEnum.class + +Name: com/netscape/jndi/ldap/schema/SchemaManager.class + +Name: com/netscape/jndi/ldap/schema/SchemaMatchingRule.class + +Name: com/netscape/jndi/ldap/schema/SchemaMatchingRuleContainer.class + +Name: com/netscape/jndi/ldap/schema/SchemaNameParser.class + +Name: com/netscape/jndi/ldap/schema/SchemaObjectClass.class + +Name: com/netscape/jndi/ldap/schema/SchemaObjectClassContainer.class + +Name: com/netscape/jndi/ldap/schema/SchemaRoot$SchemaRootBindingEnum.class + +Name: com/netscape/jndi/ldap/schema/SchemaRoot$SchemaRootNameClassPairEnum.class + +Name: com/netscape/jndi/ldap/schema/SchemaRoot.class + +Name: com/netscape/jndi/ldap/controls/LdapEntryChangeControl.class + +Name: com/netscape/jndi/ldap/controls/LdapPasswordExpiredControl.class + +Name: com/netscape/jndi/ldap/controls/LdapPasswordExpiringControl.class + +Name: com/netscape/jndi/ldap/controls/LdapPersistSearchControl.class + +Name: com/netscape/jndi/ldap/controls/LdapProxiedAuthControl.class + +Name: com/netscape/jndi/ldap/controls/LdapSortControl.class + +Name: com/netscape/jndi/ldap/controls/LdapSortKey.class + +Name: com/netscape/jndi/ldap/controls/LdapSortResponseControl.class + +Name: com/netscape/jndi/ldap/controls/LdapVirtualListControl.class + +Name: com/netscape/jndi/ldap/controls/LdapVirtualListResponseControl.class + +Name: com/netscape/jndi/ldap/controls/NetscapeControlFactory.class +