From 5f02d091e9d2893f7cb30df07a190b42bc183bc8 Mon Sep 17 00:00:00 2001 From: "chuckb%netscape.com" Date: Wed, 4 Aug 1999 02:03:56 +0000 Subject: [PATCH] Merge from LDAPJavaSDK_40beta_19990802_BRANCH. Some files removed. Some added git-svn-id: svn://10.0.0.236/trunk@42079 18797224-902f-48f8-a5cc-f745e15eee43 --- mozilla/directory/buildjsdk.txt | 51 +- mozilla/directory/java-sdk/README | 61 - mozilla/directory/java-sdk/ldap.mk | 103 +- .../ldap/beans/LDAPBasePropertySupport.java | 9 +- .../netscape/ldap/util/LDAPFilter.java | 4 + .../com/netscape/sasl/ClientFactory.java | 123 + .../com/netscape/sasl/SASLClientCB.java | 26 - .../sasl/SASLClientMechanismDriver.java | 203 -- .../com/netscape/sasl/SASLException.java | 62 - .../com/netscape/sasl/SASLGetLanguageCB.java | 34 - .../com/netscape/sasl/SASLInfoClientCB.java | 32 - .../netscape/sasl/SASLMechanismFactory.java | 94 - .../sasl/SASLNamePasswordClientCB.java | 52 - .../netscape/sasl/SASLOkayCancelClientCB.java | 37 - .../netscape/sasl/SASLPasswordClientCB.java | 45 - .../com/netscape/sasl/SASLSecurityLayer.java | 44 - .../netscape/sasl/SASLWarningClientCB.java | 31 - .../ldapjdk/com/netscape/sasl/Sasl.java | 235 ++ .../ldapjdk/com/netscape/sasl/SaslClient.java | 140 + .../com/netscape/sasl/SaslClientFactory.java | 69 + .../com/netscape/sasl/SaslException.java | 146 + .../mechanisms/SASLExternalMechanism.java | 104 - .../sasl/mechanisms/SaslExternal.java | 121 + .../directory/java-sdk/ldapjdk/lib/jaas.jar | Bin 0 -> 7759 bytes .../ldapjdk/netscape/ldap/DynamicInvoker.java | 88 + .../ldap/LDAPAsynchronousConnection.java | 438 +++ .../ldapjdk/netscape/ldap/LDAPAttribute.java | 74 +- .../netscape/ldap/LDAPAttributeSchema.java | 277 +- .../netscape/ldap/LDAPAttributeSet.java | 12 +- .../ldapjdk/netscape/ldap/LDAPBind.java | 43 + .../ldapjdk/netscape/ldap/LDAPCache.java | 34 +- .../ldapjdk/netscape/ldap/LDAPCheckComm.java | 4 +- .../netscape/ldap/LDAPConnSetupMgr.java | 408 +++ .../ldapjdk/netscape/ldap/LDAPConnThread.java | 543 +++ .../ldapjdk/netscape/ldap/LDAPConnection.java | 3093 ++++++++++------- .../netscape/ldap/LDAPConstraints.java | 344 ++ .../ldapjdk/netscape/ldap/LDAPControl.java | 270 +- .../ldapjdk/netscape/ldap/LDAPEntry.java | 15 +- .../ldapjdk/netscape/ldap/LDAPException.java | 16 +- .../netscape/ldap/LDAPExtendedResponse.java | 59 + .../ldap/LDAPInterruptedException.java | 53 + .../netscape/ldap/LDAPMatchingRuleSchema.java | 183 +- .../JDAPMessage.java => LDAPMessage.java} | 129 +- .../netscape/ldap/LDAPMessageQueue.java | 428 +++ .../netscape/ldap/LDAPObjectClassSchema.java | 300 +- .../netscape/ldap/LDAPResourceBundle.java | 1 - .../ldapjdk/netscape/ldap/LDAPResponse.java | 82 + .../netscape/ldap/LDAPResponseControl.java | 51 + .../netscape/ldap/LDAPResponseListener.java | 167 +- .../netscape/ldap/LDAPSSLSocketFactory.java | 21 +- .../ldap/LDAPSSLSocketFactoryExt.java | 2 - .../ldap/LDAPSSLSocketWrapFactory.java | 1 - .../ldapjdk/netscape/ldap/LDAPSaslBind.java | 273 ++ .../ldapjdk/netscape/ldap/LDAPSchema.java | 176 +- .../netscape/ldap/LDAPSchemaElement.java | 483 ++- .../netscape/ldap/LDAPSearchConstraints.java | 283 +- .../netscape/ldap/LDAPSearchListener.java | 135 +- .../netscape/ldap/LDAPSearchResult.java | 68 + .../ldap/LDAPSearchResultReference.java | 54 + .../netscape/ldap/LDAPSearchResults.java | 100 +- .../ldapjdk/netscape/ldap/LDAPSortKey.java | 3 - .../ldapjdk/netscape/ldap/LDAPv2.java | 58 +- .../ldapjdk/netscape/ldap/LDAPv3.java | 26 +- .../netscape/ldap/ber/stream/BERAny.java | 18 +- .../ldap/ber/stream/BERBitString.java | 18 +- .../netscape/ldap/ber/stream/BERBoolean.java | 8 +- .../ldap/ber/stream/BERCharacterString.java | 24 +- .../netscape/ldap/ber/stream/BERChoice.java | 20 +- .../ldap/ber/stream/BERConstruct.java | 8 +- .../netscape/ldap/ber/stream/BERElement.java | 37 +- .../ldap/ber/stream/BEREnumerated.java | 16 +- .../netscape/ldap/ber/stream/BERInteger.java | 2 +- .../netscape/ldap/ber/stream/BERIntegral.java | 26 +- .../netscape/ldap/ber/stream/BERNull.java | 8 +- .../ldap/ber/stream/BERNumericString.java | 26 +- .../netscape/ldap/ber/stream/BERObjectId.java | 26 +- .../ldap/ber/stream/BEROctetString.java | 31 +- .../ldap/ber/stream/BERPrintableString.java | 33 +- .../netscape/ldap/ber/stream/BERReal.java | 18 +- .../netscape/ldap/ber/stream/BERSequence.java | 12 +- .../netscape/ldap/ber/stream/BERSet.java | 19 +- .../netscape/ldap/ber/stream/BERTag.java | 31 +- .../ldap/ber/stream/BERTagDecoder.java | 19 +- .../netscape/ldap/ber/stream/BERUTCTime.java | 30 +- .../ldap/ber/stream/BERVisibleString.java | 12 +- .../ldapjdk/netscape/ldap/client/JDAPAVA.java | 1 - .../netscape/ldap/client/JDAPControl.java | 124 - .../ldap/client/JDAPFilterSubString.java | 1 - .../client/opers/JDAPExtendedRequest.java | 6 +- .../client/opers/JDAPModifyRDNRequest.java | 7 +- .../ldap/client/opers/JDAPSearchRequest.java | 18 +- .../ldap/controls/LDAPEntryChangeControl.java | 90 +- .../controls/LDAPPasswordExpiredControl.java | 45 +- .../controls/LDAPPasswordExpiringControl.java | 53 +- .../controls/LDAPPersistSearchControl.java | 60 +- .../ldap/controls/LDAPProxiedAuthControl.java | 3 +- .../ldap/controls/LDAPSortControl.java | 90 +- .../ldap/controls/LDAPStringControl.java | 28 +- .../ldap/controls/LDAPVirtualListControl.java | 65 +- .../controls/LDAPVirtualListResponse.java | 81 +- .../ldapjdk/netscape/ldap/util/ByteBuf.java | 7 +- .../netscape/ldap/util/ConnectionPool.java | 480 +++ .../ldapjdk/netscape/ldap/util/DN.java | 59 +- .../ldapjdk/netscape/ldap/util/GetOpt.java | 3 +- .../ldapjdk/netscape/ldap/util/LDIF.java | 357 +- .../netscape/ldap/util/LDIFAddContent.java | 13 +- .../ldap/util/LDIFAttributeContent.java | 14 +- .../netscape/ldap/util/LDIFBaseContent.java | 89 + .../netscape/ldap/util/LDIFContent.java | 32 +- .../netscape/ldap/util/LDIFDeleteContent.java | 17 +- .../netscape/ldap/util/LDIFModDNContent.java | 13 +- .../netscape/ldap/util/LDIFModifyContent.java | 14 +- .../netscape/ldap/util/LDIFRecord.java | 25 +- .../netscape/ldap/util/MimeBase64Decoder.java | 3 +- .../netscape/ldap/util/MimeBase64Encoder.java | 23 +- .../netscape/ldap/util/MimeEncoder.java | 8 +- .../ldapjdk/netscape/ldap/util/RDN.java | 14 +- .../Readme.html => ldapsp-relnotes.htm} | 28 +- mozilla/directory/java-sdk/ldapsp.mk | 21 +- .../netscape/jndi/ldap/AttributesImpl.java | 2 +- .../com/netscape/jndi/ldap/ContextEnv.java | 6 +- .../com/netscape/jndi/ldap/EventService.java | 672 ++-- .../netscape/jndi/ldap/LdapContextImpl.java | 10 +- .../com/netscape/jndi/ldap/LdapService.java | 4 +- .../com/netscape/jndi/ldap/ObjectMapper.java | 2 +- .../netscape/jndi/ldap/SearchResultEnum.java | 2 +- .../jndi/ldap/common/LdapContextAdapter.java | 10 +- .../ldap/controls/LdapEntryChangeControl.java | 76 +- .../controls/LdapPasswordExpiredControl.java | 14 +- .../controls/LdapPasswordExpiringControl.java | 18 +- .../controls/LdapPersistSearchControl.java | 55 +- .../ldap/controls/LdapProxiedAuthControl.java | 48 +- .../jndi/ldap/controls/LdapSortControl.java | 181 +- .../jndi/ldap/controls/LdapSortKey.java | 6 +- .../controls/LdapSortResponseControl.java | 48 +- .../ldap/controls/LdapVirtualListControl.java | 5 +- .../LdapVirtualListResponseControl.java | 11 +- .../ldap/controls/NetscapeControlFactory.java | 51 +- .../jndi/ldap/schema/SchemaAttribute.java | 2 +- .../jndi/ldap/schema/SchemaElement.java | 1 + .../jndi/ldap/schema/SchemaObjectClass.java | 9 +- .../directory/java-sdk/ldapsp/lib/jndi.jar | Bin 96941 -> 97158 bytes mozilla/directory/java-sdk/ldapsp/manifest.mf | 2 +- mozilla/directory/java-sdk/manifest.mf | 132 +- mozilla/directory/java-sdk/relnotes.htm | 311 ++ .../directory/java-sdk/tools/LDAPDelete.java | 7 +- .../directory/java-sdk/tools/LDAPModify.java | 742 ++-- .../directory/java-sdk/tools/LDAPSearch.java | 41 +- .../directory/java-sdk/tools/LDAPTool.java | 23 +- 149 files changed, 10783 insertions(+), 4663 deletions(-) delete mode 100644 mozilla/directory/java-sdk/README create mode 100644 mozilla/directory/java-sdk/ldapjdk/com/netscape/sasl/ClientFactory.java delete mode 100644 mozilla/directory/java-sdk/ldapjdk/com/netscape/sasl/SASLClientCB.java delete mode 100644 mozilla/directory/java-sdk/ldapjdk/com/netscape/sasl/SASLClientMechanismDriver.java delete mode 100644 mozilla/directory/java-sdk/ldapjdk/com/netscape/sasl/SASLException.java delete mode 100644 mozilla/directory/java-sdk/ldapjdk/com/netscape/sasl/SASLGetLanguageCB.java delete mode 100644 mozilla/directory/java-sdk/ldapjdk/com/netscape/sasl/SASLInfoClientCB.java delete mode 100644 mozilla/directory/java-sdk/ldapjdk/com/netscape/sasl/SASLMechanismFactory.java delete mode 100644 mozilla/directory/java-sdk/ldapjdk/com/netscape/sasl/SASLNamePasswordClientCB.java delete mode 100644 mozilla/directory/java-sdk/ldapjdk/com/netscape/sasl/SASLOkayCancelClientCB.java delete mode 100644 mozilla/directory/java-sdk/ldapjdk/com/netscape/sasl/SASLPasswordClientCB.java delete mode 100644 mozilla/directory/java-sdk/ldapjdk/com/netscape/sasl/SASLSecurityLayer.java delete mode 100644 mozilla/directory/java-sdk/ldapjdk/com/netscape/sasl/SASLWarningClientCB.java create mode 100644 mozilla/directory/java-sdk/ldapjdk/com/netscape/sasl/Sasl.java create mode 100644 mozilla/directory/java-sdk/ldapjdk/com/netscape/sasl/SaslClient.java create mode 100644 mozilla/directory/java-sdk/ldapjdk/com/netscape/sasl/SaslClientFactory.java create mode 100644 mozilla/directory/java-sdk/ldapjdk/com/netscape/sasl/SaslException.java delete mode 100644 mozilla/directory/java-sdk/ldapjdk/com/netscape/sasl/mechanisms/SASLExternalMechanism.java create mode 100644 mozilla/directory/java-sdk/ldapjdk/com/netscape/sasl/mechanisms/SaslExternal.java create mode 100644 mozilla/directory/java-sdk/ldapjdk/lib/jaas.jar create mode 100644 mozilla/directory/java-sdk/ldapjdk/netscape/ldap/DynamicInvoker.java create mode 100644 mozilla/directory/java-sdk/ldapjdk/netscape/ldap/LDAPAsynchronousConnection.java create mode 100644 mozilla/directory/java-sdk/ldapjdk/netscape/ldap/LDAPBind.java create mode 100644 mozilla/directory/java-sdk/ldapjdk/netscape/ldap/LDAPConnSetupMgr.java create mode 100644 mozilla/directory/java-sdk/ldapjdk/netscape/ldap/LDAPConnThread.java create mode 100644 mozilla/directory/java-sdk/ldapjdk/netscape/ldap/LDAPConstraints.java create mode 100644 mozilla/directory/java-sdk/ldapjdk/netscape/ldap/LDAPExtendedResponse.java create mode 100644 mozilla/directory/java-sdk/ldapjdk/netscape/ldap/LDAPInterruptedException.java rename mozilla/directory/java-sdk/ldapjdk/netscape/ldap/{client/JDAPMessage.java => LDAPMessage.java} (52%) create mode 100644 mozilla/directory/java-sdk/ldapjdk/netscape/ldap/LDAPMessageQueue.java create mode 100644 mozilla/directory/java-sdk/ldapjdk/netscape/ldap/LDAPResponse.java create mode 100644 mozilla/directory/java-sdk/ldapjdk/netscape/ldap/LDAPResponseControl.java create mode 100644 mozilla/directory/java-sdk/ldapjdk/netscape/ldap/LDAPSaslBind.java create mode 100644 mozilla/directory/java-sdk/ldapjdk/netscape/ldap/LDAPSearchResult.java create mode 100644 mozilla/directory/java-sdk/ldapjdk/netscape/ldap/LDAPSearchResultReference.java delete mode 100644 mozilla/directory/java-sdk/ldapjdk/netscape/ldap/client/JDAPControl.java create mode 100644 mozilla/directory/java-sdk/ldapjdk/netscape/ldap/util/ConnectionPool.java create mode 100644 mozilla/directory/java-sdk/ldapjdk/netscape/ldap/util/LDIFBaseContent.java rename mozilla/directory/java-sdk/{ldapsp/Readme.html => ldapsp-relnotes.htm} (96%) create mode 100644 mozilla/directory/java-sdk/relnotes.htm diff --git a/mozilla/directory/buildjsdk.txt b/mozilla/directory/buildjsdk.txt index ea04837c3a2..e76bcc755b5 100644 --- a/mozilla/directory/buildjsdk.txt +++ b/mozilla/directory/buildjsdk.txt @@ -1,14 +1,14 @@ ====================================================================== NETSCAPE DIRECTORY SDK FOR JAVA: BUILD INSTRUCTIONS - Last updated: June 4, 1999 + Last updated: August 2, 1999 ====================================================================== For information on the Netscape Directory SDK source release, see http://www.mozilla.org/directory/ Note the following: - The LDAP filter classes (in the netscape.ldap.util package) - use the OROMatcher(tm) regular expression package (from ORO Inc.). + use the OROMatcher(tm) regular expression package (from ORO Java Software). This is not provided with the source code release. If you want this package, you need to get it from ORO, Inc. (For details, see the ORO, Inc. home page at http://www.oroinc.com/.) @@ -21,7 +21,7 @@ System Requirements: 32MB of RAM, 128MB of swap, recommended 64MB of RAM. Tool Requirements: - Sun Microsystems Java Development Kit (JDK) 1.1.5 + Sun Microsystems Java Development Kit (JDK) 1.1.7 (or a more recent version) GNU make 3.74 or a more recent version @@ -36,10 +36,10 @@ Instructions: In csh/tcsh: setenv JAVA_HOME "" For example: - setenv JAVA_HOME "/usr/local/jdk1.1.5" + setenv JAVA_HOME "/usr/local/jdk1.1.7" setenv CLASSPATH "" For example: - setenv CLASSPATH "/usr/local/jdk1.1.5/lib/classes.zip" + setenv CLASSPATH "/usr/local/jdk1.1.7/lib/classes.zip" In sh/bash/ksh: JAVA_HOME="" @@ -56,10 +56,21 @@ Instructions: gmake -f ldap.mk basepackage + To generate javadoc specify "doc" as the target: + + gmake -f ldap.mk doc + + Note that if you are using JDK1.1, javadoc images will be missing + and you'll need to copy those images from somewhere else into + mozilla/directory/java-sdk/dist/doc/images. If you are using JDK1.2 + you do not need to do that. + The SDK will be built and copied into the following directories: mozilla/directory/java-sdk/dist/classes - class files and manifest file mozilla/directory/java-sdk/dist/packages - ldapjdk.jar JAR file (if you've specified "basepackage" as the target) + mozilla/directory/java-sdk/dist/doc - ldapjdk javadoc + (if you've specified "doc" as the target) 4. Build the JNDI LDAP Service Provider classes by entering the following commands: @@ -70,10 +81,16 @@ Instructions: gmake -f ldapsp.mk basepackage + To generate javadoc specify "doc" as the target: + + gmake -f ldapsp.mk doc + The SDK will be built and copied into the following directories: mozilla/directory/java-sdk/dist/classes - class files mozilla/directory/java-sdk/dist/packages - ldapsp.jar JAR file (if you've specified "basepackage" as the target) + mozilla/directory/java-sdk/dist/doc/ldapsp - ldapsp controls' javadoc + (if you've specified "doc" as the target) The JNDI LDAP Service Provider depends on the LDAP Java classes, so you always must build the LDAP Java first. For more information on JNDI see @@ -87,7 +104,7 @@ System Requirements: Windows NT 3.51 or 4.0 (4.0 preferred). Tool Requirements: - Sun Microsystems Java Development Kit (JDK) 1.1.5 + Sun Microsystems Java Development Kit (JDK) 1.1.7 (or a more recent version) GNU Tools for Windows (you can find these on the Internet). Specifically, you'll need: @@ -127,10 +144,10 @@ Instructions: directory is at d:\mozilla_src\mozilla.) set JAVA_HOME=(directory where the JDK is installed) For example: - set JAVA_HOME=D:\jdk1.1.5 + set JAVA_HOME=D:\jdk1.1.7 set CLASSPATH=(location of the JDK classes.zip file) For example: - set CLASSPATH=D:\jdk1.1.5\lib\classes.zip + set CLASSPATH=D:\jdk1.1.7\lib\classes.zip In addition, make sure to set your PATH environment variable to include the tools that you have downloaded. @@ -144,10 +161,22 @@ Instructions: gmake -f ldap.mk basepackage + To generate javadoc specify "doc" as the target: + + gmake -f ldap.mk doc + + Note that if you are using JDK1.1, javadoc images will be missing + and you'll need to copy those images from somewhere else into + mozilla\directory\java-sdk\dist\doc\images. If you are using JDK1.2 + you do not need to do that. + The SDK will be built and copied into the following directories: mozilla\directory\java-sdk\dist\classes - class files and manifest file mozilla\directory\java-sdk\dist\packages - ldapjdk.jar JAR file (if you've specified "basepackage" as the target) + mozilla\directory\java-sdk\dist\doc - ldapjdk javadoc + (if you've specified "doc" as the target) + 3. Build the JNDI LDAP Service Provider classes by entering the following commands: @@ -158,10 +187,16 @@ Instructions: gmake -f ldapsp.mk basepackage + To generate javadoc specify "doc" as the target: + + gmake -f ldapsp.mk doc + The SDK will be built and copied into the following directories: mozilla/directory/java-sdk/dist/classes - class files mozilla/directory/java-sdk/dist/packages - ldapsp.jar JAR file (if you've specified "basepackage" as the target) + mozilla\directory\java-sdk\dist\doc\ldapsp - ldapsp controls' javadoc + (if you've specified "doc" as the target) The JNDI LDAP Service Provider depends on the LDAP Java classes, so you always must build the LDAP Java first. For more information on JNDI see diff --git a/mozilla/directory/java-sdk/README b/mozilla/directory/java-sdk/README deleted file mode 100644 index 51aa0a96421..00000000000 --- a/mozilla/directory/java-sdk/README +++ /dev/null @@ -1,61 +0,0 @@ -README for september 28 - - - On September 28, a new source drop was assembled. The LDAPConnection -version is now 3.05. Compared to the original 3.0 source drop, this -addresses the following problems: - - On disconnect (or on finalizing an LDAPConnection object, which -implicitly calls the finalizer), a reference to the underlying socket -was kept, so that the socket was not closed. As a consequence, an -application which repeatedly did connect and disconnect would -experience a resource leak. On UNIX systems, the java VM might -eventually crash because of insufficient file descriptors. On Windows, -it might eventually crash because of memory exhaustion. - - Referrals were not correctly followed on add operations. - - Support has now been added for referrals on authentication. There is -a new overloaded authenticate method in LDAPConnection which takes an -LDAPSearchConstraints as a parameter. - - There was no check for an empty or null host String on connect. Now, -LDAPConnection.connect() throws an LDAPException for these two cases. - - Automatic reconnect when the server was restarted did not work. This -is now handled transparently on the next operation. Also, -LDAPConnection.isConnected() did not always return a correct response. - - A bad referral (one that points to a non-existent or inaccessible -server or entry) would cause any following valid results from a search -to be discarded. - - For asynchronous searches (batchSize == 1), it was possible that -search results would arrive from the server and be buffered by the SDK -faster than they could be processed by the application (using -LDAPSearchResults.next() or LDAPSearchResults.nextElement()). The -buffered results might use up more memory than was available in the -java VM, causing it to crash. Now, there is a limit of 100 on the -number of buffered entries per asynchronous search. The number can be -changed with LDAPSearchConstraints.setMaxBacklog(int backlog). If -there are no synchronous searches on a physical connection, and any -asynchronous searches have full backlog buffers, the listener thread -sleeps until search results are processed by the client. - - Persistent search response controls could be overwritten before a -client could process them. Now they are queued and served up one at a -time to the client. - - LDAPSearchResults().getCount() always returned the number of results -processed by the client, rather than the number of results available -to be processed by the client. - - LDAPSearchResults.sort() would throw a ClassCastException if the -LDAPSearchResults object contained exceptions (LDAPException and -LDAPReferralException) in addition to any LDAPEntry objects. - ----- - - There is also a new directory "tools" containing source for the -command-line tools LDAPSearch, LDAPModify, and LDAPDelete. - diff --git a/mozilla/directory/java-sdk/ldap.mk b/mozilla/directory/java-sdk/ldap.mk index cc958da4686..180109de9b0 100644 --- a/mozilla/directory/java-sdk/ldap.mk +++ b/mozilla/directory/java-sdk/ldap.mk @@ -12,7 +12,7 @@ # # 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 +# Copyright (C) 1999 Netscape Communications Corporation. All Rights # Reserved. # # Makefile for the LDAP classes @@ -20,6 +20,37 @@ # An optimized compile is done by default. You can specify "DEBUG=1" on # the make line to generate debug symbols in the bytecode. # +# The package includes com.netscape.sasl (until there is another +# home for it) +# +# You can compile only subsets of the classes by specifying one of the +# following: +# doc +# classes +# LDAPCLASSES +# MAIN +# CLIENT +# OPERS +# UTIL +# BER +# BEANS +# SASL +# TOOLS +# FILTER +# +# Create the JAR files with the following targets: +# package +# basepackage +# filterpackage +# beanpackage +# docpackage +# +# The usual mozilla environment variable must be defined: +# MOZ_SRC (the root of your CVS tree) +# +# And the Java root directory +# JAVA_HOME +# ARCH := $(shell uname -s) MCOM_ROOT=. @@ -36,6 +67,7 @@ else endif # Destination for class files and packages CLASS_DEST=$(BASEDIR)/dist/classes +FILTER_CLASS_DEST=$(BASEDIR)/dist/ldapfilt # Set up the CLASSPATH automatically, ifeq ($(ARCH), WINNT) @@ -53,12 +85,14 @@ else SEP=: endif endif -JAVACLASSPATH:=$(BASEDIR)/ldapjdk$(SEP)$(BASEDIR)/ldapbeans$(SEP)$(BASEDIR)/ldapfilter$(SEP)$(CLASSPATH) +JAASLIB=$(BASEDIR)/ldapjdk/lib/jaas.jar +JAVACLASSPATH:=$(BASEDIR)/ldapjdk$(SEP)$(JAASLIB)$(SEP)$(BASEDIR)/ldapbeans$(SEP)$(JDK)/lib/classes.zip$(SEP)$(CLASSPATH) SRCDIR=netscape/ldap BEANDIR=$(BASEDIR)/ldapbeans/netscape/ldap/beans DISTDIR=$(MCOM_ROOT)/dist CLASSDIR=$(MCOM_ROOT)/dist/classes +FILTERCLASSDIR=$(MCOM_ROOT)/dist/ldapfilt CLASSPACKAGEDIR=$(DISTDIR)/packages PACKAGENAME=javaldap.zip ifeq ($(DEBUG), 1) @@ -66,10 +100,24 @@ BASEPACKAGENAME=ldapjdk_debug.jar else BASEPACKAGENAME=ldapjdk.jar endif +FILTERJAR=ldapfilt.jar CLASSPACKAGE=$(CLASSPACKAGEDIR)/$(PACKAGENAME) +BEANPACKAGENAME=ldapbeans.jar +TOOLSTARGETDIR=$(DISTDIR)/tools +TOOLSDIR=$(BASEDIR)/tools +DOCDIR=$(DISTDIR)/doc +BERDOCPACKAGEDIR=$(DISTDIR)/doc/ber +DOCNAME=ldapdoc.zip +DOCPACKAGE=$(CLASSPACKAGEDIR)/$(DOCNAME) +EXAMPLEDIR=$(DISTDIR)/examples +#TESTSRCDIR=$(BASEDIR)/netsite/ldap/java/netscape/ldap/tests ERRORSDIR=$(CLASSDIR)/netscape/ldap/errors SASLDIR=com/netscape/sasl +SASLMECHANISMDIR=com/netscape/sasl/mechanisms +ifndef JAVADOC + JAVADOC=$(JDKBIN)javadoc -classpath "$(JAVACLASSPATH)" +endif ifndef JAVAC ifdef JAVA_HOME JDKBIN=$(JDK)/bin/ @@ -81,6 +129,11 @@ ifndef JAVAC endif endif +BERDOCCLASSES=netscape.ldap.ber.stream + +DOCCLASSES=netscape.ldap netscape.ldap.beans netscape.ldap.controls \ + netscape.ldap.util $(TOOLSDIR)/*.java $(BERDOCCLASSES) + all: classes basics: $(DISTDIR) $(CLASSDIR) @@ -88,8 +141,25 @@ basics: $(DISTDIR) $(CLASSDIR) classes: LDAPCLASSES BEANS TOOLS +doc: $(DISTDIR) $(DOCDIR) DOCS + +berdoc: $(DISTDIR) $(BERDOCPACKAGEDIR) BERDOCS + +examples: $(DISTDIR) $(EXAMPLEDIR)/java $(EXAMPLEDIR)/js $(EXAMPLEDIR)/java/beans EXAMPLES + +tests: $(CLASSDIR) + cd $(TESTSRCDIR); $(JAVAC) -d $(CLASS_DEST) *.java + +package: basepackage filterpackage beanpackage docpackage + basepackage: $(CLASSPACKAGEDIR) - cd $(DISTDIR)/classes; rm -f ../packages/$(BASEPACKAGENAME); $(JAR) cvfm ../packages/$(BASEPACKAGENAME) manifest.mf netscape/ldap/*.class netscape/ldap/client/*.class netscape/ldap/client/opers/*.class netscape/ldap/ber/stream/*.class netscape/ldap/controls/*.class netscape/ldap/util/*.class netscape/ldap/errors/*.props com/netscape/sasl/*.class tools/*.class + cd $(DISTDIR)/classes; rm -f ../packages/$(BASEPACKAGENAME); $(JAR) cvfm ../packages/$(BASEPACKAGENAME) manifest.mf netscape/ldap/*.class netscape/ldap/client/*.class netscape/ldap/client/opers/*.class netscape/ldap/ber/stream/*.class netscape/ldap/controls/*.class netscape/ldap/util/*.class netscape/ldap/errors/*.props com/netscape/sasl/*.class com/netscape/sasl/mechanisms/*.class tools/*.class + +beanpackage: $(CLASSPACKAGEDIR) + cd $(DISTDIR)/classes; rm -f ../packages/$(BEANPACKAGENAME); $(JAR) cvf ../packages/$(BEANPACKAGENAME) netscape/ldap/beans + +docpackage: $(DOCDIR) $(CLASSPACKAGEDIR) + cd $(DOCDIR); rm -f ../packages/$(DOCNAME); $(JAR) cvf ../packages/$(DOCNAME) *.html *.css netscape/ldap/*.html netscape/ldap/beans/*.html netscape/ldap/controls/*.html netscape/ldap/util/*.html netscape/ldap/ber/stream/*.html MAIN: basics cd ldapjdk/$(SRCDIR); $(JAVAC) -d "$(CLASS_DEST)" *.java @@ -106,6 +176,9 @@ BER: basics UTIL: basics cd ldapjdk/$(SRCDIR)/util; $(JAVAC) -d "$(CLASS_DEST)" *.java +SASLMECHANISM: basics + cd ldapjdk/$(SASLMECHANISMDIR); $(JAVAC) -d "$(CLASS_DEST)" *.java + SASL: basics cd ldapjdk/$(SASLDIR); $(JAVAC) -d "$(CLASS_DEST)" *.java @@ -115,7 +188,7 @@ ERRORS: basics $(ERRORSDIR) CONTROLS: basics cd ldapjdk/$(SRCDIR)/controls; $(JAVAC) -d "$(CLASS_DEST)" *.java -LDAPCLASSES: BER OPERS CLIENT MAIN UTIL CONTROLS ERRORS SASL +LDAPCLASSES: BER OPERS CLIENT MAIN UTIL CONTROLS ERRORS SASL SASLMECHANISM BEANS: OTHERBEANS @@ -125,13 +198,31 @@ OTHERBEANS: basics TOOLS: basics cd tools; $(JAVAC) -d "$(CLASS_DEST)" *.java +FILTER: $(FILTERCLASSDIR) + cd ldapfilter/netscape/ldap/util; $(JAVAC) -d "$(FILTER_CLASS_DEST)" *.java + +filterpackage: $(CLASSPACKAGEDIR) + cd "$(FILTER_CLASS_DEST)"; rm -f ../packages/$(FILTERJAR); $(JAR) cvf ../packages/$(FILTERJAR) netscape/ldap/util/*.class + +DOCS: + $(JAVADOC) -d $(DOCDIR) $(DOCCLASSES) + +BERDOCS: + $(JAVADOC) -d $(BERDOCPACKAGEDIR) $(BERDOCCLASSES) + +EXAMPLES: + -cp -p $(EXAMPLESRCDIR)/java/* $(EXAMPLEDIR)/java + -cp -p $(EXAMPLESRCDIR)/java/beans/* $(EXAMPLEDIR)/java/beans + -cp -p $(EXAMPLESRCDIR)/java/beans/makejars.* $(CLASSDIR) + -cp -p $(EXAMPLESRCDIR)/js/* $(EXAMPLEDIR)/js + clean: rm -rf $(DISTDIR) $(CLASSPACKAGEDIR): mkdir -p $@ -$(DOCPACKAGEDIR): +$(DOCDIR): mkdir -p $@ $(DISTDIR): @@ -143,3 +234,5 @@ $(CLASSDIR): $(ERRORSDIR): mkdir -p $@ +$(FILTERCLASSDIR): + mkdir -p $@ diff --git a/mozilla/directory/java-sdk/ldapbeans/netscape/ldap/beans/LDAPBasePropertySupport.java b/mozilla/directory/java-sdk/ldapbeans/netscape/ldap/beans/LDAPBasePropertySupport.java index 27a6ab4b800..bcce5c0e694 100644 --- a/mozilla/directory/java-sdk/ldapbeans/netscape/ldap/beans/LDAPBasePropertySupport.java +++ b/mozilla/directory/java-sdk/ldapbeans/netscape/ldap/beans/LDAPBasePropertySupport.java @@ -338,9 +338,12 @@ public class LDAPBasePropertySupport implements Serializable { m_conn.getAuthenticationPassword() ); } }; - LDAPSearchConstraints cons = conn.getSearchConstraints(); - cons.setReferrals( true ); - cons.setRebindProc( rebind ); + try { + conn.setOption(LDAPConnection.REFERRALS, Boolean.TRUE); + conn.setOption(LDAPConnection.REFERRALS_REBIND_PROC, rebind); + } catch (LDAPException e) { + //will never happen + } } /** diff --git a/mozilla/directory/java-sdk/ldapfilter/netscape/ldap/util/LDAPFilter.java b/mozilla/directory/java-sdk/ldapfilter/netscape/ldap/util/LDAPFilter.java index 062ed4e6bd5..bda6d981d98 100644 --- a/mozilla/directory/java-sdk/ldapfilter/netscape/ldap/util/LDAPFilter.java +++ b/mozilla/directory/java-sdk/ldapfilter/netscape/ldap/util/LDAPFilter.java @@ -284,6 +284,10 @@ public class LDAPFilter implements Cloneable { if ( cFilterTemplate[i] == '%' ) { i++; if ( cFilterTemplate[i] == 'v' ) { + if ( i == (cFilterTemplate.length-1) ) { + sbFilter.append ( strValue ); + break; + } i++; switch ( cFilterTemplate[i] ) { case '$': diff --git a/mozilla/directory/java-sdk/ldapjdk/com/netscape/sasl/ClientFactory.java b/mozilla/directory/java-sdk/ldapjdk/com/netscape/sasl/ClientFactory.java new file mode 100644 index 00000000000..9a9c3e7ca4e --- /dev/null +++ b/mozilla/directory/java-sdk/ldapjdk/com/netscape/sasl/ClientFactory.java @@ -0,0 +1,123 @@ +/* -*- 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.sasl; + +import java.util.Hashtable; +import javax.security.auth.callback.CallbackHandler; + +/** + * An interface for creating instances of SaslClient. + * + * @see SaslClient + * @see Sasl + */ +public class ClientFactory implements SaslClientFactory { + public ClientFactory() { + _mechanismTable = new Hashtable(); + for( int i = 0; i < _mechanismNames.length; i++ ) { + _mechanismTable.put( _mechanismNames[i], + PACKAGENAME + '.' + + _mechanismClasses[i] ); + } + } + /** + * Creates a SaslClient using the parameters supplied. + * + * @param mechanisms The non-null list of mechanism names to try. + * Each is the IANA-registered name of a SASL mechanism. (e.g. + * "GSSAPI", "CRAM-MD5"). + * @param authorizationId The possibly null authorization ID to + * use. When the SASL authentication completes successfully, the + * entity named by authorizationId is granted access. + * @param protocol The non-null string name of the protocol for + * which the authentication is being performed (e.g., "ldap"). + * @param serverName The non-null string name of the server to + * which we are creating an authenticated connection. + * @param props The possibly null properties to be used by the SASL + * mechanisms to configure the authentication exchange. For example, + * "javax.security.sasl.encryption.maximum" might be used to + * specify the maximum key length to use for encryption. + * @param cbh The possibly null callback handler to used by the + * SASL mechanisms to get further information from the + * application/library to complete the authentication. For example, + * a SASL mechanism might require the authentication ID and + * password from the caller. + * @return A possibly null SaslClient created using the + * parameters supplied. If null, this factory cannot produce a + * SaslClient using the parameters supplied. + * @exception SaslException if it cannot create a + * SaslClient because of an error. + */ + public SaslClient createSaslClient( + String[] mechanisms, + String authorizationId, + String protocol, + String serverName, + Hashtable props, + CallbackHandler cbh ) throws SaslException { + String mechName = null; + if ( Sasl.debug ) { + System.out.println( + "ClientFactory.createSaslClient" ); + } + for( int i = 0; (mechName == null) && + (i < mechanisms.length); i++ ) { + mechName = (String)_mechanismTable.get( mechanisms[i] ); + } + if ( mechName != null ) { + try { + Class c = Class.forName( mechName ); + SaslClient client = (SaslClient)c.newInstance(); + if ( Sasl.debug ) { + System.out.println( + "ClientFactory.createSaslClient: newInstance for " + + mechName + " returned " + client); + } + return client; + } catch ( Exception e ) { + System.err.println( + "ClientFactory.createSaslClient: " + e ); + } + } else { + if ( Sasl.debug ) { + System.out.println( + "ClientFactory.createSaslClient: does not support " + + "any of the mechanisms" ); + for( int i = 0; i < mechanisms.length; i++ ) { + System.out.println( " " + mechanisms[i] ); + } + } + } + return null; + } + + /** + * Returns an array of names of mechanisms supported by this + * factory. + * @return A non-null array containing IANA-registered SASL + * mechanism names. + */ + public String[] getMechanismNames() { + return _mechanismNames; + } + + private final String PACKAGENAME = "com.netscape.sasl.mechanisms"; + private final String[] _mechanismNames = { "EXTERNAL" }; + private final String[] _mechanismClasses = { "SaslExternal" }; + private Hashtable _mechanismTable; +} diff --git a/mozilla/directory/java-sdk/ldapjdk/com/netscape/sasl/SASLClientCB.java b/mozilla/directory/java-sdk/ldapjdk/com/netscape/sasl/SASLClientCB.java deleted file mode 100644 index 6107194740c..00000000000 --- a/mozilla/directory/java-sdk/ldapjdk/com/netscape/sasl/SASLClientCB.java +++ /dev/null @@ -1,26 +0,0 @@ -/* -*- 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.sasl; - -import com.netscape.sasl.SASLException; - -/** - * This is just a base interface which does not contain any methods. - */ -public interface SASLClientCB { -} diff --git a/mozilla/directory/java-sdk/ldapjdk/com/netscape/sasl/SASLClientMechanismDriver.java b/mozilla/directory/java-sdk/ldapjdk/com/netscape/sasl/SASLClientMechanismDriver.java deleted file mode 100644 index e9ecb9acf31..00000000000 --- a/mozilla/directory/java-sdk/ldapjdk/com/netscape/sasl/SASLClientMechanismDriver.java +++ /dev/null @@ -1,203 +0,0 @@ -/* -*- 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.sasl; - -/** - * - *
- *                                               Mechanism Drivers
- *  ---------------     -------------------      -----------------
- *  | Application |-----| Protocol Driver |------| MD5           |
- *  ---------------     -------------------   |  -----------------
- *                                            |
- *                                            |  -----------------
- *                                            |--| Kerberos v5   |
- *                                            |  -----------------
- *                                            |
- *                                            |  -----------------
- *                                            |--| PKCS-11       |
- *                                            |  -----------------
- *                                            |
- *
- *                                            |
- *
- *                                            |
- *                                            |  - - - - - - - - -
- *                                            |--| xxxYYYxxx     |
- *                                               - - - - - - - - -
- *
- * An application chooses a Protocol Driver specific to the - * protocol it wants to use, and specifies one or more acceptable - * mechanisms. The Protocol Driver controls the socket, and knows - * the format/packaging of bytes sent down and received from the - * socket, but does not know how to authenticate or to encrypt/ - * decrypt the bytes. It uses one of of the Mechanism Drivers - * to help it perform authentication, where all parameters to - * be used in encryption from then on are determined. In a protocol- - * specific way, the Protocol Driver examines each byte string received - * from the server to determine if the authentication process has - * been completed. If not, the byte string is passed to the Mechanism - * Driver to be interpreted as a server challenge; the Mechanism - * Driver returns an appropriate response, which the Protocol Driver - * can encode in a protocol-specific way and return to the server. - *

- * If the Protocol Driver concludes from the byte string received from - * the server that authentication is complete, it may query - * the Mechanism Driver if it considers the authentication process - * complete, in order to thwart early completion messages inserted by - * and intruder. - *

- * On completed - * authentication, the Protocol Driver receives from the Mechanism - * Driver a Security Layer Driver object. From this point on, - * the Protocol Driver passes byte arrays received from its socket - * to the Security Layer Driver object for decoding before - * returning them to the application, and passes - * application byte arrays to the Security Layer Driver object - * for encryption before passing them down the socket. - *

- * A complication here is that some authentication methods may - * require additional user/application input (at least on the client - * side). That means that a Mechanism Driver may need to call up to - * an application during the authentication process. In the following, - * an interface SASLAuthenticationCB has been defined, allowing - * an application to (if necessary) provide a user with prompts and - * obtain additional information required to continue the process. - *

- * For LDAP, the Protocol Driver can be considered built in to - * the LDAPConnection class (actually it is more likely an object - * to which an LDAPConnection object has a reference). - *

- * However, - * there should be a generalized framework for registering and - * finding Mechanism Drivers. Maybe best to do something like - * content and protocol handlers in java: look for them in some - * predefined place in the general class hierarchy, e.g. - * netscape.security.mechanisms. So if a Protocol Driver is - * asked to use "GSSAPI", it would attempt to instantiate - * netscape.security.mechanisms.gssapi. A non-standard place can - * also be specified, e.g. "myclasses.mechanisms.GSSAPI". - * This functionality should be folded into a mechanism driver - * factory, which knows where to find candidate classes for - * instantiation. - *

- * The Mechanism Drivers are protocol-independent, and don't deal - * directly with network connections, just byte arrays, so they - * should be implemented in a generalizable way for all protocols. - *

- * A Security Layer Driver typically inherits a State object from - * the Mechanism Driver, where parameters and resolutions reached - * during authentication have been stored. - *

- * One way to allow specifying an open-ended list of parameters is - * with a Properties object. That is what is done in the following. - * - * @author rweltman@netscape.com - * @version 1.0 - */ - -public interface SASLClientMechanismDriver { - - /** - * This method prepares a byte array to use for the initial - * request to authenticate. A SASLException is thrown if the driver - * cannot initiate authentication with the supplied parameters. - * @param id Protocol-dependent identification, e.g. user name or - * distinguished name. - * @param protocol A protocol supported by the mechanism driver, e.g. - * "pop3", "ldap" - * @param serverName Only used in kerberos, currently: fully qualified - * name of server to authenticate to. - * @param props Additional configuration for the session, e.g. - *

-     *    "security.policy.encryption.minimum"    Minimum key length;
-     *                                            default 0
-     *    "security.policy.encryption.maximum"    Maximum key length;
-     *                                            default 256
-     *    "security.policy.server_authentication" True if server must
-     *                                            authenticate to client;
-     *                                            default false
-     *    "security.ip.local"                     For kerberos v4; no default
-     *    "security.ip.remote"                    For kerberos v4; no default
-     *    "security.maxbuffer"                    Reject frames larger than
-     *                                            this; default 0 (client
-     *                                            will not use the security
-     *                                            layer)
-     *
- * @param authCB An optional object which can be invoked by the - * mechanism driver to acquire additional authentication information, - * such as user name and password. - * @return A byte array to be used for the initial authentication. It - * may be null for a standard initial sequence in some - * protocols, such as POP, SMTP, and IMAP. - * @exception SASLException if an initial authentication request can - * not be formulated with the supplied parameters. - */ - public byte[] startAuthentication( String id, - String protocol, - String serverName, - java.util.Properties props, - SASLClientCB authCB ) - throws SASLException; - - /** - * If a challenge is received from the server during the - * authentication process, this method is called by the - * Protocol Driver to prepare an appropriate next request to submit - * to the server. - * @param challenge Received server challenge. - * @return Request to submit to server. - * @exception SASLException if the server challenge could not - * be handled or the driver is unable for other reasons to - * continue the authentication process. - */ - public byte[] evaluateResponse( byte[] challenge ) - throws SASLException; - - /** - * The following method may be called at any time to determine if - * the authentication process is finished. Typically, the protocol - * driver will not do this until it has received something - * from the server which indicates (in a protocol-specific manner) - * that the process - * has completed. - * @return true if authentication is complete. - */ - public boolean isComplete(); - - /** - * Once authentication is complete, the Protocol Driver calls the - * following method to obtain an object capable of encoding/decoding - * data content for the rest of the session (or until there is a - * new round of authentication). An exception is thrown if - * authentication is not yet complete. - * @return A SASLSecurityLayer object capable of doing - * encoding/decoding during the session. - * @exception SASLException if no security layer has been negotiated - * or if authentication is not complete. - */ - public SASLSecurityLayer getSecurityLayer() throws SASLException; - - /** - * Report the name of this driver, e.g. "GSSAPI". - * @return The name of the mechanism driver, without any location - * qualification. - */ - public String getMechanismName(); -} - diff --git a/mozilla/directory/java-sdk/ldapjdk/com/netscape/sasl/SASLException.java b/mozilla/directory/java-sdk/ldapjdk/com/netscape/sasl/SASLException.java deleted file mode 100644 index 07b23e69a3f..00000000000 --- a/mozilla/directory/java-sdk/ldapjdk/com/netscape/sasl/SASLException.java +++ /dev/null @@ -1,62 +0,0 @@ -/* -*- 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.sasl; - -/* - * Exception type returned on SASL authentication failures. - */ -public class SASLException extends Exception { - /** - * Constructs a default exception with no specific error information. - */ - public SASLException() { - } - - /** - * Constructs a default exception with a specified string as - * additional information. - * @param message The additional error information. - */ - public SASLException( String message ) { - super( message ); - } - - /** - * Constructs a default exception with a specified string as - * additional information, and a result code. - * @param message The additional error information. - * @param resultCode The result code returned. - */ - public SASLException( String message, int resultCode ) { - super( message ); - this.m_resultCode = resultCode; - } - - public int getResultCode() { - return m_resultCode; - } - - public String toString() { - if (m_resultCode != -1) - return super.toString() + " (" + m_resultCode + ")" ; - else - return super.toString(); - } - - private int m_resultCode = -1; -} diff --git a/mozilla/directory/java-sdk/ldapjdk/com/netscape/sasl/SASLGetLanguageCB.java b/mozilla/directory/java-sdk/ldapjdk/com/netscape/sasl/SASLGetLanguageCB.java deleted file mode 100644 index ee3593721b5..00000000000 --- a/mozilla/directory/java-sdk/ldapjdk/com/netscape/sasl/SASLGetLanguageCB.java +++ /dev/null @@ -1,34 +0,0 @@ -/* -*- 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.sasl; - -import java.util.*; - -/** - * Obtain a language to use for localization of user-visible text. - */ -public interface SASLGetLanguageCB extends SASLClientCB { - - /** - * Returns a language to use in localizing user-visible text. - * @param locale The Locale object - * @return a localized text. - */ - public String getLanguage(Locale locale); -} - diff --git a/mozilla/directory/java-sdk/ldapjdk/com/netscape/sasl/SASLInfoClientCB.java b/mozilla/directory/java-sdk/ldapjdk/com/netscape/sasl/SASLInfoClientCB.java deleted file mode 100644 index a5bf91be15c..00000000000 --- a/mozilla/directory/java-sdk/ldapjdk/com/netscape/sasl/SASLInfoClientCB.java +++ /dev/null @@ -1,32 +0,0 @@ -/* -*- 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.sasl; - -/** - * Present information to a user. No confirmation is expected. - */ -public interface SASLInfoClientCB extends SASLClientCB { - - /** - * Presents the user with the supplied textual information. - * @param prompt Textual information to be provided to the client. It - * may be localized. - */ - public void promptInfo(String prompt); -} - diff --git a/mozilla/directory/java-sdk/ldapjdk/com/netscape/sasl/SASLMechanismFactory.java b/mozilla/directory/java-sdk/ldapjdk/com/netscape/sasl/SASLMechanismFactory.java deleted file mode 100644 index 81631b4eda2..00000000000 --- a/mozilla/directory/java-sdk/ldapjdk/com/netscape/sasl/SASLMechanismFactory.java +++ /dev/null @@ -1,94 +0,0 @@ -/* -*- 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.sasl; - -/** - * This class provides a SASLClientMechanismDriver, or a list of mechanisms. - */ -public class SASLMechanismFactory { - - /** - * Returns an object implementing a requested mechanism driver. A - * SASLException is thrown if no corresponding mechanism driver can be - * instantiated. - * @param mechanisms A list of mechanism names - * @param packageName A package from which to instantiate the mechanism - * driver, eg, "myclasses.SASL.mechanisms". If null, a system default - * is used. - */ - public static SASLClientMechanismDriver getMechanismDriver( - String[] mechanisms, String packageName) throws SASLException { - - for (int i=0; i + * This class defines the policy of how to locate, load, and instantiate + * SASL clients and servers. + * Currently, only the client methods are available. + *

+ * For example, an application or library gets a SASL client by doing + * something like: + *

+ * SaslClient sc = Sasl.createSaslClient(mechanisms,
+ *     authorizationId, protocol, serverName, props, callbackHandler);
+ *
+ * It can then proceed to use the client create an authentication connection. + * For example, an LDAP library might use the client as follows: + *
+ * InputStream is = ldap.getInputStream();
+ * OutputStream os = ldap.getOutputStream();
+ * byte[] toServer = sc.createInitialResponse();
+ * LdapResult res = ldap.sendBindRequest(dn, sc.getName(), toServer);
+ * while (!sc.isComplete() && res.status == SASL_BIND_IN_PROGRESS) {
+ *     toServer = sc.evaluateChallenge(res.getBytesFromServer());
+ *     if (toServer != null) {
+ *        res = ldap.sendBindRequest(dn, sc.getName(), toServer);
+ *     }
+ * }
+ * if (sc.isComplete() && res.status == SUCCESS) {
+ *     // Get the input and output streams; may be unchanged
+ *     is = sc.getInputStream( is );
+ *     os = sc.getOutputStream( os );
+ *     // Use these streams from now on
+ *     ldap.setInputStream( is );
+ *     ldap.setOutputStream( os );
+ * }
+ *
+ * + * IMPLEMENTATION NOTE: To use this class on JDK1.2, the caller needs: + *
    + *
  • java.lang.RuntimePermission("getSecurityManager") + *
  • java.lang.RuntimePermission("getClassLoader") + *
  • java.util.PropertyPermission("javax.security.sasl.client.pkgs", "read"); + *
+ */ +public class Sasl { + private static SaslClientFactory clientFactory = null; + static final boolean debug = false; + + // Cannot create one of these + private Sasl() { + } + + /** + * The property name containing a list of package names, separated by + * '|'. Each package contains a class named ClientFactory that + * implements the SaslClientFactory interface. + * Its value is "javax.security.sasl.client.pkgs". + */ + public static final String CLIENTPKGS = "javax.security.sasl.client.pkgs"; + + /** + * Creates a SaslClient using the parameters supplied. + * The algorithm for selection is as follows: + *
    + *
  1. If a factory has been installed via setSaslClientFactory(), + * try it first. If non-null answer produced, return it. + *
  2. The javax.security.sasl.client.pkgs property contains + * a '|'-separated list of package names. Each package contains a + * class named ClientFactory. Load each factory + * and try to create a SaslClient. + * Repeat this for + * each package on the list until a non-null answer is produced. + * If non-null answer produced, return it. + *
  3. Repeat previous step using the javax.security.sasl.client.pkgs + * System property. + *
  4. If no non-null answer produced, return null. + *
+ * + * @param mechanisms The non-null list of mechanism names to try. Each is the + * IANA-registered name of a SASL mechanism. (e.g. "GSSAPI", "CRAM-MD5"). + * @param authorizationId The possibly null authorization ID to use. When + * the SASL authentication completes successfully, the entity named + * by authorizationId is granted access. + * @param protocol The non-null string name of the protocol for which + * the authentication is being performed (e.g., "ldap"). + * @param serverName The non-null string name of the server to which + * we are creating an authenticated connection. + * @param props The possibly null properties to be used by the SASL + * mechanisms to configure the authentication exchange. For example, + * "javax.security.sasl.encryption.maximum" might be used to specify + * the maximum key length to use for encryption. + * @param cbh The possibly null callback handler to used by the SASL + * mechanisms to get further information from the application/library + * to complete the authentication. For example, a SASL mechanism might + * require the authentication ID and password from the caller. + *@return A possibly null SaslClient created using the parameters + * supplied. If null, cannot find a SaslClientFactory + * that will produce one. + *@exception SaslException If cannot create a SaslClient because + * of an error. + */ + public static SaslClient createSaslClient( + String[] mechanisms, + String authorizationId, + String protocol, + String serverName, + Hashtable props, + CallbackHandler cbh) throws SaslException { + + if (debug) { + System.out.println("Sasl.createSaslClient"); + } + SaslClient mech = null; + + // If factory has been set, try it first + if (clientFactory != null) { + mech = clientFactory.createSaslClient( + mechanisms, authorizationId, + protocol, serverName, props, cbh); + } + + // No mechanism produced + if (mech == null) { + String pkgs = (props == null) ? null : + (String) props.get(CLIENTPKGS); + + // Try properties argument + if (pkgs != null) { + mech = loadFromPkgList(pkgs, mechanisms, + authorizationId, + protocol, serverName, + props, cbh); + } + + // Try system properties + if (mech == null && + (pkgs = System.getProperty(CLIENTPKGS)) != null) { + mech = loadFromPkgList(pkgs, mechanisms, + authorizationId, + protocol, serverName, + props, cbh); + } + } + return mech; + } + + private static SaslClient loadFromPkgList(String pkgs, + String[] mechanisms, + String authorizationId, + String protocol, + String serverName, + Hashtable props, + CallbackHandler cbh) + throws SaslException { + + StringTokenizer packagePrefixIter = new StringTokenizer(pkgs, "|"); + SaslClient mech = null; + SaslClientFactory fac = null; + + while (mech == null && packagePrefixIter.hasMoreTokens()) { + + String pkg = packagePrefixIter.nextToken().trim(); + String clsName = pkg + ".ClientFactory"; + if (debug) { + System.out.println("Sasl.loadFromPkgList: " + clsName); + } + Class cls = null; + try { + cls = Class.forName(clsName); + } catch (Exception e) { + System.err.println( "Sasl.loadFromPkgList: " + e ); + } + if (cls != null) { + try { + fac = (SaslClientFactory) cls.newInstance(); + } catch (InstantiationException e) { + throw new SaslException( + "Cannot instantiate " + clsName); + } catch (IllegalAccessException e) { + throw new SaslException( + "Cannot access constructor of " + clsName); + } + mech = fac.createSaslClient(mechanisms, authorizationId, + protocol, serverName, props, + cbh); + } + } + return mech; + } + + /** + * Sets the default SaslClientFactory to use. + * This method sets fac to be the default factory. + * It can only be called with a non-null value once per VM. + * If a factory has been set already, this method throws + * IllegalStateException. + * @param fac The possibly null factory to set. If null, doesn't + * do anything. + * @exception IllegalStateException If factory already set. + */ + public static void setSaslClientFactory(SaslClientFactory fac) { + if (clientFactory != null) { + throw new IllegalStateException ( + "SaslClientFactory already defined"); + } + SecurityManager security = System.getSecurityManager(); + if (security != null) { + security.checkSetFactory(); + } + clientFactory = fac; + } +} diff --git a/mozilla/directory/java-sdk/ldapjdk/com/netscape/sasl/SaslClient.java b/mozilla/directory/java-sdk/ldapjdk/com/netscape/sasl/SaslClient.java new file mode 100644 index 00000000000..a455fc011df --- /dev/null +++ b/mozilla/directory/java-sdk/ldapjdk/com/netscape/sasl/SaslClient.java @@ -0,0 +1,140 @@ +/* -*- 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.sasl; + +import java.util.Hashtable; +import java.io.*; + +/** + * Performs SASL authentication as a client. + *

+ * A protocol library such as one for LDAP gets an instance of this + * class in order to perform authentication defined by a specific SASL + * mechanism. Invoking methods on the SaslClient instance + * process challenges and create responses according to the SASL + * mechanism implemented by the SaslClient. + * As the authentication proceeds, the instance + * encapsulates the state of a SASL client's authentication exchange. + *

+ * Here's an example of how an LDAP library might use a SaslClient. + * It first gets an instance of a SaslClient: + *

+ * SaslClient sc = Sasl.createSaslClient(mechanisms,
+ *     authorizationId, protocol, serverName, props, callbackHandler);
+ *
+ * It can then proceed to use the client for authentication. + * For example, an LDAP library might use the client as follows: + *
+ * InputStream is = ldap.getInputStream();
+ * OutputStream os = ldap.getOutputStream();
+ * byte[] toServer = sc.createInitialResponse();
+ * LdapResult res = ldap.sendBindRequest(dn, sc.getName(), toServer);
+ * while (!sc.isComplete() && res.status == SASL_BIND_IN_PROGRESS) {
+ *     toServer = sc.evaluateChallenge(res.getBytesFromServer());
+ *     if (toServer != null) {
+ *        res = ldap.sendBindRequest(dn, sc.getName(), toServer);
+ *     }
+ * }
+ * if (sc.isComplete() && res.status == SUCCESS) {
+ *     // Get the input and output streams; may be unchanged
+ *     is = sc.getInputStream( is );
+ *     os = sc.getOutputStream( os );
+ *     // Use these streams from now on
+ *     ldap.setInputStream( is );
+ *     ldap.setOutputStream( os );
+ * }
+ *
+ * + * Note that the call to createInitialResponse() is optional. + * Protocols such as IMAP4 do not invoke it but instead only use + * evaluateChallenge(), possibly with an empty challenge. + * It is the responsibility of the SaslClient implementation + * for a mechanism to take this into account so that it behaves properly + * regardless of whether createInitialResponse() is called. + * + * @see Sasl + * @see SaslClientFactory + */ +public abstract interface SaslClient { + + /** + * Returns the IANA-registered mechanism name of this SASL client. + * (e.g. "CRAM-MD5", "GSSAPI"). + * @return A non-null string representing the IANA-registered mechanism name. + */ + public abstract String getMechanismName(); + + /** + * Retrieves the initial response. + * + * @return The possibly null byte array containing the initial response. + * It is null if the mechanism does not have an initial response. + * @exception SaslException If an error occurred while creating + * the initial response. + */ + public abstract byte[] createInitialResponse() throws SaslException; + + /** + * Evaluates the challenge data and generates a response. + * + * @param challenge The non-null challenge sent from the server. + * + * @return The possibly null reponse to send to the server. + * It is null if the challenge accompanied a "SUCCESS" status and the challenge + * only contains data for the client to update its state and no response + * needs to be sent to the server. + * @exception SaslException If an error occurred while processing + * the challenge or generating a response. + */ + public abstract byte[] evaluateChallenge(byte[] challenge) + throws SaslException; + + /** + * Determines whether the authentication exchange has completed. + * @return true if the authentication exchange has completed; false otherwise. + */ + public abstract boolean isComplete(); + + /** + * Retrieves an input stream for the session. It may return + * the same stream that is passed in, if no processing is to be + * done by the client object. + * + * This method can only be called if isComplete() returns true. + * @param is The original input stream for reading from the server. + * @return An input stream for reading from the server, which + * may include processing the original stream. + * @exception IOException If the authentication exchange has not completed + * or an error occurred while getting the stream. + */ + public abstract InputStream getInputStream(InputStream is) throws IOException; + + /** + * Retrieves an output stream for the session. It may return + * the same stream that is passed in, if no processing is to be + * done by the client object. + * + * This method can only be called if isComplete() returns true. + * @param is The original output stream for writing to the server. + * @return An output stream for writing to the server, which + * may include processing the original stream. + * @exception IOException If the authentication exchange has not completed + * or an error occurred while getting the stream. + */ + public abstract OutputStream getOutputStream(OutputStream os) throws IOException; +} diff --git a/mozilla/directory/java-sdk/ldapjdk/com/netscape/sasl/SaslClientFactory.java b/mozilla/directory/java-sdk/ldapjdk/com/netscape/sasl/SaslClientFactory.java new file mode 100644 index 00000000000..5c7c4731a14 --- /dev/null +++ b/mozilla/directory/java-sdk/ldapjdk/com/netscape/sasl/SaslClientFactory.java @@ -0,0 +1,69 @@ +/* -*- 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.sasl; + +import java.util.Hashtable; +import javax.security.auth.callback.CallbackHandler; + +/** + * An interface for creating instances of SaslClient. + * + * @see SaslClient + * @see Sasl + */ +public abstract interface SaslClientFactory { + /** + * Creates a SaslClient using the parameters supplied. + * + * @param mechanisms The non-null list of mechanism names to try. Each is the + * IANA-registered name of a SASL mechanism. (e.g. "GSSAPI", "CRAM-MD5"). + * @param authorizationId The possibly null authorization ID to use. When + * the SASL authentication completes successfully, the entity named + * by authorizationId is granted access. + * @param protocol The non-null string name of the protocol for which + * the authentication is being performed (e.g., "ldap"). + * @param serverName The non-null string name of the server to which + * we are creating an authenticated connection. + * @param props The possibly null properties to be used by the SASL + * mechanisms to configure the authentication exchange. For example, + * "javax.security.sasl.encryption.maximum" might be used to specify + * the maximum key length to use for encryption. + * @param cbh The possibly null callback handler to used by the SASL + * mechanisms to get further information from the application/library + * to complete the authentication. For example, a SASL mechanism might + * require the authentication ID and password from the caller. + *@return A possibly null SaslClient created using the parameters + * supplied. If null, this factory cannot produce a SaslClient + * using the parameters supplied. + *@exception SaslException If cannot create a SaslClient because + * of an error. + */ + public abstract SaslClient createSaslClient( + String[] mechanisms, + String authorizationId, + String protocol, + String serverName, + Hashtable props, + CallbackHandler cbh) throws SaslException; + + /** + * Returns an array of names of mechanisms supported by this factory. + * @return A non-null array containing a IANA-registered SASL mechanism names. + */ + public abstract String[] getMechanismNames(); +} diff --git a/mozilla/directory/java-sdk/ldapjdk/com/netscape/sasl/SaslException.java b/mozilla/directory/java-sdk/ldapjdk/com/netscape/sasl/SaslException.java new file mode 100644 index 00000000000..cbfb687d8b9 --- /dev/null +++ b/mozilla/directory/java-sdk/ldapjdk/com/netscape/sasl/SaslException.java @@ -0,0 +1,146 @@ +/* -*- 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.sasl; + +/** + * This class represents an error that has occurred when using SASL. + * + */ +public class SaslException extends java.io.IOException { + /** + * The possibly null root cause exception. + * @serial + */ + private Throwable exception; + + /** + * Constructs a new instance of SaslException. + * The root exception and the detailed message are null. + */ + public SaslException () { + super(); + } + + /** + * Constructs a new instance of SaslException with a detailed message. + * The root exception is null. + * @param detail A possibly null string containing details of the exception. + * + * @see java.lang.Throwable#getMessage + */ + public SaslException (String detail) { + super(detail); + } + + /** + * Constructs a new instance of SaslException with a detailed message + * and a root exception. + * For example, a SaslException might result from a problem with + * the callback handler, which might throw a NoSuchCallbackException if + * it does not support the requested callback, or throw an IOException + * if it had problems obtaining data for the callback. The + * SaslException's root exception would be then be the exception thrown + * by the callback handler. + * + * @param detail A possibly null string containing details of the exception. + * @param ex A possibly null root exception that caused this exception. + * + * @see java.lang.Throwable#getMessage + * @see #getException + */ + public SaslException (String detail, Throwable ex) { + super(detail); + exception = ex; + } + + /** + * Returns the root exception that caused this exception. + * @return The possibly null root exception that caused this exception. + */ + public Throwable getException() { + return exception; + } + + /** + * Prints this exception's stack trace to System.err. + * If this exception has a root exception; the stack trace of the + * root exception is printed to System.err instead. + */ + public void printStackTrace() { + printStackTrace( System.err ); + } + + /** + * Prints this exception's stack trace to a print stream. + * If this exception has a root exception; the stack trace of the + * root exception is printed to the print stream instead. + * @param ps The non-null print stream to which to print. + */ + public void printStackTrace(java.io.PrintStream ps) { + if ( exception != null ) { + String superString = super.toString(); + synchronized ( ps ) { + ps.print(superString + + (superString.endsWith(".") ? "" : ".") + + " Root exception is "); + exception.printStackTrace( ps ); + } + } else { + super.printStackTrace( ps ); + } + } + + /** + * Prints this exception's stack trace to a print writer. + * If this exception has a root exception; the stack trace of the + * root exception is printed to the print writer instead. + * @param ps The non-null print writer to which to print. + */ + public void printStackTrace(java.io.PrintWriter pw) { + if ( exception != null ) { + String superString = super.toString(); + synchronized (pw) { + pw.print(superString + + (superString.endsWith(".") ? "" : ".") + + " Root exception is "); + exception.printStackTrace( pw ); + } + } else { + super.printStackTrace( pw ); + } + } + + /** + * Returns the string representation of this exception. + * The string representation contains + * this exception's class name, its detailed messsage, and if + * it has a root exception, the string representation of the root + * exception. This string representation + * is meant for debugging and not meant to be interpreted + * programmatically. + * @return The non-null string representation of this exception. + * @see java.lang.Throwable#getMessage + */ + public String toString() { + String answer = super.toString(); + if (exception != null && exception != this) { + answer += " [Root exception is " + exception.toString() + "]"; + } + return answer; + } +} diff --git a/mozilla/directory/java-sdk/ldapjdk/com/netscape/sasl/mechanisms/SASLExternalMechanism.java b/mozilla/directory/java-sdk/ldapjdk/com/netscape/sasl/mechanisms/SASLExternalMechanism.java deleted file mode 100644 index 8a88ed51fc9..00000000000 --- a/mozilla/directory/java-sdk/ldapjdk/com/netscape/sasl/mechanisms/SASLExternalMechanism.java +++ /dev/null @@ -1,104 +0,0 @@ -/* -*- 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.sasl.mechanisms; - -import com.netscape.sasl.*; - -/** - * This class provides the implementation of the EXTERNAL mechanism driver. - * This mechanism is passed in the SASL External bind request to retrieve the - * current result code from the server. - */ -public class SASLExternalMechanism implements SASLClientMechanismDriver { - - /** - * Default constructor - */ - public SASLExternalMechanism() { - m_mechanismName = MECHANISM_NAME; - } - - /** - * This method prepares a byte array to use for the initial request to - * authenticate. - * @param id Protocol-dependent identification, for this class, it is ignored. - * @param protocol A protocol supported by the mechanism driver, e.g. - * "pop3", "ldap" - * @param serverName For this class, it is ignored. - * @param props Additional configuration for the session. For this class, - * it is ignored. - * @param authCB An optional object which can be invoked by the mechanism - * driver to acquire additional authentication information, such - * as user name and password. For this class, it is ignored. - * @return An byte array. For the case of the SASL External bind, it is - * always null. - * @exception SASLException Never thrown by this class. - */ - public byte[] startAuthentication(String id, String protocol, - String serverName, java.util.Properties props, SASLClientCB authCB) - throws SASLException { - return null; - } - - /** - * Returns the name of mechanism driver. - * @return The mechanism name. - */ - public String getMechanismName() { - return m_mechanismName; - } - - /** - * The protocol driver prepares an appropriate next request to submit - * to the server based on the challenge received from the server. - * @param challenge For this class, it is ignored. - * @return Request to submit to server. For this class, it is always null. - * @exception SASLException Never thrown by this class. - */ - public byte[] evaluateResponse(byte[] challenge) throws SASLException { - return null; - } - - /** - * The method may be called at any time to determine if the authentication - * process is finished. - * @return true if authentication is complete. For this class, - * always returns true. - */ - public boolean isComplete() { - return true; - } - - /** - * The protocol driver calls the method to obtain an object capable of - * encoding/decoding data content for the rest of the session (or until - * there is a new round of authentication). An exception is thrown if - * authentication is not yet complete. - * @return A SASLSecurityLayer object. For this class, it is always null. - * @exception SASLException Never thrown by this class. - */ - public SASLSecurityLayer getSecurityLayer() throws SASLException { - return null; - } - - private final static String LDAP_PROTOCOL = "LDAP"; - private final static String MECHANISM_NAME = "EXTERNAL"; - private String m_packageName; - private String m_mechanismName; -} diff --git a/mozilla/directory/java-sdk/ldapjdk/com/netscape/sasl/mechanisms/SaslExternal.java b/mozilla/directory/java-sdk/ldapjdk/com/netscape/sasl/mechanisms/SaslExternal.java new file mode 100644 index 00000000000..6efd0a3d450 --- /dev/null +++ b/mozilla/directory/java-sdk/ldapjdk/com/netscape/sasl/mechanisms/SaslExternal.java @@ -0,0 +1,121 @@ +/* -*- 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.sasl.mechanisms; + +import java.io.*; +import com.netscape.sasl.*; + +/** + * This class provides the implementation of the EXTERNAL mechanism driver. + * This mechanism is passed in the SASL External bind request to retrieve the + * current result code from the server. + */ +public class SaslExternal implements SaslClient { + + /** + * Default constructor + */ + public SaslExternal() { + } + + /** + * Retrieves the initial response. + * + * @return The possibly null byte array containing the initial response. + * It is null if the mechanism does not have an initial response. + * @exception SaslException If an error occurred while creating + * the initial response. + */ + public byte[] createInitialResponse() throws SaslException { + return null; + } + + /** + * Evaluates the challenge data and generates a response. + * + * @param challenge The non-null challenge sent from the server. + * + * @return The possibly null reponse to send to the server. + * It is null if the challenge accompanied a "SUCCESS" status + * and the challenge only contains data for the client to + * update its state and no response needs to be sent to the server. + * @exception SaslException If an error occurred while processing + * the challenge or generating a response. + */ + public byte[] evaluateChallenge(byte[] challenge) + throws SaslException { + return null; + } + + /** + * Returns the name of mechanism driver. + * @return The mechanism name. + */ + public String getMechanismName() { + return MECHANISM_NAME; + } + + /** + * The method may be called at any time to determine if the authentication + * process is finished. + * @return true if authentication is complete. For this class, + * always returns true. + */ + public boolean isComplete() { + return true; + } + + /** + * Retrieves an input stream for the session. It may return + * the same stream that is passed in, if no processing is to be + * done by the client object. + * + * This method can only be called if isComplete() returns true. + * @param is The original input stream for reading from the server. + * @return An input stream for reading from the server, which + * may include processing the original stream. For this class, the + * input parameter is always returned. + * @exception IOException If the authentication exchange has not completed + * or an error occurred while getting the stream. + */ + public InputStream getInputStream(InputStream is) + throws IOException { + return is; + } + + /** + * Retrieves an output stream for the session. It may return + * the same stream that is passed in, if no processing is to be + * done by the client object. + * + * This method can only be called if isComplete() returns true. + * @param is The original output stream for writing to the server. + * @return An output stream for writing to the server, which + * may include processing the original stream. For this class, the + * input parameter is always returned. + * @exception IOException If the authentication exchange has not completed + * or an error occurred while getting the stream. + */ + public OutputStream getOutputStream(OutputStream os) + throws IOException { + return os; + } + + private final static String MECHANISM_NAME = "EXTERNAL"; +} diff --git a/mozilla/directory/java-sdk/ldapjdk/lib/jaas.jar b/mozilla/directory/java-sdk/ldapjdk/lib/jaas.jar new file mode 100644 index 0000000000000000000000000000000000000000..ddb69c412e4e4da41bae4f8c02a334ecf48f06b9 GIT binary patch literal 7759 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 T6RZfgFMbLaxc^BE|Lgw%eA!Il literal 0 HcmV?d00001 diff --git a/mozilla/directory/java-sdk/ldapjdk/netscape/ldap/DynamicInvoker.java b/mozilla/directory/java-sdk/ldapjdk/netscape/ldap/DynamicInvoker.java new file mode 100644 index 00000000000..839c6bc78db --- /dev/null +++ b/mozilla/directory/java-sdk/ldapjdk/netscape/ldap/DynamicInvoker.java @@ -0,0 +1,88 @@ +/* -*- 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 netscape.ldap; + +import java.lang.reflect.*; +import java.util.Hashtable; + +/** + * Utility class to dynamically find methods of a class and to invoke + * them + */ +class DynamicInvoker { + static Object invokeMethod(Object obj, String packageName, + String methodName, Object[] args, String[] argNames) + throws LDAPException { + try { + java.lang.reflect.Method m = getMethod(packageName, methodName, + argNames); + if (m != null) { + return (m.invoke(obj, args)); + } + } catch (Exception e) { + throw new LDAPException("Invoking "+methodName+": "+ + e.toString(), LDAPException.PARAM_ERROR); + } + + return null; + } + + static java.lang.reflect.Method getMethod(String packageName, + String methodName, String[] args) throws LDAPException { + try { + java.lang.reflect.Method method = null; + String suffix = ""; + if (args != null) + for (int i=0; iLDAPConstraints + * object. For example, you can specify whether or not to follow referrals. + * You can also apply LDAP v3 controls to the operation. + *

+ * + * @param entry LDAPEntry object specifying the distinguished name and + * attributes of the new entry. + * @param listener Handler for messages returned from a server in response + * to this request. If it is null, a listener object is created internally. + * @param cons Constraints specific to the operation. + * @return LDAPResponseListener Handler for messages returned from a server + * in response to this request. + * @exception LDAPException Failed to send request. + * @see netscape.ldap.LDAPEntry + * @see netscape.ldap.LDAPResponseListener + * @see netscape.ldap.LDAPConstraints + */ + public LDAPResponseListener add(LDAPEntry entry, + LDAPResponseListener listener, + LDAPConstraints cons) + throws LDAPException; + + + /** + * Authenticates to the LDAP server (that the object is currently + * connected to) using the specified name and password. If the object + * has been disconnected from an LDAP server, this method attempts to + * reconnect to the server. If the object had already authenticated, the + * old authentication is discarded. + * + * @param dn If non-null and non-empty, specifies that the connection + * and all operations through it should be authenticated with dn as the + * distinguished name. + * @param passwd If non-null and non-empty, specifies that the connection + * and all operations through it should be authenticated with dn as the + * distinguished name and passwd as password. + * @param listener Handler for messages returned from a server in response + * to this request. If it is null, a listener object is created internally. + * @return LDAPResponseListener Handler for messages returned from a server + * in response to this request. + * @exception LDAPException Failed to send request. + * @see netscape.ldap.LDAPResponseListener + */ + public LDAPResponseListener bind(String dn, + String passwd, + LDAPResponseListener listener) + throws LDAPException; + + /** + * Authenticates to the LDAP server (that the object is currently + * connected to) using the specified name and password and allows you + * to specify constraints for this LDAP add operation by using an + * LDAPConstraints object. If the object + * has been disconnected from an LDAP server, this method attempts to + * reconnect to the server. If the object had already authenticated, the + * old authentication is discarded. + * + * @param dn If non-null and non-empty, specifies that the connection + * and all operations through it should be authenticated with dn as the + * distinguished name. + * @param passwd If non-null and non-empty, specifies that the connection + * and all operations through it should be authenticated with dn as the + * distinguished name and passwd as password. + * @param listener Handler for messages returned from a server in response + * to this request. If it is null, a listener object is created internally. + * @param cons Constraints specific to the operation. + * @return LDAPResponseListener Handler for messages returned from a server + * in response to this request. + * @exception LDAPException Failed to send request. + * @see netscape.ldap.LDAPResponseListener + * @see netscape.ldap.LDAPConstraints + */ + public LDAPResponseListener bind(String dn, + String passwd, + LDAPResponseListener listener, + LDAPConstraints cons) + throws LDAPException; + + + + /** + * Deletes the entry for the specified DN from the directory. + * + * @param dn Distinguished name of the entry to delete. + * @param listener Handler for messages returned from a server in response + * to this request. If it is null, a listener object is created internally. + * @return LDAPResponseListener Handler for messages returned from a server + * in response to this request. + * @exception LDAPException Failed to send request. + * @see netscape.ldap.LDAPResponseListener + * @see netscape.ldap.LDAPConstraints + */ + public LDAPResponseListener delete(String dn, + LDAPResponseListener listener) + throws LDAPException; + + /** + * Deletes the entry for the specified DN from the directory. + * + * @param dn Distinguished name of the entry to delete. + * @param listener Handler for messages returned from a server in response + * to this request. If it is null, a listener object is created internally. + * @param cons Constraints specific to the operation. + * @return LDAPResponseListener Handler for messages returned from a server + * in response to this request. + * @exception LDAPException Failed to send request. + * @see netscape.ldap.LDAPResponseListener + * @see netscape.ldap.LDAPConstraints + */ + public LDAPResponseListener delete(String dn, + LDAPResponseListener listener, + LDAPConstraints cons) + throws LDAPException; + + + /** + * Makes a single change to an existing entry in the directory (for + * example, changes the value of an attribute, adds a new attribute + * value, or removes an existing attribute value).
+ * The LDAPModification object specifies both the change to be made and + * the LDAPAttribute value to be changed. + * + * @param dn Distinguished name of the entry to modify. + * @param mod A single change to be made to an entry. + * @param listener Handler for messages returned from a server in response + * to this request. If it is null, a listener object is created internally. + * @return LDAPResponseListener Handler for messages returned from a server + * in response to this request. + * @exception LDAPException Failed to send request. + * @see netscape.ldap.LDAPModification + * @see netscape.ldap.LDAPResponseListener + */ + public LDAPResponseListener modify(String dn, + LDAPModification mod, + LDAPResponseListener listener) + throws LDAPException; + + /** + * Makes a single change to an existing entry in the directory (for + * example, changes the value of an attribute, adds a new attribute + * value, or removes an existing attribute value).
+ * The LDAPModification object specifies both the change to be made and + * the LDAPAttribute value to be changed. + * + * @param dn Distinguished name of the entry to modify. + * @param mod A single change to be made to an entry. + * @param listener Handler for messages returned from a server in response + * to this request. If it is null, a listener object is created internally. + * @param cons Constraints specific to the operation. + * @return LDAPResponseListener Handler for messages returned from a server + * in response to this request. + * @exception LDAPException Failed to send request. + * @see netscape.ldap.LDAPModification + * @see netscape.ldap.LDAPResponseListener + * @see netscape.ldap.LDAPConstraints + */ + public LDAPResponseListener modify(String dn, + LDAPModification mod, + LDAPResponseListener listener, + LDAPConstraints cons) + throws LDAPException; + + /** + * Makes a set of changes to an existing entry in the directory (for + * example, changes attribute values, adds new attribute values, or + * removes existing attribute values). + *

+ * @param dn Distinguished name of the entry to modify. + * @param mods A set of modifications to be made to the entry. + * @param listener Handler for messages returned from a server in response + * to this request. If it is null, a listener object is created internally. + * @return LDAPResponseListener Handler for messages returned from a server + * in response to this request. + * @exception LDAPException Failed to send request. + * @see netscape.ldap.LDAPModificationSet + * @see netscape.ldap.LDAPResponseListener + */ + public LDAPResponseListener modify(String dn, + LDAPModificationSet mods, + LDAPResponseListener listener) + throws LDAPException; + + /** + * Makes a set of changes to an existing entry in the directory (for + * example, changes attribute values, adds new attribute values, or + * removes existing attribute values). + * + * @param dn Distinguished name of the entry to modify. + * @param mods A set of modifications to be made to the entry. + * @param listener Handler for messages returned from a server in response + * to this request. If it is null, a listener object is created internally. + * @param cons Constraints specific to the operation. + * @return LDAPResponseListener Handler for messages returned from a server + * in response to this request. + * @exception LDAPException Failed to send request. + * @see netscape.ldap.LDAPModificationSet + * @see netscape.ldap.LDAPResponseListener + * @see netscape.ldap.LDAPConstraints + */ + public LDAPResponseListener modify(String dn, + LDAPModificationSet mods, + LDAPResponseListener listener, + LDAPConstraints cons) + throws LDAPException; + + + /** + * Renames an existing entry in the directory. + * + * @param dn Current distinguished name of the entry. + * @param newRdn New relative distinguished name for the entry. + * @param deleteOldRdn If true, the old name is not retained as an + * attribute value. + * @param listener Handler for messages returned from a server in response + * to this request. If it is null, a listener object is created internally. + * @return LDAPResponseListener Handler for messages returned from a server + * in response to this request. + * @exception LDAPException Failed to send request. + * @see netscape.ldap.LDAPResponseListener + */ + public LDAPResponseListener rename(String dn, + String newRdn, + boolean deleteOldRdn, + LDAPResponseListener listener) + throws LDAPException; + + /** + * Renames an existing entry in the directory. + * + * @param dn Current distinguished name of the entry. + * @param newRdn New relative distinguished name for the entry. + * @param deleteOldRdn If true, the old name is not retained as an + * @param listener Handler for messages returned from a server in response + * to this request. If it is null, a listener object is created internally. + * @param cons Constraints specific to the operation. + * @return LDAPResponseListener Handler for messages returned from a server + * in response to this request. + * @exception LDAPException Failed to send request. + * @see netscape.ldap.LDAPResponseListener + * @see netscape.ldap.LDAPConstraints + */ + public LDAPResponseListener rename(String dn, + String newRdn, + boolean deleteOldRdn, + LDAPResponseListener listener, + LDAPConstraints cons) + throws LDAPException; + + /** + * Performs the search specified by the criteria that you enter.

+ * To abandon the search, use the abandon method. + * + * @param base The base distinguished name to search from + * @param scope The scope of the entries to search. You can specify one + * of the following:

+ *

    + *
  • LDAPv2.SCOPE_BASE (search only the base DN)

    + *

  • LDAPv2.SCOPE_ONE + * (search only entries under the base DN)

    + *

  • LDAPv2.SCOPE_SUB + * (search the base DN and all entries within its subtree)

    + *

+ *

+ * @param filter Search filter specifying the search criteria. + * @param attrs List of attributes that you want returned in the + * search results. + * @param typesOnly If true, returns the names but not the values of the + * attributes found. If false, returns the names and values for + * attributes found + * @param listener Handler for messages returned from a server in response + * to this request. If it is null, a listener object is created internally. + * @return LDAPSearchListener Handler for messages returned from a server + * in response to this request. + * @exception LDAPException Failed to send request. + * @see netscape.ldap.LDAPAsynchronousConnection#abandon(netscape.ldap.LDAPSearchListener) + */ + public LDAPSearchListener search(String base, + int scope, + String filter, + String attrs[], + boolean typesOnly, + LDAPSearchListener listener) + throws LDAPException; + + /** + * Performs the search specified by the criteria that you enter. + * This method also allows you to specify constraints for the search + * (such as the maximum number of entries to find or the + * maximum time to wait for search results).

+ * To abandon the search, use the abandon method. + * + * @param base The base distinguished name to search from + * @param scope The scope of the entries to search. You can specify one + * of the following:

+ *

    + *
  • LDAPv2.SCOPE_BASE (search only the base DN)

    + *

  • LDAPv2.SCOPE_ONE + * (search only entries under the base DN)

    + *

  • LDAPv2.SCOPE_SUB + * (search the base DN and all entries within its subtree)

    + *

+ *

+ * @param filter Search filter specifying the search criteria. + * @param attrs List of attributes that you want returned in the search + * results. + * @param typesOnly If true, returns the names but not the values of the + * attributes found. If false, returns the names and values for + * attributes found. + * @param listener Handler for messages returned from a server in response + * to this request. If it is null, a listener object is created internally. + * @param cons Constraints specific to this search (for example, the + * maximum number of entries to return). + * @return LDAPSearchListener Handler for messages returned from a server + * in response to this request. + * @exception LDAPException Failed to send request. + * @see netscape.ldap.LDAPAsynchronousConnection#abandon(netscape.ldap.LDAPSearchListener) + */ + public LDAPSearchListener search(String base, + int scope, + String filter, + String attrs[], + boolean typesOnly, + LDAPSearchListener listener, + LDAPSearchConstraints cons) + throws LDAPException; + + /** + * Compare an attribute value with one in the directory. The result can + * be obtained by calling getResultCode on the + * LDAPResponse from the LDAPResponseListener. + * The code will be LDAPException.COMPARE_TRUE or + * LDAPException.COMPARE_FALSE. + * + * @param dn Distinguished name of the entry to compare. + * @param attr Attribute with a value to compare. + * @param listener Handler for messages returned from a server in response + * to this request. If it is null, a listener object is created internally. + * @return LDAPResponseListener Handler for messages returned from a server + * in response to this request. + * @exception LDAPException Failed to send request. + */ + public LDAPResponseListener compare(String dn, + LDAPAttribute attr, + LDAPResponseListener listener) + throws LDAPException; + + /** + * Compare an attribute value with one in the directory. The result can + * be obtained by calling getResultCode on the + * LDAPResponse from the LDAPResponseListener. + * The code will be LDAPException.COMPARE_TRUE or + * LDAPException.COMPARE_FALSE. + * + * @param dn Distinguished name of the entry to compare. + * @param attr Attribute with a value to compare. + * @param listener Handler for messages returned from a server in response + * to this request. If it is null, a listener object is created internally. + * @param cons Constraints specific to this operation. + * @return LDAPResponseListener Handler for messages returned from a server + * in response to this request. + * @exception LDAPException Failed to send request. + */ + public LDAPResponseListener compare(String dn, + LDAPAttribute attr, + LDAPResponseListener listener, + LDAPConstraints cons) + throws LDAPException; + + /** + * Cancels the ldap request with the specified id and discards + * any results already received. + * + * @param id A LDAP request id + * @exception LDAPException Failed to send request. + */ + public void abandon(int id) throws LDAPException; + + /** + * Cancels all outstanding search requests associated with this + * LDAPSearchListener object and discards any results already received. + * + * @param searchlistener A search listener returned from a search. + * @exception LDAPException Failed to send request. + */ + public void abandon(LDAPSearchListener searchlistener) + throws LDAPException; +} diff --git a/mozilla/directory/java-sdk/ldapjdk/netscape/ldap/LDAPAttribute.java b/mozilla/directory/java-sdk/ldapjdk/netscape/ldap/LDAPAttribute.java index 81ef4e16b60..ba295960afa 100644 --- a/mozilla/directory/java-sdk/ldapjdk/netscape/ldap/LDAPAttribute.java +++ b/mozilla/directory/java-sdk/ldapjdk/netscape/ldap/LDAPAttribute.java @@ -12,7 +12,7 @@ * * 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 + * Copyright (C) 1999 Netscape Communications Corporation. All Rights * Reserved. */ package netscape.ldap; @@ -143,7 +143,31 @@ public class LDAPAttribute { } return v.elements(); } - + + /** + * Returns a array for the values of the attribute as String objects. + * @return Array of attribute values. Each element in the array + * will be a String object. + */ + public String[] getStringValueArray() { + + String s[] = new String[values.length]; + synchronized(this) { + try { + for (int i=0; i < values.length; i++) { + if ( values[i] !=null ) { + s[i] = new String((byte[])values[i], "UTF8"); + } else { + s[i] = new String(""); + } + } + } catch (Exception e) { + return null; + } + } + return s; + } + /** * Returns an enumerator for the values of the attribute in byte[] * format. @@ -164,6 +188,29 @@ public class LDAPAttribute { return v.elements(); } + /** + * Returns an array for the values of the attribute in byte[] + * format. + * @return Array of attribute values. Each element in the array + * will be of type byte[]. + */ + public byte[][] getByteValueArray() { + byte b[][] = new byte[values.length][]; + synchronized(this) { + try { + for (int i=0; i < values.length; i++) { + b[i] = new byte[((byte[])(values[i])).length]; + System.arraycopy((byte[])values[i], 0, (byte[])b[i], 0, + ((byte[])(values[i])).length); + } + } catch (Exception e) { + return null; + } + } + return b; + + } + /** * Returns the name of the attribute. * @return Name of the attribute. @@ -445,23 +492,28 @@ public class LDAPAttribute { * @return string representation parameters */ private String getParamString() { - String s = ""; + StringBuffer sb = new StringBuffer(); + if ( values.length > 0 ) { for (int i = 0; i < values.length; i++) { - if (i != 0) - s = s + ","; + if (i != 0) { + sb.append(","); + } byte[] val = (byte[])values[i]; try { - s = s + new String(val, "UTF8"); + sb.append(new String(val, "UTF8")); } catch (Exception e) { - if (val != null) - s = s + val.length + " bytes"; - else - s = s + "null value"; + if (val != null) { + sb.append(val.length); + sb.append(" bytes"); + } + else { + sb.append("null value"); + } } } } - return "{type='" + getName() + "', values='" + s + "'}"; + return "{type='" + getName() + "', values='" + sb.toString() + "'}"; } /** diff --git a/mozilla/directory/java-sdk/ldapjdk/netscape/ldap/LDAPAttributeSchema.java b/mozilla/directory/java-sdk/ldapjdk/netscape/ldap/LDAPAttributeSchema.java index 250aa68d4d2..4f60e943c71 100644 --- a/mozilla/directory/java-sdk/ldapjdk/netscape/ldap/LDAPAttributeSchema.java +++ b/mozilla/directory/java-sdk/ldapjdk/netscape/ldap/LDAPAttributeSchema.java @@ -12,7 +12,7 @@ * * 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 + * Copyright (C) 1999 Netscape Communications Corporation. All Rights * Reserved. */ package netscape.ldap; @@ -44,22 +44,66 @@ import java.util.*; * When you construct an LDAPAttributeSchema object, you can * specify these types of information as arguments to the constructor or * in the AttributeTypeDescription format specified in RFC 2252. - * (When an LDAP client searches an LDAP server for the schema, the server + * When an LDAP client searches an LDAP server for the schema, the server * returns schema information as an object with attribute values in this - * format.) + * format. + *

+ + * There a number of additional optional description fields which + * are not explicitly accessible through LDAPAttributeSchema, but which + * can be managed with setQualifier, getQualifier, and getQualifierNames: + *

+ * + *

    + *
  • EQUALITY + *
  • ORDERING + *
  • SUBSTR + *
  • COLLECTIVE + *
  • NO-USER-MODIFICATION + *
  • USAGE + *
  • OBSOLETE + *
*

* * You can get the name, OID, and description of this attribute type * definition by using the getName, getOID, and * getDescription methods inherited from the abstract class - * LDAPSchemaElement. + * LDAPSchemaElement. Optional and custom qualifiers are + * accessed with getQualifier and getQualifierNames + * from LDAPSchemaElement. *

* - * If you need to add or remove this attribute type definition from the - * schema, you can use the add and remove + * To add or remove this attribute type definition from the + * schema, use the add and remove * methods, which this class inherits from the LDAPSchemaElement * abstract class. *

+ * RFC 2252 defines AttributeTypeDescription as follows: + *

+ *

+ *     AttributeTypeDescription = "(" whsp
+ *          numericoid whsp              ; AttributeType identifier
+ *        [ "NAME" qdescrs ]             ; name used in AttributeType
+ *        [ "DESC" qdstring ]            ; description
+ *        [ "OBSOLETE" whsp ]
+ *        [ "SUP" woid ]                 ; derived from this other
+ *                                       ; AttributeType
+ *        [ "EQUALITY" woid              ; Matching Rule name
+ *        [ "ORDERING" woid              ; Matching Rule name
+ *        [ "SUBSTR" woid ]              ; Matching Rule name
+ *        [ "SYNTAX" whsp noidlen whsp ] ; see section 4.3
+ *        [ "SINGLE-VALUE" whsp ]        ; default multi-valued
+ *        [ "COLLECTIVE" whsp ]          ; default not collective
+ *        [ "NO-USER-MODIFICATION" whsp ]; default user modifiable
+ *        [ "USAGE" whsp AttributeUsage ]; default userApplications
+ *        whsp ")"
+ *
+ *    AttributeUsage =
+ *        "userApplications"     /
+ *        "directoryOperation"   /
+ *        "distributedOperation" / ; DSA-shared
+ *        "dSAOperation"          ; DSA-specific, value depends on server
+ * 
* * @version 1.0 * @see netscape.ldap.LDAPSchemaElement @@ -98,6 +142,27 @@ public class LDAPAttributeSchema extends LDAPSchemaElement { super( name, oid, description ); attrName = "attributetypes"; this.syntax = syntax; + this.syntaxString = internalSyntaxToString(); + this.single = single; + } + + /** + * Constructs an attribute type definition, using the specified + * information. + * @param name Name of the attribute type. + * @param oid Object identifier (OID) of the attribute type + * in dotted-string format (for example, "1.2.3.4"). + * @param description Description of attribute type. + * @param syntaxString Syntax of this attribute type in dotted-string + * format (for example, "1.2.3.4.5"). + * @param single true if the attribute type is single-valued. + */ + public LDAPAttributeSchema( String name, String oid, String description, + String syntaxString, boolean single ) { + super( name, oid, description ); + attrName = "attributetypes"; + this.syntaxString = syntaxString; + this.syntax = syntaxCheck( syntaxString ); this.single = single; } @@ -119,59 +184,13 @@ public class LDAPAttributeSchema extends LDAPSchemaElement { */ public LDAPAttributeSchema( String raw ) { attrName = "attributetypes"; - raw.trim(); - // Skip past "( " and ")" - int l = raw.length(); - raw = raw.substring( 2, l - 1 ); - l = raw.length(); - int ind = raw.indexOf( ' ' ); - oid = raw.substring( 0, ind ); - char[] ch = new char[l]; - - raw = raw.substring( ind + 1, l ); - l = raw.length(); - raw.getChars( 0, l, ch, 0 ); - - ind = 0; - l = ch.length; - while ( ind < l ) { - String s = ""; - String val; - while( ch[ind] == ' ' ) - ind++; - int last = ind + 1; - while( (last < l) && (ch[last] != ' ') ) - last++; - if ( (ind < l) && (last < l) ) { - s = new String( ch, ind, last-ind ); - ind = last; - if ( s.equalsIgnoreCase( "SINGLE-VALUE" ) ) { - single = true; - continue; - } - } else { - ind = l; - } - - while( (ind < l) && (ch[ind] != '\'') ) - ind++; - last = ind + 1; - while( (last < l) && (ch[last] != '\'') ) { - last++; - } - if ( (ind < last) && (last < l) ) { - val = new String( ch, ind+1, last-ind-1 ); - ind = last + 1; - - if ( s.equalsIgnoreCase( "NAME" ) ) { - name = val; - } else if ( s.equalsIgnoreCase( "DESC" ) ) { - description = val; - } else if ( s.equalsIgnoreCase( "SYNTAX" ) ) { - syntax = syntaxCheck( val ); - } - } + parseValue( raw ); + String val = (String)properties.get( SYNTAX ); + if ( val != null ) { + syntaxString = val; + syntax = syntaxCheck( val ); } + single = properties.containsKey( "SINGLE-VALUE" ); } /** @@ -185,12 +204,22 @@ public class LDAPAttributeSchema extends LDAPSchemaElement { *
  • telephone (telephone number -- identical to cis, * but blanks and dashes are ignored during comparisons) *
  • dn (distinguished name) + *
  • unknown (not a known syntax) * */ public int getSyntax() { return syntax; } + /** + * Gets the syntax of the attribute type in dotted-decimal format, + * for example "1.2.3.4.5" + * @return the attribute syntax in dotted-decimal format. + */ + public String getSyntaxString() { + return syntaxString; + } + /** * Determines if the attribute type is single-valued. * @return true if single-valued, @@ -215,41 +244,11 @@ public class LDAPAttributeSchema extends LDAPSchemaElement { } else if ( syntax == integer ) { s = intString; } else { - s = "unknown"; + s = syntaxString; } return s; } - /** - * Get the attribute type definition in a string representation - * of the AttributeTypeDescription data type defined in X.501 (see - * RFC 2252, Lightweight Directory Access Protocol - * (v3): Attribute Syntax Definitions - * for a description of this format). - * This is the format that LDAP servers and clients use to exchange - * schema information. (For example, when - * you search an LDAP server for its schema, the server returns an entry - * with the attributes "objectclasses" and "attributetypes". The - * values of the "attributetypes" attribute are attribute type - * descriptions in this format.) - *

    - * - * @return A string in a format that can be used as the value of - * the attributetypes attribute (which describes - * an attribute type in the schema) of a subschema object. - */ - public String getValue() { - String s = "( " + oid + " NAME \'" + name + "\' DESC \'" + - description + "\' SYNTAX \'"; - s += internalSyntaxToString(); - s += "\' "; - if ( single ) - s += "SINGLE-VALUE "; - s += ')'; - return s; - } - protected String syntaxToString() { String s; if ( syntax == cis ) { @@ -265,11 +264,43 @@ public class LDAPAttributeSchema extends LDAPSchemaElement { } else if ( syntax == dn ) { s = "distinguished name"; } else { - s = "unknown"; + s = syntaxString; } return s; } + /** + * Prepare a value in RFC 2252 format for submitting to a server + * + * @param quotingBug true if SUP and SYNTAX values are to + * be quoted. That is to satisfy bugs in certain LDAP servers. + * @return A String ready to be submitted to an LDAP server + */ + String getValue( boolean quotingBug ) { + String s = getValuePrefix(); + s += getValue( SUPERIOR, false ); + String val = getOptionalValues( MATCHING_RULES ); + if ( val.length() > 0 ) { + s += val + ' '; + } + s += getValue( SYNTAX, quotingBug ); + s += ' '; + val = getOptionalValues( NOVALS ); + if ( val.length() > 0 ) { + s += val + ' '; + } + val = getOptionalValues( new String[] {USAGE} ); + if ( val.length() > 0 ) { + s += val + ' '; + } + val = getCustomValues(); + if ( val.length() > 0 ) { + s += val + ' '; + } + s += ')'; + return s; + } + /** * Get the definition of the attribute type in a user friendly format. * This is the format that the attribute type definition uses when @@ -280,10 +311,12 @@ public class LDAPAttributeSchema extends LDAPSchemaElement { String s = "Name: " + name + "; OID: " + oid + "; Type: "; s += syntaxToString(); s += "; Description: " + description + "; "; - if ( single ) + if ( single ) { s += "single-valued"; - else + } else { s += "multi-valued"; + } + s += getQualifierString( EXPLICIT ); return s; } @@ -305,6 +338,64 @@ public class LDAPAttributeSchema extends LDAPSchemaElement { return i; } + /** + * Parses an attribute schema definition to see if the SYNTAX value + * is quoted. It shouldn't be (according to RFC 2252), but it is for + * some LDAP servers. It will either be:
    + * SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 or
    + * SYNTAX '1.3.6.1.4.1.1466.115.121.1.15' + * @param raw Definition of the attribute type in the + * AttributeTypeDescription format. + */ + static boolean isSyntaxQuoted( String raw ) { + int ind = raw.indexOf( " SYNTAX " ); + if ( ind >= 0 ) { + ind += 8; + int l = raw.length() - ind; + // Extract characters + char[] ch = new char[l]; + raw.getChars( ind, raw.length(), ch, 0 ); + ind = 0; + // Skip to ' or start of syntax value + while( (ind < ch.length) && (ch[ind] == ' ') ) { + ind++; + } + if ( ind < ch.length ) { + return ( ch[ind] == '\'' ); + } + } + return false; + } + + // Predefined qualifiers + public static final String EQUALITY = "EQUALITY"; + public static final String ORDERING = "ORDERING"; + public static final String SUBSTR = "SUBSTR"; + public static final String SINGLE = "SINGLE-VALUE"; + public static final String COLLECTIVE = "COLLECTIVE"; + public static final String NO_USER_MODIFICATION = "NO-USER-MODIFICATION"; + public static final String USAGE = "USAGE"; + public static final String SYNTAX = "SYNTAX"; + protected int syntax = unknown; - private boolean single = false; + protected String syntaxString = null; + protected boolean single = false; + + static String[] NOVALS = { SINGLE, + COLLECTIVE, + NO_USER_MODIFICATION }; + static { + for( int i = 0; i < NOVALS.length; i++ ) { + novalsTable.put( NOVALS[i], NOVALS[i] ); + } + } + static final String[] MATCHING_RULES = { EQUALITY, + ORDERING, + SUBSTR }; + // Qualifiers tracked explicitly + static final String[] EXPLICIT = { SINGLE, + OBSOLETE, + COLLECTIVE, + NO_USER_MODIFICATION, + SYNTAX}; } diff --git a/mozilla/directory/java-sdk/ldapjdk/netscape/ldap/LDAPAttributeSet.java b/mozilla/directory/java-sdk/ldapjdk/netscape/ldap/LDAPAttributeSet.java index a2b37191dd0..da356682612 100644 --- a/mozilla/directory/java-sdk/ldapjdk/netscape/ldap/LDAPAttributeSet.java +++ b/mozilla/directory/java-sdk/ldapjdk/netscape/ldap/LDAPAttributeSet.java @@ -388,11 +388,13 @@ public class LDAPAttributeSet implements Cloneable * @return String representation of all attributes in the set. */ public String toString() { - String s = "LDAPAttributeSet: "; - if ( attrs.length > 0 ) { - for( int i = 0; i < attrs.length; i++ ) - s += attrs[i].toString(); + StringBuffer sb = new StringBuffer("LDAPAttributeSet: "); + for( int i = 0; i < attrs.length; i++ ) { + if (i != 0) { + sb.append(" "); + } + sb.append(attrs[i].toString()); } - return s; + return sb.toString(); } } diff --git a/mozilla/directory/java-sdk/ldapjdk/netscape/ldap/LDAPBind.java b/mozilla/directory/java-sdk/ldapjdk/netscape/ldap/LDAPBind.java new file mode 100644 index 00000000000..ffd7600e82b --- /dev/null +++ b/mozilla/directory/java-sdk/ldapjdk/netscape/ldap/LDAPBind.java @@ -0,0 +1,43 @@ +/* -*- 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 netscape.ldap; + +/** + * Used to do explicit bind processing on a referral. A client may + * specify an instance of this class to be used on a single operation + * (through the LDAPConstraints object) or for all operations + * (through LDAPConnection.setOption()). It is typically used + * to control the authentication mechanism used on implicit referral + * handling. + */ + +public interface LDAPBind { + + /** + * This method is called by LDAPConnection when + * authenticating. An implementation may access the host, port, + * credentials, and other information in the LDAPConnection + * to decide on an appropriate authentication mechanism, and/or may + * interact with a user or external module. + * @exception netscape.ldap.LDAPException + * @see netscape.ldap.LDAPConnection#bind + * @param conn An established connection to an LDAP server. + */ + + public void bind(LDAPConnection conn) throws LDAPException; +} diff --git a/mozilla/directory/java-sdk/ldapjdk/netscape/ldap/LDAPCache.java b/mozilla/directory/java-sdk/ldapjdk/netscape/ldap/LDAPCache.java index 6b29b3879be..44ce94e352b 100644 --- a/mozilla/directory/java-sdk/ldapjdk/netscape/ldap/LDAPCache.java +++ b/mozilla/directory/java-sdk/ldapjdk/netscape/ldap/LDAPCache.java @@ -19,7 +19,6 @@ package netscape.ldap; import java.util.*; import java.io.*; -import netscape.ldap.*; import netscape.ldap.client.*; import netscape.ldap.util.*; import java.util.zip.CRC32; @@ -254,7 +253,8 @@ public class LDAPCache implements TimerEventListener { break; } } - if ((scope == LDAPConnection.SCOPE_SUB) && (dn1.contains(dn2))) { + if ((scope == LDAPConnection.SCOPE_SUB) && + (dn1.isDescendantOf(dn2))) { break; } } @@ -357,7 +357,7 @@ public class LDAPCache implements TimerEventListener { * @exception LDAPException Thrown when failed to create key. */ Long createKey(String host, int port, String baseDN, String filter, - int scope, String[] attrs, String bindDN, LDAPSearchConstraints cons) + int scope, String[] attrs, String bindDN, LDAPConstraints cons) throws LDAPException { DN dn = new DN(baseDN); @@ -617,11 +617,13 @@ public class LDAPCache implements TimerEventListener { if ((str == null) || (str.length < 1)) return "0"+DELIM; else { - sortStrings(str); + String[] sorted = new String[str.length]; + System.arraycopy( str, 0, sorted, 0, str.length ); + sortStrings(sorted); - String s = str.length+DELIM; - for (int i=0; i(delay=-1) use serial policy,
    + * (delay=0) start immediately concurrent threads to each specified server + *
    (delay>0) create a new connection setup thread after delay seconds + */ + LDAPConnSetupMgr(String[] hosts, int[] ports, LDAPSocketFactory factory, int delay) { + m_dsList = new ServerEntry[hosts.length]; + for (int i=0; i < hosts.length; i++) { + m_dsList[i] = new ServerEntry(hosts[i], ports[i], NEVER_USED); + } + m_factory = factory; + m_policy = (delay < 0) ? SERIAL : PARALLEL; + m_connSetupDelay = delay*1000; + } + + /** + * Constructor used by clone() + */ + private LDAPConnSetupMgr() {} + + /** + * Try to open the connection to any of the servers in the list. + */ + Socket openConnection() throws LDAPException{ + m_socket = null; + m_connException = null; + + // If reconnecting, sort dsList so that servers more likly to + // be available are tried first + sortDsList(); + + if (m_policy == SERIAL || m_dsList.length == 1) { + openSerial(); + } + else { + openParallel(); + } + + if (m_socket != null) { + return m_socket; + } + + if (m_connException != null) { + throw m_connException; + } + + return null; + } + + /** + * To be called when the current connection is lost. + * Put the connected server at the end of the server list for + * the next connect attempt. + */ + void invalidateConnection() { + if (m_socket != null) { + m_dsList[m_dsIdx].connSetupStatus = FAILED; + + // Move the entry to the end of the list + int srvCnt = m_dsList.length, j=0; + ServerEntry[] newDsList = new ServerEntry[m_dsList.length]; + for (int i=0; i < srvCnt; i++) { + if (i != m_dsIdx) { + newDsList[j++] = m_dsList[i]; + } + } + newDsList[j] = m_dsList[m_dsIdx]; + m_dsList = newDsList; + m_dsIdx = j; + } + + m_socket = null; + } + + /** + * To be called when the current connection is terminated by the user + * Mark the connected server status as DISCONNECTED. This will + * put it at top of the server list for the next connect attempt. + */ + void disconnect() { + if (m_socket != null) { + m_dsList[m_dsIdx].connSetupStatus = DISCONNECTED; + } + + m_socket = null; + } + + Socket getSocket() { + return m_socket; + } + + String getHost() { + if (m_dsIdx >= 0) { + return m_dsList[m_dsIdx].host; + } + return m_dsList[0].host; + } + + int getPort() { + if (m_dsIdx >= 0) { + return m_dsList[m_dsIdx].port; + } + return m_dsList[0].port; + } + + /** + * Check if the user has voluntarily closed the connection + */ + boolean isUserDisconnected() { + return (m_dsIdx >=0 && + m_dsList[m_dsIdx].connSetupStatus == DISCONNECTED); + } + + /** + * Try sequentially to open a new connection to a server. + */ + private void openSerial() { + for (int i=0; i < m_dsList.length; i++) { + m_dsList[i].connSetupThread = Thread.currentThread(); + connectServer(i); + if (m_socket != null) { + return; + } + } + } + + /** + * Try concurrently to open a new connection a server. Create a separate + * thread for each connection attempt. + */ + private synchronized void openParallel() { + for (int i=0; m_socket==null && i < m_dsList.length; i++) { + + //Create a Thread to execute connectSetver() + final int dsIdx = i; + String threadName = "ConnSetupMgr " + + m_dsList[dsIdx].host + ":" + m_dsList[dsIdx].port; + Thread t = new Thread(new Runnable() { + public void run() { + connectServer(dsIdx); + } + }, threadName); + + m_dsList[dsIdx].connSetupThread = t; + t.setDaemon(true); + t.start(); + + // Wait before starting another thread if the delay is not zero + if (m_connSetupDelay != 0 && i < (m_dsList.length-1)) { + try { + wait(m_connSetupDelay); + } + catch (InterruptedException e) {} + } + } + + // At this point all threads are started. Wait until first thread + // succeeds to connect or all threads terminate + + while (m_socket == null) { + + // Check whether there are still running threads + boolean threadsRunning = false; + for (int i=0; i < m_dsList.length; i++) { + if (m_dsList[i].connSetupThread != null) { + threadsRunning = true; + break; + } + } + if (!threadsRunning) { return; } + + // Wait for a thread to terminate + try { + wait(); + } + catch (InterruptedException e) {} + } + } + + /** + * Connect to the server at the given index + */ + private void connectServer(int idx) { + ServerEntry entry = m_dsList[idx]; + Thread currThread = Thread.currentThread(); + Socket sock = null; + LDAPException conex = null; + + try { + /* If we are to create a socket ourselves, make sure it has + sufficient privileges to connect to the desired host */ + if (m_factory == null) { + sock = new Socket (entry.host, entry.port); + //s.setSoLinger( false, -1 ); + } else { + sock = m_factory.makeSocket(entry.host, entry.port); + } + } + catch (IOException e) { + conex = new LDAPException("failed to connect to server " + + entry.host+":"+entry.port, LDAPException.CONNECT_ERROR); + } + catch (LDAPException e) { + conex = e; + } + + if (currThread.isInterrupted()) { + return; + } + + synchronized (this) { + if (m_socket == null && entry.connSetupThread == currThread) { + entry.connSetupThread = null; + if (sock != null) { + entry.connSetupStatus = CONNECTED; + m_socket = sock; + m_dsIdx = idx; + cleanup(); // Signal other concurrent threads to terminate + } + else { + entry.connSetupStatus = FAILED; + m_connException = conex; + } + notifyAll(); + } + } + } + + /** + * Terminate all concurrently running connection setup threads + */ + private synchronized void cleanup() { + Thread currThread = Thread.currentThread(); + for (int i=0; i < m_dsList.length; i++) { + ServerEntry entry = m_dsList[i]; + if (entry.connSetupThread != null && entry.connSetupThread != currThread) { + + entry.connSetupStatus = FAILED; + //Thread.stop() is considered to be dangerous, use Thread.interrupt(). + //interrupt() will however not work if the thread is blocked in the + //socket library native connect() call, but the connect() will + //eventually timeout and the thread will die. + entry.connSetupThread.interrupt(); + + entry.connSetupThread = null; + } + } + } + + + /** + * Sort Server List so that servers which are more likely to be available + * are tried first. The likelihood of making a successful connection + * is determined by the connSetupStatus. Lower values have higher + * likelihood. Thus, the order of server access is (1) disconnected by + * the user (2) never used (3) connection setup failed/connection lost + */ + private void sortDsList() { + int srvCnt = m_dsList.length; + for (int i=1; i < srvCnt; i++) { + for (int j=0; j < i; j++) { + if (m_dsList[i].connSetupStatus < m_dsList[j].connSetupStatus) { + // swap entries + ServerEntry entry = m_dsList[j]; + m_dsList[j] = m_dsList[i]; + m_dsList[i] = entry; + } + } + } + } + + public String toString() { + String str = "dsIdx="+m_dsIdx+ " dsList="; + for (int i=0; i < m_dsList.length; i++) { + str += m_dsList[i]+ " "; + } + return str; + } + + public Object clone() { + LDAPConnSetupMgr cloneMgr = new LDAPConnSetupMgr(); + cloneMgr.m_factory = m_factory; + cloneMgr.m_policy = m_policy; + cloneMgr.m_connSetupDelay = m_connSetupDelay; + cloneMgr.m_dsIdx = m_dsIdx; + cloneMgr.m_dsList = new ServerEntry[m_dsList.length]; + cloneMgr.m_socket = m_socket; + for (int i=0; inull for no cache + */ + synchronized void setCache( LDAPCache cache ) { + m_cache = cache; + m_messages = (m_cache != null) ? new Hashtable() : null; + } + + /** + * Sends LDAP request via this connection thread. + * @param request request to send + * @param toNotify response listener to invoke when the response + * is ready + */ + synchronized void sendRequest (LDAPConnection conn, JDAPProtocolOp request, + LDAPMessageQueue toNotify, LDAPConstraints cons) + throws LDAPException { + if (m_serverOutput == null) + throw new LDAPException ( "not connected to a server", + LDAPException.SERVER_DOWN ); + + LDAPMessage msg = + new LDAPMessage(allocateId(), request, cons.getServerControls()); + + if ( toNotify != null ) { + if (!(request instanceof JDAPAbandonRequest || + request instanceof JDAPUnbindRequest)) { + /* Only worry about toNotify if we expect a response... */ + this.m_requests.put (new Integer (msg.getId()), toNotify); + /* Notify the backlog checker that there may be another outstanding + request */ + resultRetrieved(); + } + toNotify.addRequest(msg.getId(), conn, this); + } + + synchronized( m_sendRequestLock ) { + try { + msg.write (m_serverOutput); + m_serverOutput.flush (); + } catch (IOException e) { + networkError(e); + } + } + } + + /** + * Register with this connection thread. + * @param conn LDAP connection + */ + public synchronized void register(LDAPConnection conn) { + if (!m_registered.contains(conn)) + m_registered.addElement(conn); + } + + synchronized int getClientCount() { + return m_registered.size(); + } + + /** + * De-Register with this connection thread. If all the connection + * is deregistered. Then, this thread should be killed. + * @param conn LDAP connection + */ + public synchronized void deregister(LDAPConnection conn) { + m_registered.removeElement(conn); + if (m_registered.size() == 0) { + try { + LDAPConstraints cons = conn.getSearchConstraints(); + sendRequest (null, new JDAPUnbindRequest (), null, cons); + cleanUp(); + if ( m_thread != null ) { + m_thread.interrupt(); + } + this.sleep(100); /* give enough time for threads to shutdown */ + } catch (Exception e) { + LDAPConnection.printDebug(e.toString()); + } + } + } + + /** + * Clean ups before shutdown the thread. + */ + private void cleanUp() { + if (!m_disconnected) { + try { + m_serverOutput.close (); + } catch (Exception e) { + } finally { + m_serverOutput = null; + } + + try { + m_serverInput.close (); + } catch (Exception e) { + } finally { + m_serverInput = null; + } + + try { + m_socket.close (); + } catch (Exception e) { + } finally { + m_socket = null; + } + + m_disconnected = true; + + /** + * Notify the Connection Setup Manager that the connection was + * terminated by the user + */ + m_connMgr.disconnect(); + + /** + * Notify all the registered about this bad moment. + * IMPORTANT: This needs to be done at last. Otherwise, the socket + * input stream and output stream might never get closed and the whole + * task will get stuck in the stop method when we tried to stop the + * LDAPConnThread. + */ + + if (m_registered != null) { + Vector registerCopy = (Vector)m_registered.clone(); + + Enumeration cancelled = registerCopy.elements(); + + while (cancelled.hasMoreElements ()) { + LDAPConnection c = (LDAPConnection)cancelled.nextElement(); + c.deregisterConnection(); + } + } + m_registered = null; + m_messages = null; + m_requests.clear(); + } + } + + /** + * Sleep if there is a backlog of search results + */ + private void checkBacklog() { + boolean doWait; + do { + doWait = false; + Enumeration listeners = m_requests.elements(); + while( listeners.hasMoreElements() ) { + LDAPMessageQueue l = + (LDAPMessageQueue)listeners.nextElement(); + // If there are any threads waiting for a regular response + // message, we have to go read the next incoming message + if ( !(l instanceof LDAPSearchListener) ) { + doWait = false; + break; + } + // If the backlog of any search thread is too great, + // wait for it to diminish, but if this is a synchronous + // search, then just keep reading + LDAPSearchListener sl = (LDAPSearchListener)l; + + // Asynch operation ? + if (sl.isAsynchOp()) { + if (sl.getMessageCount() >= sl.getConstraints().getMaxBacklog()) { + doWait = true; + } + } + // synch op with non-zero batch size ? + else if (sl.getConstraints().getBatchSize() != 0) { + if (sl.getMessageCount() >= sl.getConstraints().getMaxBacklog()) { + doWait = true; + } + } + } + if ( doWait ) { + synchronized(this) { + try { + wait(); + } catch (InterruptedException e ) { + } + } + } + } while ( doWait ); + } + + /** + * This is called when a search result has been retrieved from the incoming + * queue. We use the notification to unblock the listener thread, if it + * is waiting for the backlog to lighten. + */ + synchronized void resultRetrieved() { + notifyAll(); + } + + /** + * Reads from the LDAP server input stream for incoming LDAP messages. + */ + public void run() { + + // if there is a problem of establishing connection to the server, + // stop the thread right away... + if (m_failed) { + return; + } + + m_thread = Thread.currentThread(); + LDAPMessage msg = null; + JDAPBERTagDecoder decoder = new JDAPBERTagDecoder(); + + while (true) { + yield(); + int[] nread = new int[1]; + nread[0] = 0; + // Avoid too great a backlog of results + checkBacklog(); + try { + + if (m_thread.isInterrupted()) { + break; + } + + BERElement element = BERElement.getElement(decoder, + m_serverInput, + nread); + msg = LDAPMessage.parseMessage(element); + + // passed in the ber element size to approximate the size of the cache + // entry, thereby avoiding serialization of the entry stored in the + // cache + processResponse (msg, nread[0]); + } catch (Exception e) { + networkError(e); + } + + if (m_disconnected) + break; + } + } + + /** + * Allocates a new LDAP message ID. These are arbitrary numbers used to + * correlate client requests with server responses. + * @return new unique msgId + */ + private synchronized int allocateId () { + m_highMsgId = (m_highMsgId + 1) % MAXMSGID; + return m_highMsgId; + } + + /** + * When a response arrives from the LDAP server, it is processed by + * this routine. It will pass the message on to the listening object + * associated with the LDAP msgId. + * @param incoming New message from LDAP server + */ + private synchronized void processResponse (LDAPMessage incoming, int size) { + Integer messageID = new Integer (incoming.getId()); + LDAPMessageQueue l = (LDAPMessageQueue)m_requests.get (messageID); + + if (l == null) { + return; /* nobody is waiting for this response (!) */ + } + + // For asynch operations controls are to be read from the LDAPMessage + // For synch operations controls are copied into the LDAPConnection + // For synch search operations, controls are also copied into + // LDAPSearchResults (see LDAPConnection.checkSearchMsg()) + if ( ! l.isAsynchOp()) { + + /* Were there any controls for this client? */ + LDAPControl[] con = incoming.getControls(); + if (con != null) { + int msgid = incoming.getId(); + LDAPConnection ldc = l.getConnection(msgid); + if (ldc != null) { + ldc.setResponseControls( this, + new LDAPResponseControl(ldc, msgid, con)); + } + } + } + + Vector v = null; + JDAPProtocolOp op = incoming.getProtocolOp (); + + if ((op instanceof JDAPSearchResponse) || + (op instanceof JDAPSearchResultReference)) { + + l.addMessage (incoming); + Long key = ((LDAPSearchListener)l).getKey(); + + if ((m_cache != null) && (key != null)) { + // get the vector containing the LDAPMessages for the specified messageID + v = (Vector)m_messages.get(messageID); + + if (v == null) { + v = new Vector(); + // keeps track of the total size of all LDAPMessages belonging to the + // same messageID, now the size is 0 + v.addElement(new Long(0)); + } + + // add the size of the current LDAPMessage to the lump sum + // assume the size of LDAPMessage is more or less the same as the size + // of LDAPEntry. Eventually LDAPEntry object gets stored in the cache + // instead of LDAPMessage object. + long entrySize = ((Long)v.firstElement()).longValue() + size; + + // update the lump sum located in the first element of the vector + v.setElementAt(new Long(entrySize), 0); + + // convert LDAPMessage object into LDAPEntry which is stored to the + // end of the Vector + v.addElement(((LDAPSearchResult)incoming).getEntry()); + + // replace the entry + m_messages.put(messageID, v); + } + } else { + l.addMessage (incoming); + if (l instanceof LDAPSearchListener) { + Long key = ((LDAPSearchListener)l).getKey(); + + if (key != null) { + boolean fail = false; + JDAPProtocolOp protocolOp = incoming.getProtocolOp(); + if (protocolOp instanceof JDAPSearchResult) { + JDAPResult res = (JDAPResult)protocolOp; + if (res.getResultCode() > 0) { + fail = true; + } + } + + if ((!fail) && (m_cache != null)) { + + // Collect all the LDAPMessages for the specified messageID + // no need to keep track of this entry. Remove it. + v = (Vector)m_messages.remove(messageID); + + // If v is null, meaning there are no search results from the + // server + if (v == null) { + v = new Vector(); + + // set the entry size to be 0 + v.addElement(new Long(0)); + } + + try { + // add the new entry with key and value (a vector of + // LDAPEntry objects) + m_cache.addEntry(key, v); + } catch (LDAPException e) { + System.out.println("Exception: "+e.toString()); + } + } + } + } + m_requests.remove (messageID); + } + } + + /** + * Stop dispatching responses for a particular message ID. + * @param id Message ID for which to discard responses. + */ + synchronized void abandon (int id ) { + LDAPMessageQueue l = (LDAPMessageQueue)m_requests.remove(new Integer(id)); + if (l != null) { + l.removeRequest(id); + } + notifyAll(); // If LDAPConnThread is blocked in checkBacklog() + } + + /** + * Handles network errors. Basically shuts down the whole connection. + * @param e The exception which was caught while trying to read from + * input stream. + */ + private synchronized void networkError (Exception e) { + try { + + // notify the Connection Setup Manager that the connection is lost + m_connMgr.invalidateConnection(); + + // notify each listener that the server is down. + Enumeration requests = m_requests.elements(); + while (requests.hasMoreElements()) { + LDAPMessageQueue listener = + (LDAPMessageQueue)requests.nextElement(); + listener.setException(this, new LDAPException("Server down", + LDAPException.OTHER)); + } + cleanUp(); + + } catch (NullPointerException ee) { + System.err.println("Exception: "+ee.toString()); + } + + /** + * Notify all the registered connections. + * IMPORTANT: This needs to be done last. Otherwise, the socket + * input stream and output stream might never get closed and the whole + * task will get stuck in the stop method when we try to stop the + * LDAPConnThread. + */ + + if (m_registered != null) { + Vector registerCopy = (Vector)m_registered.clone(); + + Enumeration cancelled = registerCopy.elements(); + + while (cancelled.hasMoreElements ()) { + LDAPConnection c = (LDAPConnection)cancelled.nextElement(); + c.deregisterConnection(); + } + } + } +} diff --git a/mozilla/directory/java-sdk/ldapjdk/netscape/ldap/LDAPConnection.java b/mozilla/directory/java-sdk/ldapjdk/netscape/ldap/LDAPConnection.java index a406992ae2f..24a61bf5179 100644 --- a/mozilla/directory/java-sdk/ldapjdk/netscape/ldap/LDAPConnection.java +++ b/mozilla/directory/java-sdk/ldapjdk/netscape/ldap/LDAPConnection.java @@ -12,7 +12,7 @@ * * 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 + * Copyright (C) 1999 Netscape Communications Corporation. All Rights * Reserved. */ package netscape.ldap; @@ -24,6 +24,7 @@ import netscape.ldap.ber.stream.*; import netscape.ldap.util.*; import java.io.*; import java.net.*; +import javax.security.auth.callback.CallbackHandler; /** * Represents a connection to an LDAP server.

    @@ -51,13 +52,17 @@ import java.net.*; * the same time). *

    * - * This class also specifies a default set of search constraints - * (such as the maximum number of results returned in a search) + * This class also specifies a default set of constraints + * (such as the maximum amount of time an operation is allowed before timing out) * which apply to all operations. To get and set these constraints, * use the getOption and setOption methods. - * To override these constraints for an individual search operation, - * define a new set of constraints by creating a LDAPSearchConstraints - * option and pass the object to the search method. + * To override these constraints for an individual operation, + * define a new set of constraints by creating a LDAPConstraints + * object and pass the object to the method for the operation. For search + * operations, additional constraints are defined in LDAPSearchConstraints + * (a subclass of LDAPConstraints). To override the default search + * constraints, create a LDAPSearchConstraints object and pass it + * to the search method. *

    * * If you set up your client to follow referrals automatically, @@ -77,12 +82,14 @@ import java.net.*; *

    * * @version 1.0 + * @see netscape.ldap.LDAPConstraints * @see netscape.ldap.LDAPSearchConstraints * @see netscape.ldap.LDAPRebind * @see netscape.ldap.LDAPRebindAuth * @see netscape.ldap.LDAPException */ -public class LDAPConnection implements LDAPv3, Cloneable { +public class LDAPConnection + implements LDAPv3, LDAPAsynchronousConnection, Cloneable { /** * Version of the LDAP protocol used by default. @@ -155,40 +162,52 @@ public class LDAPConnection implements LDAPv3, Cloneable { */ public final static String LDAP_PROPERTY_SECURITY = "version.security"; + /** + * Specifies the serial connection setup policy when a list of hosts is + * passed to the connect method. + * @see netscape.ldap.LDAPConnection#setConnSetupDelay(int) + */ + public final static int NODELAY_SERIAL = -1; + /** + * Specifies the parallel connection setup policy with no delay when a + * list of hosts is passed to the connect method. + * For each host in the list, a separate thread is created to attempt + * to connect to the host. All threads are started simultaneously. + * @see netscape.ldap.LDAPConnection#setConnSetupDelay(int) + */ + public final static int NODELAY_PARALLEL = 0; + /** * Constants */ private final static String defaultFilter = "(objectClass=*)"; - private final static LDAPSearchConstraints readConstraints = new + private final static LDAPConstraints readConstraints = new LDAPSearchConstraints(); /** * Internal variables */ - transient private LDAPSearchConstraints defaultConstraints = new - LDAPSearchConstraints (); - transient private Vector responseListeners; - transient private Vector searchListeners; - transient private boolean bound; - transient private String host; - transient private String[] m_hostList; - transient private int port; - transient private int[] m_portList; - transient private int m_defaultPort; - transient private String prevBoundDN; - transient private String prevBoundPasswd; - transient private String boundDN; - transient private String boundPasswd; - transient private int protocolVersion = LDAP_VERSION; - transient private LDAPSocketFactory m_factory; - /* th does all socket i/o for the object and any clones */ - transient private LDAPConnThread th = null; + private LDAPSearchConstraints m_defaultConstraints = + new LDAPSearchConstraints(); + private Vector m_responseListeners; + private Vector m_searchListeners; + private boolean m_bound; + private String m_prevBoundDN; + private String m_prevBoundPasswd; + private String m_boundDN; + private String m_boundPasswd; + private int m_protocolVersion = LDAP_VERSION; + private LDAPConnSetupMgr m_connMgr; + private int m_connSetupDelay = -1; + private LDAPSocketFactory m_factory; + /* m_thread does all socket i/o for the object and any clones */ + private LDAPConnThread m_thread = null; /* To manage received server controls on a per-thread basis, we keep a table of active threads and a table of controls, indexed by thread */ - transient private Vector m_attachedList = new Vector(); - transient private Hashtable m_responseControlTable = new Hashtable(); - transient private LDAPCache m_cache = null; + private Vector m_attachedList = new Vector(); + private Hashtable m_responseControlTable = new Hashtable(); + private LDAPCache m_cache = null; static Hashtable m_threadConnTable = new Hashtable(); // this handles the case when the client lost the connection with the @@ -199,27 +218,31 @@ public class LDAPConnection implements LDAPv3, Cloneable { private boolean m_anonymousBound = false; private Object m_security = null; - private boolean saslBind = false; - private Object m_mechanismDriver; + private LDAPSaslBind m_saslBinder = null; private Properties m_securityProperties; - private Object m_clientCB; - private Hashtable m_methodLookup = new Hashtable(); + private Hashtable m_properties = new Hashtable(); private LDAPConnection m_referralConnection; /** * Properties */ - private final static Float SdkVersion = new Float(3.2f); + private final static Float SdkVersion = new Float(4.0f); private final static Float ProtocolVersion = new Float(3.0f); private final static String SecurityVersion = new String("none,simple,sasl"); - private final static Float MajorVersion = new Float(3.0f); - private final static Float MinorVersion = new Float(0.2f); + private final static Float MajorVersion = new Float(4.0f); + private final static Float MinorVersion = new Float(0.0f); private final static String DELIM = "#"; private final static String PersistSearchPackageName = "netscape.ldap.controls.LDAPPersistSearchControl"; - private final static String EXTERNAL_MECHANISM = "SASLExternalMechanism"; + final static String EXTERNAL_MECHANISM = "external"; private final static String EXTERNAL_MECHANISM_PACKAGE = - "com.netscape.sasl.mechanisms"; + "com.netscape.sasl"; + final static String DEFAULT_SASL_PACKAGE = + "com.netscape.sasl"; + final static String SCHEMA_BUG_PROPERTY = + "com.netscape.ldap.schema.quoting"; + final static String SASL_PACKAGE_PROPERTY = + "com.netscape.ldap.saslpackage"; /** * Constructs a new LDAPConnection object, @@ -234,8 +257,13 @@ public class LDAPConnection implements LDAPv3, Cloneable { */ public LDAPConnection () { super(); - port = -1; m_factory = null; + + m_properties.put(LDAP_PROPERTY_SDK, SdkVersion); + m_properties.put(LDAP_PROPERTY_PROTOCOL, ProtocolVersion); + m_properties.put(LDAP_PROPERTY_SECURITY, SecurityVersion); + m_properties.put("version.major", MajorVersion); + m_properties.put("version.minor", MinorVersion); } /** @@ -262,8 +290,7 @@ public class LDAPConnection implements LDAPv3, Cloneable { * @see netscape.ldap.LDAPConnection#setSocketFactory(netscape.ldap.LDAPSocketFactory) */ public LDAPConnection ( LDAPSocketFactory factory ) { - super(); - port = -1; + this(); m_factory = factory; } @@ -271,10 +298,10 @@ public class LDAPConnection implements LDAPv3, Cloneable { * Finalize method, which disconnects from the LDAP server. * @exception LDAPException Thrown when the connection cannot be disconnected. */ - public void finalize() throws LDAPException - { - if (th != null) + public void finalize() throws LDAPException { + if (isConnected()) { disconnect(); + } } /** @@ -289,6 +316,9 @@ public class LDAPConnection implements LDAPv3, Cloneable { */ public void setCache(LDAPCache cache) { m_cache = cache; + if ( m_thread != null ) { + m_thread.setCache( cache ); + } } /** @@ -354,23 +384,16 @@ public class LDAPConnection implements LDAPv3, Cloneable { * @see netscape.ldap.LDAPConnection#LDAP_PROPERTY_SECURITY */ public Object getProperty(String name) throws LDAPException { - if (name.equals(LDAP_PROPERTY_SDK)) - return SdkVersion; - else if (name.equals(LDAP_PROPERTY_PROTOCOL)) - return ProtocolVersion; - else if (name.equals(LDAP_PROPERTY_SECURITY)) - return SecurityVersion; - else if (name.equals("version.major")) - return MajorVersion; - else if (name.equals("version.minor")) - return MinorVersion; - else - return null; + return m_properties.get( name ); } /** - * This method is reserved for future use and does not currently - * allow you to set any properties. + * The following properties are defined:
    + * com.netscape.ldap.schema.quoting - "standard" or "NetscapeBug"
    + * Note: if this property is not set, the SDK will query the server + * to determine if attribute syntax values and objectclass superior + * values must be quoted when adding schema.
    + * com.netscape.ldap.saslpackage - the default is "com.netscape.sasl"
    *

    * * @param name Name of the property that you want to set. @@ -379,7 +402,15 @@ public class LDAPConnection implements LDAPv3, Cloneable { * property. */ public void setProperty(String name, Object val) throws LDAPException { - throw new LDAPException("No property has been set"); + if ( name.equalsIgnoreCase( SCHEMA_BUG_PROPERTY ) ) { + m_properties.put( SCHEMA_BUG_PROPERTY, val ); + } else if ( name.equalsIgnoreCase( SASL_PACKAGE_PROPERTY ) ) { + m_properties.put( SASL_PACKAGE_PROPERTY, val ); + } else if ( name.equalsIgnoreCase( "debug" ) ) { + debug = ((String)val).equalsIgnoreCase( "true" ); + } else { + throw new LDAPException("Unknown property: " + name); + } } /** @@ -390,7 +421,7 @@ public class LDAPConnection implements LDAPv3, Cloneable { * @param version The LDAP protocol version that your client uses. */ private void setProtocolVersion(int version) { - protocolVersion = version; + m_protocolVersion = version; } /** @@ -398,7 +429,10 @@ public class LDAPConnection implements LDAPv3, Cloneable { * @return Host name of the LDAP server. */ public String getHost () { - return host; + if (m_connMgr != null) { + return m_connMgr.getHost(); + } + return null; } /** @@ -406,7 +440,10 @@ public class LDAPConnection implements LDAPv3, Cloneable { * @return Port number of the LDAP server. */ public int getPort () { - return port; + if (m_connMgr != null) { + return m_connMgr.getPort(); + } + return -1; } /** @@ -415,7 +452,7 @@ public class LDAPConnection implements LDAPv3, Cloneable { * @return Distinguished name used for authentication over this connection. */ public String getAuthenticationDN () { - return boundDN; + return m_boundDN; } /** @@ -423,7 +460,54 @@ public class LDAPConnection implements LDAPv3, Cloneable { * @return Password used for authentication over this connection. */ public String getAuthenticationPassword () { - return boundPasswd; + return m_boundPasswd; + } + + /** + * Returns the delay in seconds when making concurrent connection attempts to + * multiple servers. + * @return The delay in seconds between connection attempts:
    + * NODELAY_SERIAL The serial connection setup policy is enabled + * (no concurrency).
    + * NODELAY_PARALLEL The parallel connection setup policy with no delay + * is enabled.
    + * delay > 0 The parallel connection setup policy with the delay of + * delay seconds is enabled. + * @see netscape.ldap.LDAPConnection#setConnSetupDelay + */ + public int getConnSetupDelay () { + return m_connSetupDelay; + } + + /** + * Specifies the delay in seconds when making concurrent connection attempts to + * multiple servers. + *

    Effectively, selects the connection setup policy when a list of hosts is passed + * to the connectmethod. + * + *
    If the serial policy, the default one, is selected, an attempt is made to + * connect to the first host in the list in the specified order. Next entry in + * the list is tried only after the connection attempt to the current one fails. + * This might cause your application to block for unacceptably long time if a host is down. + * + *
    If the parallel policy is selected, multiple connection attempts may run + * concurrently on a separate thread. A new connection attempt to the next entry + * in the list can be started with or without delay. + *

    The ConnSetupDelay must be set before the call to the connect + * method. + * + * @param delay The delay in seconds between connection attempts. Possible values are:
    + * NODELAY_SERIAL Use the serial connection setup policy.
    + * NODELAY_PARALLEL Use the parallel connection setup policy with no delay. + * Start all connection setup threads immediately.
    + * delay > 0 Use the parallel connection setup policy with delay. + * Start another connection setup thread after delay seconds.
    + * @see netscape.ldap.LDAPConnection#NODELAY_SERIAL + * @see netscape.ldap.LDAPConnection#NODELAY_PARALLEL + * @see netscape.ldap.LDAPConnection#connect(java.lang.String, int) + */ + public void setConnSetupDelay (int delay) { + m_connSetupDelay = delay; } /** @@ -470,7 +554,7 @@ public class LDAPConnection implements LDAPv3, Cloneable { // the deregisterConnection() will get called first. // This problem only exists on Solaris. Thread.yield(); - return (th != null); + return (m_thread != null); } /** @@ -480,12 +564,13 @@ public class LDAPConnection implements LDAPv3, Cloneable { * either a blank name or password), returns false. */ public boolean isAuthenticated () { - if (bound) { - if ((boundDN == null) || boundDN.equals("") || - (boundPasswd == null) || boundPasswd.equals("")) + if (m_bound) { + if ((m_boundDN == null) || m_boundDN.equals("") || + (m_boundPasswd == null) || m_boundPasswd.equals("")) { return false; + } } - return bound; + return m_bound; } /** @@ -515,10 +600,10 @@ public class LDAPConnection implements LDAPv3, Cloneable { * @param host Host name of the LDAP server that you want to connect to. * This value can also be a space-delimited list of hostnames or * hostnames and port numbers (using the syntax - * hostname:portnumber). Your client application or applet - * will attempt to contact each host in the order you specify until a - * connection is established. For example, you can specify the following - * values for the host argument:
    + * hostname:portnumber). The connection setup policy specified with + * the ConnSetupDelay property controls whether connection + * attempts are made serially or concurrently. For example, you can specify + * the following values for the host argument:
    *

          *   myhost
          *   myhost hishost:389 herhost:5000 whathost
    @@ -528,9 +613,10 @@ public class LDAPConnection implements LDAPv3, Cloneable {
          * This parameter is ignored for any host in the host
          * parameter which includes a colon and port number.
          * @exception LDAPException The connection failed.
    +     * @see netscape.ldap.LDAPConnection#setConnSetupDelay
          */
         public void connect(String host, int port) throws LDAPException {
    -      connect( host, port, null, null, defaultConstraints, false );
    +        connect( host, port, null, null, m_defaultConstraints, false );
         }
     
         /**
    @@ -574,10 +660,10 @@ public class LDAPConnection implements LDAPv3, Cloneable {
          * @param host Host name of the LDAP server that you want to connect to.
          * This value can also be a space-delimited list of hostnames or
          * hostnames and port numbers (using the syntax
    -     * hostname:portnumber). Your client application or applet
    -     * will attempt to contact each host in the order you specify until a
    -     * connection is established. For example, you can specify the following
    -     * values for the host argument:
    + * hostname:portnumber). The connection setup policy specified with + * the ConnSetupDelay property controls whether connection + * attempts are made serially or concurrently. For example, you can specify + * the following values for the host argument:
    *
          *   myhost
          *   myhost hishost:389 herhost:5000 whathost
    @@ -589,10 +675,11 @@ public class LDAPConnection implements LDAPv3, Cloneable {
          * @param dn Distinguished name used for authentication
          * @param passwd Password used for authentication
          * @exception LDAPException The connection or authentication failed.
    +     * @see netscape.ldap.LDAPConnection#setConnSetupDelay
          */
         public void connect(String host, int port, String dn, String passwd)
             throws LDAPException {
    -        connect(host, port, dn, passwd, defaultConstraints, true);
    +        connect(host, port, dn, passwd, m_defaultConstraints, true);
         }
     
         /**
    @@ -605,10 +692,10 @@ public class LDAPConnection implements LDAPv3, Cloneable {
          * @param host Host name of the LDAP server that you want to connect to.
          * This value can also be a space-delimited list of hostnames or
          * hostnames and port numbers (using the syntax
    -     * hostname:portnumber). Your client application or applet
    -     * will attempt to contact each host in the order you specify until a
    -     * connection is established. For example, you can specify the following
    -     * values for the host argument:
    + * hostname:portnumber). The connection setup policy specified with + * the ConnSetupDelay property controls whether connection + * attempts are made serially or concurrently. For example, you can specify + * the following values for the host argument:
    *
          *   myhost
          *   myhost hishost:389 herhost:5000 whathost
    @@ -621,62 +708,52 @@ public class LDAPConnection implements LDAPv3, Cloneable {
          * @param passwd Password used for authentication
          * @param cons Preferences for the bind operation.
          * @exception LDAPException The connection or authentication failed.
    +     * @see netscape.ldap.LDAPConnection#setConnSetupDelay
          */
         public void connect(String host, int port, String dn, String passwd,
    -        LDAPSearchConstraints cons) throws LDAPException {
    +        LDAPConstraints cons) throws LDAPException {
             connect(host, port, dn, passwd, cons, true);
         }
     
         private void connect(String host, int port, String dn, String passwd,
    -      LDAPSearchConstraints cons, boolean doAuthenticate) 
    +      LDAPConstraints cons, boolean doAuthenticate) 
             throws LDAPException {
    -        if (th != null) disconnect ();
    -        if ((host == null) || (host.equals("")))
    +        if ( isConnected() ) {
    +            disconnect ();
    +        }
    +        if ((host == null) || (host.equals(""))) {
                 throw new LDAPException ( "no host for connection",
    -                                  LDAPException.PARAM_ERROR );
    +                                      LDAPException.PARAM_ERROR );
    +        }
     
    -        /* Parse the list of hosts */
    -        m_defaultPort = port;
    +        /* Parse the list of hosts */    
    +        int defaultPort = port;
             StringTokenizer st = new StringTokenizer( host );
    -        m_hostList = new String[st.countTokens()];
    -        m_portList = new int[st.countTokens()];
    +        String hostList[] = new String[st.countTokens()];
    +        int portList[] = new int[st.countTokens()];
             int i = 0;
             while( st.hasMoreTokens() ) {
                 String s = st.nextToken();
                 int colon = s.indexOf( ':' );
                 if ( colon > 0 ) {
    -                m_hostList[i] = s.substring( 0, colon );
    -                m_portList[i] = Integer.parseInt( s.substring( colon+1 ) );
    +                hostList[i] = s.substring( 0, colon );
    +                portList[i] = Integer.parseInt( s.substring( colon+1 ) );
                 } else {
    -                m_hostList[i] = s;
    -                m_portList[i] = m_defaultPort;
    +                hostList[i] = s;
    +                portList[i] = defaultPort;
                 }
                 i++;
             }
     
    -        /* Try each possible host until a connection attempt doesn't cause
    -           an exception */
    -        LDAPException conex = null;
    -        for( i = 0; i < m_hostList.length; i++ ) {
    -            try {
    -                this.host = m_hostList[i];
    -                this.port = m_portList[i];
    -                connect ();
    -                conex = null;
    -                break;
    -            } catch ( LDAPException e ) {
    -                conex = e;
    -            }
    -        }
    -        if ( conex != null ) {
    -            /* All connection attempts failed */
    -            this.host = m_hostList[0];
    -            this.port = m_defaultPort;
    -            throw conex;
    -        }
    +        /* Create the Connection Setup Manager */
    +        m_connMgr = new LDAPConnSetupMgr(hostList, portList, m_factory,
    +                                         m_connSetupDelay);
    +    
    +        connect();
     
    -        if (doAuthenticate)
    +        if (doAuthenticate) {
                 authenticate(dn, passwd, cons);
    +        }
         }
     
         /**
    @@ -693,8 +770,9 @@ public class LDAPConnection implements LDAPv3, Cloneable {
          *   the IP address of a host running an LDAP server to connect to.
          *   Alternatively, it may contain a list of host names, space-delimited.
          *   Each host name may include a trailing colon and port number. In the
    -     *   case where more than one host name is specified, each host name in
    -     *   turn will be contacted until a connection can be established.

    + * case where more than one host name is specified, the connection setup + * policy specified with the ConnSetupDelay property controls + * whether connection attempts are made serially or concurrently.

    * *

          *   Examples:
    @@ -702,7 +780,6 @@ public class LDAPConnection implements LDAPv3, Cloneable {
          *      "199.254.1.2"
          *      "directory.knowledge.com:1050 people.catalog.com 199.254.1.2"
          * 
    - *

    * @param port Contains the TCP or UDP port number to connect to or contact. * The default LDAP port is 389. "port" is ignored for any host name which * includes a colon and port number. @@ -713,11 +790,12 @@ public class LDAPConnection implements LDAPv3, Cloneable { * all operations through it should be authenticated with dn as the * distinguished name and passwd as password. * @exception LDAPException The connection or authentication failed. + * @see netscape.ldap.LDAPConnection#setConnSetupDelay */ public void connect(int version, String host, int port, String dn, String passwd) throws LDAPException { - connect(version, host, port, dn, passwd, defaultConstraints); + connect(version, host, port, dn, passwd, m_defaultConstraints); } /** @@ -735,8 +813,9 @@ public class LDAPConnection implements LDAPv3, Cloneable { * the IP address of a host running an LDAP server to connect to. * Alternatively, it may contain a list of host names, space-delimited. * Each host name may include a trailing colon and port number. In the - * case where more than one host name is specified, each host name in - * turn will be contacted until a connection can be established.

    + * case where more than one host name is specified, the connection setup + * policy specified with the ConnSetupDelay property controls + * whether connection attempts are made serially or concurrently.

    * *

          *   Examples:
    @@ -744,7 +823,6 @@ public class LDAPConnection implements LDAPv3, Cloneable {
          *      "199.254.1.2"
          *      "directory.knowledge.com:1050 people.catalog.com 199.254.1.2"
          * 
    - *

    * @param port Contains the TCP or UDP port number to connect to or contact. * The default LDAP port is 389. "port" is ignored for any host name which * includes a colon and port number. @@ -756,9 +834,10 @@ public class LDAPConnection implements LDAPv3, Cloneable { * distinguished name and passwd as password. * @param cons Preferences for the bind operation. * @exception LDAPException The connection or authentication failed. + * @see netscape.ldap.LDAPConnection#setConnSetupDelay */ public void connect(int version, String host, int port, String dn, - String passwd, LDAPSearchConstraints cons) throws LDAPException { + String passwd, LDAPConstraints cons) throws LDAPException { setProtocolVersion(version); connect(host, port, dn, passwd, cons); @@ -769,17 +848,23 @@ public class LDAPConnection implements LDAPv3, Cloneable { * @exception LDAPException failed to connect */ private synchronized void connect () throws LDAPException { - if (th != null) return; + if ( isConnected() ) { + return; + } - if (host == null || port < 0) + if (m_connMgr == null) { throw new LDAPException ( "no connection parameters", - LDAPException.PARAM_ERROR ); + LDAPException.PARAM_ERROR ); + } - th = getNewThread(host, port, m_factory, m_cache); + m_connMgr.openConnection(); + m_thread = getNewThread(m_connMgr, m_cache); + authenticateSSLConnection(); } - private synchronized LDAPConnThread getNewThread(String host, int port, - LDAPSocketFactory factory, LDAPCache cache) throws LDAPException { + private synchronized LDAPConnThread getNewThread(LDAPConnSetupMgr connMgr, + LDAPCache cache) + throws LDAPException { LDAPConnThread newThread = null; Vector v = null; @@ -787,7 +872,7 @@ public class LDAPConnection implements LDAPv3, Cloneable { synchronized(m_threadConnTable) { Enumeration keys = m_threadConnTable.keys(); - boolean connExist = false; + boolean connExists = false; // transverse each thread while (keys.hasMoreElements()) { @@ -795,41 +880,40 @@ public class LDAPConnection implements LDAPv3, Cloneable { Vector connVector = (Vector)m_threadConnTable.get(connThread); Enumeration enumv = connVector.elements(); - // transverse through each LDAPConnection under the same thread + // traverse each LDAPConnection under the same thread while (enumv.hasMoreElements()) { LDAPConnection conn = (LDAPConnection)enumv.nextElement(); - // this is not the brand new connection + // this is not a brand new connection if (conn.equals(this)) { - connExist = true; + connExists = true; if (!connThread.isAlive()) { - // need to move all the LDAPConnections under the dead thread + // need to move all the LDAPConnections from the dead thread // to the new thread try { - newThread = new LDAPConnThread(host, port, factory, cache); - newThread.setMaxBacklog( - getSearchConstraints().getMaxBacklog() ); + newThread = new LDAPConnThread(connMgr, cache); v = (Vector)m_threadConnTable.remove(connThread); break; } catch (Exception e) { throw new LDAPException ("unable to establish connection", - LDAPException.UNAVAILABLE); + LDAPException.UNAVAILABLE); } } break; } } - if (connExist) break; + if (connExists) { + break; + } } // if this connection is new or the corresponding thread for the current // connection is dead - if (!connExist) { + if (!connExists) { try { - newThread = new LDAPConnThread(host, port, factory, cache); - newThread.setMaxBacklog( getSearchConstraints().getMaxBacklog() ); + newThread = new LDAPConnThread(connMgr, cache); v = new Vector(); v.addElement(this); } catch (Exception e) { @@ -844,12 +928,11 @@ public class LDAPConnection implements LDAPv3, Cloneable { for (int i=0, n=v.size(); i 1) { + if ( !isConnected() ) { + connect(); + } else { + // If the thread has more than one LDAPConnection, then + // we should disconnect first. Otherwise, + // we can reuse the same thread for the rebind. + if (m_thread.getClientCount() > 1) { disconnect(); connect(); } } - - if ((bound && rebind) || (!bound)) { - try { - // Get the initial request to start authentication - Object[] arg1 = new Object[5]; - arg1[0] = boundDN; - arg1[1] = "LDAP"; - arg1[2] = null; - arg1[3] = m_securityProperties; - arg1[4] = m_clientCB; - - String[] argNames = new String[5]; - argNames[0] = "java.lang.String"; - argNames[1] = "java.lang.String"; - argNames[2] = "java.lang.String"; - argNames[3] = "java.util.Properties"; - argNames[4] = "com.netscape.sasl.SASLClientCB"; - - String mechanismName = m_mechanismDriver.getClass().getName(); - byte[] outVals = (byte[])invokeMethod(m_mechanismDriver, - mechanismName, "startAuthentication", arg1, argNames); - - - boolean isExternal = isExternalMechanism(mechanismName); - int resultCode = LDAPException.SASL_BIND_IN_PROGRESS; - JDAPBindResponse response = null; - while (!checkForSASLBindCompletion(resultCode)) { - response = saslBind(outVals); - resultCode = response.getResultCode(); - - if (isExternal) - continue; - - String challenge = response.getCredentials(); - byte[] b = challenge.getBytes(); - - Object[] arg2 = new Object[1]; - arg2[0] = b; - String[] arg2Names = new String[1]; - arg2Names[0] = "[B"; //class name for byte array - - outVals = (byte[])invokeMethod(m_mechanismDriver, - mechanismName, "evaluateResponse", arg2, arg2Names); - } - - // Make sure authentication REALLY is complete - Boolean bool = (Boolean)invokeMethod(m_mechanismDriver, - mechanismName, "isComplete", null, null); - if (!bool.booleanValue()) { - // Authentication session hijacked! - throw new LDAPException("The server indicates that " + - "authentication is successful, but the SASL driver " + - "indicates that authentication is not yet done.", - LDAPException.OTHER); - } - - m_security = invokeMethod(m_mechanismDriver, - mechanismName, "getSecurityLayer", null, null); - th.setSecurityLayer(m_security); - updateThreadConnTable(); - } catch (LDAPException e) { - throw e; - } catch (Exception e) { - throw new LDAPException(e.toString(), LDAPException.OTHER); - } + m_boundDN = null; + m_protocolVersion = 3; // Must be 3 for SASL + if ( props == null ) { + props = new Hashtable(); } + m_saslBinder = new LDAPSaslBind( dn, mechanisms, packageName, + props, cbh ); + m_saslBinder.bind( this ); + m_boundDN = dn; + } + + /** + * Authenticates to the LDAP server (that you are currently + * connected to) using the specified name and password. + * If you are not already connected to the LDAP server, this + * method attempts to reconnect to the server. + *

    + * + * For example, the following section of code authenticates the + * client as Barbara Jensen. The code assumes that the client + * has already established a connection with an LDAP server. + *

    + * + *

    +     * String myDN = "cn=Barbara Jensen,ou=Product Development,o=Ace Industry,c=US";
    +     * String myPW = "hifalutin";
    +     * try {
    +     *     myConn.bind( myDN, myPW );
    +     * } catch ( LDAPException e ) {
    +     *     switch( e.getLDAPResultCode() ) {
    +     *         case e.NO_SUCH_OBJECT:
    +     *             System.out.println( "The specified user does not exist." );
    +     *             break;
    +     *         case e.INVALID_CREDENTIALS:
    +     *             System.out.println( "Invalid password." );
    +     *             break;
    +     *         default:
    +     *             System.out.println( "Error number: " + e.getLDAPResultCode() );
    +     *             System.out.println( "Failed to authentice as " + myDN );
    +     *             break;
    +     *     }
    +     *     return;
    +     * }
    +     * System.out.println( "Authenticated as " + myDN );
    +     * 
    + * + * @param dn Distinguished name used for authentication. + * @param passwd Password used for authentication. + * @exception LDAPException Failed to authenticate to the LDAP server. + */ + public void bind(String dn, String passwd) throws LDAPException { + authenticate(m_protocolVersion, dn, passwd, m_defaultConstraints); } - private boolean isExternalMechanism(String name) { - String s = name; - int dot = name.lastIndexOf( '.' ); - if ( (dot >= 0) && (dot < (name.length()-1)) ) { - s = name.substring( dot + 1 ); - } - return s.equals( EXTERNAL_MECHANISM ); + /** + * Authenticates to the LDAP server (that you are currently + * connected to) using the specified name and password. The + * default protocol version (version 2) is used. If the server + * doesn't support the default version, an LDAPException is thrown + * with the error code PROTOCOL_ERROR. This method allows the + * user to specify the preferences for the bind operation. + * + * @param dn Distinguished name used for authentication. + * @param passwd Password used for authentication. + * @param cons Preferences for the bind operation. + * @exception LDAPException Failed to authenticate to the LDAP server. + */ + public void bind(String dn, String passwd, + LDAPConstraints cons) throws LDAPException { + authenticate(m_protocolVersion, dn, passwd, cons); } - private Object invokeMethod(Object obj, String packageName, - String methodName, Object[] args, String[] argNames) - throws LDAPException { - try { - java.lang.reflect.Method m = getMethod(packageName, methodName, - argNames); - if (m != null) { - return (m.invoke(obj, args)); - } - } catch (Exception e) { - throw new LDAPException("Invoking "+methodName+": "+ - e.toString(), LDAPException.PARAM_ERROR); - } - - return null; + /** + * Authenticates to the LDAP server (that you are currently + * connected to) using the specified name and password, and + * requesting that the server use at least the specified + * protocol version. If the server doesn't support that + * level, an LDAPException is thrown with the error code + * PROTOCOL_ERROR. + * + * @param version Required LDAP protocol version. + * @param dn Distinguished name used for authentication. + * @param passwd Password used for authentication. + * @exception LDAPException Failed to authenticate to the LDAP server. + */ + public void bind(int version, String dn, String passwd) + throws LDAPException { + authenticate(version, dn, passwd, m_defaultConstraints); } - private java.lang.reflect.Method getMethod(String packageName, - String methodName, String[] args) throws LDAPException { - try { - java.lang.reflect.Method method = null; - String suffix = ""; - if (args != null) - for (int i=0; i 1) { + } else if (!m_bound && rebind && m_thread.getClientCount() > 1) { disconnect(); connect(); } // if the connection is still intact and no rebind request - if (bound && !rebind) + if (m_bound && !rebind) { return; + } // if the connection was lost and did not have any kind of bind // operation and the current one does not request any bind operation (ie, // no authenticate has been called) if (!m_anonymousBound && - ((boundDN == null) || (boundPasswd == null)) && - !rebind) + ((m_boundDN == null) || (m_boundPasswd == null)) && + !rebind) { return; + } - if (bound && rebind) { - if (protocolVersion == version) { - if (m_anonymousBound && ((boundDN == null) || (boundPasswd == null))) + if (m_bound && rebind) { + if (m_protocolVersion == version) { + if (m_anonymousBound && ((m_boundDN == null) || (m_boundPasswd == null))) return; - if (!m_anonymousBound && (boundDN != null) && (boundPasswd != null) && - boundDN.equals(prevBoundDN) && - boundPasswd.equals(prevBoundPasswd)) + if (!m_anonymousBound && (m_boundDN != null) && (m_boundPasswd != null) && + m_boundDN.equals(m_prevBoundDN) && + m_boundPasswd.equals(m_prevBoundPasswd)) { return; + } } - // if get to here, means that we need to do rebind since previous and + // if we got this far, we need to rebind since previous and // current credentials are not the same. - // if the current connection is the only connection for the th thread, + // if the current connection is the only connection of the thread, // then reuse this current connection. otherwise, disconnect the current - // one (ie, detach from the current th) and reconnect again (ie, have - // a new th). - if (th.getClientCount() > 1) { + // one (ie, detach from the current thread) and reconnect again (ie, get + // a new thread). + if (m_thread.getClientCount() > 1) { disconnect(); connect(); } } - protocolVersion = version; + m_protocolVersion = version; LDAPResponseListener myListener = getResponseListener (); - JDAPMessage response; try { if (m_referralConnection != null) { m_referralConnection.disconnect(); m_referralConnection = null; } - sendRequest(new JDAPBindRequest(protocolVersion, boundDN, boundPasswd), - myListener, cons); - response = myListener.getResponse(); - checkMsg(response); + sendRequest(new JDAPBindRequest(m_protocolVersion, m_boundDN, + m_boundPasswd), + myListener, cons); + checkMsg( myListener.getResponse() ); } catch (LDAPReferralException e) { m_referralConnection = createReferralConnection(e, cons); } finally { @@ -1376,49 +1450,87 @@ public class LDAPConnection implements LDAPv3, Cloneable { updateThreadConnTable(); } - private void updateThreadConnTable() { - synchronized(m_threadConnTable) { - if (m_threadConnTable.containsKey(th)) { - Vector v = (Vector)m_threadConnTable.get(th); - for (int i=0, n=v.size(); i 1) { + m_connMgr = (LDAPConnSetupMgr) m_connMgr.clone(); + m_connMgr.disconnect(); + } + if (m_cache != null) { m_cache.cleanup(); m_cache = null; @@ -1446,33 +1565,35 @@ public class LDAPConnection implements LDAPv3, Cloneable { deregisterConnection(); } + /** + * Remove this connection from the thread connection table + */ private void deleteThreadConnEntry() { - synchronized (m_threadConnTable) { - Enumeration keys = m_threadConnTable.keys(); - while (keys.hasMoreElements()) { - LDAPConnThread connThread = (LDAPConnThread)keys.nextElement(); - Vector connVector = (Vector)m_threadConnTable.get(connThread); - Enumeration enumv = connVector.elements(); - while (enumv.hasMoreElements()) { - LDAPConnection c = (LDAPConnection)enumv.nextElement(); - if (c.equals(this)) { - connVector.removeElement(c); - if (connVector.size() == 0) - m_threadConnTable.remove(connThread); - return; + Enumeration keys = m_threadConnTable.keys(); + while (keys.hasMoreElements()) { + LDAPConnThread connThread = (LDAPConnThread)keys.nextElement(); + Vector connVector = (Vector)m_threadConnTable.get(connThread); + Enumeration enumv = connVector.elements(); + while (enumv.hasMoreElements()) { + LDAPConnection c = (LDAPConnection)enumv.nextElement(); + if (c.equals(this)) { + connVector.removeElement(c); + if (connVector.size() == 0) { + m_threadConnTable.remove(connThread); } + return; } } } } /** - * Decrement the reference count for the connection + * Remove the association between this object and the connection thread */ synchronized void deregisterConnection() { - th.deregister(this); - th = null; - bound = false; + m_thread.deregister(this); + m_thread = null; + m_bound = false; } /** @@ -1517,7 +1638,7 @@ public class LDAPConnection implements LDAPv3, Cloneable { * if the entry is not found. */ public LDAPEntry read (String DN) throws LDAPException { - return read (DN, null, defaultConstraints); + return read (DN, null, m_defaultConstraints); } /** @@ -1631,15 +1752,16 @@ public class LDAPConnection implements LDAPv3, Cloneable { * the directory. */ public LDAPEntry read (String DN, String attrs[]) throws LDAPException { - return read(DN, attrs, defaultConstraints); + return read(DN, attrs, m_defaultConstraints); } public LDAPEntry read (String DN, String attrs[], LDAPSearchConstraints cons) throws LDAPException { - LDAPSearchResults results = search (DN, LDAPv2.SCOPE_BASE, - defaultFilter, attrs, false, cons); - if (results == null) + LDAPSearchResults results = search (DN, SCOPE_BASE, + defaultFilter, attrs, false, cons); + if (results == null) { return null; + } return results.next (); } @@ -1703,9 +1825,10 @@ public class LDAPConnection implements LDAPv3, Cloneable { String host = toGet.getHost (); int port = toGet.getPort(); - if (host == null) + if (host == null) { throw new LDAPException ( "no host for connection", LDAPException.PARAM_ERROR ); + } String[] attributes = toGet.getAttributeArray (); String DN = toGet.getDN(); @@ -1836,15 +1959,16 @@ public class LDAPConnection implements LDAPv3, Cloneable { String host = toGet.getHost (); int port = toGet.getPort(); - if (host == null) + if (host == null) { throw new LDAPException ( "no host for connection", LDAPException.PARAM_ERROR ); + } String[] attributes = toGet.getAttributeArray (); String DN = toGet.getDN(); String filter = toGet.getFilter(); if (filter == null) { - filter = "(objectClass=*)"; + filter = defaultFilter; } int scope = toGet.getScope (); @@ -1852,10 +1976,11 @@ public class LDAPConnection implements LDAPv3, Cloneable { connection.connect (host, port); LDAPSearchResults results; - if (cons != null) + if (cons != null) { results = connection.search (DN, scope, filter, attributes, false, cons); - else + } else { results = connection.search (DN, scope, filter, attributes, false); + } results.closeOnCompletion(connection); @@ -1929,7 +2054,7 @@ public class LDAPConnection implements LDAPv3, Cloneable { */ public LDAPSearchResults search( String base, int scope, String filter, String[] attrs, boolean attrsOnly ) throws LDAPException { - return search( base, scope, filter, attrs, attrsOnly, defaultConstraints); + return search( base, scope, filter, attrs, attrsOnly, m_defaultConstraints); } /** @@ -1998,41 +2123,49 @@ public class LDAPConnection implements LDAPv3, Cloneable { String[] attrs, boolean attrsOnly, LDAPSearchConstraints cons ) throws LDAPException { - if (cons == null) - cons = defaultConstraints; + if (cons == null) { + cons = m_defaultConstraints; + } - LDAPSearchResults returnValue = new LDAPSearchResults(this, - cons, base, scope, filter, attrs, attrsOnly); + LDAPSearchResults returnValue = + new LDAPSearchResults(this, cons, base, scope, filter, + attrs, attrsOnly); Vector cacheValue = null; Long key = null; boolean isKeyValid = true; try { // get entry from cache which is a vector of JDAPMessages - if (m_cache != null) - { + if (m_cache != null) { // create key for cache entry using search arguments - key = m_cache.createKey(host, port, base, filter, scope, attrs, boundDN, cons); + key = m_cache.createKey(getHost(), getPort(),base, filter, + scope, attrs, m_boundDN, cons); cacheValue = (Vector)m_cache.getEntry(key); - if (cacheValue != null) + if (cacheValue != null) { return (new LDAPSearchResults(cacheValue, this, cons, base, scope, - filter, attrs, attrsOnly)); + filter, attrs, attrsOnly)); + } } } catch (LDAPException e) { isKeyValid = false; printDebug("Exception: "+e); } - bind(cons); + internalBind(cons); LDAPSearchListener myListener = getSearchListener ( cons ); int deref = cons.getDereference(); - JDAPSearchRequest request = new JDAPSearchRequest (base, - scope, deref, cons.getMaxResults(), cons.getServerTimeLimit(), - attrsOnly, filter, attrs); + JDAPSearchRequest request = null; + try { + request = new JDAPSearchRequest (base, scope, deref, + cons.getMaxResults(), cons.getServerTimeLimit(), + attrsOnly, filter, attrs); + } catch (IllegalArgumentException e) { + throw new LDAPException(e.getMessage(), LDAPException.PARAM_ERROR); + } synchronized(myListener) { boolean success = false; @@ -2040,8 +2173,9 @@ public class LDAPConnection implements LDAPv3, Cloneable { sendRequest (request, myListener, cons); success = true; } finally { - if (!success) - releaseSearchListener (myListener); + if (!success) { + releaseSearchListener (myListener); + } } // if using cache, then need to add the key to the search listener. @@ -2056,8 +2190,8 @@ public class LDAPConnection implements LDAPv3, Cloneable { if ( cons.getBatchSize() == 0 ) { try { /* Block until all results are in */ - JDAPMessage response = myListener.getResponse (); - Enumeration results = myListener.getSearchResults (); + LDAPMessage response = myListener.completeSearchOperation(); + Enumeration results = myListener.getAllMessages().elements(); try { checkSearchMsg(returnValue, response, cons, base, scope, @@ -2075,7 +2209,7 @@ public class LDAPConnection implements LDAPv3, Cloneable { } while (results.hasMoreElements ()) { - JDAPMessage msg = (JDAPMessage)results.nextElement(); + LDAPMessage msg = (LDAPMessage)results.nextElement(); checkSearchMsg(returnValue, msg, cons, base, scope, filter, attrs, attrsOnly); @@ -2090,10 +2224,8 @@ public class LDAPConnection implements LDAPv3, Cloneable { * Asynchronous to retrieve one at a time, check to make sure * the search didn't fail */ - JDAPMessage firstResult = myListener.nextResult (); - if (firstResult == null) { - firstResult = myListener.getResponse (); - + LDAPMessage firstResult = myListener.nextMessage (); + if (firstResult instanceof LDAPResponse) { try { checkSearchMsg(returnValue, firstResult, cons, base, scope, filter, attrs, attrsOnly); @@ -2134,14 +2266,17 @@ public class LDAPConnection implements LDAPv3, Cloneable { return returnValue; } - void checkSearchMsg(LDAPSearchResults value, JDAPMessage msg, + void checkSearchMsg(LDAPSearchResults value, LDAPMessage msg, LDAPSearchConstraints cons, String dn, int scope, String filter, String attrs[], boolean attrsOnly) throws LDAPException { + + value.setResponseControls(msg.getControls()); + try { checkMsg (msg); // not the JDAPResult if (msg.getProtocolOp().getType() != JDAPProtocolOp.SEARCH_RESULT) { - value.add (msg.getProtocolOp()); + value.add (msg); } } catch (LDAPReferralException e) { Vector res = new Vector(); @@ -2150,8 +2285,9 @@ public class LDAPConnection implements LDAPv3, Cloneable { // the size of the vector can be more than 1 because it is possible // to visit more than one referral url to retrieve the entries - for (int i=0; i 0) + if (res.size() > 0) { bool = ((Boolean)res.elementAt(0)).booleanValue(); - res = null; + } return bool; } finally { releaseResponseListener (myListener); @@ -2306,13 +2438,13 @@ public class LDAPConnection implements LDAPv3, Cloneable { * @see netscape.ldap.LDAPEntry */ public void add( LDAPEntry entry ) throws LDAPException { - add(entry, defaultConstraints); + add(entry, m_defaultConstraints); } /** * Adds an entry to the directory and allows you to specify preferences * for this LDAP add operation by using an - * LDAPSearchConstraints object. For + * LDAPConstraints object. For * example, you can specify whether or not to follow referrals. * You can also apply LDAP v3 controls to the operation. *

    @@ -2323,27 +2455,28 @@ public class LDAPConnection implements LDAPv3, Cloneable { * @exception LDAPException Failed to add the specified entry to the * directory. * @see netscape.ldap.LDAPEntry - * @see netscape.ldap.LDAPSearchConstraints + * @see netscape.ldap.LDAPConstraints */ - public void add( LDAPEntry entry, LDAPSearchConstraints cons ) + public void add( LDAPEntry entry, LDAPConstraints cons ) throws LDAPException { - bind (cons); + internalBind (cons); LDAPResponseListener myListener = getResponseListener (); LDAPAttributeSet attrs = entry.getAttributeSet (); LDAPAttribute[] attrList = new LDAPAttribute[attrs.size()]; - for( int i = 0; i < attrs.size(); i++ ) + for( int i = 0; i < attrs.size(); i++ ) { attrList[i] = (LDAPAttribute)attrs.elementAt( i ); + } int attrPosition = 0; - JDAPMessage response; + LDAPMessage response; try { sendRequest (new JDAPAddRequest (entry.getDN(), attrList), - myListener, cons); + myListener, cons); response = myListener.getResponse(); checkMsg (response); } catch (LDAPReferralException e) { performReferrals(e, cons, JDAPProtocolOp.ADD_REQUEST, - null, 0, null, null, false, null, entry, null, null); + null, 0, null, null, false, null, entry, null, null); } finally { releaseResponseListener (myListener); } @@ -2367,7 +2500,7 @@ public class LDAPConnection implements LDAPv3, Cloneable { public LDAPExtendedOperation extendedOperation( LDAPExtendedOperation op ) throws LDAPException { - return extendedOperation(op, defaultConstraints); + return extendedOperation(op, m_defaultConstraints); } /** @@ -2388,11 +2521,12 @@ public class LDAPConnection implements LDAPv3, Cloneable { * @see netscape.ldap.LDAPExtendedOperation */ public LDAPExtendedOperation extendedOperation( LDAPExtendedOperation op, - LDAPSearchConstraints cons) throws LDAPException { - bind (cons); + LDAPConstraints cons) + throws LDAPException { + internalBind (cons); LDAPResponseListener myListener = getResponseListener (); - JDAPMessage response = null; + LDAPMessage response = null; byte[] results = null; String resultID; @@ -2444,13 +2578,13 @@ public class LDAPConnection implements LDAPv3, Cloneable { * @see netscape.ldap.LDAPModification */ public void modify( String DN, LDAPModification mod ) throws LDAPException { - modify(DN, mod, defaultConstraints); + modify(DN, mod, m_defaultConstraints); } /** * Makes a single change to an existing entry in the directory and * allows you to specify preferences for this LDAP modify operation - * by using an LDAPSearchConstraints object. For + * by using an LDAPConstraints object. For * example, you can specify whether or not to follow referrals. * You can also apply LDAP v3 controls to the operation. *

    @@ -2461,12 +2595,11 @@ public class LDAPConnection implements LDAPv3, Cloneable { * @exception LDAPException Failed to make the specified change to the * directory entry. * @see netscape.ldap.LDAPModification - * @see netscape.ldap.LDAPSearchConstraints + * @see netscape.ldap.LDAPConstraints */ public void modify( String DN, LDAPModification mod, - LDAPSearchConstraints cons ) throws LDAPException { - LDAPModification[] mods = new LDAPModification [1]; - mods[0] = mod; + LDAPConstraints cons ) throws LDAPException { + LDAPModification[] mods = { mod }; modify (DN, mods, cons); } @@ -2510,13 +2643,13 @@ public class LDAPConnection implements LDAPv3, Cloneable { */ public void modify (String DN, LDAPModificationSet mods) throws LDAPException { - modify(DN, mods, defaultConstraints); + modify(DN, mods, m_defaultConstraints); } /** * Makes a set of changes to an existing entry in the directory and * allows you to specify preferences for this LDAP modify operation - * by using an LDAPSearchConstraints object. For + * by using an LDAPConstraints object. For * example, you can specify whether or not to follow referrals. * You can also apply LDAP v3 controls to the operation. *

    @@ -2527,13 +2660,14 @@ public class LDAPConnection implements LDAPv3, Cloneable { * @exception LDAPException Failed to make the specified changes to the * directory entry. * @see netscape.ldap.LDAPModificationSet - * @see netscape.ldap.LDAPSearchConstraints + * @see netscape.ldap.LDAPConstraints */ public void modify (String DN, LDAPModificationSet mods, - LDAPSearchConstraints cons) throws LDAPException { + LDAPConstraints cons) throws LDAPException { LDAPModification[] modList = new LDAPModification[mods.size()]; - for( int i = 0; i < mods.size(); i++ ) + for( int i = 0; i < mods.size(); i++ ) { modList[i] = mods.elementAt( i ); + } modify (DN, modList, cons); } @@ -2557,13 +2691,13 @@ public class LDAPConnection implements LDAPv3, Cloneable { */ public void modify (String DN, LDAPModification[] mods) throws LDAPException { - modify(DN, mods, defaultConstraints); + modify(DN, mods, m_defaultConstraints); } /** * Makes a set of changes to an existing entry in the directory and * allows you to specify preferences for this LDAP modify operation - * by using an LDAPSearchConstraints object. For + * by using an LDAPConstraints object. For * example, you can specify whether or not to follow referrals. * You can also apply LDAP v3 controls to the operation. *

    @@ -2575,21 +2709,21 @@ public class LDAPConnection implements LDAPv3, Cloneable { * @exception LDAPException Failed to make the specified changes to the * directory entry. * @see netscape.ldap.LDAPModification - * @see netscape.ldap.LDAPSearchConstraints + * @see netscape.ldap.LDAPConstraints */ public void modify (String DN, LDAPModification[] mods, - LDAPSearchConstraints cons) throws LDAPException { - bind (cons); + LDAPConstraints cons) throws LDAPException { + internalBind (cons); LDAPResponseListener myListener = getResponseListener (); - JDAPMessage response = null; + LDAPMessage response = null; try { sendRequest (new JDAPModifyRequest (DN, mods), myListener, cons); response = myListener.getResponse(); checkMsg (response); } catch (LDAPReferralException e) { performReferrals(e, cons, JDAPProtocolOp.MODIFY_REQUEST, - DN, 0, null, null, false, mods, null, null, null); + DN, 0, null, null, false, mods, null, null, null); } finally { releaseResponseListener (myListener); } @@ -2613,13 +2747,13 @@ public class LDAPConnection implements LDAPv3, Cloneable { * the directory. */ public void delete( String DN ) throws LDAPException { - delete(DN, defaultConstraints); + delete(DN, m_defaultConstraints); } /** * Deletes the entry for the specified DN from the directory and * allows you to specify preferences for this LDAP delete operation - * by using an LDAPSearchConstraints object. For + * by using an LDAPConstraints object. For * example, you can specify whether or not to follow referrals. * You can also apply LDAP v3 controls to the operation. *

    @@ -2629,21 +2763,21 @@ public class LDAPConnection implements LDAPv3, Cloneable { * @param cons The set of preferences that you want applied to this operation. * @exception LDAPException Failed to delete the specified entry from * the directory. - * @see netscape.ldap.LDAPSearchConstraints + * @see netscape.ldap.LDAPConstraints */ - public void delete( String DN, LDAPSearchConstraints cons ) + public void delete( String DN, LDAPConstraints cons ) throws LDAPException { - bind (cons); + internalBind (cons); LDAPResponseListener myListener = getResponseListener (); - JDAPMessage response; + LDAPMessage response; try { sendRequest (new JDAPDeleteRequest (DN), myListener, cons); response = myListener.getResponse(); checkMsg (response); } catch (LDAPReferralException e) { performReferrals(e, cons, JDAPProtocolOp.DEL_REQUEST, - DN, 0, null, null, false, null, null, null, null); + DN, 0, null, null, false, null, null, null, null); } finally { releaseResponseListener (myListener); } @@ -2720,8 +2854,8 @@ public class LDAPConnection implements LDAPv3, Cloneable { * @exception LDAPException Failed to rename the specified entry. */ public void rename (String DN, String newRDN, boolean deleteOldRDN, - LDAPSearchConstraints cons ) - throws LDAPException { + LDAPConstraints cons ) + throws LDAPException { rename(DN, newRDN, null, deleteOldRDN, cons); } @@ -2753,14 +2887,14 @@ public class LDAPConnection implements LDAPv3, Cloneable { String newRDN, String newParentDN, boolean deleteOldRDN) throws LDAPException { - rename(dn, newRDN, newParentDN, deleteOldRDN, defaultConstraints); + rename(dn, newRDN, newParentDN, deleteOldRDN, m_defaultConstraints); } /** * Renames an existing entry in the directory and (optionally) * changes the location of the entry in the directory tree. Also * allows you to specify preferences for this LDAP modify DN operation - * by using an LDAPSearchConstraints object. For + * by using an LDAPConstraints object. For * example, you can specify whether or not to follow referrals. * You can also apply LDAP v3 controls to the operation. *

    @@ -2785,63 +2919,752 @@ public class LDAPConnection implements LDAPv3, Cloneable { * for the cn attribute: "cn=oldName" and "cn=newName"). * @param cons The set of preferences that you want applied to this operation. * @exception LDAPException Failed to rename the specified entry. - * @see netscape.ldap.LDAPSearchConstraints + * @see netscape.ldap.LDAPConstraints */ public void rename (String DN, String newRDN, String newParentDN, boolean deleteOldRDN, - LDAPSearchConstraints cons) + LDAPConstraints cons) throws LDAPException { - bind (cons); + internalBind (cons); LDAPResponseListener myListener = getResponseListener (); - JDAPMessage response; try { JDAPModifyRDNRequest request = null; - if ( newParentDN != null ) + if ( newParentDN != null ) { request = new JDAPModifyRDNRequest (DN, newRDN, deleteOldRDN, newParentDN); - else + } else { request = new JDAPModifyRDNRequest (DN, newRDN, deleteOldRDN); + } sendRequest (request, myListener, cons); - response = myListener.getResponse(); + LDAPMessage response = myListener.getResponse(); checkMsg (response); } catch (LDAPReferralException e) { performReferrals(e, cons, JDAPProtocolOp.MODIFY_RDN_REQUEST, - DN, 0, newRDN, null, deleteOldRDN, null, null, null, null); + DN, 0, newRDN, null, deleteOldRDN, null, null, + null, null); } finally { releaseResponseListener (myListener); } } + /** + * Adds an entry to the directory. + * + * @param entry LDAPEntry object specifying the distinguished name and + * attributes of the new entry. + * @param listener Handler for messages returned from a server in response + * to this request. If it is null, a listener object is created internally. + * @param cons Constraints specific to the operation. + * @return LDAPResponseListener Handler for messages returned from a server + * in response to this request. + * @exception LDAPException Failed to send request. + * @see netscape.ldap.LDAPEntry + * @see netscape.ldap.LDAPResponseListener + */ + public LDAPResponseListener add(LDAPEntry entry, + LDAPResponseListener listener) + throws LDAPException{ + return add(entry, listener, m_defaultConstraints); + } + + /** + * Adds an entry to the directory and allows you to specify constraints + * for this LDAP add operation by using an LDAPConstraints + * object. For example, you can specify whether or not to follow referrals. + * You can also apply LDAP v3 controls to the operation. + *

    + * + * @param entry LDAPEntry object specifying the distinguished name and + * attributes of the new entry. + * @param listener Handler for messages returned from a server in response + * to this request. If it is null, a listener object is created internally. + * @param cons Constraints specific to the operation. + * @return LDAPResponseListener Handler for messages returned from a server + * in response to this request. + * @exception LDAPException Failed to send request. + * @see netscape.ldap.LDAPEntry + * @see netscape.ldap.LDAPResponseListener + * @see netscape.ldap.LDAPConstraints + */ + public LDAPResponseListener add(LDAPEntry entry, + LDAPResponseListener listener, + LDAPConstraints cons) + throws LDAPException{ + + if (cons == null) { + cons = m_defaultConstraints; + } + + internalBind (cons); + + if (listener == null) { + listener = new LDAPResponseListener(/*asynchOp=*/true); + } + + LDAPAttributeSet attrs = entry.getAttributeSet (); + LDAPAttribute[] attrList = new LDAPAttribute[attrs.size()]; + for( int i = 0; i < attrs.size(); i++ ) + attrList[i] = (LDAPAttribute)attrs.elementAt( i ); + int attrPosition = 0; + + sendRequest (new JDAPAddRequest (entry.getDN(), attrList), + listener, cons); + + return listener; + } + + /** + * Authenticates to the LDAP server (that the object is currently + * connected to) using the specified name and password. If the object + * has been disconnected from an LDAP server, this method attempts to + * reconnect to the server. If the object had already authenticated, the + * old authentication is discarded. + * + * @param dn If non-null and non-empty, specifies that the connection + * and all operations through it should be authenticated with dn as the + * distinguished name. + * @param passwd If non-null and non-empty, specifies that the connection + * and all operations through it should be authenticated with dn as the + * distinguished name and passwd as password. + * @param listener Handler for messages returned from a server in response + * to this request. If it is null, a listener object is created internally. + * @return LDAPResponseListener Handler for messages returned from a server + * in response to this request. + * @exception LDAPException Failed to send request. + * @see netscape.ldap.LDAPResponseListener + */ + public LDAPResponseListener bind(String dn, + String passwd, + LDAPResponseListener listener) + throws LDAPException{ + return bind(dn, passwd, listener, m_defaultConstraints); + } + + /** + * Authenticates to the LDAP server (that the object is currently + * connected to) using the specified name and password and allows you + * to specify constraints for this LDAP add operation by using an + * LDAPConstraints object. If the object + * has been disconnected from an LDAP server, this method attempts to + * reconnect to the server. If the object had already authenticated, the + * old authentication is discarded. + * + * @param dn If non-null and non-empty, specifies that the connection + * and all operations through it should be authenticated with dn as the + * distinguished name. + * @param passwd If non-null and non-empty, specifies that the connection + * and all operations through it should be authenticated with dn as the + * distinguished name and passwd as password. + * @param listener Handler for messages returned from a server in response + * to this request. If it is null, a listener object is created internally. + * @param cons Constraints specific to the operation. + * @return LDAPResponseListener Handler for messages returned from a server + * in response to this request. + * @exception LDAPException Failed to send request. + * @see netscape.ldap.LDAPResponseListener + * @see netscape.ldap.LDAPConstraints + */ + public LDAPResponseListener bind(String dn, + String passwd, + LDAPResponseListener listener, + LDAPConstraints cons) + throws LDAPException{ + if (cons == null) { + cons = m_defaultConstraints; + } + + m_prevBoundDN = m_boundDN; + m_prevBoundPasswd = m_boundPasswd; + m_boundDN = dn; + m_boundPasswd = passwd; + + if (m_thread == null) { + connect(); + } + else if (m_thread.getClientCount() > 1) { + disconnect(); + connect(); + } + + if (listener == null) { + listener = new LDAPResponseListener(/*asynchOp=*/true); + } + + sendRequest(new JDAPBindRequest(m_protocolVersion, m_boundDN, m_boundPasswd), + listener, cons); + + return listener; + } + + /** + * Authenticates to the LDAP server (that the object is currently + * connected to) using the specified name and a specified SASL mechanism + * or set of mechanisms. If the requested SASL mechanism is not + * available, an exception is thrown. If the object has been + * disconnected from an LDAP server, this method attempts to reconnect + * to the server. If the object had already authenticated, the old + * authentication is discarded. + * + * @param dn If non-null and non-empty, specifies that the connection + * and all operations through it should be authenticated with dn as the + * distinguished name. + * @param mechanisms A list of acceptable mechanisms. The first one + * for which a Mechanism Driver can be instantiated is returned. + * @param packageName A package from which to instantiate the Mechanism + * Driver, e.g. "myclasses.SASL.mechanisms". If null, a system default is used. + * @param props The possibly null additional configuration properties for + * the session. + * @param cbh A class which may be called by the SASL framework to + * obtain additional information required. + * @param listener Handler for messages returned from a server in response + * to this request. If it is null, a listener object is created internally. + * @return LDAPResponseListener Handler for messages returned from a server + * in response to this request. + * @exception LDAPException Failed to send request. + * @see netscape.ldap.LDAPResponseListener + */ + public LDAPResponseListener bind(String dn, + String[] mechanisms, + String packageName, + Properties props, + CallbackHandler cbh, + LDAPResponseListener listener) + throws LDAPException{ + + return bind(dn, mechanisms, packageName, props, cbh, + listener, m_defaultConstraints); + } + + /** + * Authenticates to the LDAP server (that the object is currently + * connected to) using the specified name and a specified SASL mechanism + * or set of mechanisms. If the requested SASL mechanism is not + * available, an exception is thrown. If the object has been + * disconnected from an LDAP server, this method attempts to reconnect + * to the server. If the object had already authenticated, the old + * authentication is discarded. + * + * @param dn If non-null and non-empty, specifies that the connection + * and all operations through it should be authenticated with dn as the + * distinguished name. + * @param mechanisms A list of acceptable mechanisms. The first one + * for which a Mechanism Driver can be instantiated is returned. + * @param packageName A package from which to instantiate the Mechanism + * Driver, e.g. "myclasses.SASL.mechanisms". If null, a system default is used. + * @param props The possibly null additional configuration properties for + * the session. + * @param cbh A class which may be called by the SASL framework to + * obtain additional information required. + * @param listener Handler for messages returned from a server in response + * to this request. If it is null, a listener object is created internally. + * @param cons Constraints specific to the operation. + * @return LDAPResponseListener Handler for messages returned from a server + * in response to this request. + * @exception LDAPException Failed to bind to the directory. + * @see netscape.ldap.LDAPResponseListener + * @see netscape.ldap.LDAPConstraints + */ + public LDAPResponseListener bind(String dn, + String[] mechanisms, + String packageName, + Properties props, + CallbackHandler cbh, + LDAPResponseListener listener, + LDAPConstraints cons) + throws LDAPException{ + return null; + } + + /** + * Deletes the entry for the specified DN from the directory. + * + * @param dn Distinguished name of the entry to delete. + * @param listener Handler for messages returned from a server in response + * to this request. If it is null, a listener object is created internally. + * @return LDAPResponseListener Handler for messages returned from a server + * in response to this request. + * @exception LDAPException Failed to send request. + * @see netscape.ldap.LDAPResponseListener + * @see netscape.ldap.LDAPConstraints + */ + public LDAPResponseListener delete(String dn, + LDAPResponseListener listener) + throws LDAPException{ + + return delete(dn, listener, m_defaultConstraints); + } + + /** + * Deletes the entry for the specified DN from the directory. + * + * @param dn Distinguished name of the entry to delete. + * @param listener Handler for messages returned from a server in response + * to this request. If it is null, a listener object is created internally. + * @param cons Constraints specific to the operation. + * @return LDAPResponseListener Handler for messages returned from a server + * in response to this request. + * @exception LDAPException Failed to send request. + * @see netscape.ldap.LDAPResponseListener + * @see netscape.ldap.LDAPConstraints + */ + public LDAPResponseListener delete(String dn, + LDAPResponseListener listener, + LDAPConstraints cons) + throws LDAPException{ + if (cons == null) { + cons = m_defaultConstraints; + } + + internalBind (cons); + + if (listener == null) { + listener = new LDAPResponseListener(/*asynchOp=*/true); + } + + sendRequest (new JDAPDeleteRequest(dn), listener, cons); + + return listener; + + } + + /** + * Makes a single change to an existing entry in the directory (for + * example, changes the value of an attribute, adds a new attribute + * value, or removes an existing attribute value).
    + * The LDAPModification object specifies both the change to be made and + * the LDAPAttribute value to be changed. + * + * @param dn Distinguished name of the entry to modify. + * @param mod A single change to be made to an entry. + * @param listener Handler for messages returned from a server in response + * to this request. If it is null, a listener object is created internally. + * @return LDAPResponseListener Handler for messages returned from a server + * in response to this request. + * @exception LDAPException Failed to send request. + * @see netscape.ldap.LDAPModification + * @see netscape.ldap.LDAPResponseListener + */ + public LDAPResponseListener modify(String dn, + LDAPModification mod, + LDAPResponseListener listener) + throws LDAPException{ + + return modify(dn, mod, listener, m_defaultConstraints); + } + + /** + * Makes a single change to an existing entry in the directory (for + * example, changes the value of an attribute, adds a new attribute + * value, or removes an existing attribute value).
    + * The LDAPModification object specifies both the change to be made and + * the LDAPAttribute value to be changed. + * + * @param dn Distinguished name of the entry to modify. + * @param mod A single change to be made to an entry. + * @param listener Handler for messages returned from a server in response + * to this request. If it is null, a listener object is created internally. + * @param cons Constraints specific to the operation. + * @return LDAPResponseListener Handler for messages returned from a server + * in response to this request. + * @exception LDAPException Failed to send request. + * @see netscape.ldap.LDAPModification + * @see netscape.ldap.LDAPResponseListener + * @see netscape.ldap.LDAPConstraints + */ + public LDAPResponseListener modify(String dn, + LDAPModification mod, + LDAPResponseListener listener, + LDAPConstraints cons) + throws LDAPException{ + if (cons == null) { + cons = m_defaultConstraints; + } + + internalBind (cons); + + if (listener == null) { + listener = new LDAPResponseListener(/*asynchOp=*/true); + } + + LDAPModification[] modList = { mod }; + sendRequest (new JDAPModifyRequest (dn, modList), listener, cons); + + return listener; + } + + /** + * Makes a set of changes to an existing entry in the directory (for + * example, changes attribute values, adds new attribute values, or + * removes existing attribute values). + *

    + * @param dn Distinguished name of the entry to modify. + * @param mods A set of changes to be made to the entry. + * @param listener Handler for messages returned from a server in response + * to this request. If it is null, a listener object is created internally. + * @return LDAPResponseListener Handler for messages returned from a server + * in response to this request. + * @exception LDAPException Failed to send request. + * @see netscape.ldap.LDAPModificationSet + * @see netscape.ldap.LDAPResponseListener + */ + public LDAPResponseListener modify(String dn, + LDAPModificationSet mods, + LDAPResponseListener listener) + throws LDAPException{ + return modify(dn,mods, listener, m_defaultConstraints); + } + + /** + * Makes a set of changes to an existing entry in the directory (for + * example, changes attribute values, adds new attribute values, or + * removes existing attribute values). + * + * @param dn Distinguished name of the entry to modify. + * @param mods A set of changes to be made to the entry. + * @param listener Handler for messages returned from a server in response + * to this request. If it is null, a listener object is created internally. + * @param cons Constraints specific to the operation. + * @return LDAPResponseListener Handler for messages returned from a server + * in response to this request. + * @exception LDAPException Failed to send request. + * @see netscape.ldap.LDAPModificationSet + * @see netscape.ldap.LDAPResponseListener + * @see netscape.ldap.LDAPConstraints + */ + public LDAPResponseListener modify(String dn, + LDAPModificationSet mods, + LDAPResponseListener listener, + LDAPConstraints cons) + throws LDAPException{ + if (cons == null) { + cons = m_defaultConstraints; + } + + internalBind (cons); + + if (listener == null) { + listener = new LDAPResponseListener(/*asynchOp=*/true); + } + + LDAPModification[] modList = new LDAPModification[mods.size()]; + for( int i = 0; i < mods.size(); i++ ) { + modList[i] = mods.elementAt( i ); + } + + sendRequest (new JDAPModifyRequest (dn, modList), listener, cons); + return listener; + + } + + /** + * Renames an existing entry in the directory. + * + * @param dn Current distinguished name of the entry. + * @param newRdn New relative distinguished name for the entry. + * @param deleteOldRdn If true, the old name is not retained as an + * attribute value. + * @param listener Handler for messages returned from a server in response + * to this request. If it is null, a listener object is created internally. + * @return LDAPResponseListener Handler for messages returned from a server + * in response to this request. + * @exception LDAPException Failed to send request. + * @see netscape.ldap.LDAPResponseListener + */ + public LDAPResponseListener rename(String dn, + String newRdn, + boolean deleteOldRdn, + LDAPResponseListener listener) + throws LDAPException{ + return rename(dn, newRdn, deleteOldRdn, listener, m_defaultConstraints); + } + + /** + * Renames an existing entry in the directory. + * + * @param dn Current distinguished name of the entry. + * @param newRdn New relative distinguished name for the entry. + * @param deleteOldRdn If true, the old name is not retained as an + * @param listener Handler for messages returned from a server in response + * to this request. If it is null, a listener object is created internally. + * @param cons Constraints specific to the operation. + * @return LDAPResponseListener Handler for messages returned from a server + * in response to this request. + * @exception LDAPException Failed to send request. + * @see netscape.ldap.LDAPResponseListener + * @see netscape.ldap.LDAPConstraints + */ + public LDAPResponseListener rename(String dn, + String newRdn, + boolean deleteOldRdn, + LDAPResponseListener listener, + LDAPConstraints cons) + throws LDAPException{ + if (cons == null) { + cons = m_defaultConstraints; + } + + internalBind (cons); + + if (listener == null) { + listener = new LDAPResponseListener(/*asynchOp=*/true); + } + + sendRequest (new JDAPModifyRDNRequest (dn, newRdn, deleteOldRdn), + listener, cons); + + return listener; + + } + + + /** + * Performs the search specified by the criteria that you enter.

    + * To abandon the search, use the abandon method. + * + * @param base The base distinguished name to search from + * @param scope The scope of the entries to search. You can specify one + * of the following:

    + *

      + *
    • LDAPv2.SCOPE_BASE (search only the base DN)

      + *

    • LDAPv2.SCOPE_ONE + * (search only entries under the base DN)

      + *

    • LDAPv2.SCOPE_SUB + * (search the base DN and all entries within its subtree)

      + *

    + *

    + * @param filter Search filter specifying the search criteria. + * @param attrs List of attributes that you want returned in the + * search results. + * @param typesOnly If true, returns the names but not the values of the + * attributes found. If false, returns the names and values for + * attributes found + * @param listener Handler for messages returned from a server in response + * to this request. If it is null, a listener object is created internally. + * @return LDAPSearchListener Handler for messages returned from a server + * in response to this request. + * @exception LDAPException Failed to send request. + * @see netscape.ldap.LDAPConnection#abandon(netscape.ldap.LDAPSearchListener) + */ + public LDAPSearchListener search(String base, + int scope, + String filter, + String attrs[], + boolean typesOnly, + LDAPSearchListener listener) + throws LDAPException { + + return search(base, scope, filter, attrs, typesOnly, + listener, m_defaultConstraints); + } + + /** + * Performs the search specified by the criteria that you enter. + * This method also allows you to specify constraints for the search + * (such as the maximum number of entries to find or the + * maximum time to wait for search results).

    + * To abandon the search, use the abandon method. + * + * @param base The base distinguished name to search from + * @param scope The scope of the entries to search. You can specify one + * of the following:

    + *

      + *
    • LDAPv2.SCOPE_BASE (search only the base DN)

      + *

    • LDAPv2.SCOPE_ONE + * (search only entries under the base DN)

      + *

    • LDAPv2.SCOPE_SUB + * (search the base DN and all entries within its subtree)

      + *

    + *

    + * @param filter Search filter specifying the search criteria. + * @param attrs List of attributes that you want returned in the search + * results. + * @param typesOnly If true, returns the names but not the values of the + * attributes found. If false, returns the names and values for + * attributes found. + * @param listener Handler for messages returned from a server in response + * to this request. If it is null, a listener object is created internally. + * @param cons Constraints specific to this search (for example, the + * maximum number of entries to return). + * @return LDAPSearchListener Handler for messages returned from a server + * in response to this request. + * @exception LDAPException Failed to send request. + * @see netscape.ldap.LDAPConnection#abandon(netscape.ldap.LDAPSearchListener) + */ + public LDAPSearchListener search(String base, + int scope, + String filter, + String attrs[], + boolean typesOnly, + LDAPSearchListener listener, + LDAPSearchConstraints cons) + throws LDAPException { + if (cons == null) { + cons = m_defaultConstraints; + } + + internalBind (cons); + + if (listener == null) { + listener = new LDAPSearchListener(/*asynchOp=*/true, cons); + } + + JDAPSearchRequest request = null; + try { + request = new JDAPSearchRequest (base, scope, cons.getDereference(), + cons.getMaxResults(), cons.getServerTimeLimit(), + typesOnly, filter, attrs); + } + catch (IllegalArgumentException e) { + throw new LDAPException(e.getMessage(), LDAPException.PARAM_ERROR); + } + + sendRequest (request, listener, cons); + return listener; + + } + + /** + * Compare an attribute value with one in the directory. The result can + * be obtained by calling getResultCode on the + * LDAPResponse from the LDAPResponseListener. + * The code will be LDAPException.COMPARE_TRUE or + * LDAPException.COMPARE_FALSE. + * + * @param dn Distinguished name of the entry to compare. + * @param attr Attribute with a value to compare. + * @param listener Handler for messages returned from a server in response + * to this request. If it is null, a listener object is created internally. + * @return LDAPResponseListener Handler for messages returned from a server + * in response to this request. + * @exception LDAPException Failed to send request. + */ + public LDAPResponseListener compare(String dn, + LDAPAttribute attr, + LDAPResponseListener listener) + throws LDAPException { + + return compare(dn, attr, listener, m_defaultConstraints); + } + + /** + * Compare an attribute value with one in the directory. The result can + * be obtained by calling getResultCode on the + * LDAPResponse from the LDAPResponseListener. + * The code will be LDAPException.COMPARE_TRUE or + * LDAPException.COMPARE_FALSE. + * + * @param dn Distinguished name of the entry to compare. + * @param attr Attribute with a value to compare. + * @param listener Handler for messages returned from a server in response + * to this request. If it is null, a listener object is created internally. + * @param cons Constraints specific to this operation. + * @return LDAPResponseListener Handler for messages returned from a server + * in response to this request. + * @exception LDAPException Failed to send request. + */ + public LDAPResponseListener compare(String dn, + LDAPAttribute attr, + LDAPResponseListener listener, + LDAPConstraints cons) + throws LDAPException { + if (cons == null) { + cons = m_defaultConstraints; + } + + internalBind (cons); + + if (listener == null) { + listener = new LDAPResponseListener(/*asynchOp=*/true); + } + + Enumeration en = attr.getStringValues(); + String val = (String)en.nextElement(); + JDAPAVA ava = new JDAPAVA(attr.getName(), val); + + sendRequest (new JDAPCompareRequest (dn, ava), listener, cons); + return listener; + } + + /** + * Cancels the ldap request with the specified id and discards + * any results already received. + * + * @param id A LDAP request id + * @exception LDAPException Failed to send request. + */ + public void abandon(int id) throws LDAPException { + + if (!isConnected()) { + return; + } + + for (int i=0; i<3; i++) { + try { + /* Tell listener thread to discard results */ + m_thread.abandon( id ); + + /* Tell server to stop sending results */ + m_thread.sendRequest(null, new JDAPAbandonRequest(id), null, m_defaultConstraints); + + /* No response is forthcoming */ + break; + } catch (NullPointerException ne) { + // do nothing + } + } + if (m_thread == null) { + throw new LDAPException("Failed to send abandon request to the server.", + LDAPException.OTHER); + } + } + + /** + * Cancels all outstanding search requests associated with this + * LDAPSearchListener object and discards any results already received. + * + * @param searchlistener A search listener returned from a search. + * @exception LDAPException Failed to send request. + */ + public void abandon(LDAPSearchListener searchlistener)throws LDAPException { + int[] ids = searchlistener.getIDs(); + for (int i=0; i < ids.length; i++) { + searchlistener.removeRequest(ids[i]); + abandon(ids[i]); + } + } + /** * Returns the value of the specified option for this * LDAPConnection object.

    * - * These options represent the search constraints for the current connection. - * To get all search constraints for the current connection, call the + * These options represent the constraints for the current connection. + * To get all constraints for the current connection, call the * getSearchConstraints method. *

    * - * By default, the search constraints apply to all searches performed - * through the current connection. You can change these search constraints: + * By default, the constraints apply to all operations performed + * through the current connection. You can change these constraints: *

    * *

      - *
    • If you want to override these constraints only for a particular - * search, create an LDAPSearchConstraints object with - * your new constraints and pass it to the - * LDAPConnection.search method. + *
    • If you want to set a constraint only for a particular operation, + * create an LDAPConstraints object (or a + * LDAPSearchConstraints object for a search or find operation) + * with your new constraints + * and pass it to the LDAPConnection method that performs the + * operation. *

      * - *

    • If you want to override these constraints for all searches + *
    • If you want to override these constraints for all operations * performed under the current connection, call the - * setOption method to change the search constraint. + * setOption method to change the constraint. *

      * *

    @@ -2922,6 +3745,17 @@ public class LDAPConnection implements LDAPv3, Cloneable { * LDAPRebind interface. You must define this class and * the getRebindAuthentication method that will be used to * get the distinguished name and password to use for authentication. + * Modifying this option sets the LDAPv2.BIND option to null. + *

    By default, the value of this option is null. + * + * LDAPv2.BIND>/CODE> + * LDAPBind + * Specifies an object with a class that implements the + * LDAPBind + * interface. You must define this class and the + * bind method that will be used to autheniticate + * to the server on referrals. Modifying this option sets the + * LDAPv2.REFERRALS_REBIND_PROC to null. *

    By default, the value of this option is null. * * LDAPv2.REFERRALS_HOP_LIMIT @@ -2964,6 +3798,7 @@ public class LDAPConnection implements LDAPv3, Cloneable { * value as an Integer.) * @exception LDAPException Failed to get the specified option. * @see netscape.ldap.LDAPRebind + * @see netscape.ldap.LDAPConstraints * @see netscape.ldap.LDAPSearchConstraints * @see netscape.ldap.LDAPReferralException * @see netscape.ldap.LDAPControl @@ -2972,10 +3807,10 @@ public class LDAPConnection implements LDAPv3, Cloneable { */ public Object getOption( int option ) throws LDAPException { if (option == LDAPv2.PROTOCOL_VERSION) { - return new Integer(protocolVersion); + return new Integer(m_protocolVersion); } - return getOption(option, defaultConstraints); + return getOption(option, m_defaultConstraints); } private static Object getOption( int option, LDAPSearchConstraints cons ) @@ -2991,6 +3826,8 @@ public class LDAPConnection implements LDAPv3, Cloneable { return new Boolean (cons.getReferrals()); case LDAPv2.REFERRALS_REBIND_PROC: return cons.getRebindProc(); + case LDAPv2.BIND: + return cons.getBindProc(); case LDAPv2.REFERRALS_HOP_LIMIT: return new Integer (cons.getHopLimit()); case LDAPv2.BATCHSIZE: @@ -3006,22 +3843,25 @@ public class LDAPConnection implements LDAPv3, Cloneable { LDAPException.PARAM_ERROR ); } } - + /** * Sets the value of the specified option for this * LDAPConnection object.

    * - * These options represent the search constraints for the current + * These options represent the constraints for the current * connection. - * To get all search constraints for the current connection, call the - * getSearchConstraints method. + * To get all constraints for the current connection, call the + * getSearchConstraints method. *

    * * By default, the option that you set applies to all subsequent - * searches performed through the current connection. If you want to - * set a constraint only for a particular search, create an - * LDAPSearchConstraints object with your new constraints - * and pass it to the LDAPConnection.search method. + * operations performed through the current connection. If you want to + * set a constraint only for a particular operation, create an + * LDAPConstraints object (or a + * LDAPSearchConstraints object for a search or find operation) + * with your new constraints + * and pass it to the LDAPConnection method that performs the + * operation. *

    * * For example, the following section of code changes the constraint for @@ -3102,7 +3942,18 @@ public class LDAPConnection implements LDAPv3, Cloneable { * LDAPRebind * interface. You must define this class and the * getRebindAuthentication method that will be used to get - * the distinguished name and password to use for authentication. + * the distinguished name and password to use for authentication. + * Modifying this option sets the LDAPv2.BIND option to null. + *

    By default, the value of this option is null. + * + * LDAPv2.BIND>/CODE> + * LDAPBind + * Specifies an object with a class that implements the + * LDAPBind + * interface. You must define this class and the + * bind method that will be used to autheniticate + * to the server on referrals. Modifying this option sets the + * LDAPv2.REFERRALS_REBIND_PROC to null. *

    By default, the value of this option is null. * * LDAPv2.REFERRALS_HOP_LIMIT @@ -3145,6 +3996,7 @@ public class LDAPConnection implements LDAPv3, Cloneable { * integer->Integer) * @exception LDAPException Failed to set the specified option. * @see netscape.ldap.LDAPRebind + * @see netscape.ldap.LDAPConstraints * @see netscape.ldap.LDAPSearchConstraints * @see netscape.ldap.LDAPReferralException * @see netscape.ldap.LDAPControl @@ -3156,13 +4008,7 @@ public class LDAPConnection implements LDAPv3, Cloneable { setProtocolVersion(((Integer)value).intValue()); return; } - setOption(option, value, defaultConstraints); - if ( (option == MAXBACKLOG) && (th != null) ) { - int val = ((Integer)value).intValue(); - if ( val >= 1 ) { - th.setMaxBacklog( val ); - } - } + setOption(option, value, m_defaultConstraints); } private static void setOption( int option, Object value, LDAPSearchConstraints cons ) throws LDAPException { @@ -3175,11 +4021,17 @@ public class LDAPConnection implements LDAPv3, Cloneable { cons.setMaxResults(((Integer)value).intValue()); return; case LDAPv2.TIMELIMIT: + cons.setTimeLimit(((Integer)value).intValue()); + return; + case LDAPv2.SERVER_TIMELIMIT: cons.setServerTimeLimit(((Integer)value).intValue()); return; case LDAPv2.REFERRALS: cons.setReferrals(((Boolean)value).booleanValue()); return; + case LDAPv2.BIND: + cons.setBindProc((LDAPBind)value); + return; case LDAPv2.REFERRALS_REBIND_PROC: cons.setRebindProc((LDAPRebind)value); return; @@ -3225,29 +4077,34 @@ public class LDAPConnection implements LDAPv3, Cloneable { LDAPException.PARAM_ERROR ); } } catch (ClassCastException cc) { - throw new LDAPException ("invalid option value", - LDAPException.PARAM_ERROR ); + throw new LDAPException ("invalid option value", + LDAPException.PARAM_ERROR ); } } /** * Returns an array of the latest controls (if any) from server. + *

    + * To retrieve the controls from a search result, call the + * getResponseControls method from the LDAPSearchResults + * object returned with the result. * @return An array of the controls returned by an operation, or * null if none. * @see netscape.ldap.LDAPControl + * @see netscape.ldap.LDAPSearchResults#getResponseControls */ public LDAPControl[] getResponseControls() { LDAPControl[] controls = null; /* Get the latest controls returned for our thread */ synchronized(m_responseControlTable) { - Vector responses = (Vector)m_responseControlTable.get(th); + Vector responses = (Vector)m_responseControlTable.get(m_thread); if (responses != null) { // iterate through each response control for (int i=0,size=responses.size(); i + * + * Note that if you want to get individual constraints (rather than + * getting the + * entire set of constraints), call the getOption method. + *

    + * + * Typically, you might call the getConstraints method + * if you want to create a slightly different set of constraints + * that you want to apply to a particular operation. + *

    + * + * For example, the following section of code changes the timeout + * to 3000 milliseconds for a specific rename. Rather than construct a new + * set of constraints from scratch, the example gets the current + * settings for the connections and just changes the setting for the + * timeout. + *

    + * + * Note that this change only applies to the searches performed with this + * custom set of constraints. All other searches performed through this + * connection use the original set of search constraints. + *

    + * + *

    +     * ...
    +     * LDAPConstraints myOptions = ld.getConstraints();
    +     * myOptions.setTimeout( 3000 );
    +     * ld.search( "cn=William Jensen, ou=Accounting, o=Ace Industry,c=US",
    +     *            "cn=Will Jensen",
    +     *            null,
    +     *            false,
    +     *            myOptions );
    +     * ...
    +     * 
    + * + * @return A copy of the LDAPConstraints object representing the + * set of constraints that apply (by default) to all operations + * performed through this connection. + * @see netscape.ldap.LDAPConstraints + * @see netscape.ldap.LDAPConnection#getOption + */ + public LDAPConstraints getConstraints () { + return (LDAPConstraints)getSearchConstraints(); + } + /** * Returns the set of search constraints that apply to all searches * performed through this connection (unless you specify a different * set of search constraints when calling the search - * method).

    + * method). + *

    * * Note that if you want to get individual constraints (rather than * getting the @@ -3304,15 +4212,109 @@ public class LDAPConnection implements LDAPv3, Cloneable { * ... *

    * - * @return The LDAPSearchConstraints object representing the - * set of search constraints that apply (by default) to all searches - * performed through this connection. + * @return A copy of the LDAPSearchConstraints object + * representing the set of search constraints that apply (by default) to + * all searches performed through this connection. * @see netscape.ldap.LDAPSearchConstraints * @see netscape.ldap.LDAPConnection#getOption - * @see netscape.ldap.LDAPConnection#search(java.lang.String, int, java.lang.String, java.lang.String[], boolean, netscape.ldap.LDAPSearchConstraints) + * @see netscape.ldap.LDAPConnection#search(java.lang.String, int, java.lang.String, java.lang.String[], boolean, netscape.ldap.LDAPSearchConstraints) */ public LDAPSearchConstraints getSearchConstraints () { - return defaultConstraints; + return (LDAPSearchConstraints)m_defaultConstraints.clone(); + } + + + /** + * Set the default constraint set for all operations. + * @param cons LDAPConstraints object to be used as the default + * constraint set. + * @see netscape.ldap.LDAPConnection#getConstraints + * @see netscape.ldap.LDAPConstraints + */ + public void setConstraints(LDAPConstraints cons) { + m_defaultConstraints.setHopLimit(cons.getHopLimit()); + m_defaultConstraints.setReferrals(cons.getReferrals()); + m_defaultConstraints.setTimeLimit(cons.getTimeLimit()); + m_defaultConstraints.setBindProc(cons.getBindProc()); + m_defaultConstraints.setRebindProc(cons.getRebindProc()); + + LDAPControl[] tClientControls = cons.getClientControls(); + LDAPControl[] oClientControls = null; + + if ( (tClientControls != null) && + (tClientControls.length > 0) ) { + oClientControls = new LDAPControl[tClientControls.length]; + for( int i = 0; i < tClientControls.length; i++ ) { + oClientControls[i] = (LDAPControl)tClientControls[i].clone(); + } + } + m_defaultConstraints.setClientControls(oClientControls); + + LDAPControl[] tServerControls = cons.getServerControls(); + LDAPControl[] oServerControls = null; + + if ( (tServerControls != null) && + (tServerControls.length > 0) ) { + oServerControls = new LDAPControl[tServerControls.length]; + for( int i = 0; i < tServerControls.length; i++ ) { + oServerControls[i] = (LDAPControl)tServerControls[i].clone(); + } + } + m_defaultConstraints.setServerControls(oServerControls); + } + + /** + * Set the default constraint set for all search operations. + * @param cons LDAPSearchConstraints object to be used as the + * default constraint set. + * @see netscape.ldap.LDAPConnection#getSearchConstraints + * @see netscape.ldap.LDAPSearchConstraints + */ + public void setSearchConstraints(LDAPSearchConstraints cons) { + m_defaultConstraints = (LDAPSearchConstraints)cons.clone(); + } + + /** + * Gets the stream for reading from the listener socket + * + * @return The stream for reading from the listener socket, or + * null if there is none + */ + public InputStream getInputStream() { + return (m_thread != null) ? m_thread.getInputStream() : null; + } + + /** + * Sets the stream for reading from the listener socket if + * there is one + * + * @param is The stream for reading from the listener socket + */ + public void setInputStream( InputStream is ) { + if ( m_thread != null ) { + m_thread.setInputStream( is ); + } + } + + /** + * Gets the stream for writing to the socket + * + * @return The stream for writing to the socket, or + * null if there is none + */ + public OutputStream getOutputStream() { + return (m_thread != null) ? m_thread.getOutputStream() : null; + } + + /** + * Sets the stream for writing to the socket + * + * @param os The stream for writing to the socket, if there is one + */ + public void setOutputStream( OutputStream os ) { + if ( m_thread != null ) { + m_thread.setOutputStream( os ); + } } /** @@ -3321,17 +4323,18 @@ public class LDAPConnection implements LDAPv3, Cloneable { * synchronous. * @return response listener object */ - private synchronized LDAPResponseListener getResponseListener () { - if (responseListeners == null) - responseListeners = new Vector (5); + synchronized LDAPResponseListener getResponseListener () { + if (m_responseListeners == null) { + m_responseListeners = new Vector (5); + } LDAPResponseListener l; - if ( responseListeners.size() < 1 ) { - l = new LDAPResponseListener ( this ); + if ( m_responseListeners.size() < 1 ) { + l = new LDAPResponseListener ( /*asynchOp=*/false ); } else { - l = (LDAPResponseListener)responseListeners.elementAt (0); - responseListeners.removeElementAt (0); + l = (LDAPResponseListener)m_responseListeners.elementAt (0); + m_responseListeners.removeElementAt (0); } return l; } @@ -3343,19 +4346,18 @@ public class LDAPConnection implements LDAPv3, Cloneable { * @return A search response listener object */ private synchronized LDAPSearchListener getSearchListener ( - LDAPSearchConstraints cons ) - { - if (searchListeners == null) { - searchListeners = new Vector (5); + LDAPSearchConstraints cons ) { + if (m_searchListeners == null) { + m_searchListeners = new Vector (5); } LDAPSearchListener l; - if ( searchListeners.size() < 1 ) { - l = new LDAPSearchListener ( this, cons ); + if ( m_searchListeners.size() < 1 ) { + l = new LDAPSearchListener ( /*asynchOp=*/false, cons ); } else { - l = (LDAPSearchListener)searchListeners.elementAt (0); - searchListeners.removeElementAt (0); + l = (LDAPSearchListener)m_searchListeners.elementAt (0); + m_searchListeners.removeElementAt (0); } return l; } @@ -3366,13 +4368,13 @@ public class LDAPConnection implements LDAPv3, Cloneable { * synchronous. * @param l Listener to buffer */ - private synchronized void releaseResponseListener (LDAPResponseListener l) - { - if (responseListeners == null) - responseListeners = new Vector (5); + synchronized void releaseResponseListener (LDAPResponseListener l) { + if (m_responseListeners == null) { + m_responseListeners = new Vector (5); + } l.reset (); - responseListeners.addElement (l); + m_responseListeners.addElement (l); } /** @@ -3381,13 +4383,13 @@ public class LDAPConnection implements LDAPv3, Cloneable { * operations synchronous. * @param Listener to buffer */ - synchronized void releaseSearchListener (LDAPSearchListener l) - { - if (searchListeners == null) - searchListeners = new Vector (5); + synchronized void releaseSearchListener (LDAPSearchListener l) { + if (m_searchListeners == null) { + m_searchListeners = new Vector (5); + } l.reset (); - searchListeners.addElement (l); + m_searchListeners.addElement (l); } /** @@ -3397,33 +4399,37 @@ public class LDAPConnection implements LDAPv3, Cloneable { * @param m Server response to validate * @exception LDAPException failed to check message */ - void checkMsg (JDAPMessage m) throws LDAPException { + void checkMsg (LDAPMessage m) throws LDAPException { if (m.getProtocolOp() instanceof JDAPResult) { JDAPResult response = (JDAPResult)(m.getProtocolOp()); int resultCode = response.getResultCode (); - if (resultCode == JDAPResult.SUCCESS) + if (resultCode == JDAPResult.SUCCESS) { return; + } - if (resultCode == JDAPResult.REFERRAL) + if (resultCode == JDAPResult.REFERRAL) { throw new LDAPReferralException ("referral", resultCode, - response.getReferrals()); + response.getReferrals()); + } - if (resultCode == JDAPResult.LDAP_PARTIAL_RESULTS) + if (resultCode == JDAPResult.LDAP_PARTIAL_RESULTS) { throw new LDAPReferralException ("referral", resultCode, - response.getErrorMessage()); - else + response.getErrorMessage()); + } else { throw new LDAPException ("error result", resultCode, response.getErrorMessage(), response.getMatchedDN()); + } } else if (m.getProtocolOp() instanceof JDAPSearchResultReference) { String[] referrals = ((JDAPSearchResultReference)m.getProtocolOp()).getUrls(); throw new LDAPReferralException ("referral", - JDAPResult.SUCCESS, referrals); - } else + JDAPResult.SUCCESS, referrals); + } else { return; + } } /** @@ -3437,7 +4443,7 @@ public class LDAPConnection implements LDAPv3, Cloneable { * @param current The target thread. * @param con The server response controls. */ - void setResponseControls( LDAPConnThread current, ResponseControl con ) { + void setResponseControls( LDAPConnThread current, LDAPResponseControl con ) { synchronized(m_responseControlTable) { Vector v = (Vector)m_responseControlTable.get(current); @@ -3447,18 +4453,19 @@ public class LDAPConnection implements LDAPv3, Cloneable { // look at each response control for (int i=v.size()-1; i>=0; i--) { - ResponseControl response = - (ResponseControl)v.elementAt(i); + LDAPResponseControl response = + (LDAPResponseControl)v.elementAt(i); // if this response control belongs to this connection if (response.getConnection().equals(this)) { // if the given control is null or // the given control is not null and the current - // control does not correspond to the new JDAPMessage + // control does not correspond to the new LDAPMessage if ((con == null) || - (con.getMsgID() != response.getMsgID())) + (con.getMsgID() != response.getMsgID())) { v.removeElement(response); + } // For the same connection, if the message id from the // given control is the same as the one in the queue, @@ -3469,8 +4476,9 @@ public class LDAPConnection implements LDAPv3, Cloneable { } } } else { - if (con != null) + if (con != null) { v = new Vector(); + } } if (con != null) { @@ -3491,8 +4499,9 @@ public class LDAPConnection implements LDAPv3, Cloneable { } } /* Make sure we're registered */ - if ( m_attachedList.indexOf( current ) < 0 ) + if ( m_attachedList.indexOf( current ) < 0 ) { m_attachedList.addElement( current ); + } } /** @@ -3502,16 +4511,16 @@ public class LDAPConnection implements LDAPv3, Cloneable { * @return new LDAPConnection, already connected and authenticated */ private LDAPConnection prepareReferral( LDAPUrl u, - LDAPSearchConstraints cons ) + LDAPConstraints cons ) throws LDAPException { LDAPConnection connection = new LDAPConnection (this.getSocketFactory()); connection.setOption(REFERRALS, new Boolean(true)); - connection.setOption(REFERRALS_REBIND_PROC, - cons.getRebindProc()); - + connection.setOption(REFERRALS_REBIND_PROC, cons.getRebindProc()); + connection.setOption(BIND, cons.getBindProc()); + // need to set the protocol version which gets passed to connection connection.setOption(PROTOCOL_VERSION, - new Integer(protocolVersion)); + new Integer(m_protocolVersion)); connection.setOption(REFERRALS_HOP_LIMIT, new Integer(cons.getHopLimit()-1)); @@ -3528,10 +4537,11 @@ public class LDAPConnection implements LDAPv3, Cloneable { * @return The new LDAPConnection to the referred server */ LDAPConnection createReferralConnection(LDAPReferralException e, - LDAPSearchConstraints cons) throws LDAPException { - if (cons.getHopLimit() <= 0) + LDAPConstraints cons) throws LDAPException { + if (cons.getHopLimit() <= 0) { throw new LDAPException("exceed hop limit", - e.getLDAPResultCode(), e.getLDAPErrorMessage()); + e.getLDAPResultCode(), e.getLDAPErrorMessage()); + } if (!cons.getReferrals()) { throw e; } @@ -3539,60 +4549,68 @@ public class LDAPConnection implements LDAPv3, Cloneable { LDAPUrl u[] = e.getURLs(); // If there are no referrals (because the server isn't set up for // them), give up here - if ((u == null) || (u.length <= 0) || (u[0].equals(""))) + if ((u == null) || (u.length <= 0) || (u[0].equals(""))) { throw new LDAPException("No target URL in referral", - LDAPException.NO_RESULTS_RETURNED); + LDAPException.NO_RESULTS_RETURNED); + } LDAPConnection connection = null; connection = prepareReferral(u[0], cons); String DN = u[0].getDN(); - if ((DN == null) || (DN.equals(""))) - DN = boundDN; - connection.authenticate(protocolVersion, DN, boundPasswd); + if ((DN == null) || (DN.equals(""))) { + DN = m_boundDN; + } + connection.authenticate(m_protocolVersion, DN, m_boundPasswd); return connection; } /** - * Performs the referral. + * Follows a referral. * @param e referral exception * @param cons search constraints */ void performReferrals(LDAPReferralException e, - LDAPSearchConstraints cons, int ops, - /* unions of different operation parameters */ - String dn, int scope, String filter, String types[], - boolean attrsOnly, LDAPModification mods[], LDAPEntry entry, - LDAPAttribute attr, - /* result */ - Vector results + LDAPConstraints cons, int ops, + /* unions of different operation parameters */ + String dn, int scope, String filter, String types[], + boolean attrsOnly, LDAPModification mods[], + LDAPEntry entry, + LDAPAttribute attr, + /* result */ + Vector results ) throws LDAPException { - if (cons.getHopLimit() <= 0) + if (cons.getHopLimit() <= 0) { throw new LDAPException("exceed hop limit", - e.getLDAPResultCode(), e.getLDAPErrorMessage()); + e.getLDAPResultCode(), + e.getLDAPErrorMessage()); + } if (!cons.getReferrals()) { if (ops == JDAPProtocolOp.SEARCH_REQUEST) { LDAPSearchResults res = new LDAPSearchResults(); res.add(e); results.addElement(res); return; - } else + } else { throw e; + } } LDAPUrl u[] = e.getURLs(); - // If there are no referrals (because the server isn't set up for - // them), give up here - if ( u == null ) + // If there are no referrals (because the server isn't configured to + // return one), give up here + if ( u == null ) { return; + } for (int i = 0; i < u.length; i++) { String newDN = u[i].getDN(); String DN = null; - if ((newDN == null) || (newDN.equals(""))) + if ((newDN == null) || (newDN.equals(""))) { DN = dn; - else + } else { DN = newDN; + } // If this was a one-level search, and a direct subordinate // has a referral, there will be a "?base" in the URL, and // we have to rewrite the scope from one to base @@ -3606,40 +4624,43 @@ public class LDAPConnection implements LDAPv3, Cloneable { try { if ((m_referralConnection != null) && - (u[i].getHost().equals(m_referralConnection.host)) && - (u[i].getPort() == m_referralConnection.port)) { + (u[i].getHost().equals(m_referralConnection.getHost())) && + (u[i].getPort() == m_referralConnection.getPort())) { performReferrals(m_referralConnection, newcons, ops, DN, - scope, filter, types, attrsOnly, mods, entry, attr, - results); + scope, filter, types, attrsOnly, mods, + entry, attr, results); continue; } } catch (LDAPException le) { if (le.getLDAPResultCode() != - LDAPException.INSUFFICIENT_ACCESS_RIGHTS) { + LDAPException.INSUFFICIENT_ACCESS_RIGHTS) { throw le; } } LDAPConnection connection = null; connection = prepareReferral( u[i], cons ); - if (cons.getRebindProc() == null) { + if (cons.getRebindProc() == null && cons.getBindProc() == null) { connection.authenticate (null, null); - } else { + } else if (cons.getBindProc() == null) { LDAPRebindAuth auth = cons.getRebindProc().getRebindAuthentication(u[i].getHost(), - u[i].getPort()); + u[i].getPort()); connection.authenticate(auth.getDN(), auth.getPassword()); + } else { + cons.getBindProc().bind(connection); } performReferrals(connection, newcons, ops, DN, scope, filter, - types, attrsOnly, mods, entry, attr, results); + types, attrsOnly, mods, entry, attr, results); } } void performReferrals(LDAPConnection connection, - LDAPSearchConstraints cons, int ops, String dn, int scope, - String filter, String types[], boolean attrsOnly, - LDAPModification mods[], LDAPEntry entry, LDAPAttribute attr, - Vector results) throws LDAPException { + LDAPConstraints cons, int ops, String dn, int scope, + String filter, String types[], boolean attrsOnly, + LDAPModification mods[], LDAPEntry entry, + LDAPAttribute attr, + Vector results) throws LDAPException { LDAPSearchResults res = null; try { @@ -3647,7 +4668,8 @@ public class LDAPConnection implements LDAPv3, Cloneable { case JDAPProtocolOp.SEARCH_REQUEST: res = connection.search(dn, scope, filter, - types, attrsOnly, cons); + types, attrsOnly, + (LDAPSearchConstraints)cons); if (res != null) { res.closeOnCompletion(connection); results.addElement(res); @@ -3684,35 +4706,39 @@ public class LDAPConnection implements LDAPv3, Cloneable { throw ee; } finally { if ((connection != null) && - ((ops != JDAPProtocolOp.SEARCH_REQUEST) || (res == null)) && + ((ops != JDAPProtocolOp.SEARCH_REQUEST) || (res == null)) && ((m_referralConnection == null) || - (!connection.equals(m_referralConnection)))) + (!connection.equals(m_referralConnection)))) { connection.disconnect(); + } } } /** - * Performs the referral. + * Follows a referral for an extended operation. * @param e referral exception * @param cons search constraints */ private LDAPExtendedOperation performExtendedReferrals( LDAPReferralException e, - LDAPSearchConstraints cons, LDAPExtendedOperation op ) + LDAPConstraints cons, LDAPExtendedOperation op ) throws LDAPException { - if (cons.getHopLimit() <= 0) + if (cons.getHopLimit() <= 0) { throw new LDAPException("exceed hop limit", - e.getLDAPResultCode(), e.getLDAPErrorMessage()); + e.getLDAPResultCode(), + e.getLDAPErrorMessage()); + } if (!cons.getReferrals()) { throw e; } LDAPUrl u[] = e.getURLs(); - // If there are no referrals (because the server isn't set up for - // them), give up here - if ( u == null ) + // If there are no referrals (because the server isn't configured to + // return one), give up here + if ( u == null ) { return null; + } for (int i = 0; i < u.length; i++) { try { @@ -3729,11 +4755,11 @@ public class LDAPConnection implements LDAPv3, Cloneable { } /** - * Creates and returns an new LDAPConnection object that + * Creates and returns a new LDAPConnection object that * contains the same information as the current connection, including: *
      *
    • the default search constraints - *
    • host name and port number of the LAAP server + *
    • host name and port number of the LDAP server *
    • the DN and password used to authenticate to the LDAP server *
    *

    @@ -3744,36 +4770,34 @@ public class LDAPConnection implements LDAPv3, Cloneable { try { LDAPConnection c = (LDAPConnection)super.clone(); - if (this.th == null) - this.bind(defaultConstraints); - - c.defaultConstraints = - (LDAPSearchConstraints)defaultConstraints.clone(); - c.responseListeners = null; - c.searchListeners = null; - c.bound = this.bound; - c.host = this.host; - c.port = this.port; - c.boundDN = this.boundDN; - c.boundPasswd = this.boundPasswd; - c.prevBoundDN = this.prevBoundDN; - c.prevBoundPasswd = this.prevBoundPasswd; - c.m_anonymousBound = this.m_anonymousBound; - c.m_factory = this.m_factory; - c.th = this.th; /* share current connection thread */ - - synchronized (m_threadConnTable) { - Vector v = (Vector)m_threadConnTable.get(this.th); - if (v != null) { - v.addElement(c); - } - else { - printDebug("Failed to clone"); - return null; - } + if (!isConnected()) { + this.internalBind(m_defaultConstraints); } - c.th.register(c); + c.m_defaultConstraints = + (LDAPSearchConstraints)m_defaultConstraints.clone(); + c.m_responseListeners = null; + c.m_searchListeners = null; + c.m_bound = this.m_bound; + c.m_connMgr = m_connMgr; + c.m_connSetupDelay = m_connSetupDelay; + c.m_boundDN = this.m_boundDN; + c.m_boundPasswd = this.m_boundPasswd; + c.m_prevBoundDN = this.m_prevBoundDN; + c.m_prevBoundPasswd = this.m_prevBoundPasswd; + c.m_anonymousBound = this.m_anonymousBound; + c.m_factory = this.m_factory; + c.m_thread = this.m_thread; /* share current connection thread */ + + Vector v = (Vector)m_threadConnTable.get(this.m_thread); + if (v != null) { + v.addElement(c); + } else { + printDebug("Failed to clone"); + return null; + } + + c.m_thread.register(c); return c; } catch (Exception e) { } @@ -3786,8 +4810,8 @@ public class LDAPConnection implements LDAPv3, Cloneable { * is waiting for the backlog to lighten. */ void resultRetrieved() { - if ( th != null ) { - th.resultRetrieved(); + if ( m_thread != null ) { + m_thread.resultRetrieved(); } } @@ -3808,6 +4832,9 @@ public class LDAPConnection implements LDAPv3, Cloneable { args[0] = new String("UniversalConnect"); m.invoke( null, args); printDebug( "UniversalConnect enabled" ); + args[0] = new String("UniversalPropertyRead"); + m.invoke( null, args); + printDebug( "UniversalPropertyRead enabled" ); return true; } catch (LDAPException e) { printDebug("Exception: "+e.toString()); @@ -3853,557 +4880,5 @@ public class LDAPConnection implements LDAPv3, Cloneable { public static final int MAXBACKLOG = 30; private static boolean isCommunicator = checkCommunicator(); - private static final boolean debug = false; + private static boolean debug = false; } - -/* - * This object represents the value of the m_responseControlTable hashtable. - * It stores the response controls and its corresponding LDAPConnection and - * the message ID for its corresponding JDAPMessage. - */ -class ResponseControl { - private LDAPConnection m_connection; - private int m_messageID; - private LDAPControl[] m_controls; - - public ResponseControl(LDAPConnection conn, int msgID, - LDAPControl[] controls) { - - m_connection = conn; - m_messageID = msgID; - m_controls = new LDAPControl[controls.length]; - for (int i=0; i 0) ) { - JDAPControl[] jc = new JDAPControl[lc.length]; - for( int i = 0; i < lc.length; i++ ) - jc[i] = new JDAPControl( lc[i].getID(), - lc[i].isCritical(), - lc[i].getValue() ); - msg = new JDAPMessage(allocateId(), request, jc); - } - else - msg = new JDAPMessage(allocateId(), request); - if ( toNotify != null ) { - if (!(request instanceof JDAPAbandonRequest || - request instanceof JDAPUnbindRequest)) { - /* Only worry about toNotify if we expect a response... */ - this.m_requests.put (new Integer (msg.getId()), toNotify); - /* Notify the backlog checker that there may be another outstanding - request */ - resultRetrieved(); - } - toNotify.setID( msg.getId() ); - } - - try { - msg.write (m_serverOutput); - m_serverOutput.flush (); - } catch (IOException e) { - networkError(e); - } - } - - /** - * Register with this connection thread. - * @param conn LDAP connection - */ - public synchronized void register(LDAPConnection conn) { - if (!m_registered.contains(conn)) - m_registered.addElement(conn); - } - - void setSecurityLayer(Object layer) { - m_securityLayer = layer; - } - - synchronized int getClientCount() { - return m_registered.size(); - } - - /** - * De-Register with this connection thread. If all the connection - * is deregistered. Then, this thread should be killed. - * @param conn LDAP connection - */ - public synchronized void deregister(LDAPConnection conn) { - m_registered.removeElement(conn); - if (m_registered.size() == 0) { - try { - LDAPSearchConstraints cons = conn.getSearchConstraints(); - sendRequest (new JDAPUnbindRequest (), null, cons); - cleanUp(); - this.stop(); - this.sleep(100); /* give enough time for threads to shutdown */ - } catch (Exception e) { - LDAPConnection.printDebug(e.toString()); - } - } - } - - /** - * Clean ups before shutdown the thread. - */ - private void cleanUp() { - if (!m_disconnected) { - try { - m_serverOutput.close (); - } catch (Exception e) { - } finally { - m_serverOutput = null; - } - - try { - m_serverInput.close (); - } catch (Exception e) { - } finally { - m_serverInput = null; - } - - try { - m_socket.close (); - } catch (Exception e) { - } finally { - m_socket = null; - } - - m_disconnected = true; - - /** - * Notify all the registered about this bad moment. - * IMPORTANT: This needs to be done at last. Otherwise, the socket - * input stream and output stream might never get closed and the whole - * task will get stuck in the stop method when we tried to stop the - * LDAPConnThread. - */ - - if (m_registered != null) { - Vector registerCopy = (Vector)m_registered.clone(); - - Enumeration cancelled = registerCopy.elements(); - - while (cancelled.hasMoreElements ()) { - LDAPConnection c = (LDAPConnection)cancelled.nextElement(); - c.deregisterConnection(); - } - } - m_registered = null; - m_messages = null; - m_requests.clear(); - } - } - - /** - * Set and get the maximum number of unread entries any search listener can - * have before we stop reading from the server. - */ - void setMaxBacklog( int backlog ) { - m_maxBacklog = backlog; - } - - int getMaxBacklog() { - return m_maxBacklog; - } - - /** - * Sleep if there is a backlog of search results - */ - private void checkBacklog() { - boolean doWait; - do { - doWait = false; - Enumeration listeners = m_requests.elements(); - while( listeners.hasMoreElements() ) { - LDAPResponseListener l = - (LDAPResponseListener)listeners.nextElement(); - // If there are any threads waiting for a regular response - // message, we have to go read the next incoming message - if ( !(l instanceof LDAPSearchListener) ) { - doWait = false; - break; - } - // If the backlog of any search thread is too great, - // wait for it to diminish, but if this is a synchronous - // search, then just keep reading - LDAPSearchListener sl = (LDAPSearchListener)l; - if ( (sl.getConstraints().getBatchSize() != 0) && - (sl.getCount() >= m_maxBacklog) ) { - doWait = true; - } - } - if ( doWait ) { - synchronized(this) { - try { - wait(); - } catch (InterruptedException e ) { - } - } - } - } while ( doWait ); - } - - /** - * This is called when a search result has been retrieved from the incoming - * queue. We use the notification to unblock the listener thread, if it - * is waiting for the backlog to lighten. - */ - synchronized void resultRetrieved() { - notifyAll(); - } - - /** - * Reads from the LDAP server input stream for incoming LDAP messages. - */ - public void run() { - // if there is a problem of establishing connection to the server, - // stop the thread right away... - if (m_failed) - this.stop(); - - JDAPMessage msg = null; - JDAPBERTagDecoder decoder = new JDAPBERTagDecoder(); - - while (true) { - yield(); - int[] nread = new int[1]; - nread[0] = 0; - // Avoid too great a backlog of results - checkBacklog(); - try { - BERElement element = BERElement.getElement(decoder, m_serverInput, - nread); - msg = new JDAPMessage(element); - - // passed in the ber element size to approximate the size of the cache - // entry, thereby avoiding serialization of the entry stored in the - // cache - processResponse (msg, nread[0]); - } catch (Exception e) { - networkError(e); - } - - if (m_disconnected) - break; - } - } - - /** - * Allocates a new LDAP message ID. These are arbitrary numbers used to - * correlate client requests with server responses. - * @return new unique msgId - */ - private synchronized int allocateId () { - m_highMsgId = (m_highMsgId + 1) % MAXMSGID; - return m_highMsgId; - } - - /** - * When a response arrives from the LDAP server, it is processed by - * this routine. It will pass the message on to the listening object - * associated with the LDAP msgId. - * @param incoming New message from LDAP server - */ - private synchronized void processResponse (JDAPMessage incoming, int size) { - Integer messageID = new Integer (incoming.getId()); - LDAPResponseListener l = (LDAPResponseListener)m_requests.get (messageID); - - if (l == null) return; /* nobody is waiting for this response (!) */ - - - /* Were there any controls for this client? */ - LDAPControl[] con = checkControls( incoming ); - ResponseControl responseControl = null; - if (con != null) - responseControl = new ResponseControl(l.getConnection(), - incoming.getId(), con); - l.getConnection().setResponseControls( this, responseControl ); - - JDAPProtocolOp op = incoming.getProtocolOp (); - - Vector v = null; - - - if ((op instanceof JDAPSearchResponse) || - (op instanceof JDAPSearchResultReference)) { - - ((LDAPSearchListener)l).addSearchResult (incoming); - Long key = ((LDAPSearchListener)l).getKey(); - - if ((m_cache != null) && (key != null)) { - // get the vector containing the JDAPmessages for the specified messageID - v = (Vector)m_messages.get(messageID); - - if (v == null) - { - v = new Vector(); - // keeps track of the total size of all JDAPMessages belonging to the - // same messageID, now the size is 0 - v.addElement(new Long(0)); - } - - // add the size of the current JDAPmessage to the lump sum - // assume the size of JDAPMessage is more or less the same as the size - // of LDAPEntry. Eventually LDAPEntry object gets stored in the cache - // instead of JDAPMessage object. - long entrySize = ((Long)v.firstElement()).longValue() + size; - - // update the lump sum located in the first element of the vector - v.setElementAt(new Long(entrySize), 0); - - // convert JDAPMessage object into LDAPEntry which is stored to the - // end of the Vector - v.addElement(constructLDAPEntry(incoming)); - - // replace the entry - m_messages.put(messageID, v); - } - } else { - l.setResponse (incoming); - if (l instanceof LDAPSearchListener) { - Long key = ((LDAPSearchListener)l).getKey(); - - if (key != null) { - boolean fail = false; - JDAPProtocolOp protocolOp = incoming.getProtocolOp(); - if (protocolOp instanceof JDAPSearchResult) { - JDAPResult res = (JDAPResult)protocolOp; - if (res.getResultCode() > 0) - fail = true; - } - - if ((!fail) && (m_cache != null)) { - - // that is. Collects all the JDAPMessages for the specified messageID - // no need to keep track of this entry. Remove it. - v = (Vector)m_messages.remove(messageID); - - // If v is null, meaning there are no search results from the - // server - if (v == null) { - v = new Vector(); - - // set the entry size to be 0 - v.addElement(new Long(0)); - } - - try { - // add the new entry with key and value (a vector of - // LDAPEntry objects) - m_cache.addEntry(key, v); - } catch (LDAPException e) { - System.out.println("Exception: "+e.toString()); - } - } - } - } - m_requests.remove (messageID); - } - } - - /** - * Construct the LDAPEntry from the JDAPMessage object. - * @param msg The JDAPMessage object - * @return The LDAPEntry object required for the cache entry. - */ - private LDAPEntry constructLDAPEntry(JDAPMessage msg) { - JDAPProtocolOp op = msg.getProtocolOp(); - JDAPSearchResponse sr = (JDAPSearchResponse)op; - LDAPAttribute[] lattrs = sr.getAttributes(); - LDAPAttributeSet attrs; - if ( lattrs != null ) - attrs = new LDAPAttributeSet( lattrs ); - else - attrs = new LDAPAttributeSet(); - - String dn = sr.getObjectName(); - return(new LDAPEntry( dn, attrs )); - } - - /** - * Extract controls from message and stash them away for retrieval - * by client. - * @param response The results of an operation, possibly containing - * Controls. - */ - private LDAPControl[] checkControls( JDAPMessage response ) { - LDAPControl[] con = null; - if ( response != null ) { - JDAPControl[] jc = response.getControls(); - if ( (jc != null) && (jc.length > 0) ) { - con = new LDAPControl[jc.length]; - for( int i = 0; i < jc.length; i++ ) - con[i] = new LDAPControl( jc[i].getID(), - jc[i].isCritical(), - jc[i].getValue() ); - } - } - return con; - } - - /** - * Stop dispatching responses for a particular message ID. - * @param id Message ID for which to discard responses. - */ - void abandon (int id ) { - m_requests.remove( new Integer(id) ); - } - - /** - * Handles network errors. Basically shuts down the whole connection. - * @param e The exception which was caught while trying to read from - * input stream. - */ - private synchronized void networkError (Exception e) { - try { - - // notify each listener that the server is down. - Enumeration requests = m_requests.elements(); - while (requests.hasMoreElements()) { - LDAPResponseListener listener = - (LDAPResponseListener)requests.nextElement(); - listener.setException(new LDAPException("Server down", LDAPException.OTHER)); - } - cleanUp(); - - } catch (NullPointerException ee) { - System.err.println("Exception: "+ee.toString()); - } - - /** - * Notify all the registered about this bad moment. - * IMPORTANT: This needs to be done at last. Otherwise, the socket - * input stream and output stream might never get closed and the whole - * task will get stuck in the stop method when we tried to stop the - * LDAPConnThread. - */ - - if (m_registered != null) { - Vector registerCopy = (Vector)m_registered.clone(); - - Enumeration cancelled = registerCopy.elements(); - - while (cancelled.hasMoreElements ()) { - LDAPConnection c = (LDAPConnection)cancelled.nextElement(); - c.deregisterConnection(); - } - } - } -} - diff --git a/mozilla/directory/java-sdk/ldapjdk/netscape/ldap/LDAPConstraints.java b/mozilla/directory/java-sdk/ldapjdk/netscape/ldap/LDAPConstraints.java new file mode 100644 index 00000000000..4a96e3f7506 --- /dev/null +++ b/mozilla/directory/java-sdk/ldapjdk/netscape/ldap/LDAPConstraints.java @@ -0,0 +1,344 @@ +/* -*- 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 netscape.ldap; + +/** + * Represents a set of operation preferences. + * You can set these preferences for a particular operation + * by creating an LDAPConstraints object, + * specifying your preferences, and passing the object to + * the proper LDAPConnection method. + *

    + * + * @version 1.0 + */ +public class LDAPConstraints implements Cloneable { + + private int m_hop_limit; + private LDAPBind m_bind_proc; + private LDAPRebind m_rebind_proc; + private boolean referrals; + private int m_time_limit; + private LDAPControl[] m_clientControls; + private LDAPControl[] m_serverControls; + + /** + * Constructs an LDAPConstraints object that specifies + * the default set of constraints. + */ + public LDAPConstraints() { + m_time_limit = 0; + referrals = false; + m_bind_proc = null; + m_rebind_proc = null; + m_hop_limit = 5; + m_clientControls = null; + m_serverControls = null; + } + + /** + * Constructs a new LDAPConstraints object and allows you + * to specify the constraints in that object. + *

    + * @param msLimit Maximum time in milliseconds to wait for results (0 + * by default, which means that there is no maximum time limit) + * @param doReferrals Specify true to follow referrals + * automatically, or False to throw an + * LDAPReferralException error if the server sends back + * a referral (False by default) + * @param rebind_proc Specifies the object that + * implements the LDAPRebind interface (you need to + * define this class). The object will be used when the client + * follows referrals automatically. The object provides the client + * with a method for getting the distinguished name and password + * used to authenticate to another LDAP server during a referral. + * (This field is null by default.) + * @param hop_limit Maximum number of referrals to follow in a + * sequence when attempting to resolve a request. + * @see netscape.ldap.LDAPConnection#setOption(int, java.lang.Object) + */ + public LDAPConstraints( int msLimit, boolean doReferrals, + LDAPRebind rebind_proc, int hop_limit) { + m_time_limit = msLimit; + referrals = doReferrals; + m_bind_proc = null; + m_rebind_proc = rebind_proc; + m_hop_limit = hop_limit; + m_clientControls = null; + m_serverControls = null; + } + + /** + * Constructs a new LDAPConstraints object and allows you + * to specify the constraints in that object. + *

    + * @param msLimit Maximum time in milliseconds to wait for results (0 + * by default, which means that there is no maximum time limit) + * @param doReferrals Specify true to follow referrals + * automatically, or False to throw an + * LDAPReferralException error if the server sends back + * a referral (False by default) + * @param bind_proc Specifies the object that + * implements the LDAPBind interface (you need to + * define this class). The object will be used to authenticate to the + * server on referrals. (This field is null by default.) + * @param hop_limit Maximum number of referrals to follow in a + * sequence when attempting to resolve a request. + * @see netscape.ldap.LDAPConnection#setOption(int, java.lang.Object) + */ + public LDAPConstraints( int msLimit, boolean doReferrals, + LDAPBind bind_proc, int hop_limit) { + m_time_limit = msLimit; + referrals = doReferrals; + m_bind_proc = bind_proc; + m_rebind_proc = null; + m_hop_limit = hop_limit; + m_clientControls = null; + m_serverControls = null; + } + + /** + * Returns the maximum number of milliseconds to wait for any operation + * under these constraints. If 0, there is no maximum time limit + * on waiting for the operation results. + * @return Maximum number of milliseconds to wait for operation results. + */ + public int getTimeLimit() { + return m_time_limit; + } + + + /** + * Specifies whether nor not referrals are followed automatically. + * Returns true if referrals are to be followed automatically, + * or false if referrals throw an + * LDAPReferralException. + * @return true if referrals are followed automatically, + * False if referrals throw an + * LDAPReferralException. + */ + public boolean getReferrals() { + return referrals; + } + + /** + * Returns the object that provides the mechanism for authenticating to the + * server on referrals. This object must implement the LDAPBind + * interface. + * @return Object to be used to authenticate to the server on referrals. + * @see netscape.ldap.LDAPBind + */ + public LDAPBind getBindProc() { + return m_bind_proc; + } + + /** + * Returns the object that provides the method for getting + * authentication information. This object must + * implement the LDAPRebind interface. + * @return Object to be used to obtain information for + * authenticating to other LDAP servers during referrals. + * @see netscape.ldap.LDAPRebind + * @see netscape.ldap.LDAPRebindAuth + */ + public LDAPRebind getRebindProc() { + return m_rebind_proc; + } + + /** + * Returns the maximum number of hops to follow during a referral. + * @return Maximum number of hops to follow during a referral. + */ + public int getHopLimit() { + return m_hop_limit; + } + + /** + * Returns any client controls to be applied by the client + * to LDAP operations. + * @return Client controls to be applied by the client to LDAP operations. + * @see netscape.ldap.LDAPControl + * @see netscape.ldap.LDAPConnection#getOption + * @see netscape.ldap.LDAPConnection#setOption + */ + public LDAPControl[] getClientControls() { + return m_clientControls; + } + + /** + * Returns any server controls to be applied by the server + * to LDAP operations. + * @return Server controls to be applied by the server to LDAP operations. + * @see netscape.ldap.LDAPControl + * @see netscape.ldap.LDAPConnection#getOption + * @see netscape.ldap.LDAPConnection#setOption + */ + public LDAPControl[] getServerControls() { + return m_serverControls; + } + + /** + * Sets the maximum number of milliseconds to wait for any operation + * under these constraints. If 0, there is no maximum time limit + * on waiting for the operation results. + * @param msLimit Maximum number of milliseconds to wait for operation + * results. + * (0 by default, which means that there is no maximum time limit.) + */ + public void setTimeLimit( int msLimit ) { + m_time_limit = msLimit; + } + + + /** + * Specifies whether nor not referrals are followed automatically. + * Returns true if referrals are to be followed automatically, + * or false if referrals throw an + * LDAPReferralException. + * (By default, this is set to false.) + *

    + * If you set this to true, you need to create an object of + * this class that implements either the LDAPRebind or + * LDAPBind interface. The LDAPRebind object + * identifies the method for retrieving authentication information which + * will be used when connecting to other LDAP servers during referrals. + * This object should be passed to the setRebindProc method. + * Alternatively, the LDAPBind object identifies an + * authentication mechanism to be used instead of the default + * authentication mechanism when following referrals. This + * object should be passed to the setBindProc method. + * @param doReferrals Set to true if referrals should be + * followed automatically, or False if referrals should throw + * an LDAPReferralException. + * @see netscape.ldap.LDAPBind + * @see netscape.ldap.LDAPRebind + * @see netscape.ldap.LDAPRebindAuth + */ + public void setReferrals( boolean doReferrals ) { + referrals = doReferrals; + } + + /** + * Sets the object that provides the mechanism for authenticating + * to the server on referrals. This object must implement + * the LDAPBind interface.(By default, this is + * null.) This method sets the LDAPRebind + * object to null for this constraint. + * @param bind_proc Object to be used to authenticate to the server + * on referrals. + * @see netscape.ldap.LDAPBind + */ + public void setBindProc( LDAPBind bind_proc ) { + m_bind_proc = bind_proc; + if (bind_proc != null) { + m_rebind_proc = null; + } + } + + /** + * Specifies the object that provides the method for getting + * authentication information. This object must belong to a class + * that implements the LDAPRebind interface. + * (By default, this is null.) This method sets the + * LDAPBind object to null for this constraint. + * @param rebind_proc Object to be used to obtain information for + * authenticating to other LDAP servers during referrals. + */ + public void setRebindProc( LDAPRebind rebind_proc ) { + m_rebind_proc = rebind_proc; + if (rebind_proc != null) { + m_bind_proc = null; + } + } + + /** + * Sets maximum number of hops to follow in sequence during a referral. + * (By default, this is 5.) + * @param hop_limit Maximum number of hops to follow during a referral. + */ + public void setHopLimit( int hop_limit ) { + m_hop_limit = hop_limit; + } + + /** + * Sets a client control for LDAP operations. + * @param control Client control for LDAP operations. + * @see netscape.ldap.LDAPControl + */ + public void setClientControls( LDAPControl control ) { + m_clientControls = new LDAPControl[1]; + m_clientControls[0] = control; + } + + /** + * Sets an array of client controls for LDAP operations. + * @param controls Array of client controls for LDAP operations. + * @see netscape.ldap.LDAPControl + */ + public void setClientControls( LDAPControl[] controls ) { + m_clientControls = controls; + } + + /** + * Sets a server control for LDAP operations. + * @param control Server control for LDAP operations. + * @see netscape.ldap.LDAPControl + */ + public void setServerControls( LDAPControl control ) { + m_serverControls = new LDAPControl[1]; + m_serverControls[0] = control; + } + + /** + * Sets an array of server controls for LDAP operations. + * @param controls An array of server controls for LDAP operations. + * @see netscape.ldap.LDAPControl + */ + public void setServerControls( LDAPControl[] controls ) { + m_serverControls = controls; + } + + /** + * Makes a copy of an existing set of constraints. + * @returns A copy of an existing set of constraints. + */ + public Object clone() { + LDAPConstraints o = new LDAPConstraints(); + + o.m_time_limit = this.m_time_limit; + o.referrals = this.referrals; + o.m_bind_proc = this.m_bind_proc; + o.m_rebind_proc = this.m_rebind_proc; + o.m_hop_limit = this.m_hop_limit; + if ( (this.m_clientControls != null) && + (this.m_clientControls.length > 0) ) { + o.m_clientControls = new LDAPControl[this.m_clientControls.length]; + for( int i = 0; i < this.m_clientControls.length; i++ ) + o.m_clientControls[i] = + (LDAPControl)this.m_clientControls[i].clone(); + } + if ( (this.m_serverControls != null) && + (this.m_serverControls.length > 0) ) { + o.m_serverControls = new LDAPControl[this.m_serverControls.length]; + for( int i = 0; i < this.m_serverControls.length; i++ ) + o.m_serverControls[i] = + (LDAPControl)this.m_serverControls[i].clone(); + } + return o; + } +} diff --git a/mozilla/directory/java-sdk/ldapjdk/netscape/ldap/LDAPControl.java b/mozilla/directory/java-sdk/ldapjdk/netscape/ldap/LDAPControl.java index 581dd9b9389..2fdcb0b3583 100644 --- a/mozilla/directory/java-sdk/ldapjdk/netscape/ldap/LDAPControl.java +++ b/mozilla/directory/java-sdk/ldapjdk/netscape/ldap/LDAPControl.java @@ -12,13 +12,18 @@ * * 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 + * Copyright (C) 1999 Netscape Communications Corporation. All Rights * Reserved. */ package netscape.ldap; import java.io.*; +import java.util.*; +import java.lang.reflect.*; import netscape.ldap.ber.stream.*; +import netscape.ldap.client.*; +import netscape.ldap.util.*; +import netscape.ldap.controls.*; /** * Represents arbitrary control data that can be used with a @@ -147,10 +152,10 @@ import netscape.ldap.ber.stream.*; * @see netscape.ldap.LDAPConnection#getOption * @see netscape.ldap.LDAPConnection#setOption * @see netscape.ldap.LDAPConnection#getResponseControls - * @see netscape.ldap.LDAPSearchConstraints#getClientControls - * @see netscape.ldap.LDAPSearchConstraints#getServerControls - * @see netscape.ldap.LDAPSearchConstraints#setClientControls - * @see netscape.ldap.LDAPSearchConstraints#setServerControls + * @see netscape.ldap.LDAPConstraints#getClientControls + * @see netscape.ldap.LDAPConstraints#getServerControls + * @see netscape.ldap.LDAPConstraints#setClientControls + * @see netscape.ldap.LDAPConstraints#setServerControls */ public class LDAPControl implements Cloneable { public final static String MANAGEDSAIT = "2.16.840.1.113730.3.4.2"; @@ -176,8 +181,8 @@ public class LDAPControl implements Cloneable { * cancelled when the server does not support this control (in other * words, this control is critical to the LDAP operation). * @param vals Control-specific data. - * @see netscape.ldap.LDAPSearchConstraints#setClientControls - * @see netscape.ldap.LDAPSearchConstraints#setServerControls + * @see netscape.ldap.LDAPConstraints#setClientControls + * @see netscape.ldap.LDAPConstraints#setServerControls */ public LDAPControl(String id, boolean critical, @@ -186,7 +191,7 @@ public class LDAPControl implements Cloneable { m_critical = critical; m_value = vals; } - + /** * Gets the object ID (OID) of the control. * @return Object ID (OID) of the control. @@ -212,6 +217,225 @@ public class LDAPControl implements Cloneable { return m_value; } + /** + * Gets the ber representation of control. + * @return ber representation of control + */ + BERElement getBERElement() { + BERSequence seq = new BERSequence(); + seq.addElement(new BEROctetString (m_oid)); + seq.addElement(new BERBoolean (m_critical)); + if ( (m_value == null) || (m_value.length < 1) ) + seq.addElement(new BEROctetString ((byte[])null)); + else { + seq.addElement(new BEROctetString (m_value, 0, m_value.length)); + } + return seq; + } + + /** + * Associates a class with an oid. This class must be an extension of + * LDAPControl, and should implement the LDAPControl( + * String oid, boolean critical, byte[] value) constructor to + * instantiate the control. + * @param oid The string representation of the oid. + * @param controlClass The class that instantatiates the control associated + * with oid. + * @exception netscape.ldap.LDAPException If the class parameter is not + * a subclass of LDAPControl or the class parameter does not + * implement the LDAPControl(String oid, boolean critical, byte[] value) + * constructor. + */ + public static void register(String oid, Class controlClass) throws + LDAPException { + + if (controlClass == null) { + return; + } + + // 1. make sure controlClass is a subclass of LDAPControl + Class superClass = controlClass; + while (superClass != LDAPControl.class && superClass != null) { + superClass = superClass.getSuperclass(); + } + + if (superClass == null) + throw new LDAPException("controlClass must be a subclass of " + + "LDAPControl", LDAPException.PARAM_ERROR); + + // 2. make sure controlClass has the proper constructor + Class[] cparams = { String.class, boolean.class, byte[].class }; + try { + controlClass.getConstructor(cparams); + } catch (NoSuchMethodException e) { + throw new LDAPException("controlClass does not implement the " + + "correct contstructor", + LDAPException.PARAM_ERROR); + } + + // 3. check if the hash table exists + if (m_controlClassHash == null) { + m_controlClassHash = new Hashtable(); + } + + // 4. add the controlClass + m_controlClassHash.put(oid, controlClass); + } + + /** + * Returns the Class that has been registered to oid. + * @param oid A String that associates the control class to a control. + * @return A Class that can instantiate a control of the + * type specified by oid. + * @see netscape.ldap.LDAPControl#register + * + */ + protected static Class lookupControlClass(String oid) { + if (m_controlClassHash == null) { + return null; + } + + return (Class)m_controlClassHash.get(oid); + } + + /** + * Returns a LDAPControl object instantiated by the Class + * associated by LDAPControl.register to the oid. If + * no Class is found for the given control, or an exception occurs when + * attempting to instantiate the control, a basic LDAPControl + * is instantiated using the parameters. + * @param oid The oid of the control to be instantiated. + * @param critical true if this is a critical control. + * @param value the byte value for the control. + * @return A newly instantiated LDAPControl. + * @see netscape.ldap.LDAPControl#register + */ + protected static LDAPControl createControl(String oid, boolean critical, + byte[] value) { + + Class controlClass = lookupControlClass(oid); + + if (controlClass == null) { + return new LDAPControl(oid, critical, value); + } + + Class[] cparams = { String.class, boolean.class, byte[].class }; + Constructor creator = null; + try { + creator = controlClass.getConstructor(cparams); + } catch (NoSuchMethodException e) { + //shouldn't happen, but... + System.err.println("Caught java.lang.NoSuchMethodException while" + + " attempting to instantiate a control of type " + + oid); + return new LDAPControl(oid, critical, value); + } + + Object[] oparams = { oid, new Boolean(critical), value } ; + LDAPControl returnControl = null; + try { + returnControl = (LDAPControl)creator.newInstance(oparams); + } catch (Exception e) { + String eString = null; + if (e instanceof InvocationTargetException) { + eString = ((InvocationTargetException) + e).getTargetException().toString(); + } else { + eString = e.toString(); + } + + System.err.println("Caught " + eString + " while attempting to" + + " instantiate a control of type " + + oid); + returnControl = new LDAPControl(oid, critical, value); + } + + return returnControl; + } + + /** + * Returns a LDAPControl object instantiated by the Class + * associated by LDAPControl.register to the oid. If + * no Class is found for the given control, or an exception occurs when + * attempting to instantiate the control, a basic LDAPControl + * is instantiated using the parameters. + * @param el The BERElement containing the control. + * @return A newly instantiated LDAPControl. + * @see netscape.ldap.LPAPControl#register + * + * Note: + * This code was extracted from JDAPControl(BERElement el) + * constructor + */ + static LDAPControl parseControl(BERElement el) { + BERSequence s = (BERSequence)el; + String oid = null; + boolean critical = false; + byte[] value = null; + try{ + oid = new String(((BEROctetString)s.elementAt(0)).getValue(), "UTF8"); + } catch(Throwable x) {} + + Object obj = s.elementAt(1); + if (obj instanceof BERBoolean) { + critical = ((BERBoolean)obj).getValue(); + } + else { + value = ((BEROctetString)obj).getValue(); + } + + if (s.size() >= 3) { + value = ((BEROctetString)s.elementAt(2)).getValue(); + } + + return createControl(oid, critical, value); + } + + + /** + * Instantiates all of the controls contained within the LDAP message + * fragment specified by data and returns them in an LDAPControl + * array. This fragment can be either the entire LDAP message or just the + * control section of the message. + *

    + * If an exception occurs when instantiating a control, that control is + * returned as a basic LDAPControl. + * @param data The LDAP message fragment in raw BER format. + * @return A LDAPControl array containing all of the controls + * from the message fragment. + * @exception java.lang.IOException If the data passed to this method + * is not a valid LDAP message fragment. + * @see netscape.ldap.LDAPControl#register + */ + public static LDAPControl[] newInstance(byte[] data) throws IOException { + + int[] bread = { 0 }; + + BERElement el = BERElement.getElement(new JDAPBERTagDecoder(), + new ByteArrayInputStream(data), + bread); + + LDAPControl[] jc = null; + try { + // see if data is a LDAP message + LDAPMessage msg = LDAPMessage.parseMessage(el); + return msg.getControls(); + } catch (IOException e) { + // that didn't work; let's see if its just the controls + BERTag tag = (BERTag)el; + if ( tag.getTag() == (BERTag.CONSTRUCTED|BERTag.CONTEXT|0) ) { + BERSequence controls = (BERSequence)tag.getValue(); + jc = new LDAPControl[controls.size()]; + for (int i = 0; i < controls.size(); i++) { + jc[i] = parseControl(controls.elementAt(i)); + } + } + } + + return jc; + + } + /** * Creates a copy of the control. * @return Copy of the control. @@ -224,6 +448,7 @@ public class LDAPControl implements Cloneable { vals[i] = m_value[i]; } LDAPControl control = new LDAPControl( m_oid, m_critical, vals ); + return control; } @@ -244,8 +469,37 @@ public class LDAPControl implements Cloneable { return outStream.toByteArray(); } + /** + * Return a string representation of the control for debugging + * + * @return A string representation of the control + */ + public String toString() { + String s = getID() + ' ' + isCritical(); + if ( m_value != null ) { + s += ' ' + LDIF.toPrintableString( m_value ); + } + return "LDAPControl {" + s + '}'; + } + private String m_oid; protected boolean m_critical = false; protected byte[] m_value = null; + static private Hashtable m_controlClassHash = null; + static { + try { + LDAPControl.register( LDAPPasswordExpiringControl.EXPIRING, + LDAPPasswordExpiringControl.class ); + LDAPControl.register( LDAPPasswordExpiredControl.EXPIRED, + LDAPPasswordExpiredControl.class ); + LDAPControl.register( LDAPEntryChangeControl.ENTRYCHANGED, + LDAPEntryChangeControl.class ); + LDAPControl.register( LDAPSortControl.SORTRESPONSE, + LDAPSortControl.class ); + LDAPControl.register( LDAPVirtualListResponse.VIRTUALLISTRESPONSE, + LDAPVirtualListResponse.class ); + } catch (LDAPException e) { + } + } } diff --git a/mozilla/directory/java-sdk/ldapjdk/netscape/ldap/LDAPEntry.java b/mozilla/directory/java-sdk/ldapjdk/netscape/ldap/LDAPEntry.java index 2912be4a82e..854f893737e 100644 --- a/mozilla/directory/java-sdk/ldapjdk/netscape/ldap/LDAPEntry.java +++ b/mozilla/directory/java-sdk/ldapjdk/netscape/ldap/LDAPEntry.java @@ -188,11 +188,14 @@ public class LDAPEntry { * @return String representation of the entry's DN and its attributes. */ public String toString() { - String s = "LDAPEntry: "; - if ( dn != null ) - s += dn + "; "; - if ( attrSet != null ) - s += attrSet.toString(); - return s; + StringBuffer sb = new StringBuffer("LDAPEntry: "); + if ( dn != null ) { + sb.append(dn); + sb.append("; "); + } + if ( attrSet != null ) { + sb.append(attrSet.toString()); + } + return sb.toString(); } } diff --git a/mozilla/directory/java-sdk/ldapjdk/netscape/ldap/LDAPException.java b/mozilla/directory/java-sdk/ldapjdk/netscape/ldap/LDAPException.java index 6829c489657..5a781782812 100644 --- a/mozilla/directory/java-sdk/ldapjdk/netscape/ldap/LDAPException.java +++ b/mozilla/directory/java-sdk/ldapjdk/netscape/ldap/LDAPException.java @@ -154,8 +154,8 @@ public class LDAPException extends java.lang.Exception { * method or the LDAPSearchConstraints.setServerTimeLimit * method.

    * - * @see netscape.ldap.LDAPConnection@setOption - * @see netscape.ldap.LDAPSearchConstraints@setServerTimeLimit + * @see netscape.ldap.LDAPConnection#setOption + * @see netscape.ldap.LDAPSearchConstraints#setServerTimeLimit */ public final static int TIME_LIMIT_EXCEEDED = 3; @@ -213,7 +213,7 @@ public class LDAPException extends java.lang.Exception { /** * (9) The LDAP server is referring your client to another * LDAP server. If you set up the LDAPConnection - * options or the LDAPSearchConstraints options + * options or the LDAPConstraints options * for automatic referral, your client will automatically * connect and authenticate to the other LDAP server. * (This LDAPException will not be raised.) @@ -229,7 +229,7 @@ public class LDAPException extends java.lang.Exception { * * If instead you set LDAPConnection.REFERRALS * to false (or if you set - * LDAPSearchConstraints.setReferrals to false, + * LDAPConstraints.setReferrals to false, * an LDAPReferralException is raised. *

    * @@ -239,8 +239,8 @@ public class LDAPException extends java.lang.Exception { *

    * * @see netscape.ldap.LDAPConnection#setOption - * @see netscape.ldap.LDAPSearchConstraints#setReferrals - * @see netscape.ldap.LDAPSearchConstraints#setRebindProc + * @see netscape.ldap.LDAPConstraints#setReferrals + * @see netscape.ldap.LDAPConstraints#setRebindProc * @see netscape.ldap.LDAPRebind * @see netscape.ldap.LDAPRebindAuth * @see netscape.ldap.LDAPReferralException @@ -469,7 +469,7 @@ public class LDAPException extends java.lang.Exception { *

    * * @see netscape.ldap.LDAPConnection#rename(java.lang.String, java.lang.String, java.lang.String, boolean) - * @see netscape.ldap.LDAPConnection#rename(java.lang.String, java.lang.String, java.lang.String, boolean, LDAPSearchConstraints) + * @see netscape.ldap.LDAPConnection#rename(java.lang.String, java.lang.String, java.lang.String, boolean, LDAPConstraints) */ public final static int AFFECTS_MULTIPLE_DSAS = 71; @@ -770,7 +770,7 @@ public class LDAPException extends java.lang.Exception { *

    * * @return String representation of exception - * @see netscape.ldap.LDAPException#errorCodeToString(code) + * @see netscape.ldap.LDAPException#errorCodeToString(int) */ public String toString() { String str = super.toString() + " (" + resultCode + ")" ; diff --git a/mozilla/directory/java-sdk/ldapjdk/netscape/ldap/LDAPExtendedResponse.java b/mozilla/directory/java-sdk/ldapjdk/netscape/ldap/LDAPExtendedResponse.java new file mode 100644 index 00000000000..5f9ea13ce04 --- /dev/null +++ b/mozilla/directory/java-sdk/ldapjdk/netscape/ldap/LDAPExtendedResponse.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) 1999 Netscape Communications Corporation. All Rights + * Reserved. + */ +package netscape.ldap; + +import netscape.ldap.client.opers.JDAPExtendedResponse; + +/** + * Represents a server response to an extended operation request. + * + * @version 1.0 + */ +public class LDAPExtendedResponse extends LDAPResponse { + + /** + * Constructor + * + * @param msgid message identifier + * @param rsp Extended operation response + * @paarm controls Array of controls of null + */ + LDAPExtendedResponse(int msgid, JDAPExtendedResponse rsp, LDAPControl controls[]) { + super(msgid, rsp, controls); + } + + /** + * Returns the OID of the response. + * + * @return The response OID + */ + public String getOID() { + JDAPExtendedResponse result = (JDAPExtendedResponse)getProtocolOp(); + return result.getID(); + } + + /** + * Returns the raw bytes of the value part of the response. + * + * @return Response as a raw array of bytes + */ + public byte[] getValue() { + JDAPExtendedResponse result = (JDAPExtendedResponse)getProtocolOp(); + return result.getValue(); + } +} diff --git a/mozilla/directory/java-sdk/ldapjdk/netscape/ldap/LDAPInterruptedException.java b/mozilla/directory/java-sdk/ldapjdk/netscape/ldap/LDAPInterruptedException.java new file mode 100644 index 00000000000..7fe171dd8ed --- /dev/null +++ b/mozilla/directory/java-sdk/ldapjdk/netscape/ldap/LDAPInterruptedException.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) 1998 Netscape Communications Corporation. All Rights + * Reserved. + */ +package netscape.ldap; + +/** + * An exception thrown when the LDAP operation being invoked has + * been interrupted. For example, an application might interrupt a thread that + * is performing a search. + * + * @version 1.0 + */ +public class LDAPInterruptedException extends LDAPException { + + /** + * Constructs a default exception with a specified string of + * additional information. This string appears if you call + * the toString() method. + *

    + * + * @param message The additional information. + * @see netscape.ldap.LDAPInterruptedException#toString() + */ + LDAPInterruptedException( String message ) { + super( message, LDAPException.OTHER, null); + } + + /** + * Gets the string representation of the exception. + */ + public String toString() { + String str = "netscape.ldap.LDAPInterruptedException: "; + String msg = super.getMessage(); + if (msg != null) { + str +=msg; + } + return str; + } +} \ No newline at end of file diff --git a/mozilla/directory/java-sdk/ldapjdk/netscape/ldap/LDAPMatchingRuleSchema.java b/mozilla/directory/java-sdk/ldapjdk/netscape/ldap/LDAPMatchingRuleSchema.java index c6ce14709cc..06491bfa25e 100644 --- a/mozilla/directory/java-sdk/ldapjdk/netscape/ldap/LDAPMatchingRuleSchema.java +++ b/mozilla/directory/java-sdk/ldapjdk/netscape/ldap/LDAPMatchingRuleSchema.java @@ -12,7 +12,7 @@ * * 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 + * Copyright (C) 1999 Netscape Communications Corporation. All Rights * Reserved. */ package netscape.ldap; @@ -46,22 +46,49 @@ import java.util.*; * specify these types of information as arguments to the constructor or * in the MatchingRuleDescription and MatchingRuleUseDescription formats * specified in RFC 2252. - * (When an LDAP client searches an LDAP server for the schema, the server + * When an LDAP client searches an LDAP server for the schema, the server * returns schema information as an object with attribute values in this - * format.) + * format. *

    * * You can get the name, OID, and description of this matching rule * definition by using the getName, getOID, and * getDescription methods inherited from the abstract class - * LDAPSchemaElement. - *

    + * LDAPSchemaElement. Custom qualifiers are + * accessed with getQualifier and getQualifierNames + * from LDAPSchemaElement. + *

    * - * If you need to add or remove this matching rule definition from the - * schema, you can use the add and remove + * To add or remove this matching rule definition from the + * schema, use the add and remove * methods, which this class inherits from the LDAPSchemaElement * abstract class. *

    + * RFC 2252 defines MatchingRuleDescription and MatchingRuleUseDescription + * as follows: + *

    + * MatchingRuleDescription = "(" whsp + * numericoid whsp ; MatchingRule identifier + * [ "NAME" qdescrs ] + * [ "DESC" qdstring ] + * [ "OBSOLETE" whsp ] + * "SYNTAX" numericoid + * whsp ")" + * + * Values of the matchingRuleUse list the attributes which are suitable + * for use with an extensible matching rule. + * + * MatchingRuleUseDescription = "(" whsp + * numericoid whsp ; MatchingRule identifier + * [ "NAME" qdescrs ] + * [ "DESC" qdstring ] + * [ "OBSOLETE" ] + * "APPLIES" oids ; AttributeType identifiers + * whsp ")" *

    + * 
    + *

    + * LDAPMatchingRuleSchema abstracts away from the two types and + * manages their relationships transparently. * * @version 1.0 * @see netscape.ldap.LDAPSchemaElement @@ -105,11 +132,11 @@ public class LDAPMatchingRuleSchema extends LDAPAttributeSchema { * (see RFC 2252, Lightweight Directory Access Protocol (v3): * Attribute Syntax Definitions. This is the format that LDAP servers - * and clients use to exchange schema information. (For example, when + * and clients use to exchange schema information. For example, when * you search an LDAP server for its schema, the server returns an entry * with attributes that include "matchingrule" and "matchingruleuse". * The values of these attributes are matching rule descriptions - * in this format.) + * in this format. *

    * * @param raw Definition of the matching rule in the @@ -118,92 +145,22 @@ public class LDAPMatchingRuleSchema extends LDAPAttributeSchema { * MatchingRuleUseDescription format. */ public LDAPMatchingRuleSchema( String raw, String use ) { - super(); attrName = "matchingrules"; - oid = processRule( raw ); - String o = processRule( use ); - if ( (o != null) && (oid != null) && !o.equals(oid) ) { - System.err.println( "Rule oid " + oid + - " differs from use oid " + o ); + parseValue( raw ); + if ( use != null ) { + parseValue( use ); } - if ( oid == null ) - oid = o; - } - - /** - * Construct a rule definition from a value returned from - * a search of the schema in a Directory. - * @param raw The raw string value returned as one of the values - * of matchingRuleUse in searching the schema. - */ - private String processRule( String raw ) { - if ( raw == null ) - return null; - /* Process the matchingRuleUse value */ - raw.trim(); - // Skip past "( " and ")" - int l = raw.length(); - raw = raw.substring( 2, l - 1 ); - l = raw.length(); - int ind = raw.indexOf( ' ' ); - String o = raw.substring( 0, ind ); - char[] ch = new char[l]; - - raw = raw.substring( ind + 1, l ); - l = raw.length(); - raw.getChars( 0, l, ch, 0 ); - - ind = 0; - l = ch.length; - while ( ind < l ) { - String s = ""; - String val; - while( ch[ind] == ' ' ) - ind++; - int last = ind + 1; - while( (last < l) && (ch[last] != ' ') ) - last++; - if ( (ind < l) && (last < l) ) { - s = new String( ch, ind, last-ind ); - ind = last; - } else { - ind = l; - } - - while( (ind < l) && (ch[ind] != '\'') && (ch[ind] != '(') ) - ind++; - last = ind + 1; - while( (last < l) && (ch[last] != '\'') && (ch[last] != ')') ) { - last++; - } - if ( (ind < last) && (last < l) ) { - val = new String( ch, ind+1, last-ind-1 ); - ind = last + 1; - - if ( s.equalsIgnoreCase( "NAME" ) ) { - name = val; - } else if ( s.equalsIgnoreCase( "DESC" ) ) { - description = val; - } else if ( s.equalsIgnoreCase( "SYNTAX" ) ) { - syntax = syntaxCheck( val ); - } else if ( s.equalsIgnoreCase( "APPLIES" ) ) { - /* Parse the list of attribute OIDs */ - Vector v = new Vector(); - StringTokenizer st = new StringTokenizer( val, " " ); - while ( st.hasMoreTokens() ) { - String tok = st.nextToken(); - if ( !tok.equals( "$" ) ) { - v.addElement( tok ); - } - } - attributes = new String[v.size()]; - v.copyInto( attributes ); - v.removeAllElements(); - v = null; - } - } + Vector v = (Vector)properties.get( "APPLIES" ); + if ( v != null ) { + attributes = new String[v.size()]; + v.copyInto( attributes ); + v.removeAllElements(); + } + String val = (String)properties.get( "SYNTAX" ); + if ( val != null ) { + syntaxString = val; + syntax = syntaxCheck( val ); } - return o; } /** @@ -216,6 +173,25 @@ public class LDAPMatchingRuleSchema extends LDAPAttributeSchema { return attributes; } + String getValue( boolean quotingBug ) { + String s = getValuePrefix(); + s += "SYNTAX "; + if ( quotingBug ) { + s += '\''; + } + s += syntaxString; + if ( quotingBug ) { + s += '\''; + } + s += ' '; + String val = getCustomValues(); + if ( val.length() > 0 ) { + s += val + ' '; + } + s += ')'; + return s; + } + /** * Get the matching rule definition in the string representation * of the MatchingRuleDescription data type defined in X.501 (see @@ -236,11 +212,7 @@ public class LDAPMatchingRuleSchema extends LDAPAttributeSchema { * a matching rule in the schema) of a subschema object. */ public String getValue() { - String s = "( " + oid + " NAME \'" + name + "\' DESC \'" + - description + "\' SYNTAX \'"; - s += internalSyntaxToString(); - s += "\' )"; - return s; + return getValue( false ); } /** @@ -263,14 +235,19 @@ public class LDAPMatchingRuleSchema extends LDAPAttributeSchema { * a matching rule in the schema) of a subschema object. */ public String getUseValue() { - String s = "( " + oid; - s += " APPLIES ( "; + String s = getValuePrefix(); + s += "APPLIES ( "; for( int i = 0; i < attributes.length; i++ ) { if ( i > 0 ) s += " $ "; s += attributes[i]; } - s += ") )"; + s += ") "; + String val = getCustomValues(); + if ( val.length() > 0 ) { + s += val + ' '; + } + s += ')'; return s; } @@ -281,9 +258,10 @@ public class LDAPMatchingRuleSchema extends LDAPAttributeSchema { * @param op Type of modification to make. * @param name Name of attribute in the schema entry to modify. This * is ignored here. + * @param dn The entry at which to update the schema. * @exception LDAPException if the definition can't be added/removed. */ - protected void update( LDAPConnection ld, int op, String name ) + protected void update( LDAPConnection ld, int op, String name, String dn ) throws LDAPException { LDAPAttribute[] attrs = new LDAPAttribute[2]; attrs[0] = new LDAPAttribute( "matchingRules", @@ -291,7 +269,7 @@ public class LDAPMatchingRuleSchema extends LDAPAttributeSchema { /* Must update the matchingRuleUse value as well */ attrs[1] = new LDAPAttribute( "matchingRuleUse", getUseValue() ); - update( ld, op, attrs ); + update( ld, op, attrs, dn ); } /** @@ -312,6 +290,7 @@ public class LDAPMatchingRuleSchema extends LDAPAttributeSchema { s += attributes[i]; } } + s += getQualifierString( EXPLICIT ); return s; } diff --git a/mozilla/directory/java-sdk/ldapjdk/netscape/ldap/client/JDAPMessage.java b/mozilla/directory/java-sdk/ldapjdk/netscape/ldap/LDAPMessage.java similarity index 52% rename from mozilla/directory/java-sdk/ldapjdk/netscape/ldap/client/JDAPMessage.java rename to mozilla/directory/java-sdk/ldapjdk/netscape/ldap/LDAPMessage.java index f7e99fa8c73..8a058dc7d53 100644 --- a/mozilla/directory/java-sdk/ldapjdk/netscape/ldap/client/JDAPMessage.java +++ b/mozilla/directory/java-sdk/ldapjdk/netscape/ldap/LDAPMessage.java @@ -15,7 +15,7 @@ * Copyright (C) 1998 Netscape Communications Corporation. All Rights * Reserved. */ -package netscape.ldap.client; +package netscape.ldap; import java.util.*; import netscape.ldap.client.opers.*; @@ -24,59 +24,87 @@ import java.io.*; import java.net.*; /** - * This class represents the LDAPMessage in RFC1777. The + * Base class for LDAP request and response messages. + * This class represents the LDAPMessage in RFC2251. The * message is the entity that got transferred back and * fro between the server and the client interface. Each * message has a protocol operation. The protocol operation * indicates if it is a request or response. *

    - * LDAPMessage ::= SEQUECE {
    + * LDAPMessage ::= SEQUENCE {
      *   messageID MessageID,
      *   protocolOp CHOICE {
      *     bindRequest BindRequest,
      *     ...
      *   }
    + *   controls [0] Controls OPTIONAL
      * }
      * 
    * * @version 1.0 */ -public class JDAPMessage { +public class LDAPMessage { + + public final static int BIND_REQUEST = 0; + public final static int BIND_RESPONSE = 1; + public final static int UNBIND_REQUEST = 2; + public final static int SEARCH_REQUEST = 3; + public final static int SEARCH_RESPONSE = 4; + public final static int SEARCH_RESULT = 5; + public final static int MODIFY_REQUEST = 6; + public final static int MODIFY_RESPONSE = 7; + public final static int ADD_REQUEST = 8; + public final static int ADD_RESPONSE = 9; + public final static int DEL_REQUEST = 10; + public final static int DEL_RESPONSE = 11; + public final static int MODIFY_RDN_REQUEST = 12; + public final static int MODIFY_RDN_RESPONSE = 13; + public final static int COMPARE_REQUEST = 14; + public final static int COMPARE_RESPONSE = 15; + public final static int ABANDON_REQUEST = 16; + public final static int SEARCH_RESULT_REFERENCE = 19; + public final static int EXTENDED_REQUEST = 23; + public final static int EXTENDED_RESPONSE = 24; + /** * Internal variables */ - protected int m_msgid; - protected JDAPProtocolOp m_protocolOp = null; - protected JDAPControl m_controls[] = null; + private int m_msgid; + private JDAPProtocolOp m_protocolOp = null; + private LDAPControl m_controls[] = null; /** * Constructs a ldap message. * @param msgid message identifier * @param op operation protocol */ - public JDAPMessage(int msgid, JDAPProtocolOp op) { + LDAPMessage(int msgid, JDAPProtocolOp op) { m_msgid = msgid; m_protocolOp = op; } - public JDAPMessage(int msgid, JDAPProtocolOp op, JDAPControl controls[]) { + LDAPMessage(int msgid, JDAPProtocolOp op, LDAPControl controls[]) { m_msgid = msgid; m_protocolOp = op; m_controls = controls; /* LDAPv3 additions */ } /** - * Constructs a ldap message with BERElement. This constructor - * is used by JDAPClient to build JDAPMessage from byte - * stream. + * Creates a ldap message from a BERElement. This method is used + * to parse LDAP response messages + * * @param element ber element constructed from incoming byte stream */ - public JDAPMessage(BERElement element) throws IOException { + static LDAPMessage parseMessage(BERElement element) throws IOException { + int l_msgid; + JDAPProtocolOp l_protocolOp = null; + LDAPControl l_controls[] = null; + if (element.getType() != BERElement.SEQUENCE) throw new IOException("SEQUENCE in jdap message expected"); BERSequence seq = (BERSequence)element; BERInteger msgid = (BERInteger)seq.elementAt(0); - m_msgid = msgid.getValue(); + l_msgid = msgid.getValue(); BERElement protocolOp = (BERElement)seq.elementAt(1); if (protocolOp.getType() != BERElement.TAG) { throw new IOException("TAG in protocol operation is expected"); @@ -84,10 +112,10 @@ public class JDAPMessage { BERTag tag = (BERTag)protocolOp; switch (tag.getTag()&0x1f) { case JDAPProtocolOp.BIND_RESPONSE: - m_protocolOp = new JDAPBindResponse(protocolOp); + l_protocolOp = new JDAPBindResponse(protocolOp); break; case JDAPProtocolOp.SEARCH_RESPONSE: - m_protocolOp = new JDAPSearchResponse(protocolOp); + l_protocolOp = new JDAPSearchResponse(protocolOp); break; /* * If doing search without bind, @@ -96,31 +124,31 @@ public class JDAPMessage { */ case JDAPProtocolOp.SEARCH_REQUEST: case JDAPProtocolOp.SEARCH_RESULT: - m_protocolOp = new JDAPSearchResult(protocolOp); + l_protocolOp = new JDAPSearchResult(protocolOp); break; case JDAPProtocolOp.MODIFY_RESPONSE: - m_protocolOp = new JDAPModifyResponse(protocolOp); + l_protocolOp = new JDAPModifyResponse(protocolOp); break; case JDAPProtocolOp.ADD_RESPONSE: - m_protocolOp = new JDAPAddResponse(protocolOp); + l_protocolOp = new JDAPAddResponse(protocolOp); break; case JDAPProtocolOp.DEL_RESPONSE: - m_protocolOp = new JDAPDeleteResponse(protocolOp); + l_protocolOp = new JDAPDeleteResponse(protocolOp); break; case JDAPProtocolOp.MODIFY_RDN_RESPONSE: - m_protocolOp = new JDAPModifyRDNResponse(protocolOp); + l_protocolOp = new JDAPModifyRDNResponse(protocolOp); break; case JDAPProtocolOp.COMPARE_RESPONSE: - m_protocolOp = new JDAPCompareResponse(protocolOp); + l_protocolOp = new JDAPCompareResponse(protocolOp); break; case JDAPProtocolOp.SEARCH_RESULT_REFERENCE: - m_protocolOp = new JDAPSearchResultReference(protocolOp); + l_protocolOp = new JDAPSearchResultReference(protocolOp); break; case JDAPProtocolOp.EXTENDED_RESPONSE: - m_protocolOp = new JDAPExtendedResponse(protocolOp); + l_protocolOp = new JDAPExtendedResponse(protocolOp); break; default: - throw new IOException("Unknown rotocol operation"); + throw new IOException("Unknown protocol operation"); } /* parse control */ @@ -128,27 +156,51 @@ public class JDAPMessage { tag = (BERTag)seq.elementAt(2); if ( tag.getTag() == (BERTag.CONSTRUCTED|BERTag.CONTEXT|0) ) { BERSequence controls = (BERSequence)tag.getValue(); - m_controls = new JDAPControl[controls.size()]; + l_controls = new LDAPControl[controls.size()]; for (int i = 0; i < controls.size(); i++) { - m_controls[i] = new JDAPControl(controls.elementAt(i)); - } + l_controls[i] = LDAPControl.parseControl(controls.elementAt(i)); + } } } + + if (l_protocolOp instanceof JDAPSearchResponse) { + return new LDAPSearchResult(l_msgid, + (JDAPSearchResponse) l_protocolOp, l_controls); + } + else if (l_protocolOp instanceof JDAPSearchResultReference) { + return new LDAPSearchResultReference(l_msgid, + (JDAPSearchResultReference) l_protocolOp, l_controls); + } + else if (l_protocolOp instanceof JDAPExtendedResponse) { + return new LDAPExtendedResponse(l_msgid, + (JDAPExtendedResponse) l_protocolOp, l_controls); + } + else { + return new LDAPResponse(l_msgid, l_protocolOp, l_controls); + } } /** - * Retrieves the message identifer. + * Returns the message identifer. * @return message identifer */ public int getId(){ return m_msgid; } + /** + * Returns the LDAP operation type of the message + * @return message type + */ + public int getType(){ + return m_protocolOp.getType(); + } + /** * Retrieves the protocol operation. * @return protocol operation */ - public JDAPProtocolOp getProtocolOp() { + JDAPProtocolOp getProtocolOp() { return m_protocolOp; } @@ -156,7 +208,7 @@ public class JDAPMessage { * Retrieves list of controls. * @return controls */ - public JDAPControl[] getControls() { + public LDAPControl[] getControls() { return m_controls; } @@ -164,7 +216,7 @@ public class JDAPMessage { * Writes the ber encoding to stream. * @param s output stream */ - public void write(OutputStream s) throws IOException { + void write(OutputStream s) throws IOException { BERSequence seq = new BERSequence(); BERInteger i = new BERInteger(m_msgid); seq.addElement(i); @@ -189,6 +241,17 @@ public class JDAPMessage { * @return ldap message */ public String toString() { - return "[JDAPMessage] " + m_msgid + " " + m_protocolOp.toString(); + if (m_controls == null) { + return "[LDAPMessage] " + m_msgid + " " + m_protocolOp.toString(); + } + else { + StringBuffer sb = new StringBuffer( + "[LDAPMessage] " + m_msgid + " " + m_protocolOp.toString()); + for (int i =0; i < m_controls.length; i++) { + sb.append(" ctrl"+i+"="); + sb.append(m_controls[i].toString()); + } + return sb.toString(); + } } } diff --git a/mozilla/directory/java-sdk/ldapjdk/netscape/ldap/LDAPMessageQueue.java b/mozilla/directory/java-sdk/ldapjdk/netscape/ldap/LDAPMessageQueue.java new file mode 100644 index 00000000000..67e165362ba --- /dev/null +++ b/mozilla/directory/java-sdk/ldapjdk/netscape/ldap/LDAPMessageQueue.java @@ -0,0 +1,428 @@ +/* -*- 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 netscape.ldap; + +import java.util.Vector; + +/** + * A queue of response messsages from the server. Multiple requests + * can be multiplexed on the same queue. For synchronous LDAPConnection + * requests, there will be only one request per queue. For asynchronous + * LDAPConnection requests, the user can add multiple request to the + * same queue. + * + * Superclass for LDAResponseListener and LDAPSearchListener + * + */ +class LDAPMessageQueue { + + /** + * Request entry encapsulates request parameters + */ + private static class RequestEntry { + int id; + LDAPConnection connection; + LDAPConnThread connThread; + + RequestEntry(int id, LDAPConnection connection, LDAPConnThread connThread) { + this.id= id; + this.connection = connection; + this.connThread = connThread; + } + } + + /** + * Internal variables + */ + private /*LDAPMessage */ Vector m_messageQueue = new Vector(1); + private /*RequestEntry*/ Vector m_requestList = new Vector(1); + private LDAPException m_exception; /* For network errors */ + private boolean m_asynchOp; + + /** + * Constructor + * @param asynchOp A flag whether the object is used for asynchronous + * LDAP operations + * @see netscape.ldap.LDAPAsynchronousConnection + */ + LDAPMessageQueue (boolean asynchOp) { + m_asynchOp = asynchOp; + } + + /** + * Returns a flag whether the listener is used for asynchronous LDAP + * operations + * @return Asynchronous operation flag + * @see netscape.ldap.LDAPAsynchronousConnection + */ + boolean isAsynchOp() { + return m_asynchOp; + } + + /** + * Blocks until a response is available or until all operations + * associated with the object have completed or been canceled. + * @return LDAP message or null if there is no more outstanding requests + * @exception LDAPException Network error exception + * @exception LDAPInterruptedException The invoking thread was interrupted + */ + synchronized LDAPMessage nextMessage () throws LDAPException { + + while(m_requestList.size() != 0 && m_exception == null && m_messageQueue.size() == 0) { + try { + wait (); + } catch (InterruptedException e) { + throw new LDAPInterruptedException("Interrupted LDAP operation"); + } + } + + // Network exception occurred ? + if (m_exception != null) { + LDAPException ex = m_exception; + m_exception = null; + throw ex; + } + + // Are there any outstanding requests left + if (m_requestList.size() == 0) { + return null; // No outstanding requests + } + + // Dequeue the first entry + LDAPMessage msg = (LDAPMessage) m_messageQueue.elementAt(0); + m_messageQueue.removeElementAt(0); + + // Is the ldap operation completed? + if (msg instanceof LDAPResponse) { + removeRequest(msg.getId()); + } + + return msg; + } + + /** + * Wait for request to complete. This method blocks until a message of + * type LDAPResponse has been received. Used by synchronous search + * with batch size of zero (block until all results are received) + * @return LDAPResponse message or null if there is no more outstanding requests + * @exception LDAPException Network error exception + * @exception LDAPInterruptedException The invoking thread was interrupted + */ + synchronized LDAPResponse completeRequest () throws LDAPException { + + while (true) { + while(m_requestList.size() != 0 && m_exception == null && m_messageQueue.size() == 0) { + try { + wait (); + } catch (InterruptedException e) { + throw new LDAPInterruptedException("Interrupted LDAP operation"); + } + + } + + // Network exception occurred ? + if (m_exception != null) { + LDAPException ex = m_exception; + m_exception = null; + throw ex; + } + + // Are there any outstanding requests left? + if (m_requestList.size() == 0) { + return null; // No outstanding requests + } + + // Search an instance of LDAPResponse + for (int i= m_messageQueue.size()-1; i >=0; i--) { + LDAPMessage msg = (LDAPMessage) m_messageQueue.elementAt(i); + if (msg instanceof LDAPResponse) { + + // Dequeue the entry and return + m_messageQueue.removeElementAt(i); + return (LDAPResponse)msg; + } + } + + // Not found, wait for the next message + try { + wait (); + } catch (InterruptedException e) { + throw new LDAPInterruptedException("Interrupted LDAP operation"); + } + + } + } + + /** + * Merge two message queues. + * Move/append the content from another message queue to this one. + * + * To be used for synchronization of asynchronous LDAP operations where + * requests are sent by one thread but processed by another one + * + * A client may be implemented in such a way that one thread makes LDAP + * requests and calls l.getIDs(), while another thread is responsible for + * processing of responses (call l.getResponse()). Both threads are using + * the same listener objects. In such a case, a race + * condition may occur, where a LDAP response message is retrieved and + * the request terminated (request ID removed) before the first thread + * has a chance to execute l.getIDs(). + * The proper way to handle this scenario is to create a separate listener + * for each new request, and after l.getIDs() has been invoked, merge the + * new request with the existing one. + * @param mq2 Message queue to be merged with this one. + */ + synchronized void merge(LDAPMessageQueue mq2) { + synchronized (mq2) { + for (int i=0; i < mq2.m_requestList.size(); i++) { + m_requestList.addElement(mq2.m_requestList.elementAt(i)); + } + for (int i=0; i < mq2.m_messageQueue.size(); i++) { + m_messageQueue.addElement(mq2.m_messageQueue.elementAt(i)); + } + if (mq2.m_exception != null) { + m_exception = mq2.m_exception; + } + + mq2.reset(); + notifyAll(); // notify for mq2 + } + notifyAll(); // notify this mq + } + + + /** + * Retrieves all messages currently in the queue without blocking + * @returns Vector of messages + */ + synchronized Vector getAllMessages() { + Vector result = m_messageQueue; + m_messageQueue = new Vector(1); + return result; + } + + /** + * Queues the LDAP server's response. This causes anyone waiting + * in nextMessage() to unblock. + * @param msg response message + */ + synchronized void addMessage (LDAPMessage msg) { + m_messageQueue.addElement(msg); + notifyAll (); + } + + /** + * Signals that a network exception occured while servicing the + * request. This exception will be throw to any thread waiting + * in nextMessage() + * @param connThread LDAPConnThread on which the exception occurred + * @param e exception + */ + synchronized void setException (LDAPConnThread connThread, LDAPException e) { + m_exception = e; + removeAllRequests(connThread); + notifyAll (); + } + + /** + * Checks if response message is received. + * @return true or false + */ + boolean isMessageReceived() { + return m_messageQueue.size() != 0; + } + + /** + * Returns the count of queued messages + * @return message count + */ + public int getMessageCount () { + return m_messageQueue.size(); + } + + /** + * Remove all queued messages associated with the request ID + * Called when a LDAP operation is abandoned + * + * Not synchronized as its private and can be called only by + * abandon() and removeAllRequests() + * + * @return Count of removed messages + */ + private int removeAllMessages(int id) { + int removeCount=0; + for (int i=(m_messageQueue.size()-1); i>=0; i--) { + LDAPMessage msg = (LDAPMessage)m_messageQueue.elementAt(i); + if (msg.getId() == id) { + m_messageQueue.removeElementAt(i); + removeCount++; + } + } + return removeCount; + } + + /** + * Resets the state of this object, so it can be recycled. + * Used by LDAPConnection synchronous operations. + * @see netscape.ldap.LDAPConnection#getResponseListener + * @see netscape.ldap.LDAPConnection#getSearchListener + */ + void reset () { + m_exception = null; + m_messageQueue.removeAllElements(); + m_requestList.removeAllElements(); + } + + /** + * Returns the connection associated with the specified request id + * @parm id request id + * @return connection + */ + synchronized LDAPConnection getConnection(int id) { + for (int i=0; i < m_requestList.size(); i++) { + RequestEntry entry = (RequestEntry)m_requestList.elementAt(i); + if (id == entry.id) { + return entry.connection; + } + } + return null; + + } + + /** + * Returns the connection thread associated with the specified request id + * @parm id request id + * @return connection thread + */ + synchronized LDAPConnThread getConnThread(int id) { + for (int i=0; i < m_requestList.size(); i++) { + RequestEntry entry = (RequestEntry)m_requestList.elementAt(i); + if (id == entry.id) { + return entry.connThread; + } + } + return null; + + } + + /** + * Returns message id of the last request + * @return Message id. + */ + synchronized int getID() { + int reqCnt = m_requestList.size(); + if ( reqCnt == 0) { + return -1; + } + else { + RequestEntry entry = (RequestEntry)m_requestList.elementAt(reqCnt-1); + return entry.id; + } + } + + /** + * Returns a list of message ids for all outstanding requests + * @return Message id array + */ + synchronized int[] getIDs() { + int[] ids = new int[m_requestList.size()]; + for (int i=0; i < ids.length; i++) { + RequestEntry entry = (RequestEntry)m_requestList.elementAt(i); + ids[i] = entry.id; + } + return ids; + } + + /** + * Registers a LDAP request + * @param id LDAP request message ID + * @param connection LDAP Connection for the message ID + * @param connThread A physical connection to the server + */ + synchronized void addRequest(int id, LDAPConnection connection, + LDAPConnThread connThread) { + + m_requestList.addElement(new RequestEntry(id, connection, connThread)); + } + + /** + * Returns the number of outstanding requests. + * @return Outstanding request count. + */ + public int getRequestCount() { + return m_requestList.size(); + } + + /** + * Remove request with the specified ID + * Called when a LDAP operation is abandoned (called from + * LDAPConnThread), or terminated (called by nextMessage() when + * LDAPResponse message is received) + * @return Flag whether the request was removed + */ + synchronized boolean removeRequest(int id) { + for (int i=0; i < m_requestList.size(); i++) { + RequestEntry entry = (RequestEntry)m_requestList.elementAt(i); + if (id == entry.id) { + m_requestList.removeElementAt(i); + removeAllMessages(id); + notifyAll(); + return true; + } + } + return false; + } + + /** + * Remove all requests associated with the specified connThread + * Called when a connThread has a network error + * @return Number of removed request + */ + synchronized int removeAllRequests(LDAPConnThread connThread) { + int removeCount=0; + for (int i=(m_requestList.size()-1); i>=0; i--) { + RequestEntry entry = (RequestEntry)m_requestList.elementAt(i); + if (connThread == entry.connThread) { + m_requestList.removeElementAt(i); + removeCount++; + + // remove all queued messages as well + removeAllMessages(entry.id); + } + } + notifyAll(); + return removeCount; + } + + /** + * String representation of the object + */ + public String toString() { + StringBuffer sb = new StringBuffer("LDAPMessageQueue:"); + sb.append(" requestIDs={"); + for (int i=0; i < m_requestList.size(); i++) { + if (i>0) { + sb.append(","); + } + sb.append(((RequestEntry)m_requestList.elementAt(i)).id); + } + sb.append("} messageCount="+m_messageQueue.size()); + + return sb.toString(); + } +} diff --git a/mozilla/directory/java-sdk/ldapjdk/netscape/ldap/LDAPObjectClassSchema.java b/mozilla/directory/java-sdk/ldapjdk/netscape/ldap/LDAPObjectClassSchema.java index 8c11c17c900..83d6511b2ec 100644 --- a/mozilla/directory/java-sdk/ldapjdk/netscape/ldap/LDAPObjectClassSchema.java +++ b/mozilla/directory/java-sdk/ldapjdk/netscape/ldap/LDAPObjectClassSchema.java @@ -12,7 +12,7 @@ * * 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 + * Copyright (C) 1999 Netscape Communications Corporation. All Rights * Reserved. */ package netscape.ldap; @@ -43,23 +43,24 @@ import java.util.*; * When you construct an LDAPObjectSchema object, you can specify * these types of information as arguments to the constructor or in the * ObjectClassDescription format specified in RFC 2252. - * (When an LDAP client searches an LDAP server for the schema, the server + * When an LDAP client searches an LDAP server for the schema, the server * returns schema information as an object with attribute values in this - * format.) + * format. *

    * * RFC 2252 also notes that you can specify whether or not an object class * is abstract, structural, or auxiliary in the object description. - * (Abstract object classes are used only to derive other object classes. + * Abstract object classes are used only to derive other object classes. * Entries cannot belong to an abstract object class. top - * is an abstract object class.) Entries must belong to a structural + * is an abstract object class. Entries must belong to a structural * object class, so most object classes are structural object classes. * Objects of the LDAPObjectClassSchema class are structural * object classes by default. Auxiliary object classes can be used to - * construct entries of different types. For example, an auxiliary object - * class might be used to specify a type of entry with a structural object - * class that can vary (an auxiliary object class might specify that an - * entry must either be an organizationalPerson or residentialPerson). + * add attributes to entries of different types. For example, an + * auxiliary object class might be used to specify personal preference + * attributes. An entry can not contain just that object class, but may + * include it along with a structural object class, for example + * inetOrgPerson. * If the definition of an object (in ObjectClassDescription format) * specifies the AUXILIARY keyword, an LDAPObjectClassSchema * object created from that description represents an auxiliary object class. @@ -68,14 +69,32 @@ import java.util.*; * You can get the name, OID, and description of this object class * definition by using the getName, getOID, and * getDescription methods inherited from the abstract class - * LDAPSchemaElement. + * LDAPSchemaElement. Optional and custom qualifiers are + * accessed with getQualifier and getQualifierNames + * from LDAPSchemaElement. + *

    * - * If you need to add or remove this object class definition from the - * schema, you can use the add and remove + * To add or remove this object class definition from the + * schema, use the add and remove * methods, which this class inherits from the LDAPSchemaElement * abstract class. *

    + * RFC 2252 defines ObjectClassDescription as follows: + *

    + *

    + *    ObjectClassDescription = "(" whsp
    + *        numericoid whsp      ; ObjectClass identifier
    + *        [ "NAME" qdescrs ]
    + *        [ "DESC" qdstring ]
    + *        [ "OBSOLETE" whsp ]
    + *        [ "SUP" oids ]       ; Superior ObjectClasses
    + *        [ ( "ABSTRACT" / "STRUCTURAL" / "AUXILIARY" ) whsp ]
    + *                             ; default structural
    + *        [ "MUST" oids ]      ; AttributeTypes
    + *        [ "MAY" oids ]       ; AttributeTypes
    + *    whsp ")"
    + * 
    * * @version 1.0 * @see netscape.ldap.LDAPSchemaElement @@ -97,14 +116,41 @@ public class LDAPObjectClassSchema extends LDAPSchemaElement { */ public LDAPObjectClassSchema( String name, String oid, String superior, String description, - String[] required, String optional[] ) { + String[] required, String[] optional ) { super( name, oid, description ); attrName = "objectclasses"; - this.superior = superior; - for( int i = 0; i < required.length; i++ ) + this.superiors = new String[] { superior }; + for( int i = 0; i < required.length; i++ ) { must.addElement( required[i] ); - for( int i = 0; i < optional.length; i++ ) + } + for( int i = 0; i < optional.length; i++ ) { may.addElement( optional[i] ); + } + } + + /** + * Constructs an object class definition, using the specified + * information. + * @param name Name of the object class. + * @param oid Object identifier (OID) of the object class + * in dotted-string format (for example, "1.2.3.4"). + * @param description Description of the object class. + * @param superiors Name of parent object classes + * (the object class that the new object class inherits from). + * @param required Array of the names of the attributes required + * in this object class. + * @param optional Array of the names of the optional attributes + * allowed in this object class. + * @param type Either ABSTRACT, STRUCTURAL, or AUXILIARY + */ + public LDAPObjectClassSchema( String name, String oid, + String[] superiors, + String description, + String[] required, String[] optional, + int type ) { + this( name, oid, superiors[0], description, required, optional ); + this.superiors = superiors; + this.type = type; } /** @@ -125,85 +171,27 @@ public class LDAPObjectClassSchema extends LDAPSchemaElement { */ public LDAPObjectClassSchema( String raw ) { attrName = "objectclasses"; - raw.trim(); - // Skip past "( " and ")" - int l = raw.length(); - raw = raw.substring( 2, l - 1 ); - l = raw.length(); - int ind = raw.indexOf( ' ' ); - oid = raw.substring( 0, ind ); - char[] ch = new char[l]; - - raw = raw.substring( ind + 1, l ); - l = raw.length(); - raw.getChars( 0, l, ch, 0 ); - - ind = 0; - l = ch.length; - while ( ind < l ) { - String s = ""; - String val; - while( ch[ind] == ' ' ) - ind++; - int last = ind + 1; - while( (last < l) && (ch[last] != ' ') ) - last++; - if ( (ind < l) && (last < l) ) { - s = new String( ch, ind, last-ind ); - ind = last; - if ( s.equalsIgnoreCase( "AUXILIARY" ) ) { - auxiliary = true; - continue; - } - } else { - ind = l; - } - - while( (ind < l) && (ch[ind] != '\'') && (ch[ind] != '(') ) - ind++; - last = ind + 1; - if ( ind >= l ) { - break; - } - if ( ch[ind] == '\'' ) { - while( (last < l) && (ch[last] != '\'') ) { - last++; - } - if ( (ind < last) && (last < l) ) { - val = new String( ch, ind+1, last-ind-1 ); - - if ( s.equalsIgnoreCase( "NAME" ) ) { - name = val; - } else if ( s.equalsIgnoreCase( "DESC" ) ) { - description = val; - } else if ( s.equalsIgnoreCase( "SUP" ) ) { - superior = val; - } - } - } else { - Vector v = null; - if ( s.equalsIgnoreCase( "MAY" ) ) { - v = may; - } else if ( s.equalsIgnoreCase( "MUST" ) ) { - v = must; - } else { - continue; - } - while( (last < l) && (ch[last] != ')') ) { - last++; - } - if ( (ind < last) && (last < l) ) { - val = new String( ch, ind+1, last-ind-1 ); - StringTokenizer st = new StringTokenizer( val, " " ); - while ( st.hasMoreTokens() ) { - String tok = st.nextToken(); - if ( !tok.equals( "$" ) ) { - v.addElement( tok ); - } - } - } - } - ind = last + 1; + parseValue( raw ); + String[] vals = getQualifier( SUPERIOR ); + if ( vals != null ) { + superiors = vals; + setQualifier( SUPERIOR, (String)null ); + } + if ( properties.containsKey( "AUXILIARY" ) ) { + type = AUXILIARY; + } else if ( properties.containsKey( "ABSTRACT" ) ) { + type = ABSTRACT; + } else if ( properties.containsKey( "STRUCTURAL" ) ) { + type = STRUCTURAL; + } + obsolete = properties.containsKey( OBSOLETE ); + Vector v = (Vector)properties.get( "MAY" ); + if ( v != null ) { + may = v; + } + v = (Vector)properties.get( "MUST" ); + if ( v != null ) { + must = v; } } @@ -213,7 +201,18 @@ public class LDAPObjectClassSchema extends LDAPSchemaElement { * inherits from. */ public String getSuperior() { - return superior; + return superiors[0]; + } + + /** + * Get the names of all object classes that this class inherits + * from. Typically only one, but RFC 2252 allows multiple + * inheritance. + * @return The names of the object classes that this class + * inherits from. + */ + public String[] getSuperiors() { + return superiors; } /** @@ -237,47 +236,37 @@ public class LDAPObjectClassSchema extends LDAPSchemaElement { } /** - * Get the object class definition in a string representation - * of the ObjectClassDescription data type defined in X.501 (see - * RFC 2252, Lightweight Directory Access Protocol (v3): - * Attribute Syntax Definitions for a description of this format). - * This is the format that LDAP servers and clients use to exchange - * schema information. (For example, when - * you search an LDAP server for its schema, the server returns an entry - * with the attributes "objectclasses" and "attributetypes". The - * values of the "objectclasses" attribute are object class descriptions - * in this format.) - *

    - * - * @return A string in a format that can be used as the value of - * the objectclasses attribute (which describes - * an object class in the schema) of a subschema object. + * Get the type of the object class. + * @return STRUCTURAL, ABSTRACT, or AUXILIARY */ - public String getValue() { - String s = "( " + oid + " NAME \'" + name + "\' DESC \'" + - description + "\' SUP \'" + superior + "\' "; - if ( auxiliary ) - s += "AUXILIARY "; - s += "MUST ( "; - int i = 0; - Enumeration e = getRequiredAttributes(); - while( e.hasMoreElements() ) { - if ( i > 0 ) - s += " $ "; - i++; - s += (String)e.nextElement(); + public int getType() { + return type; + } + + /** + * Prepare a value in RFC 2252 format for submitting to a server + * + * @param quotingBug true if SUP and SYNTAX values are to + * be quoted. That is to satisfy bugs in certain LDAP servers. + * @return A String ready to be submitted to an LDAP server + */ + String getValue( boolean quotingBug ) { + String s = getValuePrefix(); + s += getValue( SUPERIOR, quotingBug ); + s += ' '; + String val = getOptionalValues( NOVALS ); + if ( val.length() > 0 ) { + s += val + ' '; } - s += " ) MAY ( "; - e = getOptionalAttributes(); - i = 0; - while( e.hasMoreElements() ) { - if ( i > 0 ) - s += " $ "; - i++; - s += (String)e.nextElement(); + s += getValue( "MUST", false ); + s += ' '; + s += getValue( "MAY", false ); + s += ' '; + val = getCustomValues(); + if ( val.length() > 0 ) { + s += val + ' '; } - s += ") )"; + s += ')'; return s; } @@ -289,8 +278,14 @@ public class LDAPObjectClassSchema extends LDAPSchemaElement { */ public String toString() { String s = "Name: " + name + "; OID: " + oid + - "; Superior: " + superior + - "; Description: " + description + "; Required: "; + "; Superior: "; + for( int i = 0; i < superiors.length; i++ ) { + s += superiors[i]; + if ( i < (superiors.length-1) ) { + s += ", "; + } + } + s += "; Description: " + description + "; Required: "; int i = 0; Enumeration e = getRequiredAttributes(); while( e.hasMoreElements() ) { @@ -308,11 +303,40 @@ public class LDAPObjectClassSchema extends LDAPSchemaElement { i++; s += (String)e.nextElement(); } + switch ( type ) { + case STRUCTURAL: + break; + case ABSTRACT: + s += "; ABSTRACT"; + break; + case AUXILIARY: + s += "; AUXILIARY"; + break; + } + if ( obsolete ) { + s += "; OBSOLETE"; + } + s += getQualifierString( IGNOREVALS ); return s; } - private String superior = ""; + public static final int STRUCTURAL = 0; + public static final int ABSTRACT = 1; + public static final int AUXILIARY = 2; + + private String[] superiors = { "" }; private Vector must = new Vector(); private Vector may = new Vector(); - private boolean auxiliary = false; + private int type = STRUCTURAL; + + // Qualifers known to not have values + static String[] NOVALS = { "ABSTRACT", "STRUCTURAL", + "AUXILIARY" }; + static { + for( int i = 0; i < NOVALS.length; i++ ) { + novalsTable.put( NOVALS[i], NOVALS[i] ); + } + } + static String[] IGNOREVALS = { "ABSTRACT", "STRUCTURAL", + "AUXILIARY", "MUST", "MAY" }; } diff --git a/mozilla/directory/java-sdk/ldapjdk/netscape/ldap/LDAPResourceBundle.java b/mozilla/directory/java-sdk/ldapjdk/netscape/ldap/LDAPResourceBundle.java index 0d263b85235..208e074eaf3 100644 --- a/mozilla/directory/java-sdk/ldapjdk/netscape/ldap/LDAPResourceBundle.java +++ b/mozilla/directory/java-sdk/ldapjdk/netscape/ldap/LDAPResourceBundle.java @@ -17,7 +17,6 @@ */ package netscape.ldap; -import netscape.ldap.*; import java.util.*; import java.io.*; diff --git a/mozilla/directory/java-sdk/ldapjdk/netscape/ldap/LDAPResponse.java b/mozilla/directory/java-sdk/ldapjdk/netscape/ldap/LDAPResponse.java new file mode 100644 index 00000000000..cc1116f6d24 --- /dev/null +++ b/mozilla/directory/java-sdk/ldapjdk/netscape/ldap/LDAPResponse.java @@ -0,0 +1,82 @@ +/* -*- 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 netscape.ldap; + +import netscape.ldap.client.opers.JDAPProtocolOp; +import netscape.ldap.client.opers.JDAPResult; + +/** + * Represents the response to a particular LDAP operation. + * + * @version 1.0 + */ +public class LDAPResponse extends LDAPMessage { + + /** + * Constructor + * + * @param msgid message identifier + * @param rsp Operation response + * @param controls Array of controls of null + */ + LDAPResponse(int msgid, JDAPProtocolOp rsp, LDAPControl controls[]) { + super(msgid, rsp, controls); + } + + /** + * Returns any error message in the response. + * + * @return The error message of the last error (or null + * if no message was set). + */ + public String getErrorMessage() { + JDAPResult result = (JDAPResult) getProtocolOp(); + return result.getErrorMessage(); + } + + /** + * Returns the partially matched DN field, if any, in a server response. + * + * @return The maximal subset of a DN which could be matched, + * or null. + */ + public String getMatchedDN() { + JDAPResult result = (JDAPResult) getProtocolOp(); + return result.getMatchedDN(); + } + + /** + * Returns all referrals, if any, in a server response. + * + * @return A list of referrals or null. + */ + public String[] getReferrals() { + JDAPResult result = (JDAPResult) getProtocolOp(); + return result.getReferrals(); + } + + /** + * Returns the result code in a server response. + * + * @return The result code. + */ + public int getResultCode() { + JDAPResult result = (JDAPResult) getProtocolOp(); + return result.getResultCode(); + } +} diff --git a/mozilla/directory/java-sdk/ldapjdk/netscape/ldap/LDAPResponseControl.java b/mozilla/directory/java-sdk/ldapjdk/netscape/ldap/LDAPResponseControl.java new file mode 100644 index 00000000000..ed0d7f5c68b --- /dev/null +++ b/mozilla/directory/java-sdk/ldapjdk/netscape/ldap/LDAPResponseControl.java @@ -0,0 +1,51 @@ +/* -*- 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 netscape.ldap; + +/* + * This object represents the value of the LDAPConnection.m_responseControlTable hashtable. + * It stores the response controls and its corresponding LDAPConnection and + * the message ID for its corresponding LDAPMessage. + */ +class LDAPResponseControl { + private LDAPConnection m_connection; + private int m_messageID; + private LDAPControl[] m_controls; + + public LDAPResponseControl(LDAPConnection conn, int msgID, + LDAPControl[] controls) { + + m_connection = conn; + m_messageID = msgID; + m_controls = new LDAPControl[controls.length]; + for (int i=0; itrue if standards-compliant + * @exception LDAPException on failure. + */ + static boolean isAttributeSyntaxStandardsCompliant( LDAPConnection ld ) + throws LDAPException { + + /* Check if this has already been investigated */ + String schemaBug = (String)ld.getProperty( ld.SCHEMA_BUG_PROPERTY ); + if ( schemaBug != null ) { + return schemaBug.equalsIgnoreCase( "standard" ); + } + + boolean compliant = true; + /* Get the schema definitions for attributes */ + String entryName = getSchemaDN( ld, "" ); + String[] attrs = { "attributetypes" }; + LDAPEntry entry = readSchema( ld, entryName, attrs ); + /* Get all attribute definitions, and check the first one */ + LDAPAttribute attr = entry.getAttribute( "attributetypes" ); + if ( attr != null ) { + Enumeration en = attr.getStringValues(); + if( en.hasMoreElements() ) { + compliant = !LDAPAttributeSchema.isSyntaxQuoted( + (String)en.nextElement() ); + } + } + ld.setProperty( ld.SCHEMA_BUG_PROPERTY, compliant ? "standard" : + "NetscapeBug" ); + return compliant; + } + /** * Displays the schema (including the descriptions of its object * classes, attribute types, and matching rules) in an easily @@ -418,34 +466,72 @@ public class LDAPSchema { * @return A string containing the schema in printable format. */ public String toString() { - String s = "Object classes: "; + String s = "Object classes:\n"; Enumeration en = getObjectClasses(); while( en.hasMoreElements() ) { - s += (String)en.nextElement(); - s += ' '; + s += en.nextElement().toString(); + s += '\n'; } - s += "Attributes: "; + s += "Attributes:\n"; en = getAttributes(); while( en.hasMoreElements() ) { - s += (String)en.nextElement(); - s += ' '; + s += en.nextElement().toString(); + s += '\n'; } - s += "Matching rules: "; + s += "Matching rules:\n"; en = getMatchingRules(); while( en.hasMoreElements() ) { - s += (String)en.nextElement(); - s += ' '; + s += en.nextElement().toString(); + s += '\n'; } return s; } - private LDAPEntry readSchema( LDAPConnection ld, String dn ) throws LDAPException { + /** + * Retrieve the DN of the schema definitions for a specific entry. + * @param ld An active connection to a Directory Server. + * @param dn The entry for which to fetch schema. + * @exception LDAPException on failure. + */ + static String getSchemaDN( LDAPConnection ld, String dn ) + throws LDAPException { + if ( (ld == null) || !ld.isConnected() ) { + throw new LDAPException( "No connection", LDAPException.OTHER ); + } + String[] attrs = { "subschemasubentry" }; + LDAPEntry entry = readSchema( ld, dn, attrs ); + if ( entry == null ) { + throw new LDAPException( "", LDAPException.NO_SUCH_OBJECT ); + } + LDAPAttribute attr = entry.getAttribute( attrs[0] ); + String entryName = "cn=schema"; + if ( attr != null ) { + Enumeration en = attr.getStringValues(); + if ( en.hasMoreElements() ) { + entryName = (String)en.nextElement(); + } + } + return entryName; + } + + private static LDAPEntry readSchema( LDAPConnection ld, String dn, + String[] attrs ) + throws LDAPException { LDAPSearchResults results = ld.search (dn, ld.SCOPE_BASE, "objectclass=subschema", - null, false); + attrs, false); + if ( !results.hasMoreElements() ) { + throw new LDAPException( "Cannot read schema", + LDAPException.INSUFFICIENT_ACCESS_RIGHTS ); + } return results.next (); } + private static LDAPEntry readSchema( LDAPConnection ld, String dn ) + throws LDAPException { + return readSchema( ld, dn, null ); + } + /** * Helper for "main" to print out schema elements. * @param en Enumeration of schema elements. @@ -454,6 +540,7 @@ public class LDAPSchema { while( en.hasMoreElements() ) { LDAPSchemaElement s = (LDAPSchemaElement)en.nextElement(); System.out.println( " " + s ); +// System.out.println( " " + s.getValue() ); } } @@ -499,5 +586,4 @@ public class LDAPSchema { private Hashtable objectClasses = new Hashtable(); private Hashtable attributes = new Hashtable(); private Hashtable matchingRules = new Hashtable(); - String entryName = null; } diff --git a/mozilla/directory/java-sdk/ldapjdk/netscape/ldap/LDAPSchemaElement.java b/mozilla/directory/java-sdk/ldapjdk/netscape/ldap/LDAPSchemaElement.java index 11a3db13d1e..b6de55c4da6 100644 --- a/mozilla/directory/java-sdk/ldapjdk/netscape/ldap/LDAPSchemaElement.java +++ b/mozilla/directory/java-sdk/ldapjdk/netscape/ldap/LDAPSchemaElement.java @@ -12,7 +12,7 @@ * * 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 + * Copyright (C) 1999 Netscape Communications Corporation. All Rights * Reserved. */ package netscape.ldap; @@ -41,9 +41,19 @@ import java.util.*; *

  • a name identifying the element *
  • an OID identifying the element *
  • a description of the element + *
  • a qualifier "OBSOLETE" * *

    * + * In addition, there are optional standard qualifiers for attribute + * types (see LDAPAttributeSchema), and implementation-specific + * qualifiers may be added. Non-standard qualifiers must have names + * starting with X-, e.g. "X-OWNER 'John Jacobson'". Optional and + * non-standard qualifiers can be accessed with getQualifier and + * setQualifier, and enumerated with + * getQualifierNames. + *

    + * * The LDAPSchemaElement class implements methods that * you can use with different types of schema elements (object class * definitions, attribute type definitions, and matching rule definitions). @@ -120,11 +130,12 @@ public abstract class LDAPSchemaElement { * @param atrr Attribute in the schema entry to modify. * @exception LDAPException if the definition can't be added/removed. */ - protected void update( LDAPConnection ld, int op, LDAPAttribute attr ) - throws LDAPException { + protected void update( LDAPConnection ld, int op, LDAPAttribute attr, + String dn ) + throws LDAPException { LDAPAttribute[] attrs = new LDAPAttribute[1]; attrs[0] = attr; - update( ld, op, attrs ); + update( ld, op, attrs, dn ); } /** @@ -135,13 +146,15 @@ public abstract class LDAPSchemaElement { * @param attrs Attributes in the schema entry to modify. * @exception LDAPException if the definition can't be added/removed. */ - protected void update( LDAPConnection ld, int op, LDAPAttribute[] attrs ) - throws LDAPException { + protected void update( LDAPConnection ld, int op, LDAPAttribute[] attrs, + String dn ) + throws LDAPException { LDAPModificationSet mods = new LDAPModificationSet(); for( int i = 0; i < attrs.length; i++ ) { mods.add( op, attrs[i] ); } - ld.modify( "cn=schema", mods ); + String entryName = LDAPSchema.getSchemaDN( ld, dn ); + ld.modify( entryName, mods ); } /** @@ -152,11 +165,14 @@ public abstract class LDAPSchemaElement { * @param name Name of attribute in the schema entry to modify. * @exception LDAPException if the definition can't be added/removed. */ - protected void update( LDAPConnection ld, int op, String name ) - throws LDAPException { + protected void update( LDAPConnection ld, int op, String name, + String dn ) + throws LDAPException { + boolean quotingBug = + !LDAPSchema.isAttributeSyntaxStandardsCompliant( ld ); LDAPAttribute attr = new LDAPAttribute( name, - getValue() ); - update( ld, op, attr ); + getValue( quotingBug ) ); + update( ld, op, attr, dn ); } /** @@ -166,11 +182,26 @@ public abstract class LDAPSchemaElement { * edit the schema. * @param ld The LDAPConnection object representing * a connection to an LDAP server. + * @param dn The entry at which to add the schema definition. + * @exception LDAPException The specified definition cannot be + * added to the schema. + */ + public void add( LDAPConnection ld, String dn ) throws LDAPException { + update( ld, LDAPModification.ADD, attrName, dn ); + } + + /** + * Adds the current object class, attribute type, or matching rule + * definition to the schema at the root DSE. Typically, most servers + * will require you to authenticate before allowing you to + * edit the schema. + * @param ld The LDAPConnection object representing + * a connection to an LDAP server. * @exception LDAPException The specified definition cannot be * added to the schema. */ public void add( LDAPConnection ld ) throws LDAPException { - update( ld, LDAPModification.ADD, attrName ); + add( ld, "" ); } /** @@ -181,17 +212,40 @@ public abstract class LDAPSchemaElement { * @param ld The LDAPConnection object representing * a connection to an LDAP server. * @param newValue The new value + * @param dn The entry at which to modify the schema definition. + * @exception LDAPException The specified definition cannot be + * modified. + */ + public void modify( LDAPConnection ld, LDAPSchemaElement newValue, + String dn ) + throws LDAPException { + boolean quotingBug = + !LDAPSchema.isAttributeSyntaxStandardsCompliant( ld ); + LDAPModificationSet mods = new LDAPModificationSet(); + mods.add( LDAPModification.DELETE, + new LDAPAttribute( attrName, getValue( quotingBug ) ) ); + mods.add( LDAPModification.ADD, + new LDAPAttribute( attrName, + newValue.getValue( quotingBug ) ) ); + String entryName = LDAPSchema.getSchemaDN( ld, dn ); + ld.modify( entryName, mods ); + } + + /** + * Replace a single value of the object class, attribute type, + * or matching rule definition in the schema at the root DSE. + * Typically, most servers + * will require you to authenticate before allowing you to + * edit the schema. + * @param ld The LDAPConnection object representing + * a connection to an LDAP server. + * @param newValue The new value * @exception LDAPException The specified definition cannot be * modified. */ public void modify( LDAPConnection ld, LDAPSchemaElement newValue ) throws LDAPException { - LDAPModificationSet mods = new LDAPModificationSet(); - mods.add( LDAPModification.DELETE, - new LDAPAttribute( attrName, getValue() ) ); - mods.add( LDAPModification.ADD, - new LDAPAttribute( attrName, newValue.getValue() ) ); - ld.modify( "cn=schema", mods ); + modify( ld, newValue, "" ); } /** @@ -201,22 +255,388 @@ public abstract class LDAPSchemaElement { * edit the schema. * @param ld The LDAPConnection object representing * a connection to an LDAP server. + * @param dn The entry at which to remove the schema definition. + * @exception LDAPException The specified definition cannot be + * removed from the schema. + */ + public void remove( LDAPConnection ld, String dn ) throws LDAPException { + update( ld, LDAPModification.DELETE, attrName, dn ); + } + + /** + * Removes the current object class, attribute type, or matching rule + * definition from the schema at the root DSE. Typically, most servers + * will require you to authenticate before allowing you to + * edit the schema. + * @param ld The LDAPConnection object representing + * a connection to an LDAP server. * @exception LDAPException The specified definition cannot be * removed from the schema. */ public void remove( LDAPConnection ld ) throws LDAPException { - update( ld, LDAPModification.DELETE, attrName ); + remove( ld, "" ); } /** - * Gets the definition of the current object class, attribute type, or - * matching rule in a format expected by the server when adding the - * definition to the directory. This must be overridden by derived classes. - * @return A String that can be used as a value of the element - * in the schema of a Directory. + * Report if the element is marked as obsolete. + * @return true if the element is defined as obsolete. */ - public abstract String getValue(); + public boolean isObsolete() { + return obsolete; + } + /** + * Parse a raw schema value into OID, name, description, and + * a Hashtable of other qualifiers and values + * + * @param raw A raw schema definition + */ + protected void parseValue( String raw ) { + if ( properties == null ) { + properties = new Hashtable(); + } + int l = raw.length(); + // Processing is faster in char array than in String + char[] ch = new char[l]; + raw.getChars( 0, l, ch, 0 ); + // Trim leading and trailing space + l--; + while( ch[l] == ' ' ) { + l--; + } + int start = 0; + while( ch[start] == ' ' ) { + start++; + } + // Skip past "( " and ")" to start of OID + start += 2; + // Find end of OID + int ind = start + 1; + while( ch[ind] != ' ' ) { + ind++; + } + oid = new String( ch, start, ind - start ); + + ind = ind + 1; + String s; + String val; + while ( ind < l ) { + // Skip past blanks to start of next token + while( ch[ind] == ' ' ) { + ind++; + } + // Find end of token + int last = ind + 1; + while( (last < l) && (ch[last] != ' ') ) + last++; + if ( last < l ) { + // Found a token + s = new String( ch, ind, last-ind ); + ind = last; + if ( novalsTable.containsKey( s ) ) { + properties.put( s, "" ); + continue; + } + } else { + // Reached end of string with no end of token + s = ""; + ind = l; + break; + } + + // Find the start of the value of the token + while( (ind < l) && (ch[ind] == ' ') ) { + ind++; + } + last = ind + 1; + if ( ind >= l ) { + break; + } + + boolean quoted = false; + boolean list = false; + if ( ch[ind] == '\'' ) { + // The value is quoted + quoted = true; + ind++; + while( (last < l) && (ch[last] != '\'') ) { + last++; + } + } else if ( ch[ind] == '(' ) { + // The value is a list + list = true; + ind++; + while( (last < l) && (ch[last] != ')') ) { + last++; + } + } else { + // The value is not quoted + while( (last < l) && (ch[last] != ' ') ) { + last++; + } + } + if ( (ind < last) && (last < l) ) { + if ( list ) { + Vector v = new Vector(); + val = new String( ch, ind+1, last-ind-1 ); + StringTokenizer st = new StringTokenizer( val, " " ); + while ( st.hasMoreTokens() ) { + String tok = st.nextToken(); + if ( !tok.equals( "$" ) ) { + // Remove quotes, if any + if ( tok.charAt( 0 ) == '\'' ) { + tok = tok.substring( 1, tok.length()-1 ); + } + v.addElement( tok ); + } + } + properties.put( s, v ); + } else { + val = new String( ch, ind, last-ind ); + if ( s.equals( "NAME" ) ) { + name = val; + } else if ( s.equals( "DESC" ) ) { + description = val; + } else { + properties.put( s, val ); + } + if ( quoted ) { + last++; + } + } + } + ind = last + 1; + } + } + + /** + * Format a String in the format defined in X.501 (see + * RFC 2252, Lightweight Directory Access Protocol + * (v3): Attribute Syntax Definitions + * for a description of this format). + * This is the format that LDAP servers and clients use to exchange + * schema information. For example, when + * you search an LDAP server for its schema, the server returns an entry + * with the attributes "objectclasses" and "attributetypes". The + * values of the "attributetypes" attribute are attribute type + * descriptions in this format. + *

    + * @param quotingBug true if single quotes are to be + * supplied around the SYNTAX and SUP value + * @return A formatted String for defining a schema element + */ + public String getValue() { + return getValue( false ); + } + + abstract String getValue( boolean quotingBug ); + + /** + * Prepare the initial common part of a schema element value in + * RFC 2252 format for submitting to a server + * + * @return The OID, name, description, and possibly OBSOLETE + * fields of a schema element definition + */ + String getValuePrefix() { + String s = "( " + oid + " NAME \'" + name + "\' DESC \'" + + description + "\' "; + if ( isObsolete() ) { + s += OBSOLETE + ' '; + } + return s; + } + + /** + * Get qualifiers which may or may not be present + * + * @param names List of qualifiers to look up + * @return String in RFC 2252 format containing any values + * found, not terminated with ' ' + */ + protected String getOptionalValues( String[] names ) { + String s = ""; + for( int i = 0; i < names.length; i++ ) { + String[] vals = getQualifier( names[i] ); + if ( (vals != null) && (vals.length > 0) ) { + s += names[i] + ' ' + vals[0]; + } + } + return s; + } + + /** + * Get any qualifiers marked as custom + * + * @return String in RFC 2252 format, without a terminating + * ' ' + */ + protected String getCustomValues() { + String s = ""; + Enumeration en = properties.keys(); + while( en.hasMoreElements() ) { + String key = (String)en.nextElement(); + if ( !key.startsWith( "X-" ) ) { + continue; + } + s += getValue( key, true ); + } + return s; + } + + /** + * Get a qualifier's value or values, if present, and format + * the String according to RFC 2252 + * + * @return String in RFC 2252 format, without a terminating + * ' ' + */ + String getValue( String key, boolean doQuote ) { + String s = ""; + Object o = properties.get( key ); + if ( o == null ) { + return s; + } + if ( o instanceof String ) { + if ( ((String)o).length() > 0 ) { + s += key + ' '; + if ( doQuote ) { + s += '\''; + } + s += (String)o; + if ( doQuote ) { + s += '\''; + } + } + } else { + s += key + " ( "; + Vector v = (Vector)o; + for( int i = 0; i < v.size(); i++ ) { + if ( doQuote ) { + s += '\''; + } + s += (String)v.elementAt(i); + if ( doQuote ) { + s += '\''; + } + s += ' '; + if ( i < (v.size() - 1) ) { + s += "$ "; + } + } + s += ')'; + } + return s; + } + + /** + * Keep track of qualifiers which are not predefined. + * @param name Name of qualifier + * @param value Value of qualifier. "" for no value + */ + public void setQualifier( String name, String value ) { + if ( properties == null ) { + properties = new Hashtable(); + } + if ( value != null ) { + properties.put( name, value ); + } else { + properties.remove( name ); + } + } + + /** + * Keep track of qualifiers which are not predefined. + * @param name Name of qualifier + * @param values Array of values + */ + public void setQualifier( String name, String[] values ) { + if ( values == null ) { + return; + } + if ( properties == null ) { + properties = new Hashtable(); + } + Vector v = new Vector(); + for( int i = 0; i < v.size(); i++ ) { + v.addElement( values[i] ); + } + properties.put( name, v ); + } + + /** + * Get the value of a qualifier which is not predefined. + * @param name Name of qualifier + * @return Value or values of qualifier. null if not + * present, a zero-length array if present but with no value + */ + public String[] getQualifier( String name ) { + if ( properties == null ) { + return null; + } + Object o = properties.get( name ); + if ( o == null ) { + return null; + } + if ( o instanceof Vector ) { + Vector v = (Vector)o; + String[] vals = new String[v.size()]; + v.copyInto( vals ); + return vals; + } + String s = (String)o; + if ( s.length() < 1 ) { + return new String[0]; + } else { + return new String[] { s }; + } + } + + /** + * Get an enumeration of all qualifiers which are not predefined. + * @return Enumeration of qualifiers. + */ + public Enumeration getQualifierNames() { + return properties.keys(); + } + + /** + * Create a string for use in toString with any qualifiers of the element. + * + * @param ignore Any qualifiers to NOT include + * @return A String with any known qualifiers + */ + String getQualifierString( String[] ignore ) { + Hashtable toIgnore = null; + if ( ignore != null ) { + toIgnore = new Hashtable(); + for( int i = 0; i < ignore.length; i++ ) { + toIgnore.put( ignore[i], ignore[i] ); + } + } + String s = ""; + Enumeration en = getQualifierNames(); + while( en.hasMoreElements() ) { + String qualifier = (String)en.nextElement(); + if ( (toIgnore != null) && toIgnore.containsKey( qualifier ) ) { + continue; + } + s += "; " + qualifier; + String[] vals = getQualifier( qualifier ); + if ( vals == null ) { + s += ' '; + continue; + } + s += ": "; + for( int i = 0; i < vals.length; i++ ) { + s += vals[i] + ' '; + } + } + return s; + } + + // Constants for known syntax types public static final int unknown = 0; public static final int cis = 1; public static final int binary = 2; @@ -237,8 +657,19 @@ public abstract class LDAPSchemaElement { "1.3.6.1.4.1.1466.115.121.1.27"; protected static final String dnString = "1.3.6.1.4.1.1466.115.121.1.12"; - protected String oid = ""; + // Predefined qualifiers which apply to any schema element type + public static final String OBSOLETE = "OBSOLETE"; + public static final String SUPERIOR = "SUP"; + + // Properties which are common to all schema elements + protected String oid = null; protected String name = ""; protected String description = ""; protected String attrName = null; + protected boolean obsolete = false; + protected String rawValue = null; + // Additional qualifiers + protected Hashtable properties = null; + // Qualifiers known to not have values + static protected Hashtable novalsTable = new Hashtable(); } diff --git a/mozilla/directory/java-sdk/ldapjdk/netscape/ldap/LDAPSearchConstraints.java b/mozilla/directory/java-sdk/ldapjdk/netscape/ldap/LDAPSearchConstraints.java index 28a74baf64c..35322c88b2b 100644 --- a/mozilla/directory/java-sdk/ldapjdk/netscape/ldap/LDAPSearchConstraints.java +++ b/mozilla/directory/java-sdk/ldapjdk/netscape/ldap/LDAPSearchConstraints.java @@ -12,7 +12,7 @@ * * 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 + * Copyright (C) 1999 Netscape Communications Corporation. All Rights * Reserved. */ package netscape.ldap; @@ -27,18 +27,13 @@ package netscape.ldap; * * @version 1.0 */ -public class LDAPSearchConstraints implements Cloneable { +public class LDAPSearchConstraints extends LDAPConstraints + implements Cloneable { - private int timeLimit; - private int serverTimeLimit; private int deref; private int maxRes; - private boolean referrals; private int batch; - private LDAPRebind m_rebind_proc; - private int m_hop_limit; - private LDAPControl[] m_clientControls; - private LDAPControl[] m_serverControls; + private int serverTimeLimit; transient private int m_maxBacklog = 100; /** @@ -46,16 +41,11 @@ public class LDAPSearchConstraints implements Cloneable { * the default set of search constraints. */ public LDAPSearchConstraints() { - timeLimit = 0; - serverTimeLimit = 0; + super(); deref = 0; maxRes = 1000; - referrals = false; batch = 1; - m_rebind_proc = null; - m_hop_limit = 10; - m_clientControls = null; - m_serverControls = null; + serverTimeLimit = 0; } /** @@ -79,7 +69,7 @@ public class LDAPSearchConstraints implements Cloneable { * (1 by default) * @param rebind_proc Specifies the object of the class that * implements the LDAPRebind interface (you need to - * define this class). The object will be using when the client + * define this class). The object will be used when the client * follows referrals automatically. The object provides the client * with a method for getting the distinguished name and password * used to authenticate to another LDAP server during a referral. @@ -93,15 +83,10 @@ public class LDAPSearchConstraints implements Cloneable { public LDAPSearchConstraints( int msLimit, int dereference, int maxResults, boolean doReferrals, int batchSize, LDAPRebind rebind_proc, int hop_limit) { - timeLimit = msLimit; + super(msLimit, doReferrals, rebind_proc, hop_limit); deref = dereference; maxRes = maxResults; - referrals = doReferrals; batch = batchSize; - m_rebind_proc = rebind_proc; - m_hop_limit = hop_limit; - m_clientControls = null; - m_serverControls = null; } /** @@ -125,9 +110,9 @@ public class LDAPSearchConstraints implements Cloneable { * a referral (False by default) * @param batchSize Specify the number of results to return at a time * (1 by default) - * @param rebind_proc Specifies the object of the class that - * implements the LDAPRebind interface (you need to - * define this class). The object will be using when the client + * @param rebind_proc Specifies the object that + * implements the LDAPRebind interface. + * The object will be used when the client * follows referrals automatically. The object provides the client * with a method for getting the distinguished name and password * used to authenticate to another LDAP server during a referral. @@ -144,26 +129,56 @@ public class LDAPSearchConstraints implements Cloneable { int batchSize, LDAPRebind rebind_proc, int hop_limit) { - timeLimit = msLimit; + super(msLimit, doReferrals, rebind_proc, hop_limit); serverTimeLimit = timeLimit; deref = dereference; maxRes = maxResults; - referrals = doReferrals; batch = batchSize; - m_rebind_proc = rebind_proc; - m_hop_limit = hop_limit; - m_clientControls = null; - m_serverControls = null; } /** - * Returns the maximum number of milliseconds to wait for any operation - * under these search constraints. If 0, there is no maximum time limit - * on waiting for the operation results. - * @return Maximum number of milliseconds to wait for operation results. + * Constructs a new LDAPSearchConstraints object and allows you + * to specify the search constraints in that object. + *

    + * @param msLimit Maximum time in milliseconds to wait for results (0 + * by default, which means that there is no maximum time limit) + * @param timeLimit Maximum time in seconds for the server to spend + * processing a search request (0 by default for no limit) + * @param dereference Either LDAPv2.DEREF_NEVER, + * LDAPv2.DEREF_FINDING, + * LDAPv2.DEREF_SEARCHING, or + * LDAPv2.DEREF_ALWAYS (see LDAPConnection.setOption). + * LDAPv2.DEREF_NEVER is the default. + * @param maxResults Maximum number of search results to return + * (1000 by default) + * @param doReferrals Specify true to follow referrals + * automatically, or False to throw an + * LDAPReferralException error if the server sends back + * a referral (False by default) + * @param batchSize Specify the number of results to return at a time + * (1 by default) + * @param bind_proc Specifies the object that + * implements the LDAPBind interface (you need to + * define this class). The object will be used to authenticate + * to the server on referrals. + * (This field is null by default.) + * @param hop_limit Maximum number of referrals to follow in a + * sequence when attempting to resolve a request. + * @see netscape.ldap.LDAPConnection#setOption(int, java.lang.Object) + * @see netscape.ldap.LDAPConnection#search(netscape.ldap.LDAPUrl, netscape.ldap.LDAPSearchConstraints) + * @see netscape.ldap.LDAPConnection#search(java.lang.String, int, java.lang.String, java.lang.String[], boolean, netscape.ldap.LDAPSearchConstraints) */ - public int getTimeLimit() { - return timeLimit; + public LDAPSearchConstraints( int msLimit, int timeLimit, + int dereference, + int maxResults, boolean doReferrals, + int batchSize, + LDAPBind bind_proc, + int hop_limit) { + super(msLimit, doReferrals, bind_proc, hop_limit); + serverTimeLimit = timeLimit; + deref = dereference; + maxRes = maxResults; + batch = batchSize; } /** @@ -200,17 +215,6 @@ public class LDAPSearchConstraints implements Cloneable { return maxRes; } - /** - * Specifies whether nor not referrals are followed automatically. - * Returns true if referrals are to be followed automatically, - * or false if referrals throw an LDAPReferralException. - * @return true if referrals are followed automatically, False - * if referrals throw an LDAPReferralException. - */ - public boolean getReferrals() { - return referrals; - } - /** * Returns the suggested number of results to return at a time during * search. This should be 0 if intermediate results are not needed, and @@ -221,63 +225,6 @@ public class LDAPSearchConstraints implements Cloneable { return batch; } - /** - * Returns the object that provides the method for getting - * authentication information. This object must belong to a class - * that implements the LDAPRebind interface. - * @return Object to be used to obtain information for - * authenticating to other LDAP servers during referrals. - * @see netscape.ldap.LDAPRebind - * @see netscape.ldap.LDAPRebindAuth - */ - public LDAPRebind getRebindProc() { - return m_rebind_proc; - } - - /** - * Returns the maximum number of hops to follow during a referral. - * @return Maximum number of hops to follow during a referral. - */ - public int getHopLimit() { - return m_hop_limit; - } - - /** - * Returns any client controls to be applied by the client - * to LDAP operations. - * @return Client controls to be applied by the client to LDAP operations. - * @see netscape.ldap.LDAPControl - * @see netscape.ldap.LDAPConnection#getOption - * @see netscape.ldap.LDAPConnection#setOption - */ - public LDAPControl[] getClientControls() { - return m_clientControls; - } - - /** - * Returns any server controls to be applied by the server - * to LDAP operations. - * @return Server controls to be applied by the server to LDAP operations. - * @see netscape.ldap.LDAPControl - * @see netscape.ldap.LDAPConnection#getOption - * @see netscape.ldap.LDAPConnection#setOption - */ - public LDAPControl[] getServerControls() { - return m_serverControls; - } - - /** - * Sets the maximum number of milliseconds to wait for any operation - * under these search constraints. If 0, there is no maximum time limit - * on waiting for the operation results. - * @param msLimit Maximum number of milliseconds to wait for operation - * results. - * (0 by default, which means that there is no maximum time limit.) - */ - public void setTimeLimit( int msLimit ) { - timeLimit = msLimit; - } - /** * Sets the maximum number of seconds for the server to spend * returning search results. If 0, there is no time limit. @@ -313,26 +260,6 @@ public class LDAPSearchConstraints implements Cloneable { maxRes = maxResults; } - /** - * Specifies whether nor not referrals are followed automatically. - * Returns true if referrals are to be followed automatically, - * or false if referrals throw an LDAPReferralException. - * (By default, this is set to false.) - *

    - * If you set this to true, you need to define a class that implements - * the LDAPRebind interface. You need to create an object of this class - * and pass the object to the setRebindProc method. This identifies the method - * for retrieving authentication information, which is used when connecting to other LDAP - * servers during referrals. - * @param doReferrals Set to true if referrals should be followed automatically, - * or False if referrals should throw an LDAPReferralException. - * @see netscape.ldap.LDAPRebind - * @see netscape.ldap.LDAPRebindAuth - */ - public void setReferrals( boolean doReferrals ) { - referrals = doReferrals; - } - /** * Sets the suggested number of results to return at a time during search. * This should be 0 if intermediate results are not needed, and 1 if @@ -343,69 +270,11 @@ public class LDAPSearchConstraints implements Cloneable { batch = batchSize; } - /** - * Specifies the object that provides the method for getting - * authentication information. This object must belong to a class - * that implements the LDAPRebind interface. - * (By default, this is null.) - * @param rebind_proc Object to be used to obtain information for - * authenticating to other LDAP servers during referrals. - */ - public void setRebindProc( LDAPRebind rebind_proc ) { - m_rebind_proc = rebind_proc; - } - - /** - * Sets maximum number of hops to follow in sequence during a referral. - * (By default, this is 10.) - * @param hop_limit Maximum number of hops to follow during a referral. - */ - public void setHopLimit( int hop_limit ) { - m_hop_limit = hop_limit; - } - - /** - * Sets a client control for LDAP operations. - * @param control Client control for LDAP operations. - * @see netscape.ldap.LDAPControl - */ - public void setClientControls( LDAPControl control ) { - m_clientControls = new LDAPControl[1]; - m_clientControls[0] = control; - } - - /** - * Sets an array of client controls for LDAP operations. - * @param controls Array of client controls for LDAP operations. - * @see netscape.ldap.LDAPControl - */ - public void setClientControls( LDAPControl[] controls ) { - m_clientControls = controls; - } - - /** - * Sets a server control for LDAP operations. - * @param control Server control for LDAP operations. - * @see netscape.ldap.LDAPControl - */ - public void setServerControls( LDAPControl control ) { - m_serverControls = new LDAPControl[1]; - m_serverControls[0] = control; - } - - /** - * Sets an array of server controls for LDAP operations. - * @param controls An array of server controls for LDAP operations. - * @see netscape.ldap.LDAPControl - */ - public void setServerControls( LDAPControl[] controls ) { - m_serverControls = controls; - } - /** * Set the maximum number of unread entries any search listener can * have before we stop reading from the server. * @param backlog The maximum number of unread entries per listener + * @deprecated Use LDAPConnection.getOption() */ public void setMaxBacklog( int backlog ) { m_maxBacklog = backlog; @@ -415,6 +284,7 @@ public class LDAPSearchConstraints implements Cloneable { * Get the maximum number of unread entries any search listener can * have before we stop reading from the server. * @return The maximum number of unread entries per listener + * @deprecated Use LDAPConnection.setOption() */ public int getMaxBacklog() { return m_maxBacklog; @@ -427,24 +297,43 @@ public class LDAPSearchConstraints implements Cloneable { public Object clone() { LDAPSearchConstraints o = new LDAPSearchConstraints(); - o.timeLimit = this.timeLimit; o.serverTimeLimit = this.serverTimeLimit; o.deref = this.deref; o.maxRes = this.maxRes; - o.referrals = this.referrals; o.batch = this.batch; - o.m_rebind_proc = this.m_rebind_proc; - o.m_hop_limit = this.m_hop_limit; - if ( (this.m_clientControls != null) && (this.m_clientControls.length > 0) ) { - o.m_clientControls = new LDAPControl[this.m_clientControls.length]; - for( int i = 0; i < this.m_clientControls.length; i++ ) - o.m_clientControls[i] = this.m_clientControls[i]; + + o.setHopLimit(this.getTimeLimit()); + o.setReferrals(this.getReferrals()); + o.setTimeLimit(this.getTimeLimit()); + + if (this.getBindProc() != null) { + o.setBindProc(this.getBindProc()); + } else { + o.setRebindProc(this.getRebindProc()); } - if ( (this.m_serverControls != null) && (this.m_serverControls.length > 0) ) { - o.m_serverControls = new LDAPControl[this.m_serverControls.length]; - for( int i = 0; i < this.m_serverControls.length; i++ ) - o.m_serverControls[i] = this.m_serverControls[i]; + + LDAPControl[] tClientControls = this.getClientControls(); + LDAPControl[] oClientControls; + + if ( (tClientControls != null) && + (tClientControls.length > 0) ) { + oClientControls = new LDAPControl[tClientControls.length]; + for( int i = 0; i < tClientControls.length; i++ ) + oClientControls[i] = (LDAPControl)tClientControls[i].clone(); + o.setClientControls(oClientControls); } + + LDAPControl[] tServerControls = this.getServerControls(); + LDAPControl[] oServerControls; + + if ( (tServerControls != null) && + (tServerControls.length > 0) ) { + oServerControls = new LDAPControl[tServerControls.length]; + for( int i = 0; i < tServerControls.length; i++ ) + oServerControls[i] = (LDAPControl)tServerControls[i].clone(); + o.setServerControls(oServerControls); + } + return o; } } diff --git a/mozilla/directory/java-sdk/ldapjdk/netscape/ldap/LDAPSearchListener.java b/mozilla/directory/java-sdk/ldapjdk/netscape/ldap/LDAPSearchListener.java index 996f311b6f1..97903d86454 100644 --- a/mozilla/directory/java-sdk/ldapjdk/netscape/ldap/LDAPSearchListener.java +++ b/mozilla/directory/java-sdk/ldapjdk/netscape/ldap/LDAPSearchListener.java @@ -12,7 +12,7 @@ * * 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 + * Copyright (C) 1999 Netscape Communications Corporation. All Rights * Reserved. */ package netscape.ldap; @@ -21,20 +21,11 @@ import java.util.*; import netscape.ldap.client.*; /** - * This implements an object for taking the asynchronous LDAP responses - * and making them appear somewhat synchronized. Uses built-in Java - * monitors to block until a response is received. This is also used - * to communicate exceptions across threads. This is used in particular - * for LDAP search operations; all other operations use the generic - * LDAPResponseListener object. + * Manages search results, references and responses returned on one or + * more search requests * - * This class is part of the implementation; it is of no interest to - * developers. - * - * @version 1.0 */ -class LDAPSearchListener extends LDAPResponseListener { - private Vector searchResults; +public class LDAPSearchListener extends LDAPMessageQueue { // this instance variable is only for cache purpose private Long m_key = null; @@ -42,78 +33,84 @@ class LDAPSearchListener extends LDAPResponseListener { /** * Constructs a LDAP search listener. + * @param asynchOp A flag whether the object is used for asynchronous + * LDAP operations + * @param cons LDAP Search Constraints + * @see netscape.ldap.LDAPAsynchronousConnection */ - LDAPSearchListener ( LDAPConnection conn, + LDAPSearchListener ( boolean asynchOp, LDAPSearchConstraints cons ) { - super ( conn ); + super ( asynchOp ); m_constraints = cons; - searchResults = new Vector (); } /** - * Informs the object of new search items (but not a response) - * @param newResult new search result message + * Block until all results are in. Used for synchronous search with + * batch size of zero. + * @return search response message + * @exception Network exception error */ - synchronized void addSearchResult (JDAPMessage newResult) { - searchResults.addElement (newResult); - notifyAll (); + LDAPResponse completeSearchOperation () throws LDAPException{ + return completeRequest(); } - /** - * Returns a collection of the currently-known search results. - * @return search result in enumeration - */ - Enumeration getSearchResults () { - return searchResults.elements (); - } /** - * Returns a count of the currently-known search results. - * @return search result count + * Blocks until a search result, reference or response is available, * or until all operations associated with the object have completed * or been canceled. + * + * @return A search result, search reference, or search response message + * or null if there is no more outstanding requests + * @exception LDAPException Network error exception + * @exception LDAPInterruptedException The invoking thread was interrupted + * @see LDAPResponse + * @see LDAPSearchResult + * @see LDAPSearchResultReference */ - int getCount () { - return searchResults.size (); - } + public LDAPMessage getResponse () throws LDAPException{ + LDAPMessage result = nextMessage(); - /** - * Resets the state of this object, so it can be recycled. - */ - void reset () { - super.reset(); - if ( searchResults != null ) - searchResults.removeAllElements(); - } - - /** - * Returns the next server search result. This method only makes - * sense in asynchronous mode. It will block if no new messages have - * been received since the last call to nextResult. If a response - * is received indicating that there are no more matches, this - * method returns null. - * @return jdap message - */ - JDAPMessage nextResult () { - JDAPMessage result; - synchronized( this ) { - while (searchResults.size() < 1) { - if (isResponseReceived()) { - searchResults.removeAllElements(); - return null; - } - try { - wait(); - } catch (InterruptedException e ) { - } - } - - result = (JDAPMessage)searchResults.elementAt (0); - /* Allow garbage collection to free this result */ - searchResults.removeElementAt (0); - } - getConnection().resultRetrieved(); + // Notify LDAPConnThread to wake up if backlog limit has been reached + if (result instanceof LDAPSearchResult || result instanceof LDAPSearchResultReference) { + LDAPConnection ld = getConnection(result.getId()); + if (ld != null) { + ld.resultRetrieved(); + } + } + return result; } + /** + * Merge two search listeners + * Move/append the content from another search listener to this one. + * + * To be used for synchronization of asynchronous LDAP operations where + * requests are sent by one thread but processed by another one + * + * A client may be implemented in such a way that one thread makes LDAP + * requests and calls l.getIDs(), while another thread is responsible for + * processing of responses (call l.getResponse()). Both threads are using + * the same listener objects. In such a case, a race + * condition may occur, where a LDAP response message is retrieved and + * the request terminated (request ID removed) before the first thread + * has a chance to execute l.getIDs(). + * The proper way to handle this scenario is to create a separate listener + * for each new request, and after l.getIDs() has been invoked, merge the + * new request with the existing one. + * @param listener2 The listener to be merged with. + */ + public void merge(LDAPSearchListener listener2) { + super.merge(listener2); + } + + /** + * Returns message ids for all outstanding requests + * @return Message id array + */ + public int[] getIDs() { + return super.getIDs(); + } + /** * Return the search constraints used to create this object * @return the search constraints used to create this object diff --git a/mozilla/directory/java-sdk/ldapjdk/netscape/ldap/LDAPSearchResult.java b/mozilla/directory/java-sdk/ldapjdk/netscape/ldap/LDAPSearchResult.java new file mode 100644 index 00000000000..d8d1ddf474b --- /dev/null +++ b/mozilla/directory/java-sdk/ldapjdk/netscape/ldap/LDAPSearchResult.java @@ -0,0 +1,68 @@ +/* -*- 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 netscape.ldap; + +import netscape.ldap.client.opers.JDAPSearchResponse; + +/** + * A LDAPSearchResult object encapsulates a single search result. + * + * @version 1.0 + */ +public class LDAPSearchResult extends LDAPMessage { + + /** + * LDAPEntry + */ + private LDAPEntry m_entry; + + /** + * Constructor + * + * @param msgid message identifier + * @param rsp Search operation response + * @param controls Array of controls of null + * @see netscape.ldap.LDAPEntry + */ + LDAPSearchResult(int msgid, JDAPSearchResponse rsp, LDAPControl[]controls) { + super(msgid, rsp, controls); + } + + /** + * Returns the entry of a server search response. + * @return An entry returned by the server in response to a search + * request + * @see netscape.ldap.LDAPEntry + */ + public LDAPEntry getEntry() { + if (m_entry == null) { + JDAPSearchResponse rsp = (JDAPSearchResponse)getProtocolOp(); + LDAPAttribute[] lattrs = rsp.getAttributes(); + LDAPAttributeSet attrs; + if ( lattrs != null ) { + attrs = new LDAPAttributeSet( lattrs ); + } + else { + attrs = new LDAPAttributeSet(); + } + String dn = rsp.getObjectName(); + m_entry = new LDAPEntry( dn, attrs ); + } + return m_entry; + } +} diff --git a/mozilla/directory/java-sdk/ldapjdk/netscape/ldap/LDAPSearchResultReference.java b/mozilla/directory/java-sdk/ldapjdk/netscape/ldap/LDAPSearchResultReference.java new file mode 100644 index 00000000000..7d23a2fff63 --- /dev/null +++ b/mozilla/directory/java-sdk/ldapjdk/netscape/ldap/LDAPSearchResultReference.java @@ -0,0 +1,54 @@ +/* -*- 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 netscape.ldap; + +import netscape.ldap.client.opers.JDAPSearchResultReference; +/** + * An LDAPSearchResultReference object encapsulates a continuation + * reference from a search operation. + * + * @version 1.0 + */ +public class LDAPSearchResultReference extends LDAPMessage { + + /** + * A lists of referred to Ldap URLs + */ + private String m_URLs[]; + + /** + * Constructor + * + * @param msgid message identifier + * @param resRef Search result reference response + * @param controls Array of controls of null + * @see netscape.ldap.LDAPEntry + */ + LDAPSearchResultReference(int msgid, JDAPSearchResultReference resRef, LDAPControl[]controls) { + super(msgid, resRef, controls); + m_URLs = resRef.getUrls(); + } + + /** + * Returns a list of referred to LDAP URLs + * @return A list of URLs + */ + public String[] getUrls() { + return m_URLs; + } +} diff --git a/mozilla/directory/java-sdk/ldapjdk/netscape/ldap/LDAPSearchResults.java b/mozilla/directory/java-sdk/ldapjdk/netscape/ldap/LDAPSearchResults.java index 682a4a57ce7..894a17dc7a4 100644 --- a/mozilla/directory/java-sdk/ldapjdk/netscape/ldap/LDAPSearchResults.java +++ b/mozilla/directory/java-sdk/ldapjdk/netscape/ldap/LDAPSearchResults.java @@ -12,7 +12,7 @@ * * 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 + * Copyright (C) 1999 Netscape Communications Corporation. All Rights * Reserved. */ package netscape.ldap; @@ -53,6 +53,7 @@ public class LDAPSearchResults implements Enumeration { private boolean currAttrsOnly; private Vector referralResults = new Vector(); private Vector exceptions; + private LDAPControl[] responseControls = null; // only used for the persistent search private boolean firstResult = false; @@ -112,46 +113,34 @@ public class LDAPSearchResults implements Enumeration { currAttrsOnly = attrsOnly; } - void add( JDAPProtocolOp sr ) { - if (sr instanceof JDAPSearchResponse) - add((JDAPSearchResponse)sr); - else if (sr instanceof JDAPSearchResultReference) - add((JDAPSearchResultReference)sr); + /** + * Add search entry of referral + * @param msg LDAPSearchResult or LDAPsearchResultReference + */ + void add( LDAPMessage msg ) { + if (msg instanceof LDAPSearchResult) { + entries.addElement( ((LDAPSearchResult)msg).getEntry()); + } + else if (msg instanceof LDAPSearchResultReference) { + /* convert to LDAPReferralException */ + String urls[] = ((LDAPSearchResultReference)msg).getUrls(); + if (urls != null) { + if (exceptions == null) { + exceptions = new Vector(); + } + exceptions.addElement(new LDAPReferralException(null, 0, urls)); + } + } } /** - * Adds one result of a search (from JDAP) to the object. - * @param sr A search response in JDAP format + * Add exception + * @param e exception */ - void add( JDAPSearchResponse sr ) { - LDAPAttribute[] lattrs = sr.getAttributes(); - LDAPAttributeSet attrs; - if ( lattrs != null ) - attrs = new LDAPAttributeSet( lattrs ); - else - attrs = new LDAPAttributeSet(); - - String dn = sr.getObjectName(); - LDAPEntry entry = new LDAPEntry( dn, attrs ); - entries.addElement( entry ); - } - - /** - * Adds search reference to this object. - */ - void add( JDAPSearchResultReference sr ) { - /* convert to LDAPReferralException */ - String urls[] = sr.getUrls(); - if (urls != null) { - if (exceptions == null) - exceptions = new Vector(); - exceptions.addElement(new LDAPReferralException(null, 0, urls)); - } - } - void add(LDAPException e) { - if (exceptions == null) + if (exceptions == null) { exceptions = new Vector(); + } exceptions.addElement(e); } @@ -223,6 +212,26 @@ public class LDAPSearchResults implements Enumeration { quicksort (toSort, compare, shigh+1, high); } + /** + * Sets the response controls for this search result. + * @param controls The controls to be stored. + */ + protected void setResponseControls(LDAPControl[] controls) { + responseControls = controls; + } + + /** + * Returns the controls returned with this search result. If any control + * is registered with LDAPControl, an attempt is made to + * instantiate the control. If the instantiation fails, the control is + * returned as a basic LDAPControl. + * @return An array of type LDAPControl + * @see netscape.ldap.LDAPControl#register + */ + public LDAPControl[] getResponseControls() { + return responseControls; + } + /** * Sorts the search results. *

    @@ -452,8 +461,10 @@ public class LDAPSearchResults implements Enumeration { totalReferralEntries = totalReferralEntries+res.getCount(); } if (resultSource != null) { - count = resultSource.getCount(); - } + count = resultSource.getMessageCount(); + } else { + count = entries.size(); + } if (exceptions != null) return (count + exceptions.size() + totalReferralEntries); return (count + totalReferralEntries); @@ -492,13 +503,20 @@ public class LDAPSearchResults implements Enumeration { return; } - JDAPMessage msg = resultSource.nextResult(); - if (msg == null) { + LDAPMessage msg = null; + try { + msg = resultSource.nextMessage(); + } catch (LDAPException e) { + add(e); + currConn.releaseSearchListener(resultSource); + return; + } + + if (msg instanceof LDAPResponse) { try { // check response and see if we need to do referral // v2: referral stored in the JDAPResult - JDAPMessage response = resultSource.getResponse(); - currConn.checkSearchMsg(this, response, currCons, + currConn.checkSearchMsg(this, msg, currCons, currBase, currScope, currFilter, currAttrs, currAttrsOnly); } catch (LDAPException e) { System.err.println("Exception: "+e); diff --git a/mozilla/directory/java-sdk/ldapjdk/netscape/ldap/LDAPSortKey.java b/mozilla/directory/java-sdk/ldapjdk/netscape/ldap/LDAPSortKey.java index af27cece4f4..1e90b53fe91 100644 --- a/mozilla/directory/java-sdk/ldapjdk/netscape/ldap/LDAPSortKey.java +++ b/mozilla/directory/java-sdk/ldapjdk/netscape/ldap/LDAPSortKey.java @@ -41,7 +41,6 @@ public class LDAPSortKey { * attribute in reverse order and use the matching rule identified * by the OID 1.2.3.4)

    * - * @see netscape.ldap.controls.LDAPPagedControl * @see netscape.ldap.controls.LDAPSortControl * @see netscape.ldap.controls.LDAPVirtualListControl */ @@ -76,7 +75,6 @@ public class LDAPSortKey { * * @param reverse If true, the sorting is done in * descending order. - * @see netscape.ldap.controls.LDAPPagedControl * @see netscape.ldap.controls.LDAPSortControl * @see netscape.ldap.controls.LDAPVirtualListControl */ @@ -102,7 +100,6 @@ public class LDAPSortKey { * descending order. * @param matchRule Object ID (OID) of the matching rule for * the attribute (for example, 1.2.3.4). - * @see netscape.ldap.controls.LDAPPagedControl * @see netscape.ldap.controls.LDAPSortControl * @see netscape.ldap.controls.LDAPVirtualListControl */ diff --git a/mozilla/directory/java-sdk/ldapjdk/netscape/ldap/LDAPv2.java b/mozilla/directory/java-sdk/ldapjdk/netscape/ldap/LDAPv2.java index 7029d516ad7..0d7520bb62d 100644 --- a/mozilla/directory/java-sdk/ldapjdk/netscape/ldap/LDAPv2.java +++ b/mozilla/directory/java-sdk/ldapjdk/netscape/ldap/LDAPv2.java @@ -12,7 +12,7 @@ * * 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 + * Copyright (C) 1999 Netscape Communications Corporation. All Rights * Reserved. */ package netscape.ldap; @@ -87,6 +87,14 @@ public interface LDAPv2 { */ public static final int TIMELIMIT = 4; + /** + * Option specifying the maximum number of milliseconds the + * server should wait when returning search results. + * @see netscape.ldap.LDAPConnection#getOption + * @see netscape.ldap.LDAPConnection#setOption + */ + public static final int SERVER_TIMELIMIT = 5; + /** * Option specifying whether or not referrals to other LDAP * servers are followed automatically. @@ -118,6 +126,15 @@ public interface LDAPv2 { * @see netscape.ldap.LDAPConnection#setOption */ public static final int REFERRALS_HOP_LIMIT = 10; + + /** + * Option specifying the object containing the method for + * authenticating to the server. + * @see netscape.ldap.LDAPConnection#getOption + * @see netscape.ldap.LDAPConnection#setOption + * @see netscape.ldap.LDAPBind + */ + public static final int BIND = 13; /** * Option specifying the version of the LDAP protocol @@ -230,6 +247,15 @@ public interface LDAPv2 { */ public void disconnect() throws LDAPException; + /** + * Notifies the server to not send additional results associated with this + * LDAPSearchResults object, and discards any results already + * received. + * @param results LDAPSearchResults object returned from a search. + * @exception LDAPException Failed to notify the server. + */ + public void abandon(LDAPSearchResults results) throws LDAPException; + /** * Authenticates user with the LDAP server. * @param Dn Distinguished name to use for authentication. @@ -238,6 +264,14 @@ public interface LDAPv2 { */ public void authenticate (String DN, String passwd) throws LDAPException; + /** + * Authenticates user with the LDAP server. + * @param Dn Distinguished name to use for authentication. + * @param passwd Password for authentication. + * @exception LDAPException Failed to authenticate to the server. + */ + public void bind (String DN, String passwd) throws LDAPException; + /** * Read the entry corresponding to the specified distinguished name (DN). * @param DN Distinguished name of the entry to retrieve. @@ -328,7 +362,7 @@ public interface LDAPv2 { * @exception LDAPException Failed to perform the comparison. */ public boolean compare (String DN, LDAPAttribute attr, - LDAPSearchConstraints cons) throws LDAPException; + LDAPConstraints cons) throws LDAPException; /** * Adds an entry to the directory. @@ -343,7 +377,7 @@ public interface LDAPv2 { * @param cons The constraints set for the add operation. * @exception LDAPException Failed to add the entry to the directory. */ - public void add (LDAPEntry entry, LDAPSearchConstraints cons) + public void add (LDAPEntry entry, LDAPConstraints cons) throws LDAPException; /** @@ -362,7 +396,7 @@ public interface LDAPv2 { * @exception LDAPException Failed to modify the specified entry. */ public void modify (String DN, LDAPModification mod, - LDAPSearchConstraints cons) throws LDAPException; + LDAPConstraints cons) throws LDAPException; /** * Modifies the attributes of a directory entry. @@ -380,7 +414,7 @@ public interface LDAPv2 { * @exception LDAPException Failed to modify the specified entry. */ public void modify (String DN, LDAPModificationSet mods, - LDAPSearchConstraints cons ) throws LDAPException; + LDAPConstraints cons ) throws LDAPException; /** * Removes an entry from the directory. @@ -395,7 +429,7 @@ public interface LDAPv2 { * @param cons The constraints set for the delete operation. * @exception LDAPException Failed to remove the entry from the directory. */ - public void delete( String DN, LDAPSearchConstraints cons ) + public void delete( String DN, LDAPConstraints cons ) throws LDAPException; /** @@ -421,13 +455,14 @@ public interface LDAPv2 { * @exception LDAPException Failed to rename the entry in the directory. */ public void rename ( String DN, String newRDN, boolean deleteOldRDN, - LDAPSearchConstraints cons ) throws LDAPException; + LDAPConstraints cons ) throws LDAPException; /** * Retrieves an option that applies to the connection. * The particular meaning may be implementation-dependent. - * The standard options are the search options described by - * the LDAPSearchConstraints class. + * The standard options are the options described by + * the LDAPSearchConstraints and LDAPConstraints + * classes. * @exception LDAPException Failed to retrieve the value of the specified option. */ public Object getOption( int option ) throws LDAPException; @@ -435,8 +470,9 @@ public interface LDAPv2 { /** * Sets an option that applies to the connection. * The particular meaning may be implementation-dependent. - * The standard options are the search options described by - * the LDAPSearchConstraints class. + * The standard options are the options described by + * the LDAPSearchConstraints and LDAPConstraints + * classes. * @exception LDAPException Failed to set the specified option. */ public void setOption( int option, Object value ) throws LDAPException; diff --git a/mozilla/directory/java-sdk/ldapjdk/netscape/ldap/LDAPv3.java b/mozilla/directory/java-sdk/ldapjdk/netscape/ldap/LDAPv3.java index add0bf87a62..90b4fc06265 100644 --- a/mozilla/directory/java-sdk/ldapjdk/netscape/ldap/LDAPv3.java +++ b/mozilla/directory/java-sdk/ldapjdk/netscape/ldap/LDAPv3.java @@ -12,7 +12,7 @@ * * 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 + * Copyright (C) 1999 Netscape Communications Corporation. All Rights * Reserved. */ package netscape.ldap; @@ -71,6 +71,28 @@ public interface LDAPv3 extends LDAPv2 { String passwd) throws LDAPException; + /** + * Authenticates to the LDAP server (that the object is currently + * connected to) using the specified name and password, with the + * specified LDAP protocol version. If the server does not support + * the requested protocol version, an exception is thrown. If the + * object has been disconnected from an LDAP server, this method + * attempts to reconnect to the server. If the object had already + * authenticated, the old authentication is discarded. + * @param version LDAP protocol version requested: currently 2 or 3. + * @param dn If non-null and non-empty, specifies that the + * connection and all operations through it should be + * authenticated with dn as the distinguished name. + * @param passwd If non-null and non-empty, specifies that the + * connection and all operations through it should be + * authenticated with passwd as password. + * @exception LDAPException Failed to authenticate to the LDAP server. + */ + public void bind(int version, + String dn, + String passwd) + throws LDAPException; + /** * Performs an extended operation on the directory. Extended operations * are part of version 3 of the LDAP protocol. @@ -107,7 +129,7 @@ public interface LDAPv3 extends LDAPv2 { * @exception LDAPException Failed to rename the specified entry. */ public void rename( String DN, String newRDN, String newParentDN, - boolean deleteOldRDN, LDAPSearchConstraints cons ) throws LDAPException; + boolean deleteOldRDN, LDAPConstraints cons ) throws LDAPException; /** * Returns an array of the latest controls (if any) from server. diff --git a/mozilla/directory/java-sdk/ldapjdk/netscape/ldap/ber/stream/BERAny.java b/mozilla/directory/java-sdk/ldapjdk/netscape/ldap/ber/stream/BERAny.java index 06dde211edb..0c2592ac97e 100644 --- a/mozilla/directory/java-sdk/ldapjdk/netscape/ldap/ber/stream/BERAny.java +++ b/mozilla/directory/java-sdk/ldapjdk/netscape/ldap/ber/stream/BERAny.java @@ -12,7 +12,7 @@ * * 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 + * Copyright (C) 1999 Netscape Communications Corporation. All Rights * Reserved. */ package netscape.ldap.ber.stream; @@ -21,12 +21,12 @@ import java.util.*; import java.io.*; /** - * This class is for the any object that is unknown to the + * This class is for the "any" object that is unknown to the * BER package but conforms to BER rules. * *

      * Encoding Rule:
    - *   The encoding is the encoding of the chosen type.
    + *   The encoding is that of the particular implementation.
      * 
    * * @version 1.0 @@ -39,7 +39,7 @@ public class BERAny extends BERElement { private BERElement m_value = null; /** - * Constructs an any element. + * Constructs an "any" element. * @param value BERElement value */ public BERAny(BERElement value) { @@ -47,10 +47,10 @@ public class BERAny extends BERElement { } /** - * Constructs an any element with the input stream. - * Note that with the current decoding architecture any types - * will not be decoded as any's but rather only as the types - * chosen. The following method will never be called. + * Constructs an "any" element from an input stream. + * Note that with the current decoding architecture "any" types + * will not be decoded as any's but rather as the particular + * implementation. The following method will never be called. * @param stream input stream * @param bytes_read array of 1 int; value incremented by * number of bytes read from stream. @@ -62,7 +62,7 @@ public class BERAny extends BERElement { } /** - * Sends the BER encoding directly to stream. + * Sends the BER encoding directly to an output stream. * @param stream output stream */ public void write(OutputStream stream) throws IOException { diff --git a/mozilla/directory/java-sdk/ldapjdk/netscape/ldap/ber/stream/BERBitString.java b/mozilla/directory/java-sdk/ldapjdk/netscape/ldap/ber/stream/BERBitString.java index 2857436126e..1fde1e4ba55 100644 --- a/mozilla/directory/java-sdk/ldapjdk/netscape/ldap/ber/stream/BERBitString.java +++ b/mozilla/directory/java-sdk/ldapjdk/netscape/ldap/ber/stream/BERBitString.java @@ -12,7 +12,7 @@ * * 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 + * Copyright (C) 1999 Netscape Communications Corporation. All Rights * Reserved. */ package netscape.ldap.ber.stream; @@ -28,6 +28,12 @@ import java.io.*; * the number of bits ourselves in m_value_num_bits. * Change is required when BitSet is fixed. * + *
    + * ENCODING RULE:
    + *   Primitive Definite length.
    + *   tag = 0x03
    + * 
    + * * @version 1.0 * @seeAlso CCITT X.209 */ @@ -47,9 +53,9 @@ public class BERBitString extends BERElement { } /** - * Constructs a bitstring element with the input stream. + * Constructs a bitstring element from an input stream * (for constructed encodings). - * @param stream input stream + * @param stream source * @param bytes_read array of 1 int; value incremented by * number of bytes read from stream. * @exception IOException failed to construct @@ -110,9 +116,9 @@ public class BERBitString extends BERElement { } /** - * Constructs a bitstring element with the input stream. + * Constructs a bitstring element from an input stream * (for primitive encodings). - * @param stream input stream + * @param stream source * @param bytes_read array of 1 int; value incremented by * number of bytes read from stream. * @exception IOException failed to construct @@ -166,7 +172,7 @@ public class BERBitString extends BERElement { } /** - * Sends the BER encoding directly to stream. + * Sends the BER encoding directly to a stream. * Always sends in primitive form. * @param stream output stream */ diff --git a/mozilla/directory/java-sdk/ldapjdk/netscape/ldap/ber/stream/BERBoolean.java b/mozilla/directory/java-sdk/ldapjdk/netscape/ldap/ber/stream/BERBoolean.java index be21c2295e6..17143e319fc 100644 --- a/mozilla/directory/java-sdk/ldapjdk/netscape/ldap/ber/stream/BERBoolean.java +++ b/mozilla/directory/java-sdk/ldapjdk/netscape/ldap/ber/stream/BERBoolean.java @@ -12,7 +12,7 @@ * * 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 + * Copyright (C) 1999 Netscape Communications Corporation. All Rights * Reserved. */ package netscape.ldap.ber.stream; @@ -53,8 +53,8 @@ public class BERBoolean extends BERElement { } /** - * Constructs a boolean element with the input stream. - * @param stream input stream + * Constructs a boolean element from an input stream. + * @param stream source * @param bytes_read array of 1 int; value incremented by * number of bytes read from stream. * @exception IOException failed to construct @@ -72,7 +72,7 @@ public class BERBoolean extends BERElement { } /** - * Sends the BER encoding directly to stream. + * Sends the BER encoding directly to a stream. * @param stream output stream */ public void write(OutputStream stream) throws IOException { diff --git a/mozilla/directory/java-sdk/ldapjdk/netscape/ldap/ber/stream/BERCharacterString.java b/mozilla/directory/java-sdk/ldapjdk/netscape/ldap/ber/stream/BERCharacterString.java index 0aee70eeeb9..5a9cbaa8751 100644 --- a/mozilla/directory/java-sdk/ldapjdk/netscape/ldap/ber/stream/BERCharacterString.java +++ b/mozilla/directory/java-sdk/ldapjdk/netscape/ldap/ber/stream/BERCharacterString.java @@ -12,7 +12,7 @@ * * 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 + * Copyright (C) 1999 Netscape Communications Corporation. All Rights * Reserved. */ package netscape.ldap.ber.stream; @@ -33,24 +33,22 @@ public abstract class BERCharacterString extends BERElement { protected String m_value = null; /** - * Constructs an characterstring element containing buffer. + * Constructs a character string element containing a buffer. */ public BERCharacterString() { } /** - * Constructs an octetstring element containing buffer. - * @param buffer string containing 'octets' + * Constructs a character string element containing buffer. + * @param buffer a string value */ public BERCharacterString(String string) { m_value = string; } /** - * Constructs a characterstring element from buffer. - * @param buffer buffer containing 'octets' - * @param start start of buffer range to copy - * @param end end of buffer range to copy + * Constructs a character string element from a byte array. + * @param buffer buffer containing UTF8 data */ public BERCharacterString(byte[] buffer) { try{ @@ -60,7 +58,7 @@ public abstract class BERCharacterString extends BERElement { } /** - * Constructs a characterstring element with the input stream. + * Constructs a character string element from an input stream * (for constructed encoding) * @param stream input stream * @param bytes_read array of 1 int, incremented by number of bytes read. @@ -113,9 +111,9 @@ public abstract class BERCharacterString extends BERElement { } /** - * Constructs a characterstring element with the input stream. + * Constructs a character string element from an input stream * (for primitive encoding) - * @param stream input stream + * @param stream source * @param bytes_read array of 1 int, incremented by number of bytes read. * @exception IOException failed to construct */ @@ -137,12 +135,12 @@ public abstract class BERCharacterString extends BERElement { } } + private byte[] byte_buf; + /** * Writes BER to stream. * @param stream output stream */ - private byte[] byte_buf; - public void write(OutputStream stream) throws IOException { stream.write(getType()); /* tag */ if (m_value == null) { diff --git a/mozilla/directory/java-sdk/ldapjdk/netscape/ldap/ber/stream/BERChoice.java b/mozilla/directory/java-sdk/ldapjdk/netscape/ldap/ber/stream/BERChoice.java index 5de24f47df8..42d7646fd8d 100644 --- a/mozilla/directory/java-sdk/ldapjdk/netscape/ldap/ber/stream/BERChoice.java +++ b/mozilla/directory/java-sdk/ldapjdk/netscape/ldap/ber/stream/BERChoice.java @@ -12,7 +12,7 @@ * * 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 + * Copyright (C) 1999 Netscape Communications Corporation. All Rights * Reserved. */ package netscape.ldap.ber.stream; @@ -21,12 +21,12 @@ import java.util.*; import java.io.*; /** - * This class is for the Choice object. Note that this class will be - * used by the client. + * This class is for the Choice object. Note that this class may be + * used by a client. * *
      * ENCODING RULE:
    - *   Encoding is the encoding of the chosen type.
    + *   Encoding is the encoding of the specific type used.
      * 
    * * @version 1.0 @@ -40,17 +40,17 @@ public class BERChoice extends BERElement { /** * Constructs a choice element. - * @param value BERElement value + * @param value Any BERElement value */ public BERChoice(BERElement value) { m_value = value; } /** - * Constructs a choice element with the input stream. + * Constructs a choice element from an input stream. * Note that with the current decoding architecture choice types - * will not be decoded as choices but rather only as the types - * chosen. The following method will never be called. + * will not be decoded as choices but rather as the types + * chosen. The following method will never be called. * @param stream input stream * @param bytes_read array of 1 int; value incremented by * number of bytes read from stream. @@ -62,9 +62,9 @@ public class BERChoice extends BERElement { } /** - * Sends the BER encoding of the chosen type directly to stream. + * Sends the BER encoding of the chosen type directly to a stream. * @param stream output stream - * @exception IOException failed to construct + * @exception IOException failed to write */ public void write(OutputStream stream) throws IOException { m_value.write(stream); diff --git a/mozilla/directory/java-sdk/ldapjdk/netscape/ldap/ber/stream/BERConstruct.java b/mozilla/directory/java-sdk/ldapjdk/netscape/ldap/ber/stream/BERConstruct.java index 8676ca0b33d..ea6bb31ad80 100644 --- a/mozilla/directory/java-sdk/ldapjdk/netscape/ldap/ber/stream/BERConstruct.java +++ b/mozilla/directory/java-sdk/ldapjdk/netscape/ldap/ber/stream/BERConstruct.java @@ -12,7 +12,7 @@ * * 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 + * Copyright (C) 1999 Netscape Communications Corporation. All Rights * Reserved. */ package netscape.ldap.ber.stream; @@ -21,7 +21,7 @@ import java.util.*; import java.io.*; /** - * This abstract class serves as a based class for constructs + * This abstract class serves as a based class for constructed * types such as sequence or set. * * @version 1.0 @@ -40,7 +40,7 @@ public abstract class BERConstruct extends BERElement { } /** - * Constructs a construct element with the input stream. + * Constructs a construct element from an input stream. * @param decoder decoder for application specific ber * @param stream input stream from socket * @param bytes_read array of 1 int; value incremented by number @@ -99,7 +99,7 @@ public abstract class BERConstruct extends BERElement { } /** - * Sends the BER encoding directly to stream. + * Sends the BER encoding directly to a stream. * @param stream output stream * @exception IOException failed to send */ diff --git a/mozilla/directory/java-sdk/ldapjdk/netscape/ldap/ber/stream/BERElement.java b/mozilla/directory/java-sdk/ldapjdk/netscape/ldap/ber/stream/BERElement.java index afc0dd0dd73..5d778f6a848 100644 --- a/mozilla/directory/java-sdk/ldapjdk/netscape/ldap/ber/stream/BERElement.java +++ b/mozilla/directory/java-sdk/ldapjdk/netscape/ldap/ber/stream/BERElement.java @@ -12,7 +12,7 @@ * * 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 + * Copyright (C) 1999 Netscape Communications Corporation. All Rights * Reserved. */ package netscape.ldap.ber.stream; @@ -21,13 +21,13 @@ import java.util.*; import java.io.*; /** - * This class is for the tagged object. Nested tag is + * This class is for the tagged object type. A nested tag is * allowed. A tagged element contains another BER element. * * @version 1.0 * @seeAlso CCITT X.209 */ -public abstract class BERElement { +public abstract class BERElement implements Serializable { /** * Possible element types. */ @@ -85,9 +85,9 @@ public abstract class BERElement { /** * Gets a ber element from the input stream. * @param decoder decoder for application specific ber - * @param stream stream where ber encoding comes + * @param stream source of ber encoding * @param bytes_read array of 1 int; value incremented by - * number of bytes fread from stream. + * number of bytes read from stream. * @exception IOException failed to decode an element. */ public static BERElement getElement(BERTagDecoder decoder, @@ -150,7 +150,8 @@ public abstract class BERElement { } /** - * Decodes length octets from stream. + * Reads and decodes a length byte and then that many octets + * from the input stream. * @param stream input stream to read from * @param bytes_read array of 1 int; value incremented by * number of bytes read from stream. @@ -227,11 +228,12 @@ public abstract class BERElement { } /** - * Reads the unsigned binary from the input stream. - * @param stream inputstream - * @param byte_read number of byte read - * @param length length of the byte to be read - * @return the value of the two complement + * Reads a number of bytes from an input stream and form + * an integer.. + * @param stream source of data + * @param bytes_read number of bytes read + * @param length number of bytes to be read (1 - 4) + * @return the value of the data as two's complement * @exception IOException failed to read octets */ protected int readUnsignedBinary(InputStream stream, @@ -249,11 +251,12 @@ public abstract class BERElement { } /** - * Reads the two complement representation from the input stream. - * @param stream inputstream - * @param byte_read number of byte read - * @param length length of the byte to be read - * @return the value of the two complement + * Reads the two's complement representation of an integer from + * an input stream. + * @param stream source of data + * @param bytes_read number of bytes read + * @param length number of bytes to be read + * @return the integer value as two's complement * @exception IOException failed to read octets */ protected int readTwosComplement(InputStream stream, @@ -295,7 +298,7 @@ public abstract class BERElement { } /** - * Sends the BER encoding directly to stream. + * Sends the BER encoding directly to a stream. * @param stream output stream. * @return bytes written to stream. */ diff --git a/mozilla/directory/java-sdk/ldapjdk/netscape/ldap/ber/stream/BEREnumerated.java b/mozilla/directory/java-sdk/ldapjdk/netscape/ldap/ber/stream/BEREnumerated.java index 380c84e32b8..fb53f1b3fa5 100644 --- a/mozilla/directory/java-sdk/ldapjdk/netscape/ldap/ber/stream/BEREnumerated.java +++ b/mozilla/directory/java-sdk/ldapjdk/netscape/ldap/ber/stream/BEREnumerated.java @@ -12,7 +12,7 @@ * * 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 + * Copyright (C) 1999 Netscape Communications Corporation. All Rights * Reserved. */ package netscape.ldap.ber.stream; @@ -23,13 +23,25 @@ import java.io.*; /** * This class is for the Enumerated object. * + *
    + * ENCODING RULE:
    + *   Primitive Definite length.
    + *   tag = 0x0a
    + *   length = (short or long form)
    + *   one or more contents octets hold integral value
    + *   value in two's complement
    + *
    + * Example:  (Enumerated - 1)
    + *   0A 01 01
    + * 
    + * * @version 1.0 * @seeAlso CCITT X.209 */ public class BEREnumerated extends BERIntegral { /** - * Constructs an enumerated element with value. + * Constructs an enumerated element with a value. * @param value integral value */ public BEREnumerated(int value) { diff --git a/mozilla/directory/java-sdk/ldapjdk/netscape/ldap/ber/stream/BERInteger.java b/mozilla/directory/java-sdk/ldapjdk/netscape/ldap/ber/stream/BERInteger.java index 570c0696800..df2e593cd8e 100644 --- a/mozilla/directory/java-sdk/ldapjdk/netscape/ldap/ber/stream/BERInteger.java +++ b/mozilla/directory/java-sdk/ldapjdk/netscape/ldap/ber/stream/BERInteger.java @@ -12,7 +12,7 @@ * * 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 + * Copyright (C) 1999 Netscape Communications Corporation. All Rights * Reserved. */ package netscape.ldap.ber.stream; diff --git a/mozilla/directory/java-sdk/ldapjdk/netscape/ldap/ber/stream/BERIntegral.java b/mozilla/directory/java-sdk/ldapjdk/netscape/ldap/ber/stream/BERIntegral.java index 3645ccd841c..fb2379d9b59 100644 --- a/mozilla/directory/java-sdk/ldapjdk/netscape/ldap/ber/stream/BERIntegral.java +++ b/mozilla/directory/java-sdk/ldapjdk/netscape/ldap/ber/stream/BERIntegral.java @@ -12,7 +12,7 @@ * * 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 + * Copyright (C) 1999 Netscape Communications Corporation. All Rights * Reserved. */ package netscape.ldap.ber.stream; @@ -47,12 +47,12 @@ import java.io.*; */ public abstract class BERIntegral extends BERElement { /** - * Internal variables + * Value of element */ private int m_value; /** - * Constructs an integral type. + * Constructs an integral type with a value. * @param value integer value */ public BERIntegral(int value) { @@ -60,8 +60,8 @@ public abstract class BERIntegral extends BERElement { } /** - * Constructs an integral element with the input stream. - * @param stream input stream + * Constructs an integral element from an input stream. + * @param stream source * @param bytes_read array of 1 int; value incremented by * number of bytes read from stream * @exception IOException failed to construct @@ -95,8 +95,8 @@ public abstract class BERIntegral extends BERElement { /** * Writes BER to stream. - * @return number of bytes written to stream. - * @exception IOException failed to send + * @param stream output stream. + * @exception IOException on failure to write */ public void write(OutputStream stream) throws IOException { int binary_value = m_value; @@ -121,9 +121,11 @@ public abstract class BERIntegral extends BERElement { do { if (m_value < 0) - content_octets[num_content_octets+offset] = (byte)((binary_value^0xFF)&0xFF); + content_octets[num_content_octets+offset] = + (byte)((binary_value^0xFF)&0xFF); else - content_octets[num_content_octets+offset] = (byte)(binary_value&0xFF); + content_octets[num_content_octets+offset] = + (byte)(binary_value&0xFF); binary_value = (binary_value>>8); num_content_octets++; @@ -134,7 +136,7 @@ public abstract class BERIntegral extends BERElement { for (i=0; i + * ENCODING RULE: + * Primitive Definite length. + * tag = 0x12 + * length = (short or long form) + * one or more contents octets + *
  • * * @version 1.0 * @seeAlso CCITT X.209 @@ -29,15 +37,15 @@ import java.io.*; public class BERNumericString extends BERCharacterString { /** - * Constructs an octetstring element containing buffer. - * @param buffer string containing 'octets' + * Constructs a numeric string element from a string + * @param buffer string with value of element */ public BERNumericString(String string) { m_value = string; } /** - * Constructs a numericstring element from buffer. + * Constructs a numeric string element from a byte array * @param buffer buffer */ public BERNumericString(byte[] buffer) { @@ -45,9 +53,9 @@ public class BERNumericString extends BERCharacterString { } /** - * Constructs a numericstring element with the input stream. + * Constructs a numeric string element from an input stream * (for constructed encoding) - * @param stream input stream + * @param stream source * @param bytes_read array of 1 int, incremented by number of bytes read. * @exception IOException failed to construct */ @@ -57,7 +65,7 @@ public class BERNumericString extends BERCharacterString { } /** - * Constructs a numericstring element with the input stream. + * Constructs a numericstring element from an input stream * (for primitive encoding) * @param stream input stream * @param bytes_read array of 1 int, incremented by number of bytes read. @@ -70,7 +78,7 @@ public class BERNumericString extends BERCharacterString { /** * Gets the element type. - * @param element type + * @return element type */ public int getType() { return BERElement.NUMERICSTRING; diff --git a/mozilla/directory/java-sdk/ldapjdk/netscape/ldap/ber/stream/BERObjectId.java b/mozilla/directory/java-sdk/ldapjdk/netscape/ldap/ber/stream/BERObjectId.java index c02cdca5877..466fcb85b85 100644 --- a/mozilla/directory/java-sdk/ldapjdk/netscape/ldap/ber/stream/BERObjectId.java +++ b/mozilla/directory/java-sdk/ldapjdk/netscape/ldap/ber/stream/BERObjectId.java @@ -12,7 +12,7 @@ * * 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 + * Copyright (C) 1999 Netscape Communications Corporation. All Rights * Reserved. */ package netscape.ldap.ber.stream; @@ -29,13 +29,13 @@ import java.io.*; */ public class BERObjectId extends BERElement { /** - * Internal variables + * Values of each component of the OID */ private int[] m_value = null; /** - * Constructs a object id element. - * @param value object id value + * Constructs an object id element from an array of values. + * @param value object id value as array of components */ public BERObjectId(int[] value) { m_value = new int[value.length]; @@ -43,7 +43,7 @@ public class BERObjectId extends BERElement { } /** - * Constructs a object id element. + * Constructs an object id element from a string. * @param value object id value in format "2.100.3" */ public BERObjectId(String value) { @@ -54,8 +54,8 @@ public class BERObjectId extends BERElement { } /** - * Constructs an object id element with the input stream. - * @param stream input stream + * Constructs an object id element from an input stream. + * @param stream source * @param bytes_read array of 1 int; value incremented by * number of bytes read from stream. * @exception IOException failed to construct @@ -77,7 +77,9 @@ public class BERObjectId extends BERElement { oid.addElement(new Integer(1)); else oid.addElement(new Integer(2)); - oid.addElement(new Integer(sub_id - (((Integer)oid.elementAt(oid.size()-1)).intValue() * 40))); + oid.addElement(new Integer( + sub_id - (((Integer)oid.elementAt( + oid.size()-1)).intValue() * 40))); while (contents_length > 0) { contents_read[0] = 0; @@ -92,10 +94,10 @@ public class BERObjectId extends BERElement { } /** - * Sends the BER encoding directly to stream. Note that OID must + * Sends the BER encoding directly to a stream. Note that OID must * have >= 2 identifier components (values). * @param stream output stream - * @exception IOException failed to construct + * @exception IOException failed to write */ public void write(OutputStream stream) throws IOException { stream.write(BERElement.OBJECTID); @@ -154,7 +156,7 @@ public class BERObjectId extends BERElement { /** * Gets the element value. - * @param element value + * @return element value */ public int[] getValue() { return m_value; @@ -162,7 +164,7 @@ public class BERObjectId extends BERElement { /** * Gets the element type. - * @param element type + * @return element type */ public int getType() { return BERElement.OBJECTID; diff --git a/mozilla/directory/java-sdk/ldapjdk/netscape/ldap/ber/stream/BEROctetString.java b/mozilla/directory/java-sdk/ldapjdk/netscape/ldap/ber/stream/BEROctetString.java index 311c6904373..c0eacd48d04 100644 --- a/mozilla/directory/java-sdk/ldapjdk/netscape/ldap/ber/stream/BEROctetString.java +++ b/mozilla/directory/java-sdk/ldapjdk/netscape/ldap/ber/stream/BEROctetString.java @@ -12,7 +12,7 @@ * * 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 + * Copyright (C) 1999 Netscape Communications Corporation. All Rights * Reserved. */ package netscape.ldap.ber.stream; @@ -21,7 +21,15 @@ import java.util.*; import java.io.*; /** - * This class is for OctetString type. + * This class is for the OctetString type. + * + *
    + * ENCODING RULE:
    + *   Primitive Definite length.
    + *   tag = 0x04
    + *   length = (short or long form)
    + *   one or more contents octets
    + * 
    * * @version 1.0 * @seeAlso CCITT X.209 @@ -29,12 +37,12 @@ import java.io.*; public class BEROctetString extends BERElement { /** - * Internal variables + * Raw value of element */ private byte[] m_value = null; /** - * Constructs an octetstring element containing a copy of + * Constructs an octet string element containing a copy of * the contents of buffer. * @param buffer A UCS-2 String. */ @@ -48,7 +56,7 @@ public class BEROctetString extends BERElement { } /** - * Constructs an octetstring element containing a reference to + * Constructs an octet string element containing a reference to * buffer. * @param buffer A byte array, which must be in UTF-8 format if * it is string data. @@ -58,7 +66,7 @@ public class BEROctetString extends BERElement { } /** - * Constructs an octetstring element containing a + * Constructs an octet string element containing a * subset of buffer. * @param buffer buffer containing 'octets' * @param start start of buffer range to copy @@ -72,9 +80,10 @@ public class BEROctetString extends BERElement { } /** - * Constructs a octetstring element with the input stream. + * Constructs an octet string element from an input stream * (for constructed encoding) - * @param stream input stream + * @param decoder a decode that understands the specific tags + * @param stream source * @param bytes_read array of 1 int, incremented by number of bytes read. * @exception IOException failed to construct */ @@ -118,9 +127,9 @@ public class BEROctetString extends BERElement { } /** - * Constructs a octetstring element with the input stream. + * Constructs an octet string element from an input stream * (for primitive encoding) - * @param stream input stream + * @param stream source * @param bytes_read array of 1 int, incremented by number of bytes read. * @exception IOException failed to construct */ @@ -141,7 +150,7 @@ public class BEROctetString extends BERElement { /** * Writes BER to stream * @return number of bytes written to stream. - * @exception IOException failed to send + * @exception IOException failed to write */ public void write(OutputStream stream) throws IOException { stream.write((byte)BERElement.OCTETSTRING); /* OCTETSTRING tag */ diff --git a/mozilla/directory/java-sdk/ldapjdk/netscape/ldap/ber/stream/BERPrintableString.java b/mozilla/directory/java-sdk/ldapjdk/netscape/ldap/ber/stream/BERPrintableString.java index bc3846f47b5..e9ceb34823e 100644 --- a/mozilla/directory/java-sdk/ldapjdk/netscape/ldap/ber/stream/BERPrintableString.java +++ b/mozilla/directory/java-sdk/ldapjdk/netscape/ldap/ber/stream/BERPrintableString.java @@ -12,7 +12,7 @@ * * 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 + * Copyright (C) 1999 Netscape Communications Corporation. All Rights * Reserved. */ package netscape.ldap.ber.stream; @@ -21,7 +21,15 @@ import java.util.*; import java.io.*; /** - * This class is for PrintableString type. + * This class is for the PrintableString type. + * + *
    + * ENCODING RULE:
    + *   Primitive Definite length.
    + *   tag = 0x13
    + *   length = (short or long form)
    + *   one or more contents octets
    + * 
    * * @version 1.0 * @seeAlso CCITT X.209 @@ -29,25 +37,25 @@ import java.io.*; public class BERPrintableString extends BERCharacterString { /** - * Constructs an octetstring element containing buffer. - * @param buffer string containing 'octets' + * Constructs a printable string element containing buffer. + * @param buffer string value */ public BERPrintableString(String string) { m_value = string; } /** - * Constructs a printablestring element from buffer. - * @param buffer buffer + * Constructs a printables tring element from buffer. + * @param buffer byte array value */ public BERPrintableString(byte[] buffer) { super(buffer); } /** - * Constructs a printablestring element with the input stream. + * Constructs a printable string element from an input stream * (for constructed encoding) - * @param stream input stream + * @param stream source * @param bytes_read array of 1 int, incremented by number of bytes read. * @exception IOException failed to construct */ @@ -57,9 +65,9 @@ public class BERPrintableString extends BERCharacterString { } /** - * Constructs a printablestring element with the input stream. + * Constructs a printablestring element from an input stream * (for primitive encoding) - * @param stream input stream + * @param stream source * @param bytes_read array of 1 int, incremented by number of bytes read. * @exception IOException failed to construct */ @@ -68,17 +76,16 @@ public class BERPrintableString extends BERCharacterString { super(stream,bytes_read); } - /** * Gets the element type. - * @param element type + * @return element type */ public int getType() { return BERElement.PRINTABLESTRING; } /** - * Gets the string representation. Note that currently prints out + * Gets the string representation. Note that it prints out * values in decimal form. * @return string representation of tag */ diff --git a/mozilla/directory/java-sdk/ldapjdk/netscape/ldap/ber/stream/BERReal.java b/mozilla/directory/java-sdk/ldapjdk/netscape/ldap/ber/stream/BERReal.java index cb3a02a6a5a..47ee01f7b56 100644 --- a/mozilla/directory/java-sdk/ldapjdk/netscape/ldap/ber/stream/BERReal.java +++ b/mozilla/directory/java-sdk/ldapjdk/netscape/ldap/ber/stream/BERReal.java @@ -12,7 +12,7 @@ * * 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 + * Copyright (C) 1999 Netscape Communications Corporation. All Rights * Reserved. */ package netscape.ldap.ber.stream; @@ -32,19 +32,19 @@ import java.io.*; * @seeAlso CCITT X.209 */ public class BERReal extends BERElement { - /* + /** * Constants: special ASN.1 real values */ public final static float PLUS_INFINITY = 1.0f/0.0f; public final static float MINUS_INFINITY = -1.0f/0.0f; /** - * Internal variables + * Value of element */ private float m_value = 0; /** - * Constructs a real element. + * Constructs a real element with a value * @param value float value */ public BERReal(float value) { @@ -52,8 +52,8 @@ public class BERReal extends BERElement { } /** - * Constructs a real element with the input stream. - * @param stream input stream + * Constructs a real element from an input stream. + * @param stream source * @param bytes_read array of 1 int; value incremented by * number of bytes read from stream. * @exception IOException failed to construct @@ -162,9 +162,9 @@ public class BERReal extends BERElement { } /** - * Sends the BER encoding directly to stream. + * Sends the BER encoding directly to a stream. * @param stream output stream - * @exception IOException failed to construct + * @exception IOException failed to write */ public void write(OutputStream stream) throws IOException { if (m_value == 0) { @@ -193,7 +193,7 @@ public class BERReal extends BERElement { /** * Gets the element type. - * @param element type + * @return element type */ public int getType() { return BERElement.REAL; diff --git a/mozilla/directory/java-sdk/ldapjdk/netscape/ldap/ber/stream/BERSequence.java b/mozilla/directory/java-sdk/ldapjdk/netscape/ldap/ber/stream/BERSequence.java index e0c03880fb6..4f8dd2d13c9 100644 --- a/mozilla/directory/java-sdk/ldapjdk/netscape/ldap/ber/stream/BERSequence.java +++ b/mozilla/directory/java-sdk/ldapjdk/netscape/ldap/ber/stream/BERSequence.java @@ -12,7 +12,7 @@ * * 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 + * Copyright (C) 1999 Netscape Communications Corporation. All Rights * Reserved. */ package netscape.ldap.ber.stream; @@ -24,6 +24,11 @@ import java.io.*; * This class is for the Sequence object. A sequence object can * contains a sequence of BER Elements. * + *
    + * ENCODING RULE:
    + *  tag    = 0x30 (always constructed)
    + * 
    + * * @version 1.0 * @seeAlso CCITT X.209 */ @@ -36,7 +41,7 @@ public class BERSequence extends BERConstruct { } /** - * Constructs a sequence element. + * Constructs a sequence element from an input stream. * @param decoder application specific ber decoder. * @param stream input stream to read ber from. * @param bytes_read array of 1 int; value is incremented by @@ -45,12 +50,13 @@ public class BERSequence extends BERConstruct { */ public BERSequence(BERTagDecoder decoder, InputStream stream, int[] bytes_read) throws IOException { + super(decoder, stream, bytes_read); } /** * Gets the element type. - * @param element type + * @return element type */ public int getType() { return BERElement.SEQUENCE; diff --git a/mozilla/directory/java-sdk/ldapjdk/netscape/ldap/ber/stream/BERSet.java b/mozilla/directory/java-sdk/ldapjdk/netscape/ldap/ber/stream/BERSet.java index 4238b6c5b02..15abad322ec 100644 --- a/mozilla/directory/java-sdk/ldapjdk/netscape/ldap/ber/stream/BERSet.java +++ b/mozilla/directory/java-sdk/ldapjdk/netscape/ldap/ber/stream/BERSet.java @@ -12,7 +12,7 @@ * * 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 + * Copyright (C) 1999 Netscape Communications Corporation. All Rights * Reserved. */ package netscape.ldap.ber.stream; @@ -21,9 +21,14 @@ import java.util.*; import java.io.*; /** - * This class is for the Set object. A set object can contains + * This class is for the Set object. A set object can contain * a set of BER elements. * + *
    + * ENCODING RULE:
    + *  tag    = 0x31 (always constructed)
    + * 
    + * * @version 1.0 * @seeAlso CCITT X.209 */ @@ -36,9 +41,9 @@ public class BERSet extends BERConstruct { } /** - * Constructs a set element with the input stream. + * Constructs a set element from an input stream. * @param decoder decoder for application specific ber - * @param stream input stream + * @param stream source * @param bytes_read array of 1 int; value incremented by number * of bytes read from stream. * @exception IOException failed to construct @@ -49,9 +54,9 @@ public class BERSet extends BERConstruct { } /** - * Sends the BER encoding directly to stream. + * Sends the BER encoding directly to a stream. * @param stream output stream - * @exception IOException failed to construct + * @exception IOException failed to write */ public void write(OutputStream stream) throws IOException { super.write(stream); @@ -59,7 +64,7 @@ public class BERSet extends BERConstruct { /** * Gets the element type. - * @param element type + * @return element type */ public int getType() { return BERElement.SET; diff --git a/mozilla/directory/java-sdk/ldapjdk/netscape/ldap/ber/stream/BERTag.java b/mozilla/directory/java-sdk/ldapjdk/netscape/ldap/ber/stream/BERTag.java index 9b15d590a16..cd8c3664550 100644 --- a/mozilla/directory/java-sdk/ldapjdk/netscape/ldap/ber/stream/BERTag.java +++ b/mozilla/directory/java-sdk/ldapjdk/netscape/ldap/ber/stream/BERTag.java @@ -12,7 +12,7 @@ * * 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 + * Copyright (C) 1999 Netscape Communications Corporation. All Rights * Reserved. */ package netscape.ldap.ber.stream; @@ -21,19 +21,30 @@ import java.util.*; import java.io.*; /** - * This class is for the tagged object. Nested tag is + * This class is for the tagged object type. A nested tag is * allowed. A tagged element contains another * ber element. * - * @version 1.0 +*
    + * ENCODING RULE:
    + *  tag    = whatever it is constructed with
    + * 
    + * + * @version 1.0 * @seeAlso CCITT X.209 */ public class BERTag extends BERElement { /** - * Internal variables + * Value of tag */ private int m_tag = 0; + /** + * Value of element + */ private BERElement m_element = null; + /** + * Implicit or not + */ private boolean m_implicit = false; /** @@ -49,10 +60,10 @@ public class BERTag extends BERElement { } /** - * Constructs a tag element with the input stream. + * Constructs a tag element from an input stream. * @param decoder decoder object for application specific tags * @param tag tag value; already stripped from stream - * @param stream input stream + * @param stream source * @param bytes_read array of 1 int; incremented by number * ofbytes read from stream * @exception IOException failed to construct @@ -81,9 +92,9 @@ public class BERTag extends BERElement { } /** - * Sets implicit tag. If it is a implicit tag - * the next element tag can be omitted. (will - * not be send to stream or buffer) + * Sets the implicit tag. If it is an implicit tag, + * the next element tag can be omitted (it will + * not be sent to a stream or buffer). * @param value implicit flag */ public void setImplicit(boolean value) { @@ -91,7 +102,7 @@ public class BERTag extends BERElement { } /** - * Sends the BER encoding directly to stream. + * Sends the BER encoding directly to a stream. * @param stream output stream * @exception IOException failed to send */ diff --git a/mozilla/directory/java-sdk/ldapjdk/netscape/ldap/ber/stream/BERTagDecoder.java b/mozilla/directory/java-sdk/ldapjdk/netscape/ldap/ber/stream/BERTagDecoder.java index b13244d4475..a5cc80f04f9 100644 --- a/mozilla/directory/java-sdk/ldapjdk/netscape/ldap/ber/stream/BERTagDecoder.java +++ b/mozilla/directory/java-sdk/ldapjdk/netscape/ldap/ber/stream/BERTagDecoder.java @@ -12,7 +12,7 @@ * * 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 + * Copyright (C) 1999 Netscape Communications Corporation. All Rights * Reserved. */ package netscape.ldap.ber.stream; @@ -23,10 +23,10 @@ import java.io.*; /** * This is an abstract class which should be extended * for use by the BERTag class in decoding application - * specific BER tags. Since different application may - * define its own tag, the BER package needs tag decoder - * to give hints on how to decode implicit tagged - * objects. Note that each application should extend this + * specific BER tags. Since different applications may + * define their own tag, the BER package needs a tag decoder + * to give hints on how to decode implicitly tagged + * objects. Each application should extend this * decoder. * * @version 1.0 @@ -35,8 +35,13 @@ import java.io.*; public abstract class BERTagDecoder { /** - * Gets an application specific ber element from a buffer. - * @param buffer ber encoding buffer + * Gets an application specific ber element from an input stream. + * @param decoder an instance of a BERTagDecoder + * @param tag element tag + * @param stream source stream + * @param bytes_read array of 1 int; value incremented by number + * of bytes read from stream. + * @param implicit array of implicit flags */ public abstract BERElement getElement(BERTagDecoder decoder, int tag, InputStream stream, int[] bytes_read, boolean[] implicit) diff --git a/mozilla/directory/java-sdk/ldapjdk/netscape/ldap/ber/stream/BERUTCTime.java b/mozilla/directory/java-sdk/ldapjdk/netscape/ldap/ber/stream/BERUTCTime.java index 7ccc221cd1e..80afc32f8bc 100644 --- a/mozilla/directory/java-sdk/ldapjdk/netscape/ldap/ber/stream/BERUTCTime.java +++ b/mozilla/directory/java-sdk/ldapjdk/netscape/ldap/ber/stream/BERUTCTime.java @@ -12,7 +12,7 @@ * * 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 + * Copyright (C) 1999 Netscape Communications Corporation. All Rights * Reserved. */ package netscape.ldap.ber.stream; @@ -21,7 +21,15 @@ import java.util.*; import java.io.*; /** - * This class is for UTCTime object. + * This class is for the UTCTime object. + * + *
    + * ENCODING RULE:
    + *   Primitive Definite length.
    + *   tag = 0x17
    + *   length = (short or long form)
    + *   one or more contents octets
    + * 
    * * @version 1.0 * @seeAlso CCITT X.209 @@ -34,17 +42,17 @@ public class BERUTCTime extends BERElement { private String m_value = null; /** - * Constructs an octetstring element containing buffer. - * @param buffer string containing 'octets' + * Constructs a UTC time element containing the specified string. + * @param utc_string string in UTC time format */ public BERUTCTime(String utc_string) { m_value = utc_string; } /** - * Constructs a UTCTime element with the input stream. + * Constructs a UTCTime element from an input stream * (for constructed encoding) - * @param stream input stream + * @param stream source * @param bytes_read array of 1 int, incremented by number of bytes read. * @exception IOException failed to construct */ @@ -85,9 +93,9 @@ public class BERUTCTime extends BERElement { } /** - * Constructs a octetstring element with the input stream. + * Constructs a UTC time element from an input stream * (for primitive encoding) - * @param stream input stream + * @param stream source * @param bytes_read array of 1 int, incremented by number of bytes read. * @exception IOException failed to construct */ @@ -109,14 +117,14 @@ public class BERUTCTime extends BERElement { } /** - * Writes ber to stream + * Writes ber to a stream * @return number of bytes written to stream. - * @exception IOException failed to send + * @exception IOException failed to write */ private byte[] byte_buf; public void write(OutputStream stream) throws IOException { - stream.write((byte)BERElement.OCTETSTRING); /* OCTETSTRING tag */ + stream.write((byte)getType()); if (m_value == null) { sendDefiniteLength(stream, 0); } else { diff --git a/mozilla/directory/java-sdk/ldapjdk/netscape/ldap/ber/stream/BERVisibleString.java b/mozilla/directory/java-sdk/ldapjdk/netscape/ldap/ber/stream/BERVisibleString.java index 85ea65f84c0..9f9551bce1e 100644 --- a/mozilla/directory/java-sdk/ldapjdk/netscape/ldap/ber/stream/BERVisibleString.java +++ b/mozilla/directory/java-sdk/ldapjdk/netscape/ldap/ber/stream/BERVisibleString.java @@ -12,7 +12,7 @@ * * 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 + * Copyright (C) 1999 Netscape Communications Corporation. All Rights * Reserved. */ package netscape.ldap.ber.stream; @@ -21,7 +21,15 @@ import java.util.*; import java.io.*; /** - * This class is for VisibleString object. + * This class is for the VisibleString object. + * + *
    + * ENCODING RULE:
    + *   Primitive Definite length.
    + *   tag = 0x1A
    + *   length = (short or long form)
    + *   one or more contents octets
    + * 
    * * @version 1.0 * @seeAlso X.209 diff --git a/mozilla/directory/java-sdk/ldapjdk/netscape/ldap/client/JDAPAVA.java b/mozilla/directory/java-sdk/ldapjdk/netscape/ldap/client/JDAPAVA.java index ad64eef69be..47950649add 100644 --- a/mozilla/directory/java-sdk/ldapjdk/netscape/ldap/client/JDAPAVA.java +++ b/mozilla/directory/java-sdk/ldapjdk/netscape/ldap/client/JDAPAVA.java @@ -20,7 +20,6 @@ package netscape.ldap.client; import java.util.*; import java.io.*; import netscape.ldap.ber.stream.*; -import netscape.ldap.client.*; /** * This class implements the attribute value assertion. diff --git a/mozilla/directory/java-sdk/ldapjdk/netscape/ldap/client/JDAPControl.java b/mozilla/directory/java-sdk/ldapjdk/netscape/ldap/client/JDAPControl.java deleted file mode 100644 index 14eafb3f21c..00000000000 --- a/mozilla/directory/java-sdk/ldapjdk/netscape/ldap/client/JDAPControl.java +++ /dev/null @@ -1,124 +0,0 @@ -/* -*- 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 netscape.ldap.client; - -import java.util.*; -import netscape.ldap.ber.stream.*; -import java.io.*; - -/** - * This class implements the controls for LDAPv3. - *
    - * Control ::= SEQUENCE {
    - *   controlType   LDAPOID,
    - *   criticality   BOOLEAN DEFAULT FALSE,
    - *   controlValue  OCTET STRING OPTIONAL
    - * }
    - * 
    - * - * @version 1.0 - * @see RFC1777 - */ -public class JDAPControl { - /** - * Internal variables - */ - String m_type = null; - boolean m_criticality = false; - byte m_vals[] = null; - - /** - * Constructs add request. - * @param dn distinguished name of adding entry - * @param attrs list of attribute associated with entry - */ - public JDAPControl(String type, boolean criticality, byte vals[]) { - m_type = type; - m_criticality = criticality; - m_vals = vals; - } - - /** - * Constructs control from BER element. - * @param e BER element - */ - public JDAPControl(BERElement e) { - BERSequence s = (BERSequence)e; - try{ - m_type = new String(((BEROctetString)s.elementAt(0)).getValue(), "UTF8"); - } catch(Throwable x) - {} - Object value = s.elementAt(1); - if (value instanceof BERBoolean) - m_criticality = ((BERBoolean)value).getValue(); - else - m_vals = ((BEROctetString)value).getValue(); - - if (s.size() >= 3) - m_vals = ((BEROctetString)s.elementAt(2)).getValue(); - } - - /** - * Gets the ber representation of control. - * @return ber representation of control - */ - public BERElement getBERElement() { - BERSequence seq = new BERSequence(); - seq.addElement(new BEROctetString (m_type)); - seq.addElement(new BERBoolean (m_criticality)); - if ( (m_vals == null) || (m_vals.length < 1) ) - seq.addElement(new BEROctetString ((byte[])null)); - else { - seq.addElement(new BEROctetString (m_vals, 0, m_vals.length)); - } - return seq; - } - - /** - * Returns the identifier of the control. - * @return Returns the type of the Control, as a string. - */ - public String getID() { - return m_type; - } - - /** - * Reports if the control is critical. - * @return true if the LDAP operation should be discarded if - * the server does not support this Control. - */ - public boolean isCritical() { - return m_criticality; - } - - /** - * Returns the data of the control. - * @return Returns the data of the Control, as a byte array. - */ - public byte[] getValue() { - return m_vals; - } - - /** - * Retrieves the string representation of extended request. - * @return string representation of extended request - */ - public String toString() { - return "Control" + " { type=" + m_type + ", criticality=" + m_criticality; - } -} diff --git a/mozilla/directory/java-sdk/ldapjdk/netscape/ldap/client/JDAPFilterSubString.java b/mozilla/directory/java-sdk/ldapjdk/netscape/ldap/client/JDAPFilterSubString.java index 11f8fa9b7fe..397449fc0ef 100644 --- a/mozilla/directory/java-sdk/ldapjdk/netscape/ldap/client/JDAPFilterSubString.java +++ b/mozilla/directory/java-sdk/ldapjdk/netscape/ldap/client/JDAPFilterSubString.java @@ -20,7 +20,6 @@ package netscape.ldap.client; import java.util.*; import netscape.ldap.ber.stream.*; import java.io.*; -import netscape.ldap.client.*; /** * This class implements the filter substring. diff --git a/mozilla/directory/java-sdk/ldapjdk/netscape/ldap/client/opers/JDAPExtendedRequest.java b/mozilla/directory/java-sdk/ldapjdk/netscape/ldap/client/opers/JDAPExtendedRequest.java index 02e7467f618..39d72b3d5e2 100644 --- a/mozilla/directory/java-sdk/ldapjdk/netscape/ldap/client/opers/JDAPExtendedRequest.java +++ b/mozilla/directory/java-sdk/ldapjdk/netscape/ldap/client/opers/JDAPExtendedRequest.java @@ -12,7 +12,7 @@ * * 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 + * Copyright (C) 1999 Netscape Communications Corporation. All Rights * Reserved. */ package netscape.ldap.client.opers; @@ -66,10 +66,10 @@ public class JDAPExtendedRequest implements JDAPProtocolOp { */ public BERElement getBERElement() { BERSequence seq = new BERSequence(); - seq.addElement(new BERTag(BERTag.APPLICATION|0, + seq.addElement(new BERTag(BERTag.CONTEXT|0, new BEROctetString (m_oid), true)); if (m_value != null) { - seq.addElement(new BERTag(BERTag.APPLICATION|1, + seq.addElement(new BERTag(BERTag.CONTEXT|1, new BEROctetString (m_value, 0, m_value.length), true)); } BERTag element = new BERTag(BERTag.APPLICATION|BERTag.CONSTRUCTED|23, diff --git a/mozilla/directory/java-sdk/ldapjdk/netscape/ldap/client/opers/JDAPModifyRDNRequest.java b/mozilla/directory/java-sdk/ldapjdk/netscape/ldap/client/opers/JDAPModifyRDNRequest.java index 4d945da6a1d..ece17d31dbc 100644 --- a/mozilla/directory/java-sdk/ldapjdk/netscape/ldap/client/opers/JDAPModifyRDNRequest.java +++ b/mozilla/directory/java-sdk/ldapjdk/netscape/ldap/client/opers/JDAPModifyRDNRequest.java @@ -40,8 +40,8 @@ import java.net.*; * ModifyDNRequest ::= [APPLICATION 12] SEQUENCE { * entry LDAPDN, * newrdn RelativeLDAPDN, - * newSuperior [0] LDAPDN OPTIONAL * deleteoldrdn BOOLEAN, + * newSuperior [0] LDAPDN OPTIONAL * } *
    * @@ -118,10 +118,11 @@ public class JDAPModifyRDNRequest extends JDAPBaseDNRequest BERSequence seq = new BERSequence(); seq.addElement(new BEROctetString(m_old_dn)); seq.addElement(new BEROctetString(m_new_rdn)); + seq.addElement(new BERBoolean(m_delete_old_dn)); /* LDAPv3 new parent dn feature support */ if (m_new_superior != null) - seq.addElement(new BEROctetString(m_new_superior)); - seq.addElement(new BERBoolean(m_delete_old_dn)); + seq.addElement(new BERTag(BERTag.CONTEXT|0, + new BEROctetString (m_new_superior), true)); BERTag element = new BERTag(BERTag.APPLICATION|BERTag.CONSTRUCTED|12, seq, true); return element; diff --git a/mozilla/directory/java-sdk/ldapjdk/netscape/ldap/client/opers/JDAPSearchRequest.java b/mozilla/directory/java-sdk/ldapjdk/netscape/ldap/client/opers/JDAPSearchRequest.java index 77424919e21..2f7f101e3d9 100644 --- a/mozilla/directory/java-sdk/ldapjdk/netscape/ldap/client/opers/JDAPSearchRequest.java +++ b/mozilla/directory/java-sdk/ldapjdk/netscape/ldap/client/opers/JDAPSearchRequest.java @@ -66,6 +66,7 @@ public class JDAPSearchRequest extends JDAPBaseDNRequest public final static int DEREF_FINDING_BASE_OBJ = 2; public final static int DEREF_ALWAYS = 3; + public final static String DEFAULT_FILTER = "(objectclass=*)"; /** * Private variables */ @@ -76,6 +77,7 @@ public class JDAPSearchRequest extends JDAPBaseDNRequest protected int m_time_limit; protected boolean m_attrs_only; protected String m_filter = null; + protected JDAPFilter m_parsedFilter = null; protected String m_attrs[] = null; /** @@ -88,17 +90,23 @@ public class JDAPSearchRequest extends JDAPBaseDNRequest * @param attrs_only should return type only * @param filter string filter based on RFC1558 * @param attrs list of attribute types + * @exception IllegalArgumentException if the filter has bad syntax + * */ public JDAPSearchRequest(String base_dn, int scope, int deref, int size_limit, int time_limit, boolean attrs_only, - String filter, String attrs[]) { + String filter, String attrs[]) throws IllegalArgumentException { m_base_dn = base_dn; m_scope = scope; m_deref = deref; m_size_limit = size_limit; m_time_limit = time_limit; m_attrs_only = attrs_only; - m_filter = filter; + m_filter = (filter == null) ? DEFAULT_FILTER : filter; + m_parsedFilter = JDAPFilter.getFilter(m_filter); + if (m_parsedFilter == null){ + throw new IllegalArgumentException("Bad search filter"); + } m_attrs = attrs; } @@ -173,10 +181,7 @@ public class JDAPSearchRequest extends JDAPBaseDNRequest seq.addElement(new BERInteger(m_size_limit)); seq.addElement(new BERInteger(m_time_limit)); seq.addElement(new BERBoolean(m_attrs_only)); - JDAPFilter filter = JDAPFilter.getFilter(m_filter); - if (filter == null) - return null; - seq.addElement(filter.getBERElement()); + seq.addElement(m_parsedFilter.getBERElement()); BERSequence attr_type_list = new BERSequence(); if (m_attrs != null) { for (int i = 0; i < m_attrs.length; i++) { @@ -196,6 +201,7 @@ public class JDAPSearchRequest extends JDAPBaseDNRequest public String toString() { String s = null; if (m_attrs != null) { + s = ""; for (int i = 0; i < m_attrs.length; i++) { if (i != 0) s = s + "+"; diff --git a/mozilla/directory/java-sdk/ldapjdk/netscape/ldap/controls/LDAPEntryChangeControl.java b/mozilla/directory/java-sdk/ldapjdk/netscape/ldap/controls/LDAPEntryChangeControl.java index befc2b843fb..a2fbd200d51 100644 --- a/mozilla/directory/java-sdk/ldapjdk/netscape/ldap/controls/LDAPEntryChangeControl.java +++ b/mozilla/directory/java-sdk/ldapjdk/netscape/ldap/controls/LDAPEntryChangeControl.java @@ -12,7 +12,7 @@ * * 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 + * Copyright (C) 1999 Netscape Communications Corporation. All Rights * Reserved. */ package netscape.ldap.controls; @@ -38,9 +38,8 @@ import netscape.ldap.*; *

    * * Typically, you use the getResponseControls method of - * the LDAPConnection object and the parseResponse - * method of the LDAPPersistSearchControl object to get - * an LDAPEntryChangeControl object. + * the LDAPConnection object to get any + * LDAPEntryChangeControl objects returned by the server. *

    * * Once you retrieve an LDAPEntryChangeControl object from @@ -64,18 +63,68 @@ public class LDAPEntryChangeControl extends LDAPControl { /** * Constructs a new LDAPEntryChangeControl object. - * Call the parseResponse method of the - * LDAPPersistSearchControl object to retrieve the - * actual "entry change notification" control from the server. - * * @see netscape.ldap.LDAPControl * @see netscape.ldap.controls.LDAPPersistSearchControl */ - public LDAPEntryChangeControl() - { + public LDAPEntryChangeControl() { super(ENTRYCHANGED, false, null); } + /** + * Contructs an LDAPEntryChangedControl object. + * This constructor is used by LDAPControl.register to + * instantiate entry change controls. + * @param oid This parameter must be + * LDAPEntryChangeControl.ENTRYCHANGED + * or a LDAPException is thrown. + * @param critical True if this control is critical. + * @param value The value associated with this control. + * @exception netscape.ldap.LDAPException If oid is not + * LDAPEntryChangeControl.ENTRYCHANGED. + * @exception java.io.IOException If value is not a valid BER sequence. + * @see netscape.ldap.LDAPControl#register + */ + public LDAPEntryChangeControl(String oid, boolean critical, byte[] value) + throws LDAPException, IOException { + super(ENTRYCHANGED, false, value); + + if (!oid.equals( ENTRYCHANGED )) { + throw new LDAPException("oid must be LDAPEntryChangeControl." + + "ENTRYCHANGED", LDAPException.PARAM_ERROR); + } + + + ByteArrayInputStream inStream = new ByteArrayInputStream(m_value); + BERSequence seq = new BERSequence(); + JDAPBERTagDecoder decoder = new JDAPBERTagDecoder(); + int[] numRead = new int[1]; + numRead[0] = 0; + + BERSequence s = (BERSequence)BERElement.getElement(decoder, inStream, + numRead); + + BEREnumerated enum = (BEREnumerated)s.elementAt(0); + + m_changeTypes = enum.getValue(); + + if (s.size() > 1) { + if (s.elementAt(1) instanceof BEROctetString) { + BEROctetString str = (BEROctetString)s.elementAt(1); + try { + m_previousDN = new String(str.getValue(), "UTF8"); + } catch (UnsupportedEncodingException e) { + } + } else if (s.elementAt(1) instanceof BERInteger) { + BERInteger num = (BERInteger)s.elementAt(1); + m_changeNumber = num.getValue(); + } + } + if (s.size() > 2) { + BERInteger num = (BERInteger)s.elementAt(2); + m_changeNumber = num.getValue(); + } + } + /** * Sets the change number (which identifies the record of the change * in the server's change log) in this "entry change notification" @@ -166,9 +215,30 @@ public class LDAPEntryChangeControl extends LDAPControl { return m_previousDN; } + public String toString() { + StringBuffer sb = new StringBuffer("{EntryChangedCtrl:"); + + sb.append(" isCritical="); + sb.append(isCritical()); + + sb.append(" changeTypes="); + sb.append(LDAPPersistSearchControl.typesToString(m_changeTypes)); + + sb.append(" previousDN="); + sb.append(m_previousDN); + + sb.append(" changeNumber="); + sb.append(m_changeNumber); + + sb.append("}"); + + return sb.toString(); + } + private int m_changeNumber = -1; private int m_changeTypes = -1; private String m_previousDN = null; public final static String ENTRYCHANGED = "2.16.840.1.113730.3.4.7"; + } diff --git a/mozilla/directory/java-sdk/ldapjdk/netscape/ldap/controls/LDAPPasswordExpiredControl.java b/mozilla/directory/java-sdk/ldapjdk/netscape/ldap/controls/LDAPPasswordExpiredControl.java index d1a5b8dc3f2..547639508ed 100644 --- a/mozilla/directory/java-sdk/ldapjdk/netscape/ldap/controls/LDAPPasswordExpiredControl.java +++ b/mozilla/directory/java-sdk/ldapjdk/netscape/ldap/controls/LDAPPasswordExpiredControl.java @@ -12,12 +12,13 @@ * * 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 + * Copyright (C) 1999 Netscape Communications Corporation. All Rights * Reserved. */ package netscape.ldap.controls; import netscape.ldap.LDAPControl; +import netscape.ldap.LDAPException; /** * Represents an LDAP v3 server control that may be returned if a @@ -30,6 +31,32 @@ import netscape.ldap.LDAPControl; */ public class LDAPPasswordExpiredControl extends LDAPStringControl { public final static String EXPIRED = "2.16.840.1.113730.3.4.4"; + + /** + * Contructs an LDAPPasswordExpiredControl object. + * This constructor is used by LDAPControl.register to + * instantiate password expired controls. + *

    + * To retrieve the message from the server, call getMessage. + * @param oid This parameter must be equal to + * LDAPPasswordExpiredControl.EXPIRED + * or a LDAPException is thrown. + * @param critical True if this control is critical. + * @param value The value associated with this control. + * @exception netscape.ldap.LDAPException If oid is not + * LDAPPasswordExpiredControl.EXPIRED. + * @see netscape.ldap.LDAPControl#register + */ + public LDAPPasswordExpiredControl( String oid, boolean critical, + byte[] value ) throws LDAPException { + super( EXPIRED, critical, value ); + if ( !oid.equals( EXPIRED )) { + throw new LDAPException( "oid must be LDAPPasswordExpiredControl." + + "PWEXPIRED", LDAPException.PARAM_ERROR); + } + + } + /** * @param controls An array of LDAPControl objects, * representing the controls returned by the server @@ -37,9 +64,23 @@ public class LDAPPasswordExpiredControl extends LDAPStringControl { * getResponseControls method of the * LDAPConnection class. * @return An error message string, or null if none is in the control. - * @see netscape.ldap.LDAPConnection#getResponseControls + * @see netscape.ldap.LDAPConnection#getResponseControls + * @deprecated LDAPPasswordExpiredControl controls are now automatically + * instantiated. */ public static String parseResponse( LDAPControl[] controls ) { return LDAPStringControl.parseResponse( controls, EXPIRED ); } + + /** + * Gets the message returned by the server with this control + * @return The message returned by the server + */ + public String getMessage() { + return m_msg; + } } + + + + diff --git a/mozilla/directory/java-sdk/ldapjdk/netscape/ldap/controls/LDAPPasswordExpiringControl.java b/mozilla/directory/java-sdk/ldapjdk/netscape/ldap/controls/LDAPPasswordExpiringControl.java index b9647a6264e..06e247ffde2 100644 --- a/mozilla/directory/java-sdk/ldapjdk/netscape/ldap/controls/LDAPPasswordExpiringControl.java +++ b/mozilla/directory/java-sdk/ldapjdk/netscape/ldap/controls/LDAPPasswordExpiringControl.java @@ -12,12 +12,13 @@ * * 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 + * Copyright (C) 1999 Netscape Communications Corporation. All Rights * Reserved. */ package netscape.ldap.controls; import netscape.ldap.LDAPControl; +import netscape.ldap.LDAPException; /** * Represents an LDAP v3 server control that may be returned if a @@ -30,6 +31,52 @@ import netscape.ldap.LDAPControl; */ public class LDAPPasswordExpiringControl extends LDAPStringControl { public final static String EXPIRING = "2.16.840.1.113730.3.4.5"; + + /** + * Contructs an LDAPPasswordExpiringControl object. + * This constructor is used by LDAPControl.register to + * instantiate password expiring controls. + *

    + * To retrieve the number of seconds until this password expires, + * call getSecondsToExpiration. + * @param oid This parameter must be + * LDAPPasswordExpiringControl.EXPIRING + * or an LDAPException is thrown. + * @param critical True if this control is critical. + * @param value The value associated with this control. + * @exception netscape.ldap.LDAPException If oid is not + * LDAPPasswordExpiringControl.EXPIRING. + * @see netscape.ldap.LDAPControl#register + */ + public LDAPPasswordExpiringControl( String oid, boolean critical, + byte[] value ) throws LDAPException { + super( EXPIRING, critical, value ); + if ( !oid.equals( EXPIRING )) { + throw new LDAPException( "oid must be LDAPPasswordExpiringControl" + + ".EXPIRING", LDAPException.PARAM_ERROR ); + } + } + + /** + * Gets the number of seconds until the password expires returned by the + * server. + * @return int The number of seconds until the password expires. + * @exception java.lang.NumberFormatException If the server returned an + * undecipherable message. In this case, use getMessage to + * retrieve the message as a string. + */ + public int getSecondsToExpiration() { + return Integer.parseInt( m_msg ); + } + + /** + * Gets the value associated with this control parsed as a string. + * @return The value associated with this control parsed as a string. + */ + public String getMessage() { + return m_msg; + } + /** * @param controls An array of LDAPControl objects, * representing the controls returned by the server @@ -37,7 +84,9 @@ public class LDAPPasswordExpiringControl extends LDAPStringControl { * getResponseControls method of the * LDAPConnection class. * @return An error message string, or null if none is in the control. - * @see netscape.ldap.LDAPConnection#getResponseControls + * @see netscape.ldap.LDAPConnection#getResponseControls + * @deprecated LDAPPasswordExpiringControl controls are now automatically + * instantiated. */ public static String parseResponse( LDAPControl[] controls ) { return LDAPStringControl.parseResponse( controls, EXPIRING ); diff --git a/mozilla/directory/java-sdk/ldapjdk/netscape/ldap/controls/LDAPPersistSearchControl.java b/mozilla/directory/java-sdk/ldapjdk/netscape/ldap/controls/LDAPPersistSearchControl.java index dd20e3ef83e..116257e6e1a 100644 --- a/mozilla/directory/java-sdk/ldapjdk/netscape/ldap/controls/LDAPPersistSearchControl.java +++ b/mozilla/directory/java-sdk/ldapjdk/netscape/ldap/controls/LDAPPersistSearchControl.java @@ -151,6 +151,9 @@ public class LDAPPersistSearchControl extends LDAPControl { super(PERSISTENTSEARCH, isCritical, null); m_value = createPersistSearchSpecification(changeTypes, changesOnly, returnControls); + m_changeTypes = changeTypes; + m_changesOnly = changesOnly; + m_returnECs = returnControls; } /** @@ -238,6 +241,8 @@ public class LDAPPersistSearchControl extends LDAPControl { * * @param c Byte array that contains BER elements. * @return The entry change control. + * @deprecated LDAPEntryChangeControl controls are now automatically + * instantiated. */ public LDAPEntryChangeControl parseResponse(byte[] c) { LDAPEntryChangeControl con = new LDAPEntryChangeControl(); @@ -270,7 +275,7 @@ public class LDAPPersistSearchControl extends LDAPControl { BERInteger num = (BERInteger)s.elementAt(2); con.setChangeNumber(num.getValue()); } - } catch (Throwable e) { + } catch (Exception e) { return null; } @@ -306,8 +311,13 @@ public class LDAPPersistSearchControl extends LDAPControl { * if ( returnedControls != null ) { * * // Get the entry change control. - * LDAPEntryChangeControl entryCtrl = - * persistCtrl.parseResponse( returnedControls ); + * LDAPEntryChangeControl entryCtrl = null; + * for ( int i = 0; i < returnedControls.length; i++ ) { + * if ( returnedControls[i] instanceof LDAPEntryChangeControl ) { + * entryCtrl = (LDAPEntryChangeControl)returnedControls[i]; + * break; + * } + * } * if ( entryCtrl != null ) { * * // Get and print the type of change made to the entry. @@ -363,6 +373,8 @@ public class LDAPPersistSearchControl extends LDAPControl { * control was sent, this method returns null. * @see netscape.ldap.controls.LDAPEntryChangeControl * @see netscape.ldap.LDAPConnection#getResponseControls + * @deprecated LDAPEntryChangeControl controls are now automatically + * instantiated. */ public static LDAPEntryChangeControl parseResponse(LDAPControl[] controls) { @@ -401,6 +413,48 @@ public class LDAPPersistSearchControl extends LDAPControl { return flattenBER( seq ); } + public String toString() { + StringBuffer sb = new StringBuffer("{PersistSearchCtrl:"); + + sb.append(" isCritical="); + sb.append(isCritical()); + + sb.append(" returnEntryChangeCtrls="); + sb.append(m_returnECs); + + sb.append(" changesOnly="); + sb.append(m_changesOnly); + + sb.append(" changeTypes="); + sb.append(typesToString(m_changeTypes)); + + sb.append("}"); + + return sb.toString(); + } + + + /** + * This method is also used by LDAPentryChangeControl.toString() + */ + static String typesToString(int changeTypes) { + String types = ""; + + if ((changeTypes & ADD) != 0) { + types += (types.length() > 0) ? "+ADD" : "ADD"; + } + if ((changeTypes & DELETE) != 0) { + types += (types.length() > 0) ? "+DEL" : "DEL"; + } + if ((changeTypes & MODIFY) != 0) { + types += (types.length() > 0) ? "+MOD" : "MOD"; + } + if ((changeTypes & MODDN) != 0) { + types += (types.length() > 0) ? "+MODDN" : "MODDN"; + } + return types; + } + private int m_changeTypes = 1; private boolean m_changesOnly = false; private boolean m_returnECs = false; diff --git a/mozilla/directory/java-sdk/ldapjdk/netscape/ldap/controls/LDAPProxiedAuthControl.java b/mozilla/directory/java-sdk/ldapjdk/netscape/ldap/controls/LDAPProxiedAuthControl.java index 6500109975c..29a1fd17665 100644 --- a/mozilla/directory/java-sdk/ldapjdk/netscape/ldap/controls/LDAPProxiedAuthControl.java +++ b/mozilla/directory/java-sdk/ldapjdk/netscape/ldap/controls/LDAPProxiedAuthControl.java @@ -66,8 +66,9 @@ import netscape.ldap.ber.stream.*; * * @version 1.0 * @see netscape.ldap.LDAPControl + * @see netscape.ldap.LDAPConstraints * @see netscape.ldap.LDAPSearchConstraints - * @see netscape.ldap.LDAPSearchConstraints#setServerControls(LDAPControl) + * @see netscape.ldap.LDAPConstraints#setServerControls(LDAPControl) */ public class LDAPProxiedAuthControl extends LDAPControl { public final static String PROXIEDAUTHREQUEST = diff --git a/mozilla/directory/java-sdk/ldapjdk/netscape/ldap/controls/LDAPSortControl.java b/mozilla/directory/java-sdk/ldapjdk/netscape/ldap/controls/LDAPSortControl.java index 66890022baf..da9b20b1d37 100644 --- a/mozilla/directory/java-sdk/ldapjdk/netscape/ldap/controls/LDAPSortControl.java +++ b/mozilla/directory/java-sdk/ldapjdk/netscape/ldap/controls/LDAPSortControl.java @@ -12,7 +12,7 @@ * * 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 + * Copyright (C) 1999 Netscape Communications Corporation. All Rights * Reserved. */ package netscape.ldap.controls; @@ -21,6 +21,7 @@ import java.io.*; import netscape.ldap.LDAPControl; import netscape.ldap.LDAPSortKey; import netscape.ldap.client.JDAPBERTagDecoder; +import netscape.ldap.LDAPException; import netscape.ldap.ber.stream.*; /** @@ -90,7 +91,8 @@ import netscape.ldap.ber.stream.*; * *

    * - * To parse this control, use the parseResponse method. + * To retrieve the data from this control, use the getResultCode + * and getFailedAttribute methods. *

    * * The following table lists what kinds of results to expect from the @@ -206,13 +208,83 @@ import netscape.ldap.ber.stream.*; * @version 1.0 * @see netscape.ldap.LDAPSortKey * @see netscape.ldap.LDAPControl + * @see netscape.ldap.LDAPConstraints * @see netscape.ldap.LDAPSearchConstraints - * @see netscape.ldap.LDAPSearchConstraints#setServerControls(LDAPControl) + * @see netscape.ldap.LDAPConstraints#setServerControls(LDAPControl) */ public class LDAPSortControl extends LDAPControl { public final static String SORTREQUEST = "1.2.840.113556.1.4.473"; public final static String SORTRESPONSE = "1.2.840.113556.1.4.474"; + private String m_failedAttribute = null; + private int m_resultCode = 0; + + /** + * Constructs a sort response LDAPSortControl object. + * This constructor is used by LDAPControl.register to + * instantiate sort response controls. + *

    + * To retrieve the result code of the sort operation, call + * getResultCode. + *

    + * To retrieve the attribute that caused the sort operation to fail, call + * getFailedAttribute. + * @param oid The oid for this control. This must be + * LDAPSortControl.SORTRESPONSE or a LDAPException + * is thrown. + * @param critical True if this control is critical to the operation. + * @param value The value associated with this control. + * @exception netscape.ldap.LDAPException If oid is not + * LDAPSortControl.SORTRESPONSE. + * @exception java.io.IOException If value contains an invalid BER sequence. + * @see netscape.ldap.LDAPControl#register + */ + public LDAPSortControl( String oid, boolean critical, byte[] value ) + throws LDAPException, IOException { + super( oid, critical, value ); + + if ( !oid.equals( SORTRESPONSE )) { + throw new LDAPException( "oid must be LDAPSortControl.SORTRESPONSE", + LDAPException.PARAM_ERROR); + } + + ByteArrayInputStream inStream = new ByteArrayInputStream( value ); + BERSequence ber = new BERSequence(); + JDAPBERTagDecoder decoder = new JDAPBERTagDecoder(); + int[] nRead = { 0 }; + + /* A sequence */ + BERSequence seq = (BERSequence)BERElement.getElement(decoder, inStream, + nRead ); + /* First is result code */ + int m_resultCode = ((BEREnumerated)seq.elementAt( 0 )).getValue(); + /* Then, possibly an attribute that failed sorting */ + if(seq.size() == 1) { + return; + } + /* If this is not an octet string, let there be an exception */ + BEROctetString t = (BEROctetString)seq.elementAt(1); + try { + m_failedAttribute = new String(t.getValue(), "UTF8"); + } catch (UnsupportedEncodingException e) { + } + + } + + /** + * @return The attribute that caused the sort operation to fail. + */ + public String getFailedAttribute() { + return m_failedAttribute; + } + + /** + * @return The result code from the search operation. + */ + public int getResultCode() { + return m_resultCode; + } + /** * Constructs an LDAPSortControl object with a single * sorting key. @@ -418,6 +490,8 @@ public class LDAPSortControl extends LDAPControl { * @return The attribute that caused the error, or null if the server did * not specify which attribute caused the error. * @see netscape.ldap.LDAPConnection#getResponseControls + * @deprecated LDAPSortControl response controls are now automatically + * instantiated. */ public static String parseResponse( LDAPControl[] controls, int[] results ) { String attr = null; @@ -450,7 +524,7 @@ public class LDAPSortControl extends LDAPControl { /* If this is not an octet string, let there be an exception */ BEROctetString t = (BEROctetString)seq.elementAt(1); attr = new String(t.getValue(), "UTF8"); - } catch(Throwable x) { + } catch(Exception x) { } } return attr; @@ -487,5 +561,13 @@ public class LDAPSortControl extends LDAPControl { /* Suck out the data and return it */ return flattenBER( ber ); } + + // register SORTRESPONSE with LDAPControl + static { + try { + LDAPControl.register( SORTRESPONSE, LDAPSortControl.class ); + } catch (LDAPException e) { + } + } } diff --git a/mozilla/directory/java-sdk/ldapjdk/netscape/ldap/controls/LDAPStringControl.java b/mozilla/directory/java-sdk/ldapjdk/netscape/ldap/controls/LDAPStringControl.java index 642e4ea136c..bf4d3a48400 100644 --- a/mozilla/directory/java-sdk/ldapjdk/netscape/ldap/controls/LDAPStringControl.java +++ b/mozilla/directory/java-sdk/ldapjdk/netscape/ldap/controls/LDAPStringControl.java @@ -12,7 +12,7 @@ * * 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 + * Copyright (C) 1999 Netscape Communications Corporation. All Rights * Reserved. */ package netscape.ldap.controls; @@ -24,6 +24,32 @@ import netscape.ldap.LDAPControl; * only value. This is to be used as a base class by real such controls. */ abstract class LDAPStringControl extends LDAPControl { + protected String m_msg = null; + + LDAPStringControl() { + super(); + } + + /** + * Constructs a LDAPStringControl object, and stores the + * value as a string. To retrieve this string value, use + * getMsg(). + * @param oid The oid of defining this control. + * @param critical True if this control is critical. + * @param value The value associated with this control. + * @see netscape.ldap.LDAPcontrol + */ + public LDAPStringControl( String oid, boolean critical, byte[] value ) { + super( oid, critical, value ); + + if ( value != null ) { + try { + m_msg = new String( value, "UTF8" ); + } catch ( java.io.IOException e ) { + } + } + } + /** * Parses a response control sent by the server and * retrieves a string. diff --git a/mozilla/directory/java-sdk/ldapjdk/netscape/ldap/controls/LDAPVirtualListControl.java b/mozilla/directory/java-sdk/ldapjdk/netscape/ldap/controls/LDAPVirtualListControl.java index 522ab934cfb..d4fc2087f68 100644 --- a/mozilla/directory/java-sdk/ldapjdk/netscape/ldap/controls/LDAPVirtualListControl.java +++ b/mozilla/directory/java-sdk/ldapjdk/netscape/ldap/controls/LDAPVirtualListControl.java @@ -141,8 +141,13 @@ import netscape.ldap.ber.stream.*; * * // Check if we have a control returned * LDAPControl[] c = _ldc.getResponseControls(); - * LDAPVirtualListResponse nextCont = - * LDAPVirtualListResponse.parseResponse( c ); + * LDAPVirtualListResponse nextCont = null; + * for ( int i = 0; i < c.length; i++ ) { + * if ( c[i] instanceof LDAPVirtualListResponse ) { + * nextCont = (LDAPVirtualListResponse)c[i]; + * break; + * } + * } * if ( nextCont != null ) { * _selectedIndex = nextCont.getFirstPosition() - 1; * _top = Math.max( 0, _selectedIndex - _beforeCount ); @@ -197,14 +202,18 @@ import netscape.ldap.ber.stream.*; *} * *

    - *      VirtualListViewRequest ::= SEQUENCE {
    - *                      beforeCount    INTEGER,
    - *                      afterCount     INTEGER,
    - *                      CHOICE {
    - *                      byIndex [0] SEQUENCE {
    - *                          index           INTEGER,
    - *                          contentCount    INTEGER }
    - *                      byFilter [1] jumpTo    Substring }
    + *   VirtualListViewRequest ::= SEQUENCE {
    + *            beforeCount    INTEGER (0 .. maxInt),
    + *            afterCount     INTEGER (0 .. maxInt),
    + *            CHOICE {
    + *                byIndex [0] SEQUENCE {
    + *                    index           INTEGER,
    + *                    contentCount    INTEGER
    + *                }
    + *                byFilter [1] jumpTo    Substring
    + *            },
    + *            contextID     OCTET STRING OPTIONAL
    + *  }
      * 
    * * @version 1.0 @@ -238,6 +247,12 @@ public class LDAPVirtualListControl extends LDAPControl { setRange( jumpTo, beforeCount, afterCount ); } + public LDAPVirtualListControl( String jumpTo, int beforeCount, + int afterCount, String context ) { + this( jumpTo, beforeCount, afterCount ); + m_context = context; + } + /** * Constructs a new LDAPVirtualListControl object. Use this * constructor on a subsquent search operation, after we know the @@ -257,6 +272,13 @@ public class LDAPVirtualListControl extends LDAPControl { setRange( startIndex, beforeCount, afterCount ); } + public LDAPVirtualListControl( int startIndex, int beforeCount, + int afterCount, int contentCount, + String context ) { + this( startIndex, beforeCount, afterCount, contentCount ); + m_context = context; + } + /** * Sets the starting index, and the number of entries before and after * to return. Apply this method to a control returned from a previous @@ -334,6 +356,22 @@ public class LDAPVirtualListControl extends LDAPControl { return m_afterCount; } + /** + * Gets the optional context cookie + * @return The optional context cookie + */ + public String getContext() { + return m_context; + } + + /** + * Sets the optional context cookie + * @param context The optional context cookie + */ + public void setContext( String context ) { + m_context = context; + } + /** * Creates a "flattened" BER encoding of the requested page * specifications and return it as a byte array. @@ -351,6 +389,9 @@ public class LDAPVirtualListControl extends LDAPControl { seq.addElement( new BERInteger( afterCount ) ); seq.addElement( new BERTag(BERTag.CONTEXT|TAG_BYFILTER, new BEROctetString(subFilter), true) ); + if ( m_context != null ) { + seq.addElement( new BEROctetString(m_context) ); + } /* Suck out the data and return it */ return flattenBER( seq ); } @@ -379,6 +420,9 @@ public class LDAPVirtualListControl extends LDAPControl { seq.addElement( new BERTag(BERTag.CONTEXT|BERTag.CONSTRUCTED|TAG_BYINDEX, indexSeq,true) ); + if ( m_context != null ) { + seq.addElement( new BEROctetString(m_context) ); + } /* Suck out the data and return it */ return flattenBER( seq ); @@ -390,4 +434,5 @@ public class LDAPVirtualListControl extends LDAPControl { private int m_afterCount = 0; private int m_listIndex = -1; private int m_listSize = 0; + private String m_context = null; } diff --git a/mozilla/directory/java-sdk/ldapjdk/netscape/ldap/controls/LDAPVirtualListResponse.java b/mozilla/directory/java-sdk/ldapjdk/netscape/ldap/controls/LDAPVirtualListResponse.java index 6229e59d7d1..fe29033955d 100644 --- a/mozilla/directory/java-sdk/ldapjdk/netscape/ldap/controls/LDAPVirtualListResponse.java +++ b/mozilla/directory/java-sdk/ldapjdk/netscape/ldap/controls/LDAPVirtualListResponse.java @@ -12,7 +12,7 @@ * * 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 + * Copyright (C) 1999 Netscape Communications Corporation. All Rights * Reserved. */ package netscape.ldap.controls; @@ -21,6 +21,7 @@ import java.io.*; import netscape.ldap.client.JDAPBERTagDecoder; import netscape.ldap.LDAPControl; import netscape.ldap.ber.stream.*; +import netscape.ldap.LDAPException; /** * Represents control data for returning paged results from a search. @@ -28,21 +29,23 @@ import netscape.ldap.ber.stream.*; * @version 1.0 * *
    - *      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          (?),
    - *               }
    - *     }
    + *   VirtualListViewResponse ::= SEQUENCE {
    + *       targetPosition   INTEGER (0 .. maxInt),
    + *       contentCount     INTEGER (0 .. maxInt),
    + *       virtualListViewResult ENUMERATED {
    + *           success                  (0),
    + *           operatonsError           (1),
    + *           timeLimitExceeded        (3),
    + *           adminLimitExceeded       (11),
    + *           insufficientAccessRights (50),
    + *           busy                     (51),
    + *           unwillingToPerform       (53),
    + *           sortControlMissing       (60),
    + *           offsetRangeError         (61),
    + *           other                    (80)
    + *       },
    + *       contextID     OCTET STRING OPTIONAL 
    + *  }
      *
    */ @@ -57,6 +60,29 @@ public class LDAPVirtualListResponse extends LDAPControl { super( VIRTUALLISTRESPONSE, true, null ); } + /** + * Contructs an LDAPVirtualListResponse object. + * @param oid This parameter must be equal to + * LDAPVirtualListResponse.VIRTUALLISTRESPONSE or a + * LDAPExceptionis thrown. + * @param critical True if this control is critical. + * @param value The value associated with this control. + * @exception netscape.ldap.LDAPException If oid is not + * LDAPVirtualListResponse.VIRTUALLISTRESPONSE. + * @see netscape.ldap.LDAPControl#register + */ + public LDAPVirtualListResponse( String oid, boolean critical, + byte[] value ) throws LDAPException { + super( VIRTUALLISTRESPONSE, critical, value ); + if ( !oid.equals( VIRTUALLISTRESPONSE ) ) { + throw new LDAPException( "oid must be LDAPVirtualListResponse." + + "VIRTUALLISTRESPONSE", + LDAPException.PARAM_ERROR); + } + + parseResponse(); + } + /** * Constructs a new LDAPVirtualListResponse object. * @param value A BER encoded byte array. @@ -92,6 +118,14 @@ public class LDAPVirtualListResponse extends LDAPControl { return m_resultCode; } + /** + * Gets the context cookie, if any. + * @return The result context cookie. + */ + public String getContext() { + return m_context; + } + /** * Returns a control useful for subsequent paged results searches. * "this" should be a control returned on a previous paged results @@ -115,11 +149,14 @@ public class LDAPVirtualListResponse extends LDAPControl { /* First is firstPosition */ m_firstPosition = ((BERInteger)seq.elementAt( 0 )).getValue(); m_contentCount = ((BERInteger)seq.elementAt( 1 )).getValue(); - BEREnumerated enum = (BEREnumerated)seq.elementAt( 2 ); - m_resultCode = enum.getValue(); - } catch(Throwable x) { + m_resultCode = ((BEREnumerated)seq.elementAt( 2 )).getValue(); + if( seq.size() > 3 ) { + BEROctetString str = (BEROctetString)seq.elementAt( 3 ); + m_context = new String(str.getValue(), "UTF8"); + } + } catch(Exception x) { m_firstPosition = m_contentCount = m_resultCode = -1; - } + m_context = null; } } /** @@ -127,6 +164,8 @@ public class LDAPVirtualListResponse extends LDAPControl { * @param controls An array of controls that may include a VLV * results control. * @return The control, if any; otherwise null. + * @deprecated LDAPVirtualListResponse controls are now automatically + * instantiated. */ public static LDAPVirtualListResponse parseResponse( LDAPControl[] controls ) { @@ -148,4 +187,6 @@ public class LDAPVirtualListResponse extends LDAPControl { private int m_firstPosition = 0; private int m_contentCount = 0; private int m_resultCode = -1; + private String m_context = null; + } diff --git a/mozilla/directory/java-sdk/ldapjdk/netscape/ldap/util/ByteBuf.java b/mozilla/directory/java-sdk/ldapjdk/netscape/ldap/util/ByteBuf.java index 4f062535247..2dc41bead99 100644 --- a/mozilla/directory/java-sdk/ldapjdk/netscape/ldap/util/ByteBuf.java +++ b/mozilla/directory/java-sdk/ldapjdk/netscape/ldap/util/ByteBuf.java @@ -25,6 +25,7 @@ import java.io.IOException; import java.io.OutputStream; import java.io.PrintStream; import java.io.RandomAccessFile; +import java.io.Serializable; /** * This class is similar to the java.lang.StringBuffer @@ -49,7 +50,9 @@ import java.io.RandomAccessFile; * functions for comparing data). * */ -public final class ByteBuf { +public final class ByteBuf implements Serializable { + + static final long serialVersionUID = -786393456618166871L; private byte value[]; private int count; @@ -148,7 +151,7 @@ public final class ByteBuf { if (count < newLength) { ensureCapacity(newLength); for (; count < newLength; count++) { - value[count] = '\0'; + value[count] = 0; } } count = newLength; diff --git a/mozilla/directory/java-sdk/ldapjdk/netscape/ldap/util/ConnectionPool.java b/mozilla/directory/java-sdk/ldapjdk/netscape/ldap/util/ConnectionPool.java new file mode 100644 index 00000000000..eb6e898e617 --- /dev/null +++ b/mozilla/directory/java-sdk/ldapjdk/netscape/ldap/util/ConnectionPool.java @@ -0,0 +1,480 @@ +/* -*- 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 netscape.ldap.util; + +import java.util.*; +import netscape.ldap.*; + +/** + * Class to maintain a pool of individual connections to the + * same server. Specify the initial size and the max size + * when constructing a pool. Call getConnection() to obtain + * a connection from the pool and close() to return it. If + * the pool is fully extended and there are no free connections, + * getConnection() blocks until a connection has been returned + * to the pool.
    + * Call destroy() to release all connections. + *

    Example:
    + *
    + * ConnectionPool pool = null;
    + * try {
    + *     pool = new ConnectionPool( 10, 30,
    + *                                "foo.acme.com",389,
    + *                                "uid=me, o=acme.com",
    + *                                "password" );
    + * } catch ( LDAPException e ) {
    + *    System.err.println( "Unable to create connection pool" );
    + *    System.exit( 1 );
    + * }
    + * while ( clientsKnocking ) {
    + *     String filter = getSearchFilter();
    + *     LDAPConnection ld = pool.getConnection();
    + *     try {
    + *         LDAPSearchResults res = ld.search( BASE, ld.SCOPE_SUB,
    + *                                            filter, attrs,
    + *                                            false );
    + *         pool.close( ld );
    + *         while( res.hasMoreElements() ) {
    + *             ...
    + *
    + */ + +/** + * Connection pool, typically used by a server to avoid creating + * a new connection for each client + * + * @version 1.0 + **/ +public class ConnectionPool { + + /** + * Constructor for specifying all parameters + * + * @param min Initial number of connections + * @param max Maximum number of connections + * @param host Hostname of LDAP server + * @param port Port number of LDAP server + * @param authdn DN to authenticate as + * @param authpw Password for authentication + * @exception LDAPException on failure to create connections + */ + public ConnectionPool( int min, int max, + String host, int port, + String authdn, String authpw ) + throws LDAPException { + this( min, max, host, port, authdn, authpw, null ); + } + + /** + * Constructor for specifying all parameters, anonymous + * identity + * + * @param min Initial number of connections + * @param max Maximum number of connections + * @param host Hostname of LDAP server + * @param port Port number of LDAP server + * @exception LDAPException on failure to create connections + */ + public ConnectionPool( int min, int max, + String host, int port) + throws LDAPException { + this( min, max, host, port, "", ""); + } + + /** + * Constructor for using default parameters, anonymous identity + * + * @param host Hostname of LDAP server + * @param port Port number of LDAP server + * @exception LDAPException on failure to create connections + */ + public ConnectionPool( String host, int port ) + throws LDAPException { + // poolsize=10,max=20,host,port, + // noauth,nopswd + this( 10, 20, host, port, "", "" ); + } + + /* + * Constructor for using an existing connection to clone + * from + * + * @param min Initial number of connections + * @param max Maximum number of connections + * @param ldc Connection to clone + * @exception LDAPException on failure to create connections + */ + public ConnectionPool( int min, int max, LDAPConnection ldc ) + throws LDAPException { + this( min, max, ldc.getHost(), ldc.getPort(), + ldc.getAuthenticationDN(), ldc.getAuthenticationPassword(), + ldc ); + } + + /* + * Constructor for using an existing connection to clone + * from + * + * @param min Initial number of connections + * @param max Maximum number of connections + * @param host Hostname of LDAP server + * @param port Port number of LDAP server + * @param authdn DN to authenticate as + * @param authpw Password for authentication + * @param ldc Connection to clone + * @exception LDAPException on failure to create connections + */ + private ConnectionPool( int min, int max, + String host, int port, + String authdn, String authpw, + LDAPConnection ldc ) + throws LDAPException { + this.poolSize = min; + this.poolMax = max; + this.host = host; + this.port = port; + this.authdn = authdn; + this.authpw = authpw; + this.ldc = ldc; + this.debugMode = false; + createPool(); + } + + /** + * Destroy the whole pool - called during a shutdown + */ + public void destroy() { + for ( int i = 0; i < pool.size(); i++ ) { + disconnect( + (LDAPConnectionObject)pool.elementAt(i) ); + } + pool.removeAllElements(); + } + + /** + * Get a connection from the pool + * + * If no connections are available, the pool will be + * extended if the number of connections is less than + * the maximum; if the pool cannot be extended, the method + * blocks until a free connection becomes available. + * + * @return An active connection + */ + public LDAPConnection getConnection() { + LDAPConnection con; + + while( (con = getConnFromPool()) == null ) { + synchronized( pool ) { + try { + pool.wait(); + } catch ( InterruptedException e ) { + } + } + } + return con; + } + + /** + * Get a connection from the pool + * + * If no connections are available, the pool will be + * extended if the number of connections is less than + * the maximum; if the pool cannot be extended, the method + * returns null. + * + * @return An active connection or null + */ + protected synchronized LDAPConnection getConnFromPool() { + LDAPConnection con = null; + LDAPConnectionObject ldapconnobj = null; + + int pSize = pool.size(); + + // Get an available connection + for ( int i = 0; i < pSize; i++ ) { + + // Get the ConnectionObject from the pool + LDAPConnectionObject co = + (LDAPConnectionObject)pool.elementAt(i); + + if ( co.isAvailable() ) { // Conn available? + ldapconnobj = co; + break; + } + } + + if ( ldapconnobj == null ) { + // If there there were no conns in pool, can we grow + // the pool? + if ( (poolMax < 0) || + ( (poolMax > 0) && + (pSize < poolMax)) ) { + + // Yes we can grow it + int i = addConnection(); + + // If a new connection was created, use it + if ( i >= 0 ) { + ldapconnobj = + (LDAPConnectionObject)pool.elementAt(i); + } + } else { + debug("All pool connections in use",true); + } + } + + if ( ldapconnobj != null ) { + ldapconnobj.setInUse( true ); // Mark as in use + con = ldapconnobj.getLDAPConn(); + } + return con; + } + + /** + * This is our soft close - all we do is mark + * the connection available for others to use + * + * @param ld A connection to be returned to the pool + */ + public synchronized void close( LDAPConnection ld ) { + + int index = find( ld ); + if ( index != -1 ) { + LDAPConnectionObject co = + (LDAPConnectionObject)pool.elementAt(index); + co.setInUse( false ); // Mark as available + synchronized( pool ) { + pool.notifyAll(); + } + } + } + + /** + * Debug method to print the contents of the pool + */ + public void printPool(){ + System.out.println("--ConnectionPool--"); + for ( int i = 0; i < pool.size(); i++ ) { + LDAPConnectionObject co = + (LDAPConnectionObject)pool.elementAt(i); + System.out.println( "" + i + "=" + co ); + } + } + + private void disconnect( + LDAPConnectionObject ldapconnObject ) { + if ( ldapconnObject != null ) { + if (ldapconnObject.isAvailable()) { + LDAPConnection ld = ldapconnObject.getLDAPConn(); + if ( (ld != null) && (ld.isConnected()) ) { + try { + ld.disconnect(); + } catch (LDAPException e) { + debug("disconnect: "+e.toString()); + } + } + ldapconnObject.setLDAPConn(null); // Clear conn + } + } + } + + private void createPool() throws LDAPException { + // Called by the constructors + if ( poolSize <= 0 ) { + throw new LDAPException("ConnectionPoolSize invalid"); + } + if ( poolMax < poolSize ) { + debug("ConnectionPoolMax is invalid, set to " + + poolSize); + poolMax = poolSize; + } + + debug("****Initializing LDAP Pool****",true); + debug("LDAP host = "+host+" on port "+port,true); + debug("Number of connections="+poolSize,true); + debug("Maximum number of connections="+poolMax,true); + debug("******",true); + + pool = new java.util.Vector(); // Create pool vector + setUpPool( poolSize ); // Initialize it + } + + private int addConnection() { + int index = -1; + + debug("adding a connection to pool..."); + try { + int size = pool.size() + 1; // Add one connection + setUpPool( size ); + + if ( size == pool.size() ) { + // New size is size requested? + index = size - 1; + } + } catch (Exception ex) { + debug("Adding a connection: "+ex.toString(),true); + } + return index; + } + + private synchronized void setUpPool( int size ) + throws LDAPException { + // Loop on creating connections + while( pool.size() < size ) { + LDAPConnectionObject co = + new LDAPConnectionObject(); + // Make LDAP connection, using template if available + LDAPConnection newConn = + (ldc != null) ? (LDAPConnection)ldc.clone() : + new LDAPConnection(); + co.setLDAPConn(newConn); + try { + if ( newConn.isConnected() ) { + // If using a template, then reconnect + // to create a separate physical connection + newConn.reconnect(); + } else { + // Not using a template, so connect with + // simple authentication + newConn.connect( host, port, + authdn, authpw); + } + } catch ( LDAPException le ) { + debug("Creating pool:"+le.toString(),true); + debug("aborting....",true); + throw le; + } + co.setInUse( false ); // Mark not in use + pool.addElement( co ); + } + } + + private int find( LDAPConnection con ) { + // Find the matching Connection in the pool + if ( con != null ) { + for ( int i = 0; i < pool.size(); i++ ) { + LDAPConnectionObject co = + (LDAPConnectionObject)pool.elementAt(i); + if ( co.getLDAPConn() == con ) { + return i; + } + } + } + return -1; + } + + /** + * Set the debug printout mode + * + * @param mode Debug mode to use + */ + public synchronized void setDebug( boolean mode ) { + debugMode = mode; + } + + /** + * Report the debug printout mode + * + * @return Debug mode in use + */ + public boolean getDebug() { + return debugMode; + } + + private void debug( String s ) { + if ( debugMode ) + System.out.println("ConnectionPool ("+ + new Date()+") : " + s); + } + + private void debug(String s, boolean severe) { + if ( debugMode || severe ) { + System.out.println("ConnectionPool ("+ + new Date()+") : " + s); + } + } + + /** + * Wrapper for LDAPConnection object in pool + */ + class LDAPConnectionObject{ + + /** + * Returns the associated LDAPConnection + * + * @return the LDAPConnection + * + */ + LDAPConnection getLDAPConn() { + return this.ld; + } + + /** + * Sets the associated LDAPConnection + * + * @param ld the LDAPConnection + * + */ + void setLDAPConn( LDAPConnection ld ) { + this.ld = ld; + } + + /** + * Marks a connection in use or available + * + * @param inUse true to mark in use, false available + * + */ + void setInUse( boolean inUse ) { + this.inUse = inUse; + } + + /** + * Returns whether the connection is available + * for use by another user. + * + * @return true if available + */ + boolean isAvailable() { + return !inUse; + } + + /** + * Debug method + * + * @return A user-friendly rendering of the object + */ + public String toString() { + return "LDAPConnection=" + ld + ",inUse=" + inUse; + } + + private LDAPConnection ld; // LDAP Connection + private boolean inUse; // In use? (true = yes) + } + + private int poolSize; // Min pool size + private int poolMax; // Max pool size + private String host; // LDAP host + private int port; // Port to connect at + private String authdn; // Identity of connections + private String authpw; // Password for authdn + private LDAPConnection ldc = null; // Connection to clone + private java.util.Vector pool; // the actual pool + private boolean debugMode; +} diff --git a/mozilla/directory/java-sdk/ldapjdk/netscape/ldap/util/DN.java b/mozilla/directory/java-sdk/ldapjdk/netscape/ldap/util/DN.java index b40a5c8b81d..e916951896b 100644 --- a/mozilla/directory/java-sdk/ldapjdk/netscape/ldap/util/DN.java +++ b/mozilla/directory/java-sdk/ldapjdk/netscape/ldap/util/DN.java @@ -12,7 +12,7 @@ * * 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 + * Copyright (C) 1999 Netscape Communications Corporation. All Rights * Reserved. */ package netscape.ldap.util; @@ -45,13 +45,13 @@ import java.util.StringTokenizer; * @version 1.0 * @see netscape.ldap.LDAPDN */ -public final class DN { +public final class DN implements Serializable { /** * List of RDNs. DN consists of one or more RDNs. * RDNs follow RFC1485 order. */ - Vector m_rdns = new Vector(); + private Vector m_rdns = new Vector(); /** * Type specifying a DN in the RFC format. @@ -72,6 +72,7 @@ public final class DN { public static int OSF = 1; private int m_dnType = RFC; + static final long serialVersionUID = -8867457218975952548L; /** * Constructs an empty DN object. @@ -164,7 +165,7 @@ public final class DN { * * @param rdn The relative distinguished name that you want * to add to the beginning of the current DN. - * @see netscape.ldap.utils.RDN + * @see netscape.ldap.util.RDN */ public void addRDNToFront(RDN rdn) { m_rdns.insertElementAt(rdn, 0); @@ -177,7 +178,7 @@ public final class DN { * * @param rdn The relative distinguished name that you want * to append to the current DN. - * @see netscape.ldap.utils.RDN + * @see netscape.ldap.util.RDN */ public void addRDNToBack(RDN rdn) { m_rdns.addElement(rdn); @@ -192,7 +193,7 @@ public final class DN { * * @param rdn The relative distinguished name that you want to add to * the current DN. - * @see netscape.ldap.utils.RDN + * @see netscape.ldap.util.RDN */ public void addRDN(RDN rdn) { if (m_dnType == RFC) { @@ -243,7 +244,7 @@ public final class DN { * Returns a list of the components (RDN objects) * that make up the current DN. * @return A list of the components of this DN. - * @see netscape.ldap.utils.RDN + * @see netscape.ldap.util.RDN */ public Vector getRDNs() { return m_rdns; @@ -328,6 +329,9 @@ public final class DN { * @return true or false */ public static boolean isDN(String dn) { + if ( dn.equals( "" ) ) { + return true; + } DN newdn = new DN(dn); return (newdn.countRDNs() > 0); } @@ -385,14 +389,53 @@ public final class DN { * @param dn The DN of a subtree that you want to check. * @return true if the current DN belongs to the subtree * specified by dn. + * @deprecated Please use isDescendantOf() instead. */ + public boolean contains(DN dn) { + + return isDescendantOf(dn); + } + + /** + * Determines if this DN is a descendant of the given DN. + *

    + * + * For example, the following section of code determines if the + * DN specified by dn1 is a descendant of the DN specified + * by dn2. + *

    +     *    DN dn1 = new DN("uid=bjensen, ou=People, o=Airius.com");
    +     *    DN dn2 = new DN("ou=People, o=Airius.com");
    +     *
    +     *    boolean isDescendant = dn1.isDescendantOf(dn2)
    +     * 
    + * In this case, since "uid=bjensen, ou=People, o=Airius.com" + * is an entry under the subtree "ou=People, o=Airius.com", + * the value of isDescendant is true. + *

    + * + * In the case where the given DN is equal to this DN + * it returns false. + * + * @param dn The DN of a subtree that you want to check. + * @return true if the current DN is a descendant of the DN + * specified by dn. + */ + + public boolean isDescendantOf(DN dn) { Vector rdns1 = dn.m_rdns; Vector rdns2 = this.m_rdns; - for (int i=rdns1.size()-1,j=rdns2.size()-1; i>=0 && j>=0; i--, j--) { + int i = rdns1.size() - 1; + int j = rdns2.size() - 1; + + if ((j < i) || (equals(dn) == true)) + return false; + + for (; i>=0 && j>=0; i--, j--) { RDN rdn1 = (RDN)rdns1.elementAt(i); RDN rdn2 = (RDN)rdns2.elementAt(j); if (!rdn2.equals(rdn1)) { diff --git a/mozilla/directory/java-sdk/ldapjdk/netscape/ldap/util/GetOpt.java b/mozilla/directory/java-sdk/ldapjdk/netscape/ldap/util/GetOpt.java index 1217fed518c..faca6db161d 100644 --- a/mozilla/directory/java-sdk/ldapjdk/netscape/ldap/util/GetOpt.java +++ b/mozilla/directory/java-sdk/ldapjdk/netscape/ldap/util/GetOpt.java @@ -119,7 +119,7 @@ import java.util.*; * * @version 1.0 */ -public class GetOpt { +public class GetOpt implements java.io.Serializable { /** * Internal variables */ @@ -130,6 +130,7 @@ public class GetOpt { private Vector m_ParameterList; private Hashtable m_optionHashTable; private Hashtable m_optionParamHashTable; + static final long serialVersionUID = -2570196909939660248L; /** * Constructs a GetOpt object. diff --git a/mozilla/directory/java-sdk/ldapjdk/netscape/ldap/util/LDIF.java b/mozilla/directory/java-sdk/ldapjdk/netscape/ldap/util/LDIF.java index a1056f8d876..d6d0382d640 100644 --- a/mozilla/directory/java-sdk/ldapjdk/netscape/ldap/util/LDIF.java +++ b/mozilla/directory/java-sdk/ldapjdk/netscape/ldap/util/LDIF.java @@ -12,7 +12,7 @@ * * 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 + * Copyright (C) 1999 Netscape Communications Corporation. All Rights * Reserved. */ package netscape.ldap.util; @@ -41,12 +41,13 @@ import java.net.*; * @version 1.0 * @see netscape.ldap.util.LDIFRecord */ -public class LDIF { +public class LDIF implements Serializable { /** * Internal constants */ private final static char COMMENT = '#'; + static final long serialVersionUID = -2710382547996750924L; /** * Constructs an LDIF object to parse the @@ -99,7 +100,6 @@ public class LDIF { * @see netscape.ldap.util.LDIFRecord */ public LDIFRecord nextRecord() throws IOException { - /* TBD: parse the version number */ if ( m_done ) return null; else @@ -124,6 +124,23 @@ public class LDIF { return null; } + if (line.startsWith("version:")) { + m_version = Integer.parseInt( + line.substring("version:".length()).trim() ); + if ( m_version != 1 ) { + throw( new IOException( "Unexpected " + line ) ); + } + // Do the next record + line = d.readLine(); + if ( (line != null) && (line.length() == 0) ) { + // Skip the newline + line = d.readLine(); + } + if (line == null) { + return null; + } + } + if (!line.startsWith("dn:")) throw new IOException("no dn found in <" + line + ">"); dn = line.substring(3).trim(); @@ -147,45 +164,58 @@ public class LDIF { throws IOException { String line = d.readLine(); if ((line == null) || (line.length() < 1) || (line.equals("-"))) { - // if this is empty line, then we finish reading all the info for - // the current entry - if ((line != null) && (line.length() < 1)) + // if this is empty line, then we're finished reading all + // the info for the current entry + if ((line != null) && (line.length() < 1)) { m_currEntryDone = true; + } return null; } if (line.startsWith("changetype:")) { /* handles (changerecord) */ + LDIFContent lc = null; String changetype = line.substring(11).trim(); if (changetype.equals("modify")) { - LDIFModifyContent mc = parse_mod_spec(d); - return mc; + lc = parse_mod_spec(d); } else if (changetype.equals("add")) { - LDIFAddContent ac = parse_add_spec(d); - return ac; + lc = parse_add_spec(d); } else if (changetype.equals("delete")) { - LDIFDeleteContent dc = parse_delete_spec(d); - return dc; + lc = parse_delete_spec(d); } else if (changetype.equals("moddn") || - changetype.equals("modrdn")) { - LDIFModDNContent mdnc = parse_moddn_spec(d); - return mdnc; - } else + changetype.equals("modrdn")) { + lc = parse_moddn_spec(d); + } else { throw new IOException("change type not supported"); - } else { - /* handles 1*(attrval-spec) */ - Hashtable ht = new Hashtable(); - String newtype; - Object val; - LDAPAttribute newAttr = null; - do { + } + return lc; + } + + /* handles 1*(attrval-spec) */ + Hashtable ht = new Hashtable(); + String newtype = null; + Object val = null; + LDAPAttribute newAttr = null; + Vector controlVector = null; + + /* Read lines until we're past the record */ + while( true ) { + if (line.startsWith("control:")) { + if ( controlVector == null ) { + controlVector = new Vector(); + } + controlVector.addElement( parse_control_spec( line ) ); + } else { + /* An attribute */ int len = line.length(); - if ( len < 1 ) + if ( len < 1 ) { break; + } int idx = line.indexOf(':'); /* Must have a colon */ if (idx == -1) - throw new IOException("no ':' found in <" + line + ">"); + throw new IOException("no ':' found in <" + + line + ">"); /* attribute type */ newtype = line.substring(0,idx).toLowerCase(); val = ""; @@ -197,14 +227,17 @@ public class LDIF { String substr = line.substring(idx).trim(); val = getDecodedBytes(substr); } else if (line.charAt(idx) == '<') { - try { - URL url = new URL(line.substring(idx+1).trim()); - String filename = url.getFile(); - val = getFileContent(filename); - } catch (MalformedURLException ex) { - throw new IOException("Exception: cannot construct url "+ - line.substring(idx+1).trim()); - } + try { + URL url = + new URL(line.substring(idx+1).trim()); + String filename = url.getFile(); + val = getFileContent(filename); + } catch (MalformedURLException ex) { + throw new IOException( + ex + + ": cannot construct url "+ + line.substring(idx+1).trim()); + } } else { val = line.substring(idx).trim(); } @@ -220,24 +253,31 @@ public class LDIF { newAttr.addValue( (byte[])val ); } ht.put( newtype, newAttr ); - - line = d.readLine(); - if (line == null || (line.length() < 1) || (line.equals("-"))) { - if ((line != null) && (line.length() < 1)) - m_currEntryDone = true; - break; - } - } while ( true ); - Enumeration en = ht.elements(); - LDIFAttributeContent ac = new LDIFAttributeContent(); - while( en.hasMoreElements() ) { - ac.addElement( (LDAPAttribute)en.nextElement() ); } - ht.clear(); - ht = null; - - return ac; + line = d.readLine(); + if (line == null || (line.length() < 1) || + (line.equals("-"))) { + if ((line != null) && (line.length() < 1)) { + m_currEntryDone = true; + } + break; + } } + LDIFAttributeContent ac = new LDIFAttributeContent(); + // Copy over the attributes to the record + Enumeration en = ht.elements(); + while( en.hasMoreElements() ) { + ac.addElement( (LDAPAttribute)en.nextElement() ); + } + ht.clear(); + if( controlVector != null ) { + LDAPControl[] controls = + new LDAPControl[controlVector.size()]; + controlVector.copyInto( controls ); + ac.setControls( controls ); + controlVector.removeAllElements(); + } + return ac; } private byte[] getDecodedBytes(String line) { @@ -279,6 +319,10 @@ public class LDIF { m_currEntryDone = false; LDAPAttribute attrs[] = ac.getAttributes(); LDIFAddContent rc = new LDIFAddContent(attrs); + LDAPControl[] controls = ac.getControls(); + if ( controls != null ) { + rc.setControls( controls ); + } return rc; } @@ -288,12 +332,28 @@ public class LDIF { */ private LDIFDeleteContent parse_delete_spec(LineReader d) throws IOException { + Vector controlVector = null; + LDIFDeleteContent dc = new LDIFDeleteContent(); String line = d.readLine(); - if (line == null || line.equals("")) { - LDIFDeleteContent dc = new LDIFDeleteContent(); - return dc; - } else - throw new IOException("invalid SEP" ); + while( line != null && !line.equals("") ) { + if (line.startsWith("control:")) { + if ( controlVector == null ) { + controlVector = new Vector(); + } + controlVector.addElement( parse_control_spec( line ) ); + } else { + throw new IOException("invalid SEP" ); + } + line = d.readLine(); + } + if( controlVector != null ) { + LDAPControl[] controls = new LDAPControl[controlVector.size()]; + controlVector.copyInto( controls ); + dc.setControls( controls ); + controlVector.removeAllElements(); + } + + return dc; } /** @@ -303,6 +363,7 @@ public class LDIF { private LDIFModifyContent parse_mod_spec(LineReader d) throws IOException { + Vector controlVector = null; String line = null; line = d.readLine(); LDIFModifyContent mc = new LDIFModifyContent(); @@ -325,6 +386,15 @@ public class LDIF { LDAPModification mod = new LDAPModification(oper, attrs[i]); mc.addElement( mod ); } + LDAPControl[] controls = ac.getControls(); + if ( controls != null ) { + if ( controlVector == null ) { + controlVector = new Vector(); + } + for( int i = 0; i < controls.length; i++ ) { + controlVector.addElement( controls[i] ); + } + } // if there is no attrval-spec, go into the else statement } else { int index = line.indexOf(":"); @@ -346,6 +416,12 @@ public class LDIF { line = d.readLine(); } while (line != null && !line.equals("")); + if( controlVector != null ) { + LDAPControl[] controls = new LDAPControl[controlVector.size()]; + controlVector.copyInto( controls ); + mc.setControls( controls ); + controlVector.removeAllElements(); + } return mc; } @@ -355,6 +431,7 @@ public class LDIF { */ private LDIFModDNContent parse_moddn_spec(LineReader d) throws IOException { + Vector controlVector = null; String line = null; line = d.readLine(); LDIFModDNContent mc = new LDIFModDNContent(); @@ -373,15 +450,124 @@ public class LDIF { else throw new IOException("Incorrect input for deleteOldRdn "); } else if (line.startsWith("newsuperior:") && - (line.length() > ("newsuperior:".length()+1))) { - mc.setNewParent(line.substring("newsuperior:".length()).trim()); + (line.length() > ("newsuperior:".length()+1))) { + mc.setNewParent(line.substring( + "newsuperior:".length()).trim()); + } else if (line.startsWith("newparent:") && + (line.length() > ("newparent:".length()+1))) { + mc.setNewParent(line.substring( + "newparent:".length()).trim()); + } else if (line.startsWith("control:")) { + if ( controlVector == null ) { + controlVector = new Vector(); + } + controlVector.addElement( parse_control_spec( line ) ); } line = d.readLine(); } while (line != null && !line.equals("")); + if( controlVector != null ) { + LDAPControl[] controls = new LDAPControl[controlVector.size()]; + controlVector.copyInto( controls ); + mc.setControls( controls ); + controlVector.removeAllElements(); + } + return mc; } + /** + * Parse the specification of a control
    + * + * A control looks line one of the following: + *
    + * control: 1.2.3.4.10.210 + *
    + * control: 1.2.3.4.10.210 true + *
    + * control: 1.2.3.4.10.210 true: someASCIIvalue + *
    + * control: 1.2.3.4.10.210: someASCIIvalue + *
    + * control: 1.2.3.4.10.210 true:: 44GK44GM44GV44KP44KJ + *
    + * control: 1.2.3.4.10.210:: 44GK44GM44GV44KP44KJ + *
    + * control: 1.2.3.4.10.210 true:< file:///usr/local/directory/cont.dta + *
    + * control: 1.2.3.4.10.210:< file:///usr/local/directory/cont.dta + * + * @param line A line containing a control spec + * @return A parsed control + * @exception IOException if the line could not be parsed + */ + protected LDAPControl parse_control_spec( String line ) + throws IOException { + boolean criticality = true; + String OID; + byte[] val = null; + int len = line.length(); + int idx = line.indexOf(':') + 2; + /* OID, must be present */ + if ( idx >= len ) { + throw new IOException("OID required for control"); + } + line = line.substring(idx).trim(); + idx = line.indexOf(' '); + if ( idx < 0 ) { + OID = line; + } else { + /* Optional criticality */ + OID = line.substring(0, idx); + line = line.substring(idx+1); + idx = line.indexOf(':'); + String criticalVal; + if (idx > 0) { + criticalVal = line.substring(0, idx); + } else { + criticalVal = line; + } + if ( criticalVal.compareTo("true") == 0 ) { + criticality = true; + } else if ( criticalVal.compareTo("false") == 0 ) { + criticality = false; + } else { + throw new IOException( + "Criticality for control must be true" + + " or false, not " + criticalVal); + } + /* Optional value */ + if ( idx > 0 ) { + /* Could be :: for binary */ + idx++; + if ( line.length() > idx ) { + if ( line.charAt(idx) == ':' ) { + idx++; + line = line.substring(idx).trim(); + val = getDecodedBytes(line); + } else if (line.charAt(idx) == '<') { + String urlString = line.substring(idx+1).trim(); + try { + URL url = new URL(urlString); + String filename = url.getFile(); + val = getFileContent(filename); + } catch (MalformedURLException ex) { + throw new IOException( + ex + ": cannot construct url "+ + urlString); + } + } else { + try { + val = line.substring(idx).trim().getBytes("UTF8"); + } catch(Exception x) { + } + } + } + } + } + return new LDAPControl( OID, criticality, val ); + } + /** * Return true if all the bytes in the given array are valid for output as a * String according to the LDIF specification. If not, the array should @@ -515,4 +701,63 @@ public class LDIF { private BufferedReader _d; String _next = null; } + + /** + * Convert a byte array to a printable string following + * the LDIF rules (encode in base64 if necessary) + * + * @param b The byte array to convert + * @return A converted string which is printable + */ + public static String toPrintableString( byte[] b ) { + String s = ""; + if (isPrintable(b)) { + try { + s = new String(b, "UTF8"); + } catch ( java.io.UnsupportedEncodingException e ) { + } + } else { + ByteBuf inBuf = new ByteBuf( b, 0, b.length ); + ByteBuf encodedBuf = new ByteBuf(); + // Translate to base 64 + MimeBase64Encoder encoder = new MimeBase64Encoder(); + encoder.translate( inBuf, encodedBuf ); + int nBytes = encodedBuf.length(); + if ( nBytes > 0 ) { + s = new String(encodedBuf.toBytes(), 0, nBytes); + } + } + return s; + } + + /** + * Test driver - just read and parse an LDIF file, printing + * each record as interpreted + * + * @param args Name of LDIF file to parse + */ + public static void main( String[] args ) { + if ( args.length != 1 ) { + System.out.println( "Usage: java LDIF " ); + System.exit( 1 ); + } + LDIF ldif = null; + try { + ldif = new LDIF( args[0] ); + } catch (Exception e) { + System.err.println("Failed to read LDIF file " + args[0] + + ", " + e.toString()); + System.exit(1); + } + try { + for( LDIFRecord rec = ldif.nextRecord(); + rec != null; rec = ldif.nextRecord() ) { + System.out.println( rec.toString() + '\n' ); + } + } catch ( IOException ex ) { + System.out.println( ex ); + System.exit( 1 ); + } + System.exit( 0 ); + } } diff --git a/mozilla/directory/java-sdk/ldapjdk/netscape/ldap/util/LDIFAddContent.java b/mozilla/directory/java-sdk/ldapjdk/netscape/ldap/util/LDIFAddContent.java index 663dbeba809..f904109eb3c 100644 --- a/mozilla/directory/java-sdk/ldapjdk/netscape/ldap/util/LDIFAddContent.java +++ b/mozilla/directory/java-sdk/ldapjdk/netscape/ldap/util/LDIFAddContent.java @@ -12,15 +12,12 @@ * * 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 + * Copyright (C) 1999 Netscape Communications Corporation. All Rights * Reserved. */ package netscape.ldap.util; -import java.util.*; -import netscape.ldap.*; -import netscape.ldap.client.*; -import java.io.*; +import netscape.ldap.LDAPAttribute; /** * @@ -37,12 +34,13 @@ import java.io.*; * @version 1.0 * @see netscape.ldap.util.LDIFRecord#getContent */ -public class LDIFAddContent implements LDIFContent { +public class LDIFAddContent extends LDIFBaseContent { /** * Internal variables */ private LDAPAttribute m_attrs[] = null; + static final long serialVersionUID = -665548826721177756L; /** * Constructs a new LDIFAddContent object with @@ -85,6 +83,9 @@ public class LDIFAddContent implements LDIFContent { for (int i = 0; i < m_attrs.length; i++) { s = s + m_attrs[i].toString(); } + if ( getControls() != null ) { + s += getControlString(); + } return "LDIFAddContent {" + s + "}"; } } diff --git a/mozilla/directory/java-sdk/ldapjdk/netscape/ldap/util/LDIFAttributeContent.java b/mozilla/directory/java-sdk/ldapjdk/netscape/ldap/util/LDIFAttributeContent.java index 9e2fe3097fb..2f0d69b7f0e 100644 --- a/mozilla/directory/java-sdk/ldapjdk/netscape/ldap/util/LDIFAttributeContent.java +++ b/mozilla/directory/java-sdk/ldapjdk/netscape/ldap/util/LDIFAttributeContent.java @@ -12,15 +12,13 @@ * * 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 + * Copyright (C) 1999 Netscape Communications Corporation. All Rights * Reserved. */ package netscape.ldap.util; -import java.util.*; -import java.io.*; -import netscape.ldap.*; -import netscape.ldap.client.*; +import java.util.Vector; +import netscape.ldap.LDAPAttribute; /** * An object of this class represents the content of an LDIF record that @@ -36,12 +34,13 @@ import netscape.ldap.client.*; * @version 1.0 * @see netscape.ldap.util.LDIFRecord#getContent */ -public class LDIFAttributeContent implements LDIFContent { +public class LDIFAttributeContent extends LDIFBaseContent { /** * Internal variables */ private Vector m_attrs = new Vector(); + static final long serialVersionUID = -2912294697848028220L; /** * Constructs an empty LDIFAttributeContent object with @@ -95,6 +94,9 @@ public class LDIFAttributeContent implements LDIFContent { for (int i = 0; i < m_attrs.size(); i++) { s = s + ((LDAPAttribute)m_attrs.elementAt(i)).toString(); } + if ( getControls() != null ) { + s += getControlString(); + } return "LDIFAttributeContent {" + s + "}"; } } diff --git a/mozilla/directory/java-sdk/ldapjdk/netscape/ldap/util/LDIFBaseContent.java b/mozilla/directory/java-sdk/ldapjdk/netscape/ldap/util/LDIFBaseContent.java new file mode 100644 index 00000000000..2a4519bad11 --- /dev/null +++ b/mozilla/directory/java-sdk/ldapjdk/netscape/ldap/util/LDIFBaseContent.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 netscape.ldap.util; + +import netscape.ldap.LDAPControl; + +/** + * + * An object of this class represents the content of an LDIF record. + * This class implements the LDIFContent interface, but + * it is abstract and must be extended for the various record types. + *

    + * + * @version 1.0 + * @see netscape.ldap.util.LDIFRecord#getContent + */ +public abstract class LDIFBaseContent + implements LDIFContent, java.io.Serializable { + + static final long serialVersionUID = -8542611537447295949L; + + /** + * Internal variables + */ + private LDAPControl[] m_controls = null; + + /** + * Blank constructor for deserialization + */ + public LDIFBaseContent() { + } + + /** + * Retrieves the list of controls specified in the content + * of the LDIF record, if any + * @return An array of LDAPControl objects that + * represent any controls specified in the the LDIF record, + * or null if none were specified. + */ + public LDAPControl[] getControls() { + return m_controls; + } + + /** + * Sets the list of controls + * @param controls An array of LDAPControl objects + * or null if none are to be specified. + */ + public void setControls( LDAPControl[] controls ) { + m_controls = controls; + } + + /** + * Get the OIDs of all controls, if any, as a string + * + * @return The OIDs of all controls, if any, as a string, + * or an empty string if there are no controls + */ + protected String getControlString() { + String s = ""; + if ( getControls() != null ) { + s += ' '; + LDAPControl[] controls = getControls(); + int len = controls.length; + for( int i = 0; i < len; i++ ) { + s += controls[i].toString(); + if ( i < (len-1) ) { + s += ' '; + } + } + } + return s; + } +} diff --git a/mozilla/directory/java-sdk/ldapjdk/netscape/ldap/util/LDIFContent.java b/mozilla/directory/java-sdk/ldapjdk/netscape/ldap/util/LDIFContent.java index 1730f07613a..7de173f0a8c 100644 --- a/mozilla/directory/java-sdk/ldapjdk/netscape/ldap/util/LDIFContent.java +++ b/mozilla/directory/java-sdk/ldapjdk/netscape/ldap/util/LDIFContent.java @@ -17,9 +17,7 @@ */ package netscape.ldap.util; -import java.util.*; -import netscape.ldap.client.*; -import java.io.*; +import netscape.ldap.LDAPControl; /** * This interface represents the content of an LDIF record. @@ -45,12 +43,12 @@ import java.io.*; *

    * * @version 1.0 - * @see netscape.ldap.LDIFRecord - * @see netscape.ldap.LDIFAttrContent - * @see netscape.ldap.LDIFAddContent - * @see netscape.ldap.LDIFModifyContent - * @see netscape.ldap.LDIFDeleteContent - * @see netscape.ldap.LDIFModDNContent + * @see netscape.ldap.util.LDIFRecord + * @see netscape.ldap.util.LDIFAttributeContent + * @see netscape.ldap.util.LDIFAddContent + * @see netscape.ldap.util.LDIFModifyContent + * @see netscape.ldap.util.LDIFDeleteContent + * @see netscape.ldap.util.LDIFModDNContent */ public interface LDIFContent { @@ -92,6 +90,22 @@ public interface LDIFContent { */ public int getType(); + /** + * Retrieves the list of controls specified in the content + * of the LDIF record, if any + * @return An array of LDAPControl objects that + * represent any controls specified in the the LDIF record, + * or null if none were specified. + */ + public LDAPControl[] getControls(); + + /** + * Sets the list of controls + * @param controls An array of LDAPControl objects + * or null if none are to be specified. + */ + public void setControls( LDAPControl[] controls ); + /** * Returns the string representation of the content of the LDIF record. * @return String representation of the content of the LDIF record. diff --git a/mozilla/directory/java-sdk/ldapjdk/netscape/ldap/util/LDIFDeleteContent.java b/mozilla/directory/java-sdk/ldapjdk/netscape/ldap/util/LDIFDeleteContent.java index 74cc1855482..a744a26d123 100644 --- a/mozilla/directory/java-sdk/ldapjdk/netscape/ldap/util/LDIFDeleteContent.java +++ b/mozilla/directory/java-sdk/ldapjdk/netscape/ldap/util/LDIFDeleteContent.java @@ -12,15 +12,11 @@ * * 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 + * Copyright (C) 1999 Netscape Communications Corporation. All Rights * Reserved. */ package netscape.ldap.util; -import java.util.*; -import netscape.ldap.client.*; -import java.io.*; - /** * An object of this class represents the content of an LDIF record that * specifies the deletion of an entry. This class implements the @@ -35,7 +31,10 @@ import java.io.*; * @version 1.0 * @see netscape.ldap.util.LDIFRecord#getContent */ -public class LDIFDeleteContent implements LDIFContent { +public class LDIFDeleteContent extends LDIFBaseContent { + + static final long serialVersionUID = -6581979396116035503L; + /** * Constructs an LDIFDeleteContent object * to specify that an entry should be deleted. (The DN @@ -62,6 +61,10 @@ public class LDIFDeleteContent implements LDIFContent { * @return The string representation of the content of the LDIF record. */ public String toString() { - return "LDIFDeleteContent {}"; + String s = ""; + if ( getControls() != null ) { + s += getControlString(); + } + return "LDIFDeleteContent {" + s + "}"; } } diff --git a/mozilla/directory/java-sdk/ldapjdk/netscape/ldap/util/LDIFModDNContent.java b/mozilla/directory/java-sdk/ldapjdk/netscape/ldap/util/LDIFModDNContent.java index 921f999b755..5cde614300d 100644 --- a/mozilla/directory/java-sdk/ldapjdk/netscape/ldap/util/LDIFModDNContent.java +++ b/mozilla/directory/java-sdk/ldapjdk/netscape/ldap/util/LDIFModDNContent.java @@ -12,16 +12,11 @@ * * 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 + * Copyright (C) 1999 Netscape Communications Corporation. All Rights * Reserved. */ package netscape.ldap.util; -import java.util.*; -import java.io.*; -import netscape.ldap.*; -import netscape.ldap.client.*; - /** * An object of this class represents the content of an LDIF record that * specifies changes to an RDN or the DN of an entry. This class @@ -36,13 +31,14 @@ import netscape.ldap.client.*; * @version 1.0 * @see netscape.ldap.util.LDIFRecord#getContent */ -public class LDIFModDNContent implements LDIFContent { +public class LDIFModDNContent extends LDIFBaseContent { /** * Internal variables */ private String m_newParent = null; private String m_rdn = null; private boolean m_deleteOldRDN = false; + static final long serialVersionUID = 1352504898614557791L; /** * Constructs an empty LDIFModDNContent object. @@ -148,6 +144,9 @@ public class LDIFModDNContent implements LDIFContent { else s = s + "new rdn( "+m_rdn+" )"; + if ( getControls() != null ) { + s += getControlString(); + } return "LDIFModDNContent {" + s + "}"; } } diff --git a/mozilla/directory/java-sdk/ldapjdk/netscape/ldap/util/LDIFModifyContent.java b/mozilla/directory/java-sdk/ldapjdk/netscape/ldap/util/LDIFModifyContent.java index 1d30e583a38..4d91f8364df 100644 --- a/mozilla/directory/java-sdk/ldapjdk/netscape/ldap/util/LDIFModifyContent.java +++ b/mozilla/directory/java-sdk/ldapjdk/netscape/ldap/util/LDIFModifyContent.java @@ -12,15 +12,13 @@ * * 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 + * Copyright (C) 1999 Netscape Communications Corporation. All Rights * Reserved. */ package netscape.ldap.util; -import java.util.*; -import java.io.*; -import netscape.ldap.*; -import netscape.ldap.client.*; +import java.util.Vector; +import netscape.ldap.LDAPModification; /** * An object of this class represents the content of an LDIF record that @@ -36,11 +34,12 @@ import netscape.ldap.client.*; * @version 1.0 * @see netscape.ldap.util.LDIFRecord#getContent */ -public class LDIFModifyContent implements LDIFContent { +public class LDIFModifyContent extends LDIFBaseContent { /** * Internal variables */ private Vector m_mods = new Vector(); + static final long serialVersionUID = -710573832339780084L; /** * Constructs an empty LDIFModifyContent object. @@ -98,6 +97,9 @@ public class LDIFModifyContent implements LDIFContent { for (int i = 0; i < m_mods.size(); i++) { s = s + ((LDAPModification)m_mods.elementAt(i)).toString(); } + if ( getControls() != null ) { + s += getControlString(); + } return "LDIFModifyContent {" + s + "}"; } } diff --git a/mozilla/directory/java-sdk/ldapjdk/netscape/ldap/util/LDIFRecord.java b/mozilla/directory/java-sdk/ldapjdk/netscape/ldap/util/LDIFRecord.java index 2b59ae4ba33..d35eab7fd36 100644 --- a/mozilla/directory/java-sdk/ldapjdk/netscape/ldap/util/LDIFRecord.java +++ b/mozilla/directory/java-sdk/ldapjdk/netscape/ldap/util/LDIFRecord.java @@ -3,7 +3,7 @@ * 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/ + * http://www.m5ozilla.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 @@ -17,9 +17,8 @@ */ package netscape.ldap.util; -import java.util.*; -import netscape.ldap.client.*; -import java.io.*; +import java.io.Serializable; +import netscape.ldap.LDAPControl; /** * An object of this class represents an LDIF record in an LDIF @@ -59,13 +58,14 @@ import java.io.*; * @see netscape.ldap.util.LDIFDeleteContent * @see netscape.ldap.util.LDIFAttributeContent */ -public class LDIFRecord { +public class LDIFRecord implements Serializable { /** * Internal variables */ private String m_dn = null; - private LDIFContent m_content = null; + private LDIFBaseContent m_content = null; + static final long serialVersionUID = -6537481934870076178L; /** * Constructs a new LDIFRecord object with the @@ -83,7 +83,7 @@ public class LDIFRecord { */ public LDIFRecord(String dn, LDIFContent content) { m_dn = dn; - m_content = content; + m_content = (LDIFBaseContent)content; } /** @@ -172,6 +172,17 @@ public class LDIFRecord { return m_content; } + /** + * Retrieves the list of controls specified in the content + * of the LDIF record, if any + * @return An array of LDAPControl objects that + * represent any controls specified in the the LDIF record, + * or null if none were specified. + */ + public LDAPControl[] getControls() { + return (m_content == null) ? null : m_content.getControls(); + } + /** * Gets the string representation of the LDIFRecord * object. diff --git a/mozilla/directory/java-sdk/ldapjdk/netscape/ldap/util/MimeBase64Decoder.java b/mozilla/directory/java-sdk/ldapjdk/netscape/ldap/util/MimeBase64Decoder.java index de50034c708..fc0bfacd6c3 100644 --- a/mozilla/directory/java-sdk/ldapjdk/netscape/ldap/util/MimeBase64Decoder.java +++ b/mozilla/directory/java-sdk/ldapjdk/netscape/ldap/util/MimeBase64Decoder.java @@ -21,6 +21,7 @@ package netscape.ldap.util; */ public final class MimeBase64Decoder extends MimeEncoder { + static final long serialVersionUID = 797397585345375903L; private byte token[] = new byte[4]; // input buffer private byte bytes[] = new byte[3]; // output buffer private int token_length = 0; // input buffer length @@ -163,7 +164,7 @@ public final class MimeBase64Decoder extends MimeEncoder { public final void eof(ByteBuf out) { if (token != null && token_length != 0) { while (token_length < 4) - token[token_length++] = '='; + token[token_length++] = (byte)'='; decode_final_token(out); } token_length = 0; diff --git a/mozilla/directory/java-sdk/ldapjdk/netscape/ldap/util/MimeBase64Encoder.java b/mozilla/directory/java-sdk/ldapjdk/netscape/ldap/util/MimeBase64Encoder.java index 234db4ab986..10303b6578e 100644 --- a/mozilla/directory/java-sdk/ldapjdk/netscape/ldap/util/MimeBase64Encoder.java +++ b/mozilla/directory/java-sdk/ldapjdk/netscape/ldap/util/MimeBase64Encoder.java @@ -22,6 +22,7 @@ package netscape.ldap.util; */ public final class MimeBase64Encoder extends MimeEncoder { + static final long serialVersionUID = 8781620079813078315L; private int buf = 0; // a 24-bit quantity private int buf_bytes = 0; // how many octets are set in it private byte line[] = new byte[74]; // output buffer @@ -29,7 +30,7 @@ public final class MimeBase64Encoder extends MimeEncoder { static private final byte crlf[] = "\r\n".getBytes(); - static private final byte map[] = { + static private final char map[] = { 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', // 0-7 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', // 8-15 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', // 16-23 @@ -42,10 +43,10 @@ public final class MimeBase64Encoder extends MimeEncoder { private final void encode_token() { int i = line_length; - line[i] = map[0x3F & (buf >> 18)]; // sextet 1 (octet 1) - line[i+1] = map[0x3F & (buf >> 12)]; // sextet 2 (octet 1 and 2) - line[i+2] = map[0x3F & (buf >> 6)]; // sextet 3 (octet 2 and 3) - line[i+3] = map[0x3F & buf]; // sextet 4 (octet 3) + line[i] = (byte)map[0x3F & (buf >> 18)]; // sextet 1 (octet 1) + line[i+1] = (byte)map[0x3F & (buf >> 12)]; // sextet 2 (octet 1 and 2) + line[i+2] = (byte)map[0x3F & (buf >> 6)]; // sextet 3 (octet 2 and 3) + line[i+3] = (byte)map[0x3F & buf]; // sextet 4 (octet 3) line_length += 4; buf = 0; buf_bytes = 0; @@ -53,18 +54,18 @@ public final class MimeBase64Encoder extends MimeEncoder { private final void encode_partial_token() { int i = line_length; - line[i] = map[0x3F & (buf >> 18)]; // sextet 1 (octet 1) - line[i+1] = map[0x3F & (buf >> 12)]; // sextet 2 (octet 1 and 2) + line[i] = (byte)map[0x3F & (buf >> 18)]; // sextet 1 (octet 1) + line[i+1] = (byte)map[0x3F & (buf >> 12)]; // sextet 2 (octet 1 and 2) if (buf_bytes == 1) - line[i+2] = '='; + line[i+2] = (byte)'='; else - line[i+2] = map[0x3F & (buf >> 6)]; // sextet 3 (octet 2 and 3) + line[i+2] = (byte)map[0x3F & (buf >> 6)]; // sextet 3 (octet 2 and 3) if (buf_bytes <= 2) - line[i+3] = '='; + line[i+3] = (byte)'='; else - line[i+3] = map[0x3F & buf]; // sextet 4 (octet 3) + line[i+3] = (byte)map[0x3F & buf]; // sextet 4 (octet 3) line_length += 4; buf = 0; buf_bytes = 0; diff --git a/mozilla/directory/java-sdk/ldapjdk/netscape/ldap/util/MimeEncoder.java b/mozilla/directory/java-sdk/ldapjdk/netscape/ldap/util/MimeEncoder.java index 300afca4cc4..e2627cca4e9 100644 --- a/mozilla/directory/java-sdk/ldapjdk/netscape/ldap/util/MimeEncoder.java +++ b/mozilla/directory/java-sdk/ldapjdk/netscape/ldap/util/MimeEncoder.java @@ -19,11 +19,13 @@ package netscape.ldap.util; /** This abstract class is the parent of those classes which implement MIME encoding and decoding: base64. - @see netcape.ldap.util.MimeBase64Encoder - @see netcape.ldap.util.MimeBase64Decoder + @see netscape.ldap.util.MimeBase64Encoder + @see netscape.ldap.util.MimeBase64Decoder */ -public abstract class MimeEncoder { +public abstract class MimeEncoder implements java.io.Serializable { + + static final long serialVersionUID = 5179250095383961512L; /** Given a sequence of input bytes, produces a sequence of output bytes. Note that some (small) amount of buffering may be necessary, if the diff --git a/mozilla/directory/java-sdk/ldapjdk/netscape/ldap/util/RDN.java b/mozilla/directory/java-sdk/ldapjdk/netscape/ldap/util/RDN.java index 331744f9fc3..799937a4b53 100644 --- a/mozilla/directory/java-sdk/ldapjdk/netscape/ldap/util/RDN.java +++ b/mozilla/directory/java-sdk/ldapjdk/netscape/ldap/util/RDN.java @@ -12,13 +12,11 @@ * * 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 + * Copyright (C) 1999 Netscape Communications Corporation. All Rights * Reserved. */ package netscape.ldap.util; -import java.util.*; -import java.io.*; import java.util.StringTokenizer; /** @@ -39,15 +37,17 @@ import java.util.StringTokenizer; *

    * * @version 1.0 - * @see netscape.ldap.utils.DN + * @see netscape.ldap.util.DN */ -public final class RDN { +public final class RDN implements java.io.Serializable { + + static final long serialVersionUID = 7895454691174650321L; /** * List of RDNs. DN consists of one or more RDNs. */ - String m_type = null; - String m_value = null; + private String m_type = null; + private String m_value = null; /** * Constructs a new RDN object from the specified diff --git a/mozilla/directory/java-sdk/ldapsp/Readme.html b/mozilla/directory/java-sdk/ldapsp-relnotes.htm similarity index 96% rename from mozilla/directory/java-sdk/ldapsp/Readme.html rename to mozilla/directory/java-sdk/ldapsp-relnotes.htm index e4bdcc6842c..d49d35356b1 100644 --- a/mozilla/directory/java-sdk/ldapsp/Readme.html +++ b/mozilla/directory/java-sdk/ldapsp-relnotes.htm @@ -10,7 +10,7 @@

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

    +Netscape LDAP Service Provider for JNDI (08-02-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 @@ -26,7 +26,7 @@ an equivalent of a directory entry in the ldapjdk.
     

    Using Netscape LDAP Service Provider

    -The current implementation  is based on the JNDI 1.2 beta1. In addition +The current implementation  is based on the JNDI 1.2 FCS. 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 @@ -48,13 +48,13 @@ listed jar files are: ldapjdk.jar -Netscape Directory SDK for Java 3.1 +Netscape Directory SDK for Java 4.0 Beta jndi.jar -JNDI 1.2 beta1 +JNDI 1.2 FCS @@ -460,14 +460,11 @@ to search subtree */ String[] { "sn" });

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

            /* search for all entries with surname of Jensen */ @@ -532,9 +529,12 @@ Therefore, the class name specified with the javaClassName attribute must be available in the local CLASSPATH.

  • -search() method for schema directory contexts. Instead of DirContext.search(), -the  Context.lookup() request should be used for a schema directory -context.
  • +search() and modifyAttribute() methods for schema directory +contexts. Instead of DirContext.search(), the  Context.lookup() +request should be used for a schema directory context. Instead of DirContext.modifyAttribute(), +DirContext.deleteSubcontext() +followed the DirContext.craeteSubcontext() +should be used. diff --git a/mozilla/directory/java-sdk/ldapsp.mk b/mozilla/directory/java-sdk/ldapsp.mk index fbd03c0dbad..61db1ce96f2 100644 --- a/mozilla/directory/java-sdk/ldapsp.mk +++ b/mozilla/directory/java-sdk/ldapsp.mk @@ -12,7 +12,7 @@ # # 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 +# Copyright (C) 1999 Netscape Communications Corporation. All Rights # Reserved. # # Makefile for the LDAP classes @@ -60,7 +60,7 @@ SRCDIR=com/netscape/jndi/ldap DISTDIR=$(MCOM_ROOT)/dist CLASSDIR=$(MCOM_ROOT)/dist/classes CLASSPACKAGEDIR=$(DISTDIR)/packages -#PACKAGENAME=jndi.zip +DOCDIR=$(DISTDIR)/doc/ldapsp ifeq ($(DEBUG), full) BASEPACKAGENAME=ldapsp_debug.jar else @@ -69,6 +69,9 @@ endif CLASSPACKAGE=$(CLASSPACKAGEDIR)/$(PACKAGENAME) MANIFEST=$(BASEDIR)/ldapsp/manifest.mf +ifndef JAVADOC + JAVADOC=javadoc -classpath "$(JAVACLASSPATH)" +endif ifndef JAVAC ifdef JAVA_HOME JDKBIN=$(JDK)/bin/ @@ -80,8 +83,12 @@ ifndef JAVAC endif endif +DOCCLASSES=com.netscape.jndi.ldap.controls + all: classes +doc: $(DISTDIR) $(DOCDIR) DOCS + basics: $(DISTDIR) $(CLASSDIR) classes: JNDICLASSES @@ -101,18 +108,22 @@ COMMON: basics CONTROLS: basics cd ldapsp/$(SRCDIR)/controls; $(JAVAC) -d $(CLASS_DEST) *.java - JNDICLASSES: COMMON CONTROLS SCHEMA MAIN +DOCS: + $(JAVADOC) -d $(DOCDIR) $(DOCCLASSES) + clean: rm -rf $(DISTDIR)/classes/com/netscape/jndi/ldap $(CLASSPACKAGEDIR): mkdir -p $@ +$(DOCDIR): + mkdir -p $@ + $(DISTDIR): mkdir -p $@ $(CLASSDIR): - mkdir -p $@ - + mkdir -p $@ \ No newline at end of file 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 index bf5cddbed7b..64b17096801 100644 --- a/mozilla/directory/java-sdk/ldapsp/com/netscape/jndi/ldap/AttributesImpl.java +++ b/mozilla/directory/java-sdk/ldapsp/com/netscape/jndi/ldap/AttributesImpl.java @@ -25,7 +25,7 @@ import netscape.ldap.*; import java.util.*; /** - * Wrapper for LDAPAttributes which implements JNDI Attribute interface + * Wrapper for LDAPAttributeSet which implements JNDI Attribute interface */ class AttributesImpl implements Attributes { 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 index cb455ea524a..aadb7d77e2b 100644 --- a/mozilla/directory/java-sdk/ldapsp/com/netscape/jndi/ldap/ContextEnv.java +++ b/mozilla/directory/java-sdk/ldapsp/com/netscape/jndi/ldap/ContextEnv.java @@ -286,8 +286,10 @@ class ContextEnv extends ShareableEnv { cons.setReferrals(false); } else if(mode.equalsIgnoreCase(V_REFERRAL_IGNORE)) { + //TODO If MANAGEDSAIT control is not supported by the server + //(e.g. Active Directory) should enable exception and ignore it cons.setServerControls(new LDAPControl( - LDAPControl.MANAGEDSAIT, true, null)); + LDAPControl.MANAGEDSAIT, /*critical=*/false, null)); } else { throw new IllegalArgumentException("Illegal value for " + P_REFERRAL_MODE); @@ -532,4 +534,4 @@ class ContextEnv extends ShareableEnv { 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 index 8c86118da71..ae9a69be3c2 100644 --- a/mozilla/directory/java-sdk/ldapsp/com/netscape/jndi/ldap/EventService.java +++ b/mozilla/directory/java-sdk/ldapsp/com/netscape/jndi/ldap/EventService.java @@ -19,6 +19,7 @@ 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.*; @@ -26,76 +27,37 @@ 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 + * Event Service monitors changes on the server + * Implemented with the persistant search control. Uses ldapjdk asynchronous + * interfaces so that multiple search requests can be processed by a single + * thread * */ -class EventService { +class EventService implements Runnable{ LdapService m_ldapSvc; - Vector m_eventThreads = new Vector(); + Vector m_eventList = new Vector(); + Thread m_monitorThread; + LDAPSearchListener m_msgQueue; // for asynch ldap search - public EventService(LdapService ldapSvc) { + /** + * Constructor + */ + 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"); + synchronized void addListener (LdapContextImpl ctx, String name, + String filter,SearchControls jndiCtrls, NamingListener l) + throws NamingException{ + + EventEntry event = null; + LDAPSearchListener sl = null; // Search listener for this request + + Debug.println(1, "ADD LISTENER"); // Create DN by appending the name to the current context String base = ctx.getDN(); @@ -117,58 +79,391 @@ class EventService { // 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 + // Attributes in jndiCtrls.getReturningAttributes() are ignored + // This is because we are not returning objects in the NamingEvent + // and thus listeners can not read attributes from the event. + // 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); + // Check if such change is already monitored, search for the event entry + for (int i=0; i < m_eventList.size(); i++) { + EventEntry ee = (EventEntry) m_eventList.elementAt(i); + if (ee.isEqualEvent(base, scope, filter, attrs, cons)) { + event = ee; break; } } - + + // If event entry does not exist, send an asynch persistent search + // request and create a new event entry + if (event == null) { + try { + Debug.println(1, "Do persistent search for " + base); + sl = ld.search( base, scope, filter, attrs, + false, /*l=*/null, cons); + int[] ids = sl.getIDs(); + int id = ids[ids.length-1]; + event = new EventEntry(id, ctx, base, scope, filter, attrs, cons); + m_eventList.addElement(event); + } + catch(Exception ex) { + throw ExceptionMapper.getNamingException(ex); + } + } + + // regiter naming listener with the event + event.addListener(l); + + // Add this search request to the m_msgQueue so it can be + // processed by the monitor thread + if (m_msgQueue == null) { + m_msgQueue = sl; + } + else { + m_msgQueue.merge(sl); + } + // 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(); + if (m_monitorThread == null) { + m_monitorThread = new Thread (this, "EventService"); + m_monitorThread.setDaemon(true); + m_monitorThread.start(); } } - /** - * Change Monitoring Thread + * Remove change event listener */ - static private class EventThread extends Thread { + synchronized void removeListener(NamingListener listener)throws NamingException { + boolean removed = false; + + // Check and the listener against all event entries. If an event is + // left with no listeners, abandon associated ldap request + for(int i = m_eventList.size()-1; i>=0; i--) { + EventEntry ee = (EventEntry)m_eventList.elementAt(i); + if (ee.removeListener(listener)) { + removed = true; + + // If no listeners left abandon persistant search and + // delete entry + if (ee.isEmpty()) { + abandonRequest(ee.id); + m_eventList.removeElement(ee); + } + } + } + + // Stop the monitor thread if no events are left + // Actually, the thread should stop by itself, as when no outstanding + // events are left LDAPSearchListener.getResponse() should return null + if (m_eventList.size() == 0) { + m_monitorThread = null; + } + + if (!removed) { + throw new NamingException("Listener not found"); + } + } + + /** + * Abandon LDAP request with the specified message ID + */ + private void abandonRequest(int id) { + LDAPConnection ldc = m_ldapSvc.getConnection(); + try { + ldc.abandon(id); + } + catch (LDAPException ex) {} + } + + + /** + * Main monitor thread loop. Wait for persistent search change notifications + */ + public void run() { + + LDAPMessage msg = null; + + + while (m_monitorThread != null) { + + try { + + // Block untill a message is received + msg = m_msgQueue.getResponse(); + + } + catch (LDAPException ex) { + processNetworkError(ex); + } + + // Terminate if no more requests left + if (msg == null) { + Debug.println(1, "No more messages, bye"); + m_monitorThread = null; + return; + } + + synchronized (EventService.this) { + + EventEntry eventEntry = getEventEntry(msg.getId()); + + // If no listeners, abandon this message id + if (eventEntry == null) { + Debug.println(1, "Received ldap msg with unknown id="+msg.getId()); + + if (! (msg instanceof LDAPResponse)) { + abandonRequest(msg.getId()); + } + continue; + } + + // Check for error message ... + if (msg instanceof LDAPResponse) { + processResponseMsg((LDAPResponse) msg, eventEntry); + } + + // ... or referral ... + else if (msg instanceof LDAPSearchResultReference) { + processSearchResultRef((LDAPSearchResultReference) msg, eventEntry); + } + + + // ... then must be a LDAPSearchResult carrying change control + else if (msg instanceof LDAPSearchResult) { + processSearchResultMsg((LDAPSearchResult) msg, eventEntry); + } + } + } // end of synchronized block + } + + /** + * On network error, create NamingExceptionEvent and delever it to all + * listeners on all events. + */ + private void processNetworkError(LDAPException ex) { + NamingException nameEx = ExceptionMapper.getNamingException(ex); + for(int i=0; i0) { + str += " "; } + str += attrs[i]; } - catch(Exception ex) { - NamingException nameEx = ExceptionMapper.getNamingException(ex); - dispatchEvent(new NamingExceptionEvent(ctx, nameEx)); - } + str += "} chanageTypes=" + psearch.getChangeTypes(); + str += " listeners=" + listeners.size(); + str += " id=" + id; + return str; } - - /** - * 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/LdapContextImpl.java b/mozilla/directory/java-sdk/ldapsp/com/netscape/jndi/ldap/LdapContextImpl.java index 53bc6d19119..025005d9178 100644 --- a/mozilla/directory/java-sdk/ldapsp/com/netscape/jndi/ldap/LdapContextImpl.java +++ b/mozilla/directory/java-sdk/ldapsp/com/netscape/jndi/ldap/LdapContextImpl.java @@ -591,14 +591,20 @@ public class LdapContextImpl implements EventDirContext, LdapContext { public LdapContext newInstance(Control[] reqCtls) throws NamingException { LdapContextImpl clone = new LdapContextImpl(m_ctxDN, this); - // This controls are to be set on the the LDAPConnection + // 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 { + public void reconnect(Control[] reqCtls) throws NamingException { close(); m_ldapSvc = new LdapService(this); + // This controls are to be set on the the LDAPConnection + m_ctxEnv.setProperty(ContextEnv.P_CONNECT_CTRLS, reqCtls); m_ldapSvc.connect(); } + + public Control[] getConnectControls() { + return (Control[])m_ctxEnv.getProperty(ContextEnv.P_CONNECT_CTRLS); + } } 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 index 659458e67f6..04061d8f3f1 100644 --- a/mozilla/directory/java-sdk/ldapsp/com/netscape/jndi/ldap/LdapService.java +++ b/mozilla/directory/java-sdk/ldapsp/com/netscape/jndi/ldap/LdapService.java @@ -147,7 +147,7 @@ class LdapService { /** * Physically disconect only if the client count is zero */ - void disconnect() { + synchronized void disconnect() { try { if (m_clientCount > 0) { m_clientCount--; @@ -162,7 +162,7 @@ class LdapService { /** * Increment client count */ - void incrementClientCount() { + synchronized void incrementClientCount() { m_clientCount++; } 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 index 50257f390c7..7092ba9d5db 100644 --- a/mozilla/directory/java-sdk/ldapsp/com/netscape/jndi/ldap/ObjectMapper.java +++ b/mozilla/directory/java-sdk/ldapsp/com/netscape/jndi/ldap/ObjectMapper.java @@ -452,7 +452,7 @@ public class ObjectMapper { } public static void main(String[] args) { - byte[] serialRefAddr = { 'a', '0', 'A', (byte)0x10, (byte)0x7f, (byte)0xaa }; + byte[] serialRefAddr = { (byte)'a', (byte)'0', (byte)'A', (byte)0x10, (byte)0x7f, (byte)0xaa }; MimeBase64Encoder base64 = new MimeBase64Encoder(); MimeBase64Decoder base64Dec = new MimeBase64Decoder(); ByteBuf in = new ByteBuf(), out = new ByteBuf(); 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 index a344d843164..2497d45c06a 100644 --- a/mozilla/directory/java-sdk/ldapsp/com/netscape/jndi/ldap/SearchResultEnum.java +++ b/mozilla/directory/java-sdk/ldapsp/com/netscape/jndi/ldap/SearchResultEnum.java @@ -61,7 +61,7 @@ class SearchResultEnum implements NamingEnumeration { Attributes attrs = new AttributesImpl(entry.getAttributeSet(), m_userBinaryAttrs); // check for response controls - LDAPControl[] ldapCtls = m_ld.getResponseControls(); + LDAPControl[] ldapCtls = m_res.getResponseControls(); if (ldapCtls != null) { // Parse raw controls Control[] ctls = new Control[ldapCtls.length]; 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 index 5828b2672a7..8fef4208e97 100644 --- 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 @@ -38,15 +38,19 @@ public class LdapContextAdapter extends DirContextAdapter implements EventDirCon throw new OperationNotSupportedException(); } - public LdapContext newInstance(Control[] reqCtls) throws NamingException { + public Control[] getConnectControls() throws NamingException { throw new OperationNotSupportedException(); } - public void reconnect() throws NamingException { + public LdapContext newInstance(Control[] reqCtls) throws NamingException { throw new OperationNotSupportedException(); } - public void setRequestControls(Control[] reqCtls) throws NamingException { + public void reconnect(Control[] reqCtls) throws NamingException { + throw new OperationNotSupportedException(); + } + + public void setRequestControls(Control[] reqCtls) throws NamingException { throw new OperationNotSupportedException(); } /** 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 index b07db150125..35d23c5ce5a 100644 --- 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 @@ -17,6 +17,9 @@ */ package com.netscape.jndi.ldap.controls; +import javax.naming.ldap.Control; +import netscape.ldap.controls.*; + /** * Represents an LDAP v3 server control that specifies information * about a change to an entry in the directory. (The OID for this @@ -32,12 +35,6 @@ package com.netscape.jndi.ldap.controls; * 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: @@ -52,77 +49,22 @@ package com.netscape.jndi.ldap.controls; *

    * * @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); + LdapEntryChangeControl(boolean critical, byte[] value) throws Exception { + super(ENTRYCHANGED, critical, value); } /** * 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(); @@ -146,8 +88,6 @@ public class LdapEntryChangeControl extends LDAPEntryChangeControl implements Co * renamed) * *

    - * - * @see com.netscape.jndi.ldap.controls.LdapEntryChangeControl#setChangeType */ public int getChangeType() { return super.getChangeType(); @@ -156,14 +96,16 @@ public class LdapEntryChangeControl extends LDAPEntryChangeControl implements Co /** * 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 + * Retrieves the ASN.1 BER encoded value of the LDAP control. + * Null is returned if the value is absent. + * @return A possibly null byte array representing the ASN.1 BER + * encoded value of the LDAP control. */ 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 index 569c4113e3f..924fc69ac7a 100644 --- 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 @@ -29,26 +29,26 @@ import netscape.ldap.controls.*; 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"); + super(EXPIRED, critical, value); } /** * Return string message passed in the control - * TODO what is the information in this string value ? + * @return message string */ public String getMessage() { - return m_message; + return super.getMessage(); } /** - * Implements Control interface + * Retrieves the ASN.1 BER encoded value of the LDAP control. + * Null is returned if the value is absent. + * @return A possibly null byte array representing the ASN.1 BER + * encoded value of the LDAP control. */ 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 index 2a573a0a255..89d74348e13 100644 --- 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 @@ -28,30 +28,26 @@ import netscape.ldap.controls.*; */ 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); - } + super(EXPIRING, critical, value); } /** * Return parsed number of seconds before password expires + * @return number of seconds before password expires */ public int getSecondsToExipre() { - return m_secondsToExpire; + return super.getSecondsToExpiration(); } /** - * Implements Control interface + * Retrieves the ASN.1 BER encoded value of the LDAP control. + * Null is returned if the value is absent. + * @return A possibly null byte array representing the ASN.1 BER + * encoded value of the LDAP control. */ 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 index 32af3fa882a..1b154f7b825 100644 --- 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 @@ -25,8 +25,7 @@ import netscape.ldap.controls.*; * 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. + * conjunction with an "entry change notification" control. *

    * * To use persistent searching for change notification, you create a @@ -62,17 +61,51 @@ import netscape.ldap.controls.*; * server * *

    - * @see com.netscape.jndi.ldap.controls.LDAPEntryChangeControl + * @see com.netscape.jndi.ldap.controls.LdapEntryChangeControl */ public class LdapPersistSearchControl extends LDAPPersistSearchControl implements Control { - /** - * Default constructor + /** + * Change type specifying that you want to track additions of new + * entries to the directory. You can either specify this change type + * when constructing an LdapPersistSearchControl or + * by using the setChangeTypes method. + * @see com.netscape.jndi.ldap.controls.LdapPersistSearchControl#getChangeTypes + * @see com.netscape.jndi.ldap.controls.LdapPersistSearchControl#setChangeTypes */ - public LdapPersistSearchControl() { - } + public static final int ADD = LDAPPersistSearchControl.ADD; + /** + * Change type specifying that you want to track removals of + * entries from the directory. You can either specify this change type + * when constructing an LdapPersistSearchControl or + * by using the setChangeTypes method. + * @see com.netscape.jndi.ldap.controls.LdapPersistSearchControl#getChangeTypes + * @see com.netscape.jndi.ldap.controls.LdapPersistSearchControl#setChangeTypes + */ + public static final int DELETE = LDAPPersistSearchControl.DELETE; + + /** + * Change type specifying that you want to track modifications of + * entries in the directory. You can either specify this change type + * when constructing an LdapPersistSearchControl or + * by using the setChangeTypes method. + * @see com.netscape.jndi.ldap.controls.LdapPersistSearchControl#getChangeTypes + * @see com.netscape.jndi.ldap.controls.LdapPersistSearchControl#setChangeTypes + */ + public static final int MODIFY = LDAPPersistSearchControl.MODIFY; + + /** + * Change type specifying that you want to track modifications of the + * DNs of entries in the directory. You can either specify this change type + * when constructing an LdapPersistSearchControl or + * by using the setChangeTypes method. + * @see com.netscape.jndi.ldap.controls.LdapPersistSearchControl#getChangeTypes + * @see com.netscape.jndi.ldap.controls.LdapPersistSearchControl#setChangeTypes + */ + public static final int MODDN = LDAPPersistSearchControl.MODDN; + /** * Constructs an LdapPersistSearchControl object * that specifies a persistent search. @@ -112,7 +145,6 @@ public class LdapPersistSearchControl extends LDAPPersistSearchControl implement * 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(); @@ -126,7 +158,6 @@ public class LdapPersistSearchControl extends LDAPPersistSearchControl implement * 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(); @@ -140,7 +171,6 @@ public class LdapPersistSearchControl extends LDAPPersistSearchControl implement * 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(); @@ -186,7 +216,10 @@ public class LdapPersistSearchControl extends LDAPPersistSearchControl implement } /** - * Implements Control interface + * Retrieves the ASN.1 BER encoded value of the LDAP control. + * Null is returned if the value is absent. + * @return A possibly null byte array representing the ASN.1 BER + * encoded value of the LDAP control. */ 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 index ff5ed80b868..94ac91c2df3 100644 --- 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 @@ -23,45 +23,7 @@ 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) + * (The OID for this control is 2.16.840.1.113730.3.4.12.) * */ public class LdapProxiedAuthControl extends LDAPProxiedAuthControl implements Control { @@ -79,11 +41,13 @@ public class LdapProxiedAuthControl extends LDAPProxiedAuthControl implements Co } /** - * Implements Control interface + * Retrieves the ASN.1 BER encoded value of the LDAP control. + * Null is returned if the value is absent. + * @return A possibly null byte array representing the ASN.1 BER + * encoded value of the LDAP control. */ 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 index 23157a5fcc4..d794752afcd 100644 --- 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 @@ -32,176 +32,17 @@ import netscape.ldap.controls.*; * 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 + * 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) + * @see com.netscape.jndi.ldap.LdapSortResponseControl */ public class LdapSortControl extends LDAPSortControl implements Control{ /** @@ -241,6 +82,15 @@ public class LdapSortControl extends LDAPSortControl implements Control{ return keysOut; } + /** + * 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(String[] keys, boolean critical) { super(toSortKey(keys), critical); @@ -248,7 +98,10 @@ public class LdapSortControl extends LDAPSortControl implements Control{ /** - * Implements Control interface + * Retrieves the ASN.1 BER encoded value of the LDAP control. + * Null is returned if the value is absent. + * @return A possibly null byte array representing the ASN.1 BER + * encoded value of the LDAP control. */ 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 index 2adabe6f97e..51b6825d8e8 100644 --- 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 @@ -41,7 +41,6 @@ public class LdapSortKey extends LDAPSortKey{ * 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 */ @@ -61,7 +60,6 @@ public class LdapSortKey extends LDAPSortKey{ * * @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 */ @@ -85,7 +83,6 @@ public class LdapSortKey extends LDAPSortKey{ * 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 */ @@ -120,5 +117,4 @@ public class LdapSortKey extends LDAPSortKey{ public String getMatchRule() { return super.getMatchRule(); } -} - +} \ No newline at end of file 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 index 5ad9d9e796e..fdaa3f21c23 100644 --- 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 @@ -154,67 +154,53 @@ import com.netscape.jndi.ldap.common.ExceptionMapper; * *

    * @see com.netscape.jndi.ldap.LdapSortKey - * @see com.netscape.jndi.ldap.LdapSearchConstraints - * @see com.netscape.jndi.ldap.LdapSearchConstraints#setServerControls(LDAPControl) + * @see com.netscape.jndi.ldap.LdapSortControl */ -public class LdapSortResponseControl extends LDAPControl implements Control { +public class LdapSortResponseControl extends LDAPSortControl 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; + LdapSortResponseControl(boolean critical, byte[] value) throws Exception{ + super(SORTRESPONSE, critical, value); } /** * Get the first attribute type from the sort key list that * resulted in an error + * @return Attribute name that resulted in an error */ public String getFailedAttribute() { - return m_failedAttribute; + return super.getFailedAttribute(); } /** * Return the sort result code + * @return The sort result code */ public int getResultCode() { - return m_resultCode; + return super.getResultCode(); } /** * Return corresponding NamingException for the sort error code + * @return NamingException for the sort error code */ public NamingException getSortException() { - if (m_resultCode == 0) { // success + if (getResultCode() == 0) { // success return null; } return ExceptionMapper.getNamingException( - new LDAPException("Server Sort Failed", m_resultCode)); - } + new LDAPException("Server Sort Failed", getResultCode())); + } + /** - * Implements Control interface + * Retrieves the ASN.1 BER encoded value of the LDAP control. + * Null is returned if the value is absent. + * @return A possibly null byte array representing the ASN.1 BER + * encoded value of the LDAP control. */ public byte[] getEncodedValue() { return getValue(); 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 index 17f40f6ef2a..7233aa15f56 100644 --- 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 @@ -139,7 +139,10 @@ public class LdapVirtualListControl extends LDAPVirtualListControl implements Co } /** - * Implements Control interface + * Retrieves the ASN.1 BER encoded value of the LDAP control. + * Null is returned if the value is absent. + * @return A possibly null byte array representing the ASN.1 BER + * encoded value of the LDAP control. */ 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 index 15c8d86877d..2a985b95344 100644 --- 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 @@ -51,10 +51,8 @@ public class LdapVirtualListResponseControl extends LDAPVirtualListResponse impl * @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); + LdapVirtualListResponseControl(boolean critical, byte[] value)throws Exception { + super(VIRTUALLISTRESPONSE, critical, value); } /** @@ -82,7 +80,10 @@ public class LdapVirtualListResponseControl extends LDAPVirtualListResponse impl } /** - * Implements Control interface + * Retrieves the ASN.1 BER encoded value of the LDAP control. + * Null is returned if the value is absent. + * @return A possibly null byte array representing the ASN.1 BER + * encoded value of the LDAP control. */ 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 index 6bb1c82fc4b..54dc26cd389 100644 --- 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 @@ -63,7 +63,11 @@ public class NetscapeControlFactory extends ControlFactory { /** - * Implements abstract getControlInstance() from ConrolFactory + * Creates a control using this control factory + * @param ctrl A non-null control. + * @return A possibly null Control. + * @exception NamingException If ctrl contains invalid data that prevents it from + * being used to create a control. */ public Control getControlInstance(Control ctrl) throws NamingException { if (ctrl == null) { @@ -71,11 +75,15 @@ public class NetscapeControlFactory extends ControlFactory { } LDAPControl rawCtrl = new LDAPControl( ctrl.getID(), ctrl.isCritical(), ctrl.getEncodedValue()); - return getControlInstance(rawCtrl); + return getControlInstance(rawCtrl); } /** - * Create control using parseResponse() methods in ldapjdk controls + * Create a JNDI control from a raw ldapjdk control + * @param ctrl A non-null control. + * @return A possibly null Control. + * @exception NamingException If ctrl contains invalid data that prevents it from + * being used to create a control. */ public static Control getControlInstance(LDAPControl rawCtrl) throws NamingException { if (rawCtrl == null) { @@ -87,49 +95,32 @@ public class NetscapeControlFactory extends ControlFactory { // 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; - } + return new LdapEntryChangeControl( + rawCtrl.isCritical(), rawCtrl.getValue()); } // Password Expired control else if(ctrlID.equals(RSP_PWDEXPIRED)) { - LdapPasswordExpiredControl ctrl = new LdapPasswordExpiredControl( - rawCtrl.isCritical(), rawCtrl.getValue()); + return new LdapPasswordExpiredControl( + rawCtrl.isCritical(), rawCtrl.getValue()); } // Password Expiring control else if(ctrlID.equals(RSP_PWDEXPIRING)) { - LdapPasswordExpiringControl ctrl = new LdapPasswordExpiringControl( - rawCtrl.isCritical(), rawCtrl.getValue()); + return 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; + return new LdapSortResponseControl( + rawCtrl.isCritical(), rawCtrl.getValue()); } // Virtual List Response control else if(ctrlID.equals(RSP_VIRTUALLIST)) { - return new LdapVirtualListResponseControl(rawCtrl.getValue()); + return new LdapVirtualListResponseControl( + rawCtrl.isCritical(), rawCtrl.getValue()); } // No match try another ControlFactory 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 index af8ea990e82..2cb01997146 100644 --- 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 @@ -97,7 +97,7 @@ public class SchemaAttribute extends SchemaElement { } } else if (attrName.equals(OBSOLETE) || attrName.equals(SUP) || attrName.equals(USAGE) || - attrName.equals(EQUALITY) || attrName.equals(ORDERING) || + attrName.equals(EQUALITY) || attrName.equals(ORDERING) || attrName.equals(SUBSTRING) || attrName.equals(COLLECTIVE) || attrName.equals(NOUSERMOD)) { ; //ignore } 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 index 66c5f5c0b6a..771766bab0c 100644 --- 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 @@ -45,6 +45,7 @@ public class SchemaElement extends SchemaDirContext { static final String OBSOLETE = "OBSOLETE"; static final String EQUALITY = "EQUALITY"; static final String ORDERING = "ORDERING"; + static final String SUBSTRING = "SUBSTRING"; static final String COLLECTIVE = "COLLECTIVE"; static final String NOUSERMOD = "NO-USER-MODIFICATION"; static final String USAGE = "USAGE"; 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 index 9e8a685c312..428265c5fae 100644 --- 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 @@ -132,19 +132,24 @@ public class SchemaObjectClass extends SchemaElement { for (Enumeration e = m_ldapObjectClass.getOptionalAttributes(); e.hasMoreElements();) { optional.add(e.nextElement()); } - attrs.put(optional); + if (optional.size() != 0) { + 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); + if (required.size() != 0) { + attrs.put(required); + } } else { // ignore other attrIds as not supported by Netscape LdapJDK APIs } } + return attrs; } diff --git a/mozilla/directory/java-sdk/ldapsp/lib/jndi.jar b/mozilla/directory/java-sdk/ldapsp/lib/jndi.jar index 54dcb6527921bc426dc75cb0e648b0508e7825b4..3e331266bc9a73d8c745a8783031971e581e50f1 100644 GIT binary patch delta 76599 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

    ZtKJo8jYRGD3^yf+7)R_DsKGJzeunas&6^ihlw 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 diff --git a/mozilla/directory/java-sdk/ldapsp/manifest.mf b/mozilla/directory/java-sdk/ldapsp/manifest.mf index 99b9c1baf2b..3dc2c73acc1 100644 --- a/mozilla/directory/java-sdk/ldapsp/manifest.mf +++ b/mozilla/directory/java-sdk/ldapsp/manifest.mf @@ -10,7 +10,7 @@ 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$EventEntry.class Name: com/netscape/jndi/ldap/EventService.class diff --git a/mozilla/directory/java-sdk/manifest.mf b/mozilla/directory/java-sdk/manifest.mf index cb62fe3924b..49cf6daa4a8 100644 --- a/mozilla/directory/java-sdk/manifest.mf +++ b/mozilla/directory/java-sdk/manifest.mf @@ -1,24 +1,28 @@ -Name: com/netscape/sasl/SASLClientCB.class +Name: LDAPDelete.class -Name: com/netscape/sasl/SASLClientMechanismDriver.class +Name: LDAPModify.class -Name: com/netscape/sasl/SASLException.class +Name: LDAPSearch.class -Name: com/netscape/sasl/SASLGetLanguageCB.class +Name: LDAPTool$1.class -Name: com/netscape/sasl/SASLInfoClientCB.class +Name: LDAPTool.class -Name: com/netscape/sasl/SASLMechanismFactory.class +Name: com/netscape/sasl/ClientFactory.class -Name: com/netscape/sasl/SASLNamePasswordClientCB.class +Name: com/netscape/sasl/Sasl.class -Name: com/netscape/sasl/SASLOkayCancelClientCB.class +Name: com/netscape/sasl/SaslClient.class -Name: com/netscape/sasl/SASLPasswordClientCB.class +Name: com/netscape/sasl/SaslClientFactory.class -Name: com/netscape/sasl/SASLSecurityLayer.class +Name: com/netscape/sasl/SaslException.class -Name: com/netscape/sasl/SASLWarningClientCB.class +Name: com/netscape/sasl/mechanisms/SaslExternal.class + +Name: netscape/ldap/DynamicInvoker.class + +Name: netscape/ldap/LDAPAsynchronousConnection.class Name: netscape/ldap/LDAPAttribute.class @@ -26,16 +30,26 @@ Name: netscape/ldap/LDAPAttributeSchema.class Name: netscape/ldap/LDAPAttributeSet.class +Name: netscape/ldap/LDAPBind.class + Name: netscape/ldap/LDAPCache.class Name: netscape/ldap/LDAPCheckComm.class Name: netscape/ldap/LDAPCompareAttrNames.class -Name: netscape/ldap/LDAPConnection.class +Name: netscape/ldap/LDAPConnSetupMgr$1.class + +Name: netscape/ldap/LDAPConnSetupMgr$ServerEntry.class + +Name: netscape/ldap/LDAPConnSetupMgr.class Name: netscape/ldap/LDAPConnThread.class +Name: netscape/ldap/LDAPConnection.class + +Name: netscape/ldap/LDAPConstraints.class + Name: netscape/ldap/LDAPControl.class Name: netscape/ldap/LDAPDN.class @@ -48,8 +62,18 @@ Name: netscape/ldap/LDAPException.class Name: netscape/ldap/LDAPExtendedOperation.class +Name: netscape/ldap/LDAPExtendedResponse.class + +Name: netscape/ldap/LDAPInterruptedException.class + Name: netscape/ldap/LDAPMatchingRuleSchema.class +Name: netscape/ldap/LDAPMessage.class + +Name: netscape/ldap/LDAPMessageQueue$RequestEntry.class + +Name: netscape/ldap/LDAPMessageQueue.class + Name: netscape/ldap/LDAPModification.class Name: netscape/ldap/LDAPModificationSet.class @@ -64,8 +88,22 @@ Name: netscape/ldap/LDAPReferralException.class Name: netscape/ldap/LDAPResourceBundle.class +Name: netscape/ldap/LDAPResponse.class + +Name: netscape/ldap/LDAPResponseControl.class + Name: netscape/ldap/LDAPResponseListener.class +Name: netscape/ldap/LDAPSSLSocket.class + +Name: netscape/ldap/LDAPSSLSocketFactory.class + +Name: netscape/ldap/LDAPSSLSocketFactoryExt.class + +Name: netscape/ldap/LDAPSSLSocketWrapFactory.class + +Name: netscape/ldap/LDAPSaslBind.class + Name: netscape/ldap/LDAPSchema.class Name: netscape/ldap/LDAPSchemaElement.class @@ -74,18 +112,16 @@ Name: netscape/ldap/LDAPSearchConstraints.class Name: netscape/ldap/LDAPSearchListener.class +Name: netscape/ldap/LDAPSearchResult.class + +Name: netscape/ldap/LDAPSearchResultReference.class + Name: netscape/ldap/LDAPSearchResults.class Name: netscape/ldap/LDAPSocketFactory.class Name: netscape/ldap/LDAPSortKey.class -Name: netscape/ldap/LDAPSSLSocket.class - -Name: netscape/ldap/LDAPSSLSocketFactory.class - -Name: netscape/ldap/LDAPSSLSocketWrapFactory.class - Name: netscape/ldap/LDAPUrl.class Name: netscape/ldap/LDAPv2.class @@ -100,6 +136,30 @@ Name: netscape/ldap/TimerEventListener.class Name: netscape/ldap/TimerRunnable.class +Name: netscape/ldap/beans/DisplayString.class + +Name: netscape/ldap/beans/DisplayStringBeanInfo.class + +Name: netscape/ldap/beans/LDAPBasePropertySupport$1.class + +Name: netscape/ldap/beans/LDAPBasePropertySupport.class + +Name: netscape/ldap/beans/LDAPGetEntries.class + +Name: netscape/ldap/beans/LDAPGetEntriesBeanInfo.class + +Name: netscape/ldap/beans/LDAPGetProperty.class + +Name: netscape/ldap/beans/LDAPGetPropertyBeanInfo.class + +Name: netscape/ldap/beans/LDAPIsMember.class + +Name: netscape/ldap/beans/LDAPIsMemberBeanInfo.class + +Name: netscape/ldap/beans/LDAPSimpleAuth.class + +Name: netscape/ldap/beans/LDAPSimpleAuthBeanInfo.class + Name: netscape/ldap/ber/stream/BERAny.class Name: netscape/ldap/ber/stream/BERBitString.class @@ -148,16 +208,14 @@ Name: netscape/ldap/client/JDAPAVA.class Name: netscape/ldap/client/JDAPBERTagDecoder.class -Name: netscape/ldap/client/JDAPControl.class - Name: netscape/ldap/client/JDAPFilter.class +Name: netscape/ldap/client/JDAPFilterAVA.class + Name: netscape/ldap/client/JDAPFilterAnd.class Name: netscape/ldap/client/JDAPFilterApproxMatch.class -Name: netscape/ldap/client/JDAPFilterAVA.class - Name: netscape/ldap/client/JDAPFilterEqualityMatch.class Name: netscape/ldap/client/JDAPFilterExtensible.class @@ -178,8 +236,6 @@ Name: netscape/ldap/client/JDAPFilterSet.class Name: netscape/ldap/client/JDAPFilterSubString.class -Name: netscape/ldap/client/JDAPMessage.class - Name: netscape/ldap/client/opers/JDAPAbandonRequest.class Name: netscape/ldap/client/opers/JDAPAddRequest.class @@ -228,18 +284,36 @@ Name: netscape/ldap/client/opers/JDAPUnbindRequest.class Name: netscape/ldap/controls/LDAPEntryChangeControl.class +Name: netscape/ldap/controls/LDAPPasswordExpiredControl.class + +Name: netscape/ldap/controls/LDAPPasswordExpiringControl.class + Name: netscape/ldap/controls/LDAPPersistSearchControl.class +Name: netscape/ldap/controls/LDAPProxiedAuthControl.class + Name: netscape/ldap/controls/LDAPSortControl.class +Name: netscape/ldap/controls/LDAPStringControl.class + Name: netscape/ldap/controls/LDAPVirtualListControl.class Name: netscape/ldap/controls/LDAPVirtualListResponse.class Name: netscape/ldap/errors/ErrorCodes.props +Name: netscape/ldap/errors/ErrorCodes_de.props + +Name: netscape/ldap/errors/ErrorCodes_fr.props + +Name: netscape/ldap/errors/ErrorCodes_ja.props + Name: netscape/ldap/util/ByteBuf.class +Name: netscape/ldap/util/ConnectionPool$LDAPConnectionObject.class + +Name: netscape/ldap/util/ConnectionPool.class + Name: netscape/ldap/util/DN.class Name: netscape/ldap/util/GetOpt.class @@ -252,6 +326,8 @@ Name: netscape/ldap/util/LDIFAddContent.class Name: netscape/ldap/util/LDIFAttributeContent.class +Name: netscape/ldap/util/LDIFBaseContent.class + Name: netscape/ldap/util/LDIFContent.class Name: netscape/ldap/util/LDIFDeleteContent.class @@ -270,11 +346,3 @@ Name: netscape/ldap/util/MimeEncoder.class Name: netscape/ldap/util/RDN.class -Name: LDAPTool.class - -Name: LDAPDelete.class - -Name: LDAPSearch.class - -Name: LDAPModify.class - diff --git a/mozilla/directory/java-sdk/relnotes.htm b/mozilla/directory/java-sdk/relnotes.htm new file mode 100644 index 00000000000..fbce1910dfd --- /dev/null +++ b/mozilla/directory/java-sdk/relnotes.htm @@ -0,0 +1,311 @@ + + + + + + Java 4.0 BETA Release Notes + + + +

    +

    +BETA
    +Netscape Directory SDK 4.0 for Java 
    +Release Notes

    + +
    Last updated August 03, 1999
    + +


    +
    +
    +

    These release notes apply +to the Netscape Directory SDK 4.0 for Java. Please read these notes before +using the product. +

    + +
    Here +is a summary of new features: +

    +Changes to  Java LDAP +API

    + +
      +
    • +Added  LDAPBind +interface +for sophisticated client management of referral authentication.
    • + +
    • +LDAPControl has new +methods register() +and newInstance(), +to support dynamic registration and instantiation of server response controls.
    • + +
    • +LDAPConnection.getServerControls() +automatically parses controls.  As a result, calling parseResponse()is +no longer necessary.
    • + +
    • +LDAPSearchConstraints +has been divided into LDAPConstraints +and a subclass LDAPSearchConstraints. +LDAPConstraints +contains only methods implemented in the interface  which apply to +all operations (not  parameters passed to the server).  LDAPSearchConstraints +is unchanged.    All LDAPConnection +methods that take LDAPSearchConstraints +as an argument (except for read and search methods) change to pass LDAPConstraints +instead.
    • + +
    • +When searching from LDAPConnection, +retrieval +of server response controls  is moved to LDAPSearchResults.  +So you would call
    • + +
      LDAPSearchResults.getResponseControls() +instead of LDAPConnection.getResponseControls(). +For backwards compatibility, you can still get the controls from LDAPConnection, +but it is recommended for new code to get them from LDAPSearchResults. +
    • +Added support for transparent +failover connection policies. The available policies are: (1) serial , +(2) parallel with no delay and (3) parallel  with delay. See LDAPConnection.setConnSetupDelay().
    • + +
    • +LDAPConnection has +a new method reconnect() +which first disconnects and then connects and binds again using the same +parameters and mechanisms.
    • + +
    • +Added support for connection +pools.  See netscape.ldap.util.ConnectionPool +in the online JavaDocs.
    • + +
    • +Added LDAPInterruptedException +which is thrown if an LDAP operation is interrupted.
    • + +
    • +LDAPSchema and the +associated schema element classes now fully support RFC2252.
    • +
    +New asynchronous Java LDAP +API +

    This new API works at a +low level, close to the LDAP protocol, providing a means for mutiplexing +LDAP operations from the same thread.  For detailed information, see +Internet +Draft for The Java LDAP Application Program Interface. +

      +
    • +New interface LDAPAsynchronousConnection +is defined.
    • + +
    • +LDAPConnection  +implements the asynchronous interface and supports both synchronous as +well as asynchronous programming models.
    • + +
    • +New classes for asynchronously +receiving server response messages: LDAPResponseListener, +LDAPSearchListener.
    • + +
    • +New classes for representing +LDAP messages: LDAPMessage, LDAPResponse, +LDAPExtendedResponse, LDAPSearchResult and LDAPSearchResponseReference.
    • +
    +Support for the Java Naming +and Directory Interface (JNDI) Service Provider +

    The JNDI service provider +support for event notification is implemented more efficiently on top of +the asynchronous Java LDAP API.  For more information on JNDI, see +the java.sun website +and +the Release Notes for the Netscape LDAP Service +Provider for JNDI. +

    Simple Authentication +and Security Layer (SASL) API changes +

    The new SASL API is rewritten +including  new  SASL interface classes.  For more information, +see the  Internet Draft +for Simple Authentication +and Security Layer (SASL). You will need to have jaas.jar +in your CLASSPATH if you are using SASL authentication. +

    +


    +
    BUGS +FIXED SINCE 3.10 +
    355365  LDAPSearchResults.getCount() +returns 0 for synchronous search +
    342196   SmartUpdate +for java LDAP SDK doesn't work for Comm 4.5 +
    346367   -1 incorrectly +encoded in BERIntegral +
    357382   Can't +compile JNDI SP with JDK 1.2 +
    357361   Filter +code doesn't work with JDK 1.2 +
    351004   Applet +in Communicator can't fetch error string +
    313834   "rejects" +file on import (e.g. LDAPModify) contains binary +
    339833   RFE: +smarter failover +
    325394   getCount() +after sorting throws exception +
    330523   DN.equals() +does not normalize +
    354615   DN.contains() +implemented and documented incorrectly +
    339633  The SDK should +catch Exception, +not Throwable +
    352056   LDAPModify +should support LDIF version 1 +
    351022   LDAPCompareAttrNames +should +allow specifying a locale +
    350624   LDAPSearch +aborts on size limit exception +
    350998    +Search timeout not handled correctly +
    350997   LDAPSchemaElement +needs +modify() method +
    350996   LDAPBasePropertySupport +should enable referrals +
    350995   LDAPBasePropertySupport +should +enable referrals +
    347586   Schema +interpretations are specific to Netscape +
    357772   LDAPFilter +gets array bounds exception +
    357526   Syntactically +incorrect filter is causing disconnect +
    355904   Cannot +set cache after LDAPConnection +is connected +
    355942   LDAPCache +changes contents of attributes to return +
    354503   The +SDK calls Thread.stop()/suspend()/resume() +
    355215    +VLV controls don't support optional context field +
    354347    +SDK doesn't support all schema options of RFC 2252 +
    353306    +LDAP Beans don't handle referrals +
    353305    +LDAPIsMember doesn't handle dynamic groups +
    353187    +Ensure that SDK works with Java 2 +
    351926    +Must call System.exit() to shutdown SDK +
    351719    +LDAPModificationSet has no toString method +
    339632    +The SDK should not use Thread.stop() +
    329534   getCount +doesnt work in asynchronous search +
    330261   StringIndexOutOfBoundsException +if searched for backslash " \ " +
    116611    +SASLMechanismFactory is missing getMechanisms method +
    353307   LDAPGetEntries +should allow choice of attributes to return +
    353308   LDAPModificationSet +is +missing a toString method +
    358370   Incorrect +tag written for UTCTime BER +
    358371   Missing +and incorrect javadocs for BER +
    356470   Should +support latest LDIF specification +
    324606   RFE: +get attribute values as vector +
    354682   Should +treat null +for filter as meaning objectclass=* +
    354997   Unneeded +public add +methods in LDAPSchema +
    339831   RFE: +connection pool support +
    359305   Smart +Failover should not auto-connect after disconnect() +
    346368   BERReal +incomplete and incorrect +
    352804   netscape.ldap.util.DN +class does not consider "" a valid DN +
    344077   Maybe +wrong tag used for extended operation +
    359855   No way +to specify custom qualifiers in object class definition +
    359580   LDAPConnection.setMaxBacklog() +doesn't work +
    359646   modDN +doesn't work
    + +
    REPORTING +PROBLEMS +

    Please submit your problem +via the the Bugzilla bugsystem +. Use the "LDAP Java SDK" component. +

    If you have additional questions +or need more information about the LDAP Java SDK, see the directory +newsgroup at Mozilla and the Directory +Developers newsgroup at Netscape +DevEdge. +
      +

    +

    For more Internet development +resources, try Netscape +TechSearch

    + +
    +
    + + + + + + + + + + + +
    DevEdge +Online FAQ +
    Developer +Response Center +
    Join +DevEdge Program
    Copyright +© 1999 Netscape +Communications Corporation. +
    This site powered by: Netscape +Enterprise Server and Netscape +Compass Server.
    + +
      +
      + + diff --git a/mozilla/directory/java-sdk/tools/LDAPDelete.java b/mozilla/directory/java-sdk/tools/LDAPDelete.java index a846fe4f8b0..041d674620b 100644 --- a/mozilla/directory/java-sdk/tools/LDAPDelete.java +++ b/mozilla/directory/java-sdk/tools/LDAPDelete.java @@ -167,8 +167,7 @@ public class LDAPDelete extends LDAPTool { /* LDAPDelete */ */ private static void dodelete() { /* dodelete */ int msgid = 0; - LDAPSearchConstraints cons = - (LDAPSearchConstraints)m_client.getSearchConstraints().clone(); + LDAPConstraints cons = m_client.getConstraints(); Vector controlVector = new Vector(); if (m_proxyControl != null) @@ -190,7 +189,7 @@ public class LDAPDelete extends LDAPTool { /* LDAPDelete */ dodelete(cons); } /* dodelete */ - private static void dodelete(LDAPSearchConstraints cons) { + private static void dodelete(LDAPConstraints cons) { try { if (m_reader == null) { for (int i=0; i * usage : java LDAPModify [options] - * example : java LDAPModify -w "password" -h ldap.netscape.com -p 389 - * -f modify.cfg + * example : java LDAPModify -D "uid=johnj,ou=People,o=Airius.com" + * -w "password" -h ldap.netscape.com -p 389 + * -f modify.cfg * * options: {np = no parameters, p = requires parameters} * 'D' bind DN --------------------------------------------- p + * 'w' bind password --------------------------------------- p * 'f' input file ------------------------------------------ p * 'h' LDAP host ------------------------------------------- p * 'p' LDAP port ------------------------------------------- p - * 'n' override DN to modify ------------------------------- p - * 'w' bind password --------------------------------------- p * 'e' record rejected records in a text file -------------- p - * 'c' continuous, do not stop on error - * 'a' add, if no operation is specified - * 'r' replace, if no operation is specified - * 'b' binary, read values starting with / from a file + * 'c' continuous, do not stop on error ------------------- np + * 'a' add, if no operation is specified ------------------ np + * 'r' replace, if no operation is specified -------------- np + * 'b' binary, read values starting with / from a file ---- np + * 'd' debugging level ------------------------------------- p + * 'V' version, specify LDAP protocol version (2 or 3) ----- p + * 'R' do not follow referrals ---------------------------- np + * 'O' hop limit ------------------------------------------- p + * 'H' help, display usage--------------------------------- np + * 'M' manage referrals, do not follow them --------------- np + * 'n' show what would be done but do not do it ----------- np + * 'v' verbose mode --------------------------------------- np + * 'e' reject file, where to list rejected records --------- p + * 'y' proxy DN, DN to use for access control -------------- p * - * note: '-' or '/' is used to distinct the option field. + * note: '-' or '/' is used to mark an option field. * e.g. -a -b /c /d parameter -e parameter * * @@ -49,340 +70,369 @@ public class LDAPModify extends LDAPTool { /* LDAPModify */ public static void main(String args[]) { /* main */ - /* extract parameters from the arguments list */ - extractParameters(args); + /* extract parameters from the arguments list */ + extractParameters(args); - /* perform an LDAP client connection operation */ - try { - if (!m_justShow) { - m_client = new LDAPConnection(); - m_client.connect( m_ldaphost, m_ldapport ); - } - } catch(Exception e) { - System.err.println("Error: client connection failed!"); - System.exit(0); - } + /* perform an LDAP client connection operation */ + try { + if (!m_justShow) { + m_client = new LDAPConnection(); + m_client.connect( m_ldaphost, m_ldapport ); + } + } catch(Exception e) { + System.err.println("Error: client connection failed!"); + System.exit(0); + } - /* perform an LDAP bind operation */ - try { - if (!m_justShow) - m_client.authenticate( m_version, m_binddn, m_passwd ); - } catch (Exception e) { - System.err.println( e.toString() ); - System.exit(0); - } + /* perform an LDAP bind operation */ + try { + if (!m_justShow) + m_client.authenticate( m_version, m_binddn, m_passwd ); + } catch (Exception e) { + System.err.println( e.toString() ); + System.exit(0); + } - try { - if ( m_file != null ) - m_ldif = new LDIF(m_file); - else { - m_ldif = new LDIF(); + try { + if ( m_file != null ) + m_ldif = new LDIF(m_file); + else { + m_ldif = new LDIF(); } - } catch (Exception e) { - if ( m_file == null ) - m_file = "stdin"; - System.err.println("Failed to read LDIF file " + m_file + - ", " + e.toString()); - System.exit(0); - } + } catch (Exception e) { + if ( m_file == null ) + m_file = "stdin"; + System.err.println("Failed to read LDIF file " + m_file + + ", " + e.toString()); + System.exit(0); + } - /* performs a JDAP Modify operation */ - try { - doModify(); - } catch (Exception e) { - System.err.println( e.toString() ); - } + /* performs a JDAP Modify operation */ + try { + doModify(); + } catch (Exception e) { + System.err.println( e.toString() ); + } - /* disconnect */ - try { - if (!m_justShow) - m_client.disconnect(); - } catch (Exception e) { - System.err.println( e.toString() ); - } - System.exit(0); - } /* main */ + /* disconnect */ + try { + if (!m_justShow) + m_client.disconnect(); + } catch (Exception e) { + System.err.println( e.toString() ); + } + System.exit(0); + } /* main */ - /** - * Prints usage. - */ + /** + * Prints usage. + */ private static void doUsage() { - System.err.println( "usage: LDAPModify [options]" ); - System.err.println("options"); - System.err.println(" -h host LDAP server name or IP address"); - System.err.println(" -p port LDAP server TCP port number"); - System.err.println(" -V version LDAP protocol version " + - "number (default is 3)"); - System.err.println(" -D binddn bind dn"); - System.err.println(" -w password bind passwd (for simple " + - "authentication)"); - System.err.println(" -d level set LDAP debugging level " + - "to \'level\'"); - System.err.println(" -R do not automatically follow " + - "referrals"); - System.err.println(" -O hop limit maximum number of referral " + - "hops to traverse"); - System.err.println(" -H display usage information"); - System.err.println(" -c continuous mode (do not " + - "stop on errors)"); - System.err.println(" -M manage references (treat them " + - "as regular entries)"); - System.err.println(" -f file read modifications from " + - "file instead of standard input"); - System.err.println(" -a add entries"); - System.err.println(" -b read values that start with " + - "/ from files (for bin attrs)"); - System.err.println(" -n show what would be done but " + - "don\'t actually do it"); - System.err.println(" -v run in verbose mode"); - System.err.println(" -r replace existing values by " + - "default"); - System.err.println(" -e rejectfile save rejected entries in " + - "\'rejfile\'"); - System.err.println(" -y proxy-DN DN to use for access control"); - } + System.err.println( "usage: LDAPModify [options]" ); + System.err.println("options"); + System.err.println(" -h host LDAP server name or IP address"); + System.err.println(" -p port LDAP server TCP port number"); + System.err.println(" -V version LDAP protocol version " + + "number (default is 3)"); + System.err.println(" -D binddn bind dn"); + System.err.println(" -w password bind passwd (for simple " + + "authentication)"); + System.err.println(" -d level set LDAP debugging level " + + "to \'level\'"); + System.err.println(" -R do not automatically follow " + + "referrals"); + System.err.println(" -O hop limit maximum number of referral " + + "hops to traverse"); + System.err.println(" -H display usage information"); + System.err.println(" -c continuous mode (do not " + + "stop on errors)"); + System.err.println(" -M manage references (treat them " + + "as regular entries)"); + System.err.println(" -f file read modifications from " + + "file instead of standard input"); + System.err.println(" -a add entries"); + System.err.println(" -b read values that start with " + + "/ from files (for bin attrs)"); + System.err.println(" -n show what would be done but " + + "don\'t actually do it"); + System.err.println(" -v run in verbose mode"); + System.err.println(" -r replace existing values by " + + "default"); + System.err.println(" -e rejectfile save rejected entries in " + + "\'rejfile\'"); + System.err.println(" -y proxy-DN DN to use for access control"); + } - /** - * This class-method is used to extract specified parameters from the - * arguments list. - */ + /** + * This class-method is used to extract specified parameters from the + * arguments list. + */ /* extract parameters */ protected static void extractParameters(String args[]) { - String privateOpts = "abcHFre:f:"; + String privateOpts = "abcHFre:f:"; - GetOpt options = LDAPTool.extractParameters( privateOpts, args ); + GetOpt options = LDAPTool.extractParameters( privateOpts, args ); - /* -H Help */ - if (options.hasOption('H')) { - doUsage(); - System.exit(0); - } /* Help */ + /* -H Help */ + if (options.hasOption('H')) { + doUsage(); + System.exit(0); + } /* Help */ - if (options.hasOption('F')) - m_force = true; + if (options.hasOption('F')) + m_force = true; - if (options.hasOption('a')) - m_add = true; + if (options.hasOption('a')) + m_add = true; - if (options.hasOption('c')) - m_continuous = true; + if (options.hasOption('c')) + m_continuous = true; - if (options.hasOption('r')) - m_add = false; + if (options.hasOption('r')) + m_add = false; - if (options.hasOption('b')) - m_binaryFiles = true; + if (options.hasOption('b')) + m_binaryFiles = true; - /* -f input file */ - if(options.hasOption('f')) { /* Is input file */ - m_file = (String)options.getOptionParam('f'); - } /* End Is input file */ + /* -f input file */ + if(options.hasOption('f')) { /* Is input file */ + m_file = (String)options.getOptionParam('f'); + } /* End Is input file */ - /* -e rejects file */ - if(options.hasOption('e')) { /* rejects file */ - m_rejectsFile = (String)options.getOptionParam('e'); - } /* End rejects file */ + /* -e rejects file */ + if(options.hasOption('e')) { /* rejects file */ + m_rejectsFile = (String)options.getOptionParam('e'); + } /* End rejects file */ - } /* extract parameters */ + } /* extract parameters */ - /** - * Call the LDAPConnection modify operation with the - * specified options, and/or parameters. - */ + /** + * Call the LDAPConnection modify operation with the + * specified options, and/or parameters. + */ private static void doModify() throws IOException { /* doModify */ - PrintWriter reject = null; - LDAPSearchConstraints cons = null; - if (!m_justShow) { - cons = - (LDAPSearchConstraints)m_client.getSearchConstraints().clone(); - Vector controlVector = new Vector(); - if (m_proxyControl != null) - controlVector.addElement(m_proxyControl); - if (m_ordinary) { - controlVector.addElement( new LDAPControl( - LDAPControl.MANAGEDSAIT, true, null) ); - } - if (controlVector.size() > 0) { - LDAPControl[] controls = new LDAPControl[controlVector.size()]; - controlVector.copyInto(controls); - cons.setServerControls(controls); - } - cons.setReferrals( m_referrals ); - if ( m_referrals ) { - setDefaultReferralCredentials( cons ); - } - cons.setHopLimit( m_hopLimit ); - } + PrintWriter reject = null; + LDAPConstraints baseCons = null; + if (!m_justShow) { + baseCons = m_client.getConstraints(); + Vector controlVector = new Vector(); + if (m_proxyControl != null) + controlVector.addElement(m_proxyControl); + if (m_ordinary) { + controlVector.addElement( new LDAPControl( + LDAPControl.MANAGEDSAIT, true, null) ); + } + if (controlVector.size() > 0) { + LDAPControl[] controls = new LDAPControl[controlVector.size()]; + controlVector.copyInto(controls); + baseCons.setServerControls(controls); + } + baseCons.setReferrals( m_referrals ); + if ( m_referrals ) { + setDefaultReferralCredentials( baseCons ); + } + baseCons.setHopLimit( m_hopLimit ); + } - LDIFRecord rec = m_ldif.nextRecord(); + LDIFRecord rec = m_ldif.nextRecord(); - for (; rec != null; rec = m_ldif.nextRecord() ) { - LDIFContent content = rec.getContent(); - LDAPModification mods[] = null; - LDAPAttribute addAttrs[] = null; - boolean doDelete = false; - boolean doModDN = false; - LDAPEntry newEntry = null; + for (; rec != null; rec = m_ldif.nextRecord() ) { + LDAPConstraints cons = baseCons; + if (!m_justShow) { + // Were there any controls specified in the LDIF + // record? + LDAPControl[] newControls = rec.getControls(); + if ( newControls != null ) { + LDAPControl[] controls = newControls; + LDAPControl[] oldControls = baseCons.getServerControls(); + if ( oldControls != null ) { + // If there were already controls defined, merge in + // the new ones + controls = + new LDAPControl[oldControls.length + + newControls.length]; + for( int i = 0; i < oldControls.length; i++ ) { + controls[i] = oldControls[i]; + } + for( int i = 0; i < newControls.length; i++ ) { + controls[i+oldControls.length] = newControls[i]; + } + } + // Assign the merged controls to a copy of the constraints + cons = (LDAPSearchConstraints)baseCons.clone(); + cons.setServerControls( controls ); + } + } - /* What type of record is this? */ - if ( content instanceof LDIFModifyContent ) { - mods = ((LDIFModifyContent)content).getModifications(); - } else if ( content instanceof LDIFAddContent ) { - addAttrs = ((LDIFAddContent)content).getAttributes(); - } else if ( content instanceof LDIFAttributeContent ) { - /* No change type; decide what to do based on options */ - if ( m_add ) - addAttrs = - ((LDIFAttributeContent)content).getAttributes(); - else { - LDAPAttribute[] tmpAttrs = - ((LDIFAttributeContent)content).getAttributes(); - mods = new LDAPModification[tmpAttrs.length]; - for( int ta = 0; ta < tmpAttrs.length; ta++ ) { - mods[ta] = new LDAPModification( - LDAPModification.REPLACE, tmpAttrs[ta] ); - } - } - } else if ( content instanceof LDIFDeleteContent ) { - doDelete = true; - } else if (content instanceof LDIFModDNContent ) { - doModDN = true; - } else { - } + LDIFContent content = rec.getContent(); + LDAPModification mods[] = null; + LDAPAttribute addAttrs[] = null; + boolean doDelete = false; + boolean doModDN = false; + LDAPEntry newEntry = null; - /* Prepare for adding */ - if ( addAttrs != null ) { - LDAPAttributeSet newAttrSet = new LDAPAttributeSet(); - for( int a = 0; a < addAttrs.length; a++ ) - newAttrSet.add( addAttrs[a] ); - newEntry = new LDAPEntry( rec.getDN(), newAttrSet ); - } + /* What type of record is this? */ + if ( content instanceof LDIFModifyContent ) { + mods = ((LDIFModifyContent)content).getModifications(); + } else if ( content instanceof LDIFAddContent ) { + addAttrs = ((LDIFAddContent)content).getAttributes(); + } else if ( content instanceof LDIFAttributeContent ) { + /* No change type; decide what to do based on options */ + if ( m_add ) { + addAttrs = + ((LDIFAttributeContent)content).getAttributes(); + } else { + LDAPAttribute[] tmpAttrs = + ((LDIFAttributeContent)content).getAttributes(); + mods = new LDAPModification[tmpAttrs.length]; + for( int ta = 0; ta < tmpAttrs.length; ta++ ) { + mods[ta] = new LDAPModification( + LDAPModification.REPLACE, tmpAttrs[ta] ); + } + } + } else if ( content instanceof LDIFDeleteContent ) { + doDelete = true; + } else if (content instanceof LDIFModDNContent ) { + doModDN = true; + } else { + } - /* Get values from files? */ - boolean skip = false; - if ( m_binaryFiles ) { - /* Check each value of each attribute, to see if it - needs replacing with the contents of a file */ - if ( mods != null ) { - for( int m = 0; m < mods.length; m++ ) { - LDAPModification mod = mods[m]; - LDAPAttribute attr = mods[m].getAttribute(); + /* Prepare for adding */ + if ( addAttrs != null ) { + LDAPAttributeSet newAttrSet = new LDAPAttributeSet(); + for( int a = 0; a < addAttrs.length; a++ ) + newAttrSet.add( addAttrs[a] ); + newEntry = new LDAPEntry( rec.getDN(), newAttrSet ); + } - LDAPAttribute newAttr = checkFiles( attr ); - if ( newAttr == null ) - skip = true; - else - mods[m] = new LDAPModification( - mod.getOp(), newAttr ); - } - } else if ( addAttrs != null ) { - LDAPAttributeSet newAttrSet = new LDAPAttributeSet(); - for( int a = 0; a < addAttrs.length; a++ ) { - LDAPAttribute attr = addAttrs[a]; + /* Get values from files? */ + boolean skip = false; + if ( m_binaryFiles ) { + /* Check each value of each attribute, to see if it + needs replacing with the contents of a file */ + if ( mods != null ) { + for( int m = 0; m < mods.length; m++ ) { + LDAPModification mod = mods[m]; + LDAPAttribute attr = mods[m].getAttribute(); - LDAPAttribute newAttr = checkFiles( attr ); - if ( newAttr == null ) { - skip = true; - break; - } else { - newAttrSet.add( newAttr ); - } - } - if ( !skip ) { - newEntry = new LDAPEntry( rec.getDN(), newAttrSet ); - } - } - } + LDAPAttribute newAttr = checkFiles( attr ); + if ( newAttr == null ) + skip = true; + else + mods[m] = new LDAPModification( + mod.getOp(), newAttr ); + } + } else if ( addAttrs != null ) { + LDAPAttributeSet newAttrSet = new LDAPAttributeSet(); + for( int a = 0; a < addAttrs.length; a++ ) { + LDAPAttribute attr = addAttrs[a]; - /* Do the directory operation */ - int errCode = 0; - if ( !skip ) { - try { - if ( mods != null ) { - LDAPModificationSet modSet = - new LDAPModificationSet(); - System.out.println("\nmodifying entry "+rec.getDN() ); - for( int m = 0; m < mods.length; m++ ) { - if (m_verbose) - System.out.println("\t"+mods[m] ); - modSet.add( mods[m].getOp(), - mods[m].getAttribute() ); - } + LDAPAttribute newAttr = checkFiles( attr ); + if ( newAttr == null ) { + skip = true; + break; + } else { + newAttrSet.add( newAttr ); + } + } + if ( !skip ) { + newEntry = new LDAPEntry( rec.getDN(), newAttrSet ); + } + } + } - if (!m_justShow) - m_client.modify( rec.getDN(), modSet, cons ); - } else if ( newEntry != null ) { - System.out.println( "\nadding new entry " + newEntry.getDN() ); - if ( m_verbose ) { - LDAPAttributeSet set = newEntry.getAttributeSet(); - for( int a = 0; a < set.size(); a++ ) { - System.out.println("\t"+set.elementAt(a) ); - } - } - if (!m_justShow) - m_client.add( newEntry, cons ); - } else if ( doDelete ) { - System.out.println( "\ndeleting entry " + rec.getDN() ); - if (!m_justShow) - m_client.delete( rec.getDN(), cons ); - } else if ( doModDN) { - System.out.println( "\nmodifying RDN of entry " + - rec.getDN()+" and/or moving it beneath a new parent"); - if ( m_verbose ) { - System.out.println( "\t"+content.toString()); - } - if (!m_justShow) { - LDIFModDNContent moddnContent = (LDIFModDNContent)content; - m_client.rename( rec.getDN(), moddnContent.getRDN(), - moddnContent.getNewParent(), - moddnContent.getDeleteOldRDN(), cons ); - System.out.println( "rename completed"); - } - } - } catch (LDAPException e) { - System.err.println( rec.getDN() + ": " + - e.errorCodeToString() ); - if (e.getLDAPErrorMessage() != null) - System.err.println( "additional info: " + - e.getLDAPErrorMessage() ); - if ( !m_continuous ) - System.exit(1); - skip = true; - errCode = e.getLDAPResultCode(); - } - } + /* Do the directory operation */ + int errCode = 0; + if ( !skip ) { + try { + if ( mods != null ) { + LDAPModificationSet modSet = + new LDAPModificationSet(); + System.out.println("\nmodifying entry "+rec.getDN() ); + for( int m = 0; m < mods.length; m++ ) { + if (m_verbose) + System.out.println("\t"+mods[m] ); + modSet.add( mods[m].getOp(), + mods[m].getAttribute() ); + } - /* Write to rejects file? */ - if ( skip && (m_rejectsFile != null) ) { - try { - if ( reject == null ) { - reject = new PrintWriter( - new FileOutputStream( m_rejectsFile ) ); - } - } catch ( Exception e ) { - } - if ( reject != null ) { - reject.println( "dn: "+rec.getDN()+ " # Error: " + - errCode ); - if ( mods != null ) { - for( int m = 0; m < mods.length; m++ ) { - reject.println( mods[m].toString() ); - } - } else if ( newEntry != null ) { - reject.println( "Add " + newEntry.toString() ); - } else if ( doDelete ) { - reject.println( "Delete " + rec.getDN() ); - } else if (doModDN) { - reject.println( "ModDN "+ - ((LDIFModDNContent)content).toString() ); - } - reject.flush(); - } - } - } - System.exit(0); - } /* doModify */ + if (!m_justShow) { + m_client.modify( rec.getDN(), modSet, cons ); + } + } else if ( newEntry != null ) { + System.out.println( "\nadding new entry " + newEntry.getDN() ); + if ( m_verbose ) { + LDAPAttributeSet set = newEntry.getAttributeSet(); + for( int a = 0; a < set.size(); a++ ) { + System.out.println("\t"+set.elementAt(a) ); + } + } + if (!m_justShow) { + m_client.add( newEntry, cons ); + } + } else if ( doDelete ) { + System.out.println( "\ndeleting entry " + rec.getDN() ); + if (!m_justShow) + m_client.delete( rec.getDN(), cons ); + } else if ( doModDN) { + System.out.println( "\nmodifying RDN of entry " + + rec.getDN()+" and/or moving it beneath a new parent"); + if ( m_verbose ) { + System.out.println( "\t"+content.toString()); + } + if (!m_justShow) { + LDIFModDNContent moddnContent = (LDIFModDNContent)content; + m_client.rename( rec.getDN(), moddnContent.getRDN(), + moddnContent.getNewParent(), + moddnContent.getDeleteOldRDN(), cons ); + System.out.println( "rename completed"); + } + } + } catch (LDAPException e) { + System.err.println( rec.getDN() + ": " + + e.errorCodeToString() ); + if (e.getLDAPErrorMessage() != null) + System.err.println( "additional info: " + + e.getLDAPErrorMessage() ); + if ( !m_continuous ) { + System.exit(1); + } + skip = true; + errCode = e.getLDAPResultCode(); + } + } + + /* Write to rejects file? */ + if ( skip && (m_rejectsFile != null) ) { + try { + if ( reject == null ) { + reject = new PrintWriter( + new FileOutputStream( m_rejectsFile ) ); + } + } catch ( Exception e ) { + } + if ( reject != null ) { + reject.println( "dn: "+rec.getDN()+ " # Error: " + + errCode ); + if ( mods != null ) { + for( int m = 0; m < mods.length; m++ ) { + reject.println( mods[m].toString() ); + } + } else if ( newEntry != null ) { + reject.println( "Add " + newEntry.toString() ); + } else if ( doDelete ) { + reject.println( "Delete " + rec.getDN() ); + } else if (doModDN) { + reject.println( "ModDN "+ + ((LDIFModDNContent)content).toString() ); + } + reject.flush(); + } + } + } + System.exit(0); + } /* doModify */ /** @@ -391,40 +441,40 @@ public class LDAPModify extends LDAPTool { /* LDAPModify */ * @return Updated attribute. **/ private static LDAPAttribute checkFiles ( LDAPAttribute attr ) { - LDAPAttribute newAttr = - new LDAPAttribute( attr.getName() ); + LDAPAttribute newAttr = + new LDAPAttribute( attr.getName() ); - /* Check each value and see if it is a file name */ - Enumeration e = attr.getStringValues(); - if (e != null) { - while ( e.hasMoreElements() ) { - String val = (String)e.nextElement(); - if ( (val != null) && (val.length() > 1)) { - try { - File file = new File( val ); - FileInputStream fi = - new FileInputStream( file ); - byte[] bval = new byte[(int)file.length()]; - fi.read( bval, 0, (int)file.length() ); - newAttr.addValue( bval ); - } catch (FileNotFoundException ex) { - newAttr.addValue(val) ; - } catch ( IOException ex ) { - System.err.println( "Unable to read value " + - "from file " + val ); - if ( !m_continuous ) - System.exit(1); - newAttr = null; + /* Check each value and see if it is a file name */ + Enumeration e = attr.getStringValues(); + if (e != null) { + while ( e.hasMoreElements() ) { + String val = (String)e.nextElement(); + if ( (val != null) && (val.length() > 1)) { + try { + File file = new File( val ); + FileInputStream fi = + new FileInputStream( file ); + byte[] bval = new byte[(int)file.length()]; + fi.read( bval, 0, (int)file.length() ); + newAttr.addValue( bval ); + } catch (FileNotFoundException ex) { + newAttr.addValue(val) ; + } catch ( IOException ex ) { + System.err.println( "Unable to read value " + + "from file " + val ); + if ( !m_continuous ) + System.exit(1); + newAttr = null; } - } else { - newAttr.addValue( val ); - } - } - } - else - System.err.println("Failed to do string conversion for "+attr.getName()); - return newAttr; - } + } else { + newAttr.addValue( val ); + } + } + } + else + System.err.println("Failed to do string conversion for "+attr.getName()); + return newAttr; + } private static boolean m_continuous = false; private static boolean m_force = false; diff --git a/mozilla/directory/java-sdk/tools/LDAPSearch.java b/mozilla/directory/java-sdk/tools/LDAPSearch.java index 6c675e479d2..0b5602c7e8c 100644 --- a/mozilla/directory/java-sdk/tools/LDAPSearch.java +++ b/mozilla/directory/java-sdk/tools/LDAPSearch.java @@ -384,7 +384,7 @@ public class LDAPSearch extends LDAPTool { LDAPSearchResults res = null; try { LDAPSearchConstraints cons = - (LDAPSearchConstraints)m_client.getSearchConstraints().clone(); + m_client.getSearchConstraints(); cons.setServerControls(controls); cons.setDereference( m_deref ); cons.setMaxResults( m_sizelimit ); @@ -527,20 +527,35 @@ public class LDAPSearch extends LDAPTool { * @param controls Any server controls returned. **/ private static void showControls( LDAPControl[] controls ) { - if ( controls == null ) + if ( controls == null ) { return; - int[] results = new int[1]; - String bad = LDAPSortControl.parseResponse( controls, results ); - if ( results[0] != LDAPException.SUCCESS ) { - System.err.println( "Error code: " + results[0] ); - if ( bad != null ) - System.err.println( "Offending attribute: " + bad ); - else - System.err.println( "No offending attribute returned" ); - } else + } + + LDAPSortControl sControl = null; + LDAPVirtualListResponse vResponse = null; + for ( int i = 0; i < controls.length; i++ ) { + if ( controls[i] instanceof LDAPSortControl ) { + sControl = (LDAPSortControl)controls[i]; + } else if ( controls[i] instanceof LDAPVirtualListResponse ) { + vResponse = (LDAPVirtualListResponse)controls[i]; + } + } + + if (sControl != null ) { + String bad = sControl.getFailedAttribute(); + int result = sControl.getResultCode(); + if ( result != LDAPException.SUCCESS ) { + System.err.println( "Error code: " + result ); + if ( bad != null ) { + System.err.println( "Offending attribute: " + bad ); + } else { + System.err.println( "No offending attribute returned" ); + } + } else { m_pw.println("Server indicated results sorted OK"); - LDAPVirtualListResponse vResponse = - LDAPVirtualListResponse.parseResponse(controls); + } + } + if (vResponse != null) { int resultCode = vResponse.getResultCode(); if (resultCode == LDAPException.SUCCESS) { diff --git a/mozilla/directory/java-sdk/tools/LDAPTool.java b/mozilla/directory/java-sdk/tools/LDAPTool.java index 01caeb1111c..c0a2457ce3f 100644 --- a/mozilla/directory/java-sdk/tools/LDAPTool.java +++ b/mozilla/directory/java-sdk/tools/LDAPTool.java @@ -1,8 +1,19 @@ -/* ====================================================================== - * Copyright (c) 1997 Netscape Communications Corporation - * This file contains proprietary information of Netscape Communications. - * Copying or reproduction without prior written approval is prohibited. - * ====================================================================== +/* -*- 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.m5ozilla.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. */ import netscape.ldap.*; @@ -96,7 +107,7 @@ class LDAPTool { } protected static void setDefaultReferralCredentials( - LDAPSearchConstraints cons ) { + LDAPConstraints cons ) { LDAPRebind rebind = new LDAPRebind() { public LDAPRebindAuth getRebindAuthentication( String host,