You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@directory.apache.org by el...@apache.org on 2005/08/21 18:54:00 UTC
svn commit: r234264 [7/11] - in
/directory/shared/ldap/branches/new-codec-integration/apache2-provider/src:
./ java/ java/main/ java/main/org/ java/main/org/apache/
java/main/org/apache/asn1new/ java/main/org/apache/asn1new/ldap/
java/main/org/apache/a...
Added: directory/shared/ldap/branches/new-codec-integration/apache2-provider/src/java/main/org/apache/asn1new/ldap/codec/grammar/SearchResultReferenceGrammar.java
URL: http://svn.apache.org/viewcvs/directory/shared/ldap/branches/new-codec-integration/apache2-provider/src/java/main/org/apache/asn1new/ldap/codec/grammar/SearchResultReferenceGrammar.java?rev=234264&view=auto
==============================================================================
--- directory/shared/ldap/branches/new-codec-integration/apache2-provider/src/java/main/org/apache/asn1new/ldap/codec/grammar/SearchResultReferenceGrammar.java (added)
+++ directory/shared/ldap/branches/new-codec-integration/apache2-provider/src/java/main/org/apache/asn1new/ldap/codec/grammar/SearchResultReferenceGrammar.java Sun Aug 21 09:53:27 2005
@@ -0,0 +1,158 @@
+/*
+ * Copyright 2005 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.asn1new.ldap.codec.grammar;
+
+import org.apache.asn1new.DecoderException;
+import org.apache.asn1new.ber.containers.IAsn1Container;
+import org.apache.asn1new.ber.grammar.AbstractGrammar;
+import org.apache.asn1new.ber.grammar.GrammarAction;
+import org.apache.asn1new.ber.grammar.GrammarTransition;
+import org.apache.asn1new.ber.grammar.IGrammar;
+import org.apache.asn1new.ber.tlv.TLV;
+import org.apache.asn1new.ber.tlv.UniversalTag;
+import org.apache.asn1new.ldap.codec.LdapConstants;
+import org.apache.asn1new.ldap.codec.LdapMessageContainer;
+import org.apache.asn1new.ldap.codec.primitives.LdapURL;
+import org.apache.asn1new.ldap.pojo.LdapMessage;
+import org.apache.asn1new.ldap.pojo.SearchResultReference;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+
+/**
+ * This class implements the SearchResultReference LDAP message. All the actions are declared in this
+ * class. As it is a singleton, these declaration are only done once.
+ *
+ * If an action is to be added or modified, this is where the work is to be done !
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ */
+public class SearchResultReferenceGrammar extends AbstractGrammar implements IGrammar
+{
+ //~ Static fields/initializers -----------------------------------------------------------------
+
+ /** The logger */
+ private static final Logger log = LoggerFactory.getLogger( SearchResultReferenceGrammar.class );
+
+ /** Logging speed up */
+ private static final boolean DEBUG = log.isDebugEnabled();
+
+ /** The instance of grammar. SearchResultReferenceGrammar is a singleton */
+ private static IGrammar instance = new SearchResultReferenceGrammar();
+
+ //~ Constructors -------------------------------------------------------------------------------
+
+ /**
+ * Creates a new SearchResultReferenceGrammar object.
+ */
+ private SearchResultReferenceGrammar()
+ {
+ name = SearchResultReferenceGrammar.class.getName();
+ statesEnum = LdapStatesEnum.getInstance();
+
+ // Initialisation of the transitions table
+ super.transitions = new GrammarTransition[LdapStatesEnum.LAST_SEARCH_RESULT_REFERENCE_STATE_STATE][256];
+
+ //============================================================================================
+ // SearchResultReference Message
+ //============================================================================================
+ // SearchResultReference ::= [APPLICATION 19] SEQUENCE OF LDAPURL (Tag)
+ // Nothing to do
+ super.transitions[LdapStatesEnum.SEARCH_RESULT_REFERENCE_TAG][LdapConstants.SEARCH_RESULT_REFERENCE_TAG] = new GrammarTransition(
+ LdapStatesEnum.SEARCH_RESULT_REFERENCE_TAG, LdapStatesEnum.SEARCH_RESULT_REFERENCE_VALUE, null );
+
+ // SearchResultReference ::= [APPLICATION 19] SEQUENCE OF LDAPURL (Value)
+ // We won't have a value. The next Tag will be the LDAPUrl Tag (0x04)
+ super.transitions[LdapStatesEnum.SEARCH_RESULT_REFERENCE_VALUE][LdapConstants.SEARCH_RESULT_REFERENCE_TAG] = new GrammarTransition(
+ LdapStatesEnum.SEARCH_RESULT_REFERENCE_VALUE, LdapStatesEnum.SEARCH_RESULT_REFERENCE_LDAP_URL_TAG,
+ new GrammarAction( "Init SearchResultReference" )
+ {
+ public void action( IAsn1Container container ) throws DecoderException
+ {
+
+ LdapMessageContainer ldapMessageContainer = ( LdapMessageContainer )
+ container;
+ LdapMessage ldapMessage =
+ ldapMessageContainer.getLdapMessage();
+
+ // Now, we can allocate the BindRequest Object
+ SearchResultReference searchResultReference = new SearchResultReference();
+
+ // As this is a new Constructed object, we have to init its length
+ searchResultReference.setParent( ldapMessage );
+
+ // And we associate it to the ldapMessage Object
+ ldapMessage.setProtocolOP( searchResultReference );
+ }
+ } );
+
+ // LDAPURL (Tag)
+ super.transitions[LdapStatesEnum.SEARCH_RESULT_REFERENCE_LDAP_URL_TAG][UniversalTag.OCTET_STRING_TAG] = new GrammarTransition(
+ LdapStatesEnum.SEARCH_RESULT_REFERENCE_LDAP_URL_TAG, LdapStatesEnum.SEARCH_RESULT_REFERENCE_LDAP_URL_VALUE,
+ null );
+
+ // LDAPURL loop (Tag)
+ super.transitions[LdapStatesEnum.SEARCH_RESULT_REFERENCE_LOOP_OR_END_TAG][UniversalTag.OCTET_STRING_TAG] = new GrammarTransition(
+ LdapStatesEnum.SEARCH_RESULT_REFERENCE_LOOP_OR_END_TAG, LdapStatesEnum.SEARCH_RESULT_REFERENCE_LDAP_URL_VALUE,
+ null );
+
+ // LDAPURL (Value)
+ super.transitions[LdapStatesEnum.SEARCH_RESULT_REFERENCE_LDAP_URL_VALUE][UniversalTag.OCTET_STRING_TAG] = new GrammarTransition(
+ LdapStatesEnum.SEARCH_RESULT_REFERENCE_LDAP_URL_VALUE,
+ LdapStatesEnum.SEARCH_RESULT_REFERENCE_LOOP_OR_END_TAG,
+ new GrammarAction( "Store ldapUrl value" )
+ {
+ public void action( IAsn1Container container ) throws DecoderException
+ {
+
+ LdapMessageContainer ldapMessageContainer = ( LdapMessageContainer )
+ container;
+
+ SearchResultReference searchResultReference =
+ ldapMessageContainer.getLdapMessage().getSearchResultReference();
+
+ // Get the Value and store it in the BindRequest
+ TLV tlv = ldapMessageContainer.getCurrentTLV();
+
+ // We have to handle the special case of a 0 length server sasl credentials
+ if ( tlv.getLength().getLength() == 0 )
+ {
+ searchResultReference.addSearchResultReference( LdapURL.EMPTY_STRING );
+ }
+ else
+ {
+ searchResultReference.addSearchResultReference( new LdapURL( tlv.getValue().getData() ) );
+ }
+
+ return;
+ }
+ } );
+
+ }
+
+ //~ Methods ------------------------------------------------------------------------------------
+
+ /**
+ * Get the instance of this grammar
+ *
+ * @return An instance on the LdapMessage Grammar
+ */
+ public static IGrammar getInstance()
+ {
+ return instance;
+ }
+}
Added: directory/shared/ldap/branches/new-codec-integration/apache2-provider/src/java/main/org/apache/asn1new/ldap/codec/grammar/UnBindRequestGrammar.java
URL: http://svn.apache.org/viewcvs/directory/shared/ldap/branches/new-codec-integration/apache2-provider/src/java/main/org/apache/asn1new/ldap/codec/grammar/UnBindRequestGrammar.java?rev=234264&view=auto
==============================================================================
--- directory/shared/ldap/branches/new-codec-integration/apache2-provider/src/java/main/org/apache/asn1new/ldap/codec/grammar/UnBindRequestGrammar.java (added)
+++ directory/shared/ldap/branches/new-codec-integration/apache2-provider/src/java/main/org/apache/asn1new/ldap/codec/grammar/UnBindRequestGrammar.java Sun Aug 21 09:53:27 2005
@@ -0,0 +1,126 @@
+/*
+ * Copyright 2005 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.asn1new.ldap.codec.grammar;
+
+import org.apache.asn1new.DecoderException;
+import org.apache.asn1new.ber.containers.IAsn1Container;
+import org.apache.asn1new.ber.grammar.AbstractGrammar;
+import org.apache.asn1new.ber.grammar.GrammarAction;
+import org.apache.asn1new.ber.grammar.GrammarTransition;
+import org.apache.asn1new.ber.grammar.IGrammar;
+import org.apache.asn1new.ber.tlv.TLV;
+import org.apache.asn1new.ldap.codec.LdapConstants;
+import org.apache.asn1new.ldap.codec.LdapMessageContainer;
+import org.apache.asn1new.ldap.pojo.LdapMessage;
+import org.apache.asn1new.ldap.pojo.UnBindRequest;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+
+/**
+ * This class implements the UnBindRequest LDAP message. All the actions are declared in this
+ * class. As it is a singleton, these declaration are only done once.
+ *
+ * If an action is to be added or modified, this is where the work is to be done !
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ */
+public class UnBindRequestGrammar extends AbstractGrammar implements IGrammar
+{
+ //~ Static fields/initializers -----------------------------------------------------------------
+
+ /** The logger */
+ private static final Logger log = LoggerFactory.getLogger( UnBindRequestGrammar.class );
+
+ /** Logging speed up */
+ private static final boolean DEBUG = log.isDebugEnabled();
+
+ /** The instance of grammar. UnBindRequestGrammar is a singleton */
+ private static IGrammar instance = new UnBindRequestGrammar();
+
+ //~ Methods ------------------------------------------------------------------------------------
+
+ /**
+ * Get the instance of this grammar
+ *
+ * @return An instance on the UnBindRequest Grammar
+ */
+ public static IGrammar getInstance()
+ {
+ return instance;
+ }
+
+ //~ Constructors -------------------------------------------------------------------------------
+
+ /**
+ * Creates a new UnBindRequestGrammar object.
+ */
+ private UnBindRequestGrammar()
+ {
+
+ name = UnBindRequestGrammar.class.getName();
+
+ statesEnum = LdapStatesEnum.getInstance();
+
+ // We have 3 differents states, so 2 transitions between states.
+ super.transitions = new GrammarTransition[LdapStatesEnum.LAST_UNBIND_REQUEST_STATE][256];
+
+ //============================================================================================
+ // protocolOp : UnBind Request
+ //============================================================================================
+ // LdapMessage ::= ... UnBindRequest ...
+ // UnbindRequest ::= [APPLICATION 2] NULL (Length)
+ super.transitions[LdapStatesEnum.UNBIND_REQUEST_TAG][LdapConstants.UNBIND_REQUEST_TAG] = new GrammarTransition(
+ LdapStatesEnum.UNBIND_REQUEST_TAG, LdapStatesEnum.UNBIND_REQUEST_VALUE, null );
+
+ // LdapMessage ::= ... UnBindRequest ...
+ // UnbindRequest ::= [APPLICATION 2] NULL (Value)
+ // We have to check that the length is null (the Value is empty). This is the end of this grammar.
+ // We also have to allocate a UnBindRequest
+ super.transitions[LdapStatesEnum.UNBIND_REQUEST_VALUE][LdapConstants.UNBIND_REQUEST_TAG] = new GrammarTransition(
+ LdapStatesEnum.UNBIND_REQUEST_VALUE, LdapStatesEnum.GRAMMAR_END,
+ new GrammarAction( "Init UnBindRequest" )
+ {
+ public void action( IAsn1Container container ) throws DecoderException
+ {
+
+ LdapMessageContainer ldapMessageContainer = ( LdapMessageContainer )
+ container;
+ LdapMessage ldapMessage =
+ ldapMessageContainer.getLdapMessage();
+
+ // Now, we can allocate the UnBindRequest Object
+ UnBindRequest unBindRequest = new UnBindRequest();
+
+ // As this is a new Constructed object, we have to init its length
+ TLV tlv = ldapMessageContainer.getCurrentTLV();
+ int expectedLength = tlv.getLength().getLength();
+
+ // If the length is not null, this is an error.
+ if (expectedLength != 0)
+ {
+ throw new DecoderException("The length of a UnBindRequest must be null");
+ }
+
+ unBindRequest.setParent( ldapMessage );
+
+ // And we associate it to the ldapMessage Object
+ ldapMessage.setProtocolOP( unBindRequest );
+ }
+ } );
+ }
+}
Added: directory/shared/ldap/branches/new-codec-integration/apache2-provider/src/java/main/org/apache/asn1new/ldap/codec/primitives/LdapDN.java
URL: http://svn.apache.org/viewcvs/directory/shared/ldap/branches/new-codec-integration/apache2-provider/src/java/main/org/apache/asn1new/ldap/codec/primitives/LdapDN.java?rev=234264&view=auto
==============================================================================
--- directory/shared/ldap/branches/new-codec-integration/apache2-provider/src/java/main/org/apache/asn1new/ldap/codec/primitives/LdapDN.java (added)
+++ directory/shared/ldap/branches/new-codec-integration/apache2-provider/src/java/main/org/apache/asn1new/ldap/codec/primitives/LdapDN.java Sun Aug 21 09:53:27 2005
@@ -0,0 +1,119 @@
+/*
+ * Copyright 2005 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.asn1new.ldap.codec.primitives;
+
+import org.apache.asn1new.DecoderException;
+import org.apache.asn1new.util.StringUtils;
+
+/**
+ * This class parses a DN.
+ *
+ * The DN MUST respect this BNF grammar (as of RFC2253, par. 3, and RFC1779, fig. 1) <br>
+ *
+ * <p>
+ *- <distinguishedName> ::= <name> | e <br>
+ *- <name> ::= <name-component> <name-components> <br>
+ *- <name-components> ::= <spaces> <separator> <spaces> <name-component> <name-components> | e <br>
+ *- <name-component> ::= <attributeType> <spaces> '=' <spaces> <attributeValue> <attributeTypeAndValues> <br>
+ *- <attributeTypeAndValues> ::= <spaces> '+' <spaces> <attributeType> <spaces> '=' <spaces> <attributeValue> <attributeTypeAndValues> | e <br>
+ *- <attributeType> ::= [a-zA-Z] <keychars> | <oidPrefix> [0-9] <digits> <oids> | [0-9] <digits> <oids> <br>
+ *- <keychars> ::= [a-zA-Z] <keychars> | [0-9] <keychars> | '-' <keychars> | e <br>
+ *- <oidPrefix> ::= 'OID.' | 'oid.' | e <br>
+ *- <oids> ::= '.' [0-9] <digits> <oids> | e <br>
+ *- <attributeValue> ::= <pairs-or-strings> | '#' <hexstring> |'"' <quotechar-or-pairs> '"' <br>
+ *- <pairs-or-strings> ::= '\' <pairchar> <pairs-or-strings> | <stringchar> <pairs-or-strings> | e <br>
+ *- <quotechar-or-pairs> ::= <quotechar> <quotechar-or-pairs> | '\' <pairchar> <quotechar-or-pairs> | e <br>
+ *- <pairchar> ::= ',' | '=' | '+' | '<' | '>' | '#' | ';' | '\' | '"' | [0-9a-fA-F] [0-9a-fA-F] <br>
+ *- <hexstring> ::= [0-9a-fA-F] [0-9a-fA-F] <hexpairs> <br>
+ *- <hexpairs> ::= [0-9a-fA-F] [0-9a-fA-F] <hexpairs> | e <br>
+ *- <digits> ::= [0-9] <digits> | e <br>
+ *- <stringchar> ::= [0x00-0xFF] - [,=+<>#;\"\n\r] <br>
+ *- <quotechar> ::= [0x00-0xFF] - [\"] <br>
+ *- <separator> ::= ',' | ';' <br>
+ *- <spaces> ::= ' ' <spaces> | e <br>
+ * </p>
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ */
+public class LdapDN extends RelativeLdapDN
+{
+ //~ Static fields/initializers -----------------------------------------------------------------
+
+ /** A null LdapDN */
+ public transient static final LdapDN EMPTY_STRING = new LdapDN();
+
+ //~ Methods ------------------------------------------------------------------------------------
+
+ /**
+ * Construct an empty LdapDN object
+ */
+ public LdapDN()
+ {
+ super(0, false);
+ }
+
+ /**
+ * Parse a buffer and checks that it is a valid DN <br>
+ * <p>
+ * <distinguishedName> ::= <name> | e <br>
+ * <name> ::= <name-component> <name-components> <br>
+ * <name-components> ::= <spaces> <separator> <spaces> <name-component> <name-components> | e <br>
+ * </p>
+ *
+ * @param bytes The byte buffer that contains the DN
+ * @exception A DecoderException is thrown if the buffer does not contains a valid DN.
+ */
+ public LdapDN( byte[] bytes ) throws DecoderException
+ {
+
+ if ( bytes == null || bytes.length == 0)
+ {
+ return;
+ }
+
+ int pos = 0;
+
+ // <name> ::= <name-component> <name-components>
+ // <name-components> ::= <spaces> <separator> <spaces> <name-component> <name-components> | e
+ if ( ( pos = parseNameComponent( bytes, pos ) ) != -1 )
+ {
+
+ do
+ {
+
+ if ( ( StringUtils.isCharASCII( bytes, pos, ',' ) == false ) &&
+ ( StringUtils.isCharASCII( bytes, pos, ';' ) == false ) )
+ {
+
+ break;
+ }
+
+ bytes[pos] = ',';
+ pos++;
+
+ pos = parseSpaces( bytes, pos );
+ }
+ while ( ( pos = parseNameComponent( bytes, pos ) ) != -1 );
+ }
+ else
+ {
+ throw new DecoderException( "Bad DN : " + new String( bytes) );
+ }
+
+ setData(bytes);
+ }
+}
Added: directory/shared/ldap/branches/new-codec-integration/apache2-provider/src/java/main/org/apache/asn1new/ldap/codec/primitives/LdapString.java
URL: http://svn.apache.org/viewcvs/directory/shared/ldap/branches/new-codec-integration/apache2-provider/src/java/main/org/apache/asn1new/ldap/codec/primitives/LdapString.java?rev=234264&view=auto
==============================================================================
--- directory/shared/ldap/branches/new-codec-integration/apache2-provider/src/java/main/org/apache/asn1new/ldap/codec/primitives/LdapString.java (added)
+++ directory/shared/ldap/branches/new-codec-integration/apache2-provider/src/java/main/org/apache/asn1new/ldap/codec/primitives/LdapString.java Sun Aug 21 09:53:27 2005
@@ -0,0 +1,63 @@
+/*
+ * Copyright 2005 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.asn1new.ldap.codec.primitives;
+
+import org.apache.asn1new.DecoderException;
+import org.apache.asn1new.util.MutableString;
+
+/**
+ * Decodes a LdapString, and checks that the character set used comply
+ * the ISO 10646 encoded following the UTF-8 algorithm (RFC 2044, RFC 2279)
+ * .
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ */
+public class LdapString extends MutableString
+{
+ /** A null LdapString */
+ public transient static final LdapString EMPTY_STRING = new LdapString();
+
+ /**
+ * Construct an empty LdapString
+ *
+ */
+ public LdapString()
+ {
+ super( 0, false );
+ }
+
+ //~ Methods ------------------------------------------------------------------------------------
+
+ /**
+ * Transform a byte array to a MutableString. The byte array contains
+ * an UTF-8 representation of a String
+ *
+ * @param bytes The byte buffer that contains the LDAPSTRING
+ * @return A MutableString containing the LDAPSTRING
+ *
+ * @throws DecoderException If the byte array does not comply with RFC 2279
+ */
+ public LdapString( byte[] bytes ) throws DecoderException
+ {
+ if ( bytes == null )
+ {
+ return;
+ }
+
+ setData(bytes);
+ }
+}
Added: directory/shared/ldap/branches/new-codec-integration/apache2-provider/src/java/main/org/apache/asn1new/ldap/codec/primitives/LdapURL.java
URL: http://svn.apache.org/viewcvs/directory/shared/ldap/branches/new-codec-integration/apache2-provider/src/java/main/org/apache/asn1new/ldap/codec/primitives/LdapURL.java?rev=234264&view=auto
==============================================================================
--- directory/shared/ldap/branches/new-codec-integration/apache2-provider/src/java/main/org/apache/asn1new/ldap/codec/primitives/LdapURL.java (added)
+++ directory/shared/ldap/branches/new-codec-integration/apache2-provider/src/java/main/org/apache/asn1new/ldap/codec/primitives/LdapURL.java Sun Aug 21 09:53:27 2005
@@ -0,0 +1,1105 @@
+/*
+ * Copyright 2005 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.asn1new.ldap.codec.primitives;
+
+import org.apache.asn1new.DecoderException;
+import org.apache.asn1new.util.StringUtils;
+
+import org.apache.commons.httpclient.URIException;
+import org.apache.commons.httpclient.util.URIUtil;
+
+import org.apache.ldap.common.filter.FilterParserImpl;
+
+import java.io.IOException;
+
+import java.text.ParseException;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Iterator;
+
+import javax.naming.directory.SearchControls;
+
+
+/**
+ * Decodes a LdapUrl, and checks that it complies with
+ * the RFC 2255. The grammar is the following :
+ * ldapurl = scheme "://" [hostport] ["/"
+ * [dn ["?" [attributes] ["?" [scope]
+ * ["?" [filter] ["?" extensions]]]]]]
+ * scheme = "ldap"
+ * attributes = attrdesc *("," attrdesc)
+ * scope = "base" / "one" / "sub"
+ * dn = LdapDN
+ * hostport = hostport from Section 5 of RFC 1738
+ * attrdesc = AttributeDescription from Section 4.1.5 of RFC 2251
+ * filter = filter from Section 4 of RFC 2254
+ * extensions = extension *("," extension)
+ * extension = ["!"] extype ["=" exvalue]
+ * extype = token / xtoken
+ * exvalue = LDAPString
+ * token = oid from section 4.1 of RFC 2252
+ * xtoken = ("X-" / "x-") token
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ */
+public class LdapURL extends LdapString
+{
+ //~ Static fields/initializers -----------------------------------------------------------------
+
+ /** A null LdapURL */
+ public static final transient LdapURL EMPTY_STRING = new LdapURL();
+
+ /** The filter parser */
+ private static FilterParserImpl filterParser = new FilterParserImpl();
+
+ //~ Instance fields ----------------------------------------------------------------------------
+
+ /** The host */
+ private String host;
+
+ /** The port */
+ private int port;
+
+ /** The DN */
+ private LdapDN dn;
+
+ /** The attributes */
+ private ArrayList attributes;
+
+ /** The scope */
+ private int scope;
+
+ /** The filter as a string*/
+ private String filter;
+
+ /** The extensions */
+ private HashMap extensions;
+
+ /** The criticals extensions */
+ private HashMap criticalExtensions;
+
+ //~ Constructors -------------------------------------------------------------------------------
+
+ /**
+ * Construct an empty LdapURL
+ *
+ */
+ public LdapURL()
+ {
+ super();
+ host = null;
+ port = -1;
+ dn = null;
+ attributes = new ArrayList();
+ scope = SearchControls.OBJECT_SCOPE;
+ filter = null;
+ extensions = new HashMap();
+ criticalExtensions = new HashMap();
+ }
+
+ /**
+ * Create a new LdapURL after having parsed it.
+ *
+ * @param bytes The byte buffer that contains the LDAPURL
+ * @return A MutableString containing the LDAPURL
+ *
+ * @throws DecoderException If the byte array does not comply with RFC 2255
+ */
+ public LdapURL( byte[] bytes ) throws DecoderException
+ {
+ super(bytes);
+ host = null;
+ port = -1;
+ dn = null;
+ attributes = new ArrayList();
+ scope = SearchControls.OBJECT_SCOPE;
+ filter = null;
+ extensions = new HashMap();
+ criticalExtensions = new HashMap();
+
+ if ( ( bytes == null ) || ( bytes.length == 0 ) )
+ {
+ host = "";
+ return;
+ }
+
+ // ldapurl = scheme "://" [hostport] ["/"
+ // [dn ["?" [attributes] ["?" [scope]
+ // ["?" [filter] ["?" extensions]]]]]]
+ // scheme = "ldap"
+
+ int pos = 0;
+
+ // The scheme
+ if ( ( pos = StringUtils.areEquals( bytes, pos, "ldap://" ) ) == -1 )
+ {
+ throw new DecoderException( "A LdapUrl must start with \"ldap://\"" );
+ }
+
+ // The hostport
+ if ( ( pos = parseHostPort( bytes, pos ) ) == -1 )
+ {
+ throw new DecoderException( "The hostport is invalid" );
+ }
+
+ if ( pos == bytes.length )
+ {
+ return;
+ }
+
+ // An optional '/'
+ if ( StringUtils.isCharASCII( bytes, pos, '/' ) == false )
+ {
+ throw new DecoderException( "Bad character, position " + pos + ", '" + bytes[pos] +
+ "', '/' expected" );
+ }
+
+ pos++;
+
+ if ( pos == bytes.length )
+ {
+ return;
+ }
+
+ // An optional DN
+ if ( ( pos = parseDN( bytes, pos ) ) == -1 )
+ {
+ throw new DecoderException( "The DN is invalid" );
+ }
+
+ if ( pos == bytes.length )
+ {
+ return;
+ }
+
+ // Optionals attributes
+ if ( StringUtils.isCharASCII( bytes, pos, '?' ) == false )
+ {
+ throw new DecoderException( "Bad character, position " + pos + ", '" + bytes[pos] +
+ "', '?' expected" );
+ }
+
+ pos++;
+
+ if ( ( pos = parseAttributes( bytes, pos ) ) == -1 )
+ {
+ throw new DecoderException( "Attributes are invalid" );
+ }
+
+ if ( pos == bytes.length )
+ {
+ return;
+ }
+
+ // Optional scope
+ if ( StringUtils.isCharASCII( bytes, pos, '?' ) == false )
+ {
+ throw new DecoderException( "Bad character, position " + pos + ", '" + bytes[pos] +
+ "', '?' expected" );
+ }
+
+ pos++;
+
+ if ( ( pos = parseScope( bytes, pos ) ) == -1 )
+ {
+ throw new DecoderException( "Scope is invalid" );
+ }
+
+ if ( pos == bytes.length )
+ {
+ return;
+ }
+
+ // Optional filter
+ if ( StringUtils.isCharASCII( bytes, pos, '?' ) == false )
+ {
+ throw new DecoderException( "Bad character, position " + pos + ", '" + bytes[pos] +
+ "', '?' expected" );
+ }
+
+ pos++;
+
+ if ( pos == bytes.length )
+ {
+ return;
+ }
+
+ if ( ( pos = parseFilter( bytes, pos ) ) == -1 )
+ {
+ throw new DecoderException( "Filter is invalid" );
+ }
+
+ if ( pos == bytes.length )
+ {
+ return;
+ }
+
+ // Optional extensions
+ if ( StringUtils.isCharASCII( bytes, pos, '?' ) == false )
+ {
+ throw new DecoderException( "Bad character, position " + pos + ", '" + bytes[pos] +
+ "', '?' expected" );
+ }
+
+ pos++;
+
+ if ( ( pos = parseExtensions( bytes, pos ) ) == -1 )
+ {
+ throw new DecoderException( "Extensions are invalid" );
+ }
+
+ if ( pos == bytes.length )
+ {
+ return;
+ }
+ else
+ {
+ throw new DecoderException( "Invalid character at the end of the ldapUrl" );
+ }
+ }
+
+ //~ Methods ------------------------------------------------------------------------------------
+
+ /**
+ * Parse this rule : <br>
+ * <p>
+ * <host> ::= <hostname> ':' <hostnumber><br>
+ * <hostname> ::= *[ <domainlabel> "." ] <toplabel><br>
+ * <domainlabel> ::= <alphadigit> | <alphadigit> *[ <alphadigit> | "-" ] <alphadigit><br>
+ * <toplabel> ::= <alpha> | <alpha> *[ <alphadigit> | "-" ] <alphadigit><br>
+ * <hostnumber> ::= <digits> "." <digits> "." <digits> "." <digits>
+ * </p>
+ *
+ * @param bytes The buffer to parse
+ * @param pos The current position in the byte buffer
+ * @return The new position in the byte buffer, or -1 if the rule does not apply to the byte buffer
+ *
+ * TODO check that the topLabel is valid (it must start with an alpha)
+ */
+ private int parseHost( byte[] bytes, int pos )
+ {
+
+ int start = pos;
+ boolean hadDot = false;
+ boolean hadMinus = false;
+ boolean isHostNumber = true;
+ boolean invalidIp = false;
+ int nbDots = 0;
+ int[] ipElem = new int[4];
+
+ // The host will be followed by a '/' or a ':', or by nothing if it's
+ // the end.
+ // We will search the end of the host part, and we will check some
+ // elements.
+ if ( StringUtils.isCharASCII( bytes, pos, '-' ) )
+ {
+
+ // We can't have a '-' on first position
+ return -1;
+ }
+
+ while ( ( pos < bytes.length ) && ( bytes[pos] != ':' ) && ( bytes[pos] != '/' ) )
+ {
+
+ if ( StringUtils.isCharASCII( bytes, pos, '.' ) )
+ {
+
+ if ( ( hadMinus ) || ( hadDot ) )
+ {
+
+ // We already had a '.' just before : this is not allowed.
+ // Or we had a '-' before a '.' : ths is not allowed either.
+ return -1;
+ }
+
+ // Let's check the string we had before the dot.
+ if ( isHostNumber )
+ {
+
+ if ( nbDots < 4 )
+ {
+
+ // We had only digits. It may be an IP adress? Check it
+ if ( ipElem[nbDots] > 65535 )
+ {
+ invalidIp = true;
+ }
+ }
+ }
+
+ hadDot = true;
+ nbDots++;
+ pos++;
+ continue;
+ }
+ else
+ {
+
+ if ( hadDot && StringUtils.isCharASCII( bytes, pos, '-' ) )
+ {
+
+ // We can't have a '-' just after a '.'
+ return -1;
+ }
+
+ hadDot = false;
+ }
+
+ if ( StringUtils.isDigit( bytes, pos ) )
+ {
+
+ if ( isHostNumber && ( nbDots < 4 ) )
+ {
+ ipElem[nbDots] = ( ipElem[nbDots] * 10 ) + ( bytes[pos] - '0' );
+
+ if ( ipElem[nbDots] > 65535 )
+ {
+ invalidIp = true;
+ }
+ }
+
+ hadMinus = false;
+ }
+ else if ( StringUtils.isAlphaDigitMinus( bytes, pos ) )
+ {
+ isHostNumber = false;
+
+ if ( StringUtils.isCharASCII( bytes, pos, '-' ) )
+ {
+ hadMinus = true;
+ }
+ else
+ {
+ hadMinus = false;
+ }
+ }
+ else
+ {
+ return -1;
+ }
+
+ pos++;
+ }
+
+ if ( start == pos )
+ {
+
+ // An empty host is valid
+ return pos;
+ }
+
+ // Checks the hostNumber
+ if ( isHostNumber )
+ {
+
+ // As this is a host number, we must have 3 dots.
+ if ( nbDots != 3 )
+ {
+ return -1;
+ }
+
+ if ( invalidIp )
+ {
+ return -1;
+ }
+ }
+
+ // Check if we have a '.' or a '-' in last position
+ if ( hadDot || hadMinus )
+ {
+ return -1;
+ }
+
+ host = new String( bytes, start, pos - start );
+ return pos;
+ }
+
+ /**
+ * Parse this rule : <br>
+ * <p>
+ * <port> ::= <digits><br>
+ * <digits> ::= <digit> <digits-or-null><br>
+ * <digits-or-null> ::= <digit> <digits-or-null> | e<br>
+ * <digit> ::= 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9
+ * </p>
+ *
+ * The port must be between 0 and 65535.
+ *
+ * @param bytes The buffer to parse
+ * @param pos The current position in the byte buffer
+ * @return The new position in the byte buffer, or -1 if the rule does not apply to the byte buffer
+ */
+ private int parsePort( byte[] bytes, int pos )
+ {
+
+ if ( StringUtils.isDigit( bytes, pos ) == false )
+ {
+ return -1;
+ }
+
+ port = bytes[pos] - '0';
+
+ pos++;
+
+ while ( StringUtils.isDigit( bytes, pos ) )
+ {
+ port = ( port * 10 ) + ( bytes[pos] - '0' );
+
+ if ( port > 65535 )
+ {
+ return -1;
+ }
+
+ pos++;
+ }
+
+ return pos;
+ }
+
+ /**
+ * Parse this rule : <br>
+ * <p>
+ * <hostport> ::= <host> ':' <port>
+ * </p>
+ *
+ * @param bytes The buffer to parse
+ * @param pos The current position in the byte buffer
+ * @return The new position in the byte buffer, or -1 if the rule does not apply to the byte buffer
+ */
+ private int parseHostPort( byte[] bytes, int pos )
+ {
+
+ if ( ( pos = parseHost( bytes, pos ) ) == -1 )
+ {
+ return -1;
+ }
+
+ // We may have a port.
+ if ( StringUtils.isCharASCII( bytes, pos, ':' ) )
+ {
+ pos++;
+ }
+ else
+ {
+ return pos;
+ }
+
+ // As we have a ':', we must have a valid port (between 0 and 65535).
+ if ( ( pos = parsePort( bytes, pos ) ) == -1 )
+ {
+ return -1;
+ }
+
+ return pos;
+ }
+
+ /**
+ * Parse a string and check that it complies with RFC 2253.
+ * Here, we will just call the LdapDN parser to do the job.
+ *
+ * @param bytes The bytes array to be checked
+ * @param pos the starting position
+ * @return -1 if the bytes array does not contains a DN
+ */
+ private int parseDN( byte[] bytes, int pos )
+ {
+
+ int end = pos;
+
+ for ( int i = pos; ( i < bytes.length ) && ( bytes[i] != '?' ); i++ )
+ {
+ end++;
+ }
+
+ try
+ {
+ dn = new LdapDN( URIUtil.decode( new String( bytes, pos, end - pos ) ).getBytes() );
+ }
+ catch ( URIException ue )
+ {
+ return -1;
+ }
+ catch ( DecoderException de )
+ {
+ return -1;
+ }
+
+ return end;
+ }
+
+ /**
+ * Parse the attributes part
+ *
+ * @param bytes The bytes array to be checked
+ * @param pos the starting position
+ * @return -1 if the bytes array does not contains attributes
+ */
+ private int parseAttributes( byte[] bytes, int pos )
+ {
+
+ int start = pos;
+ int end = pos;
+ HashSet hAttributes = new HashSet();
+ boolean hadComma = false;
+
+ try
+ {
+
+ for ( int i = pos; ( i < bytes.length ) && ( bytes[i] != '?' ); i++ )
+ {
+
+ if ( StringUtils.isCharASCII( bytes, i, ',' ) )
+ {
+ hadComma = true;
+
+ if ( ( end - start ) == 0 )
+ {
+
+ // An attributes must not be null
+ return -1;
+ }
+ else
+ {
+
+ // get the attribute. It must not be blank
+ String attribute = new String( bytes, start, end - start ).trim();
+
+ if ( attribute.length() == 0 )
+ {
+ return -1;
+ }
+
+ String decodedAttr = URIUtil.decode( attribute );
+
+
+ if ( hAttributes.contains( decodedAttr ) == false )
+ {
+ attributes.add( decodedAttr );
+ hAttributes.add( decodedAttr );
+ }
+ }
+
+ start = i + 1;
+ }
+ else
+ {
+ hadComma = false;
+ }
+
+ end++;
+ }
+
+ if ( hadComma )
+ {
+
+ // We are not allowed to have a comma at the end of the attributes
+ return -1;
+ }
+ else
+ {
+
+ if ( end == start )
+ {
+
+ // We don't have any attributes. This is valid.
+ return end;
+ }
+
+ // Store the last attribute
+ // get the attribute. It must not be blank
+ String attribute = new String( bytes, start, end - start ).trim();
+
+ if ( attribute.length() == 0 )
+ {
+ return -1;
+ }
+
+ String decodedAttr = URIUtil.decode( attribute );
+
+
+ if ( hAttributes.contains( decodedAttr ) == false )
+ {
+ attributes.add( decodedAttr );
+ hAttributes.add( decodedAttr );
+ }
+ }
+
+ return end;
+ }
+ catch ( URIException ue )
+ {
+ return -1;
+ }
+ }
+
+ /**
+ * Parse the filter part. We will use the FilterParserImpl class
+ *
+ * @param bytes The bytes array to be checked
+ * @param pos the starting position
+ * @return -1 if the bytes array does not contains a filter
+ */
+ private int parseFilter( byte[] bytes, int pos )
+ {
+
+ int end = pos;
+
+ for ( int i = pos; ( i < bytes.length ) && ( bytes[i] != '?' ); i++ )
+ {
+ end++;
+ }
+
+ try
+ {
+ filter = URIUtil.decode( new String( bytes, pos, end - pos ) );
+ filterParser.parse( filter );
+ }
+ catch ( URIException ue )
+ {
+ return -1;
+ }
+ catch ( IOException ioe )
+ {
+ return -1;
+ }
+ catch ( ParseException pe )
+ {
+ return -1;
+ }
+
+ return end;
+ }
+
+ /**
+ * Parse the scope part.
+ *
+ * @param bytes The bytes array to be checked
+ * @param pos the starting position
+ * @return -1 if the bytes array does not contains a scope
+ */
+ private int parseScope( byte[] bytes, int pos )
+ {
+
+ if ( StringUtils.isCharASCII( bytes, pos, 'b' ) ||
+ StringUtils.isCharASCII( bytes, pos, 'B' ) )
+ {
+ pos++;
+
+ if ( StringUtils.isCharASCII( bytes, pos, 'a' ) ||
+ StringUtils.isCharASCII( bytes, pos, 'A' ) )
+ {
+ pos++;
+
+ if ( StringUtils.isCharASCII( bytes, pos, 's' ) ||
+ StringUtils.isCharASCII( bytes, pos, 'S' ) )
+ {
+ pos++;
+
+ if ( StringUtils.isCharASCII( bytes, pos, 'e' ) ||
+ StringUtils.isCharASCII( bytes, pos, 'E' ) )
+ {
+ pos++;
+ scope = SearchControls.OBJECT_SCOPE;
+ return pos;
+ }
+ }
+ }
+ }
+ else if ( StringUtils.isCharASCII( bytes, pos, 'o' ) ||
+ StringUtils.isCharASCII( bytes, pos, 'O' ) )
+ {
+ pos++;
+
+ if ( StringUtils.isCharASCII( bytes, pos, 'n' ) ||
+ StringUtils.isCharASCII( bytes, pos, 'N' ) )
+ {
+ pos++;
+
+ if ( StringUtils.isCharASCII( bytes, pos, 'e' ) ||
+ StringUtils.isCharASCII( bytes, pos, 'E' ) )
+ {
+ pos++;
+
+ scope = SearchControls.ONELEVEL_SCOPE;
+ return pos;
+ }
+ }
+ }
+ else if ( StringUtils.isCharASCII( bytes, pos, 's' ) ||
+ StringUtils.isCharASCII( bytes, pos, 'S' ) )
+ {
+ pos++;
+
+ if ( StringUtils.isCharASCII( bytes, pos, 'u' ) ||
+ StringUtils.isCharASCII( bytes, pos, 'U' ) )
+ {
+ pos++;
+
+ if ( StringUtils.isCharASCII( bytes, pos, 'b' ) ||
+ StringUtils.isCharASCII( bytes, pos, 'B' ) )
+ {
+ pos++;
+
+ scope = SearchControls.SUBTREE_SCOPE;
+ return pos;
+ }
+ }
+ }
+ else if ( StringUtils.isCharASCII( bytes, pos, '?' ) )
+ {
+
+ // An empty scope. This is valid
+ return pos;
+ }
+
+ // The scope is not one of "one", "sub" or "base". It's an error
+ return -1;
+ }
+
+ /**
+ * Parse extensions and critical extensions.
+ *
+ * The grammar is :
+ *
+ * extensions ::= extension [ ',' extension ]*
+ * extension ::= [ '!' ] ( token | ( 'x-' | 'X-' ) token ) ) [ '=' exvalue ]
+ * @param bytes The bytes array to be checked
+ * @param pos the starting position
+ * @return -1 if the bytes array does not contains valid extensions or critical extensions
+ */
+ private int parseExtensions( byte[] bytes, int pos )
+ {
+
+ int start = pos;
+ boolean isCritical = false;
+ boolean isNewExtension = true;
+ boolean hasValue = false;
+ String extension = null;
+ String value = null;
+
+ if ( pos == bytes.length )
+ {
+ return pos;
+ }
+
+ try
+ {
+
+ for ( int i = pos; ( i < bytes.length ); i++ )
+ {
+
+ if ( StringUtils.isCharASCII( bytes, i, ',' ) )
+ {
+
+ if ( isNewExtension )
+ {
+
+ // a ',' is not allowed when we have already had one
+ // or if we just started to parse the extensions.
+ return -1;
+ }
+ else
+ {
+ value = new String( URIUtil.decode( new String( bytes, start, i - start ) ) )
+ .trim();
+
+ if ( value.length() == 0 )
+ {
+ return -1;
+ }
+
+ if ( isCritical )
+ {
+ criticalExtensions.put( extension, value );
+ }
+ else
+ {
+ extensions.put( extension, value );
+ }
+
+ isNewExtension = true;
+ hasValue = false;
+ isCritical = false;
+ start = i + 1;
+ extension = null;
+ value = null;
+ }
+ }
+ else if ( StringUtils.isCharASCII( bytes, i, '=' ) )
+ {
+
+ if ( hasValue )
+ {
+
+ // We may have two '=' for the same extension
+ continue;
+ }
+
+ // An optionnal value
+ extension = new String( URIUtil.decode( new String( bytes, start, i - start ) ) )
+ .trim();
+
+ if ( extension.length() == 0 )
+ {
+
+ // We must have an extension
+ return -1;
+ }
+
+ isNewExtension = false;
+ hasValue = true;
+ start = i + 1;
+ }
+ else if ( StringUtils.isCharASCII( bytes, i, '!' ) )
+ {
+
+ if ( isNewExtension == false )
+ {
+
+ // '!' must appears first
+ return -1;
+ }
+
+ isCritical = true;
+ start++;
+ }
+ }
+
+ if ( extension == null )
+ {
+ extension = new String( URIUtil.decode(
+ new String( bytes, start, bytes.length - start ) ) ).trim();
+ }
+ else
+ {
+ value = new String( URIUtil.decode(
+ new String( bytes, start, bytes.length - start ) ) ).trim();
+ }
+
+ if ( isCritical )
+ {
+ criticalExtensions.put( extension, value );
+ }
+ else
+ {
+ extensions.put( extension, value );
+ }
+
+ return bytes.length;
+ }
+ catch ( URIException ue )
+ {
+ return -1;
+ }
+ }
+
+ /**
+ * Encode a String to avoid special characters
+ *
+ * *NOTE* : this is an ugly function, just needed because the
+ * RFC 2255 is VERY unclear about the way LDAP searches are
+ * to be encoded. Some references to RFC 1738 are made,
+ * but they are really useless and inadequat.
+ *
+ * @param string The String to encode
+ * @param doubleEncode Set if we need to encode the comma
+ * @return An encoded string
+ */
+ private String urlEncode( String string, boolean doubleEncode)
+ {
+ StringBuffer sb = new StringBuffer();
+
+ for (int i = 0; i < string.length(); i++)
+ {
+ char c = string.charAt(i);
+
+ switch (c)
+ {
+ case ' ' :
+ sb.append("%20");
+ break;
+
+ case '?' :
+ sb.append("%3f");
+ break;
+
+ case '\\' :
+ sb.append("%5c");
+ break;
+
+ case ',' :
+ if (doubleEncode)
+ {
+ sb.append("%2c");
+ }
+ else
+ {
+ sb.append( c );
+ }
+ break;
+
+ default :
+ sb.append( c );
+ }
+ }
+
+ return sb.toString();
+ }
+
+ /**
+ * Get a string representation of a LdapURL.
+ * @return A LdapURL string
+ */
+ public String toString()
+ {
+
+ StringBuffer sb = new StringBuffer( "ldap://" );
+
+ sb.append( ( host == null ) ? "" : host );
+
+ if ( port != -1 )
+ {
+ sb.append( ':' ).append( port );
+ }
+
+ if ( dn != null )
+ {
+ sb.append( '/' ).append( urlEncode(dn.toString(), false ) );
+
+ if ( ( attributes.size() != 0 ) ||
+ ( ( scope != SearchControls.OBJECT_SCOPE ) ||
+ ( filter != null ) ||
+ ( extensions.size() != 0 ) ||
+ ( criticalExtensions.size() != 0 ) ) )
+ {
+ sb.append( '?' );
+
+ for ( int i = 0; i < attributes.size(); i++ )
+ {
+
+ if ( i > 0 )
+ {
+ sb.append( ',' );
+ }
+
+ sb.append( urlEncode((String)attributes.get( i ), false ) );
+ }
+ }
+
+ if ( ( scope != SearchControls.OBJECT_SCOPE ) ||
+ ( filter != null ) ||
+ ( extensions.size() != 0 ) ||
+ ( criticalExtensions.size() != 0 ) )
+ {
+ sb.append( '?' );
+
+ switch ( scope )
+ {
+
+ case SearchControls.OBJECT_SCOPE :
+
+ // This is the default value.
+ break;
+
+ case SearchControls.ONELEVEL_SCOPE :
+ sb.append( "one" );
+ break;
+
+ case SearchControls.SUBTREE_SCOPE :
+ sb.append( "sub" );
+ break;
+ }
+
+ if ( ( filter != null ) ||
+ ( ( extensions.size() != 0 ) ||
+ ( criticalExtensions.size() != 0 ) ) )
+ {
+ sb.append( "?" );
+
+ if ( filter != null )
+ {
+ sb.append( urlEncode( filter, false ) );
+ }
+
+ if ( ( extensions.size() != 0 ) || ( criticalExtensions.size() != 0 ) )
+ {
+ sb.append( '?' );
+
+ boolean isFirst = true;
+
+ if ( extensions.size() != 0 )
+ {
+
+ Iterator keys = extensions.keySet().iterator();
+
+ while ( keys.hasNext() )
+ {
+
+ if ( isFirst == false )
+ {
+ sb.append( ',' );
+ }
+ else
+ {
+ isFirst = false;
+ }
+
+ String key = ( String ) keys.next();
+
+ sb.append( urlEncode( key, false ) ).append( '=' ).append( urlEncode( (String)extensions.get( key ), true ) );
+ }
+ }
+
+ isFirst = true;
+
+ if ( criticalExtensions.size() != 0 )
+ {
+
+ Iterator keys = criticalExtensions.keySet().iterator();
+
+ while ( keys.hasNext() )
+ {
+
+ if ( isFirst == false )
+ {
+ sb.append( ",!" );
+ }
+ else
+ {
+ sb.append( '!' );
+ isFirst = false;
+ }
+
+ String key = ( String ) keys.next();
+
+ sb.append( urlEncode( key, false ) ).append( '=' ).append( urlEncode( (String)criticalExtensions.get( key ), true ) );
+ }
+ }
+ }
+ }
+ }
+ }
+ else
+ {
+ sb.append( '/' );
+ }
+
+ return sb.toString();
+ }
+}
Added: directory/shared/ldap/branches/new-codec-integration/apache2-provider/src/java/main/org/apache/asn1new/ldap/codec/primitives/RelativeLdapDN.java
URL: http://svn.apache.org/viewcvs/directory/shared/ldap/branches/new-codec-integration/apache2-provider/src/java/main/org/apache/asn1new/ldap/codec/primitives/RelativeLdapDN.java?rev=234264&view=auto
==============================================================================
--- directory/shared/ldap/branches/new-codec-integration/apache2-provider/src/java/main/org/apache/asn1new/ldap/codec/primitives/RelativeLdapDN.java (added)
+++ directory/shared/ldap/branches/new-codec-integration/apache2-provider/src/java/main/org/apache/asn1new/ldap/codec/primitives/RelativeLdapDN.java Sun Aug 21 09:53:27 2005
@@ -0,0 +1,492 @@
+/*
+ * Copyright 2005 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.asn1new.ldap.codec.primitives;
+
+import org.apache.asn1new.DecoderException;
+import org.apache.asn1new.util.MutableString;
+import org.apache.asn1new.util.StringUtils;
+import org.apache.asn1new.ldap.codec.utils.DNUtils;
+
+
+/**
+ * This class parses a relative LDdapDN, which is a name-component
+ * of the LdapDN grammar
+ *
+ * The relative DN MUST respect this BNF grammar (a subset of the RFC2253, par. 3, and RFC1779, fig. 1) <br>
+ *
+ * <p>
+ *- <name-component> ::= <attributeType> <spaces> '=' <spaces> <attributeValue> <attributeTypeAndValues> <br>
+ *- <attributeTypeAndValues> ::= <spaces> '+' <spaces> <attributeType> <spaces> '=' <spaces> <attributeValue> <attributeTypeAndValues> | e <br>
+ *- <attributeType> ::= [a-zA-Z] <keychars> | <oidPrefix> [0-9] <digits> <oids> | [0-9] <digits> <oids> <br>
+ *- <keychars> ::= [a-zA-Z] <keychars> | [0-9] <keychars> | '-' <keychars> | e <br>
+ *- <oidPrefix> ::= 'OID.' | 'oid.' | e <br>
+ *- <oids> ::= '.' [0-9] <digits> <oids> | e <br>
+ *- <attributeValue> ::= <pairs-or-strings> | '#' <hexstring> |'"' <quotechar-or-pairs> '"' <br>
+ *- <pairs-or-strings> ::= '\' <pairchar> <pairs-or-strings> | <stringchar> <pairs-or-strings> | e <br>
+ *- <quotechar-or-pairs> ::= <quotechar> <quotechar-or-pairs> | '\' <pairchar> <quotechar-or-pairs> | e <br>
+ *- <pairchar> ::= ',' | '=' | '+' | '<' | '>' | '#' | ';' | '\' | '"' | [0-9a-fA-F] [0-9a-fA-F] <br>
+ *- <hexstring> ::= [0-9a-fA-F] [0-9a-fA-F] <hexpairs> <br>
+ *- <hexpairs> ::= [0-9a-fA-F] [0-9a-fA-F] <hexpairs> | e <br>
+ *- <digits> ::= [0-9] <digits> | e <br>
+ *- <stringchar> ::= [0x00-0xFF] - [,=+<>#;\"\n\r] <br>
+ *- <quotechar> ::= [0x00-0xFF] - [\"] <br>
+ *- <spaces> ::= ' ' <spaces> | e <br>
+ * </p>
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ */
+public class RelativeLdapDN extends MutableString
+{
+ //~ Static fields/initializers -----------------------------------------------------------------
+
+ /** "oid." static */
+ private static final byte[] OID_LOWER = new byte[] { 'o', 'i', 'd', '.' };
+
+ /** "OID." static */
+ private static final byte[] OID_UPPER = new byte[] { 'O', 'I', 'D', '.' };
+
+ /** A null LdapDN */
+ public transient static final RelativeLdapDN EMPTY_STRING = new RelativeLdapDN();
+
+ //~ Methods ------------------------------------------------------------------------------------
+
+ /**
+ * Walk the buffer while the current char is a Space char
+ * <p>
+ * <spaces> ::= ' ' <spaces> | e
+ * </p>
+ *
+ * @param bytes The buffer to parse
+ * @param pos The current position in the byte buffer
+ * @return The new position in the byte buffer
+ */
+ protected static int parseSpaces( byte[] bytes, int pos )
+ {
+
+ while ( StringUtils.isCharASCII( bytes, pos, ' ' ) )
+ {
+ pos++;
+ }
+
+ return pos;
+ }
+
+ /**
+ * Parse this rule : <br>
+ * <p>
+ * <attributeValue> ::= <pairs-or-strings> | '#' <hexstring> |'"' <quotechar-or-pairs> '"' <br>
+ * <pairs-or-strings> ::= '\' <pairchar> <pairs-or-strings> | <stringchar> <pairs-or-strings> | | e <br>
+ * <quotechar-or-pairs> ::= <quotechar> <quotechar-or-pairs> | '\' <pairchar> <quotechar-or-pairs> | e <br>
+ * </p>
+ *
+ * @param bytes The buffer to parse
+ * @param pos The current position in the byte buffer
+ * @return The new position in the byte buffer, or -1 if the rule does not apply to the byte buffer
+ */
+ protected static int parseAttributeValue( byte[] bytes, int pos )
+ {
+ if ( StringUtils.isCharASCII( bytes, pos, '#' ) )
+ {
+ pos++;
+
+ // <attributeValue> ::= '#' <hexstring>
+ if ( ( pos = DNUtils.parseHexString( bytes, pos ) ) == -1 )
+ {
+
+ return -1;
+ }
+
+ return parseSpaces( bytes, pos );
+ }
+ else if ( StringUtils.isCharASCII( bytes, pos, '"' ) )
+ {
+ pos++;
+ int nbBytes = 0;
+
+ // <attributeValue> ::= '"' <quotechar-or-pair> '"'
+ // <quotechar-or-pairs> ::= <quotechar> <quotechar-or-pairs> | '\' <pairchar> <quotechar-or-pairs> | e
+ while ( true )
+ {
+ if ( StringUtils.isCharASCII( bytes, pos, '\\' ) )
+ {
+ pos++;
+
+ if ( DNUtils.isPairChar( bytes, pos ) )
+ {
+ pos++;
+ }
+ else
+ {
+ return -1;
+ }
+ }
+ else if ( (nbBytes = DNUtils.isQuoteChar( bytes, pos ) ) != -1 )
+ {
+ pos += nbBytes;
+ }
+ else
+ {
+ break;
+ }
+ }
+
+ if ( StringUtils.isCharASCII( bytes, pos, '"' ) )
+ {
+ pos++;
+
+ return parseSpaces( bytes, pos );
+ }
+ else
+ {
+ return -1;
+ }
+ }
+ else
+ {
+ while ( true )
+ {
+ if ( StringUtils.isCharASCII( bytes, pos, '\\' ) )
+ {
+ // '\' <pairchar> <pairs-or-strings>
+ pos++;
+
+ if ( DNUtils.isPairChar( bytes, pos ) == false )
+ {
+ return -1;
+ }
+ else
+ {
+ pos++;
+ }
+ }
+ else
+ {
+ int nbBytes = 0;
+
+ // <stringchar> <pairs-or-strings>
+ if ( (nbBytes = DNUtils.isStringChar( bytes, pos )) != -1)
+ {
+ // A special case : if we have some spaces before the '+' character,
+ // we MUST skip them.
+ if ( StringUtils.isCharASCII( bytes, pos, ' ') )
+ {
+ pos = parseSpaces( bytes, pos );
+
+ if ( ( DNUtils.isStringChar( bytes, pos ) == -1 ) &&
+ ( StringUtils.isCharASCII( bytes, pos, '\\' ) == false ) )
+ {
+ // Ok, we are done with the stringchar.
+ return pos;
+ }
+ }
+ else
+ {
+ // An unicode char could be more than one byte long
+ pos += nbBytes;
+ }
+ }
+ else
+ {
+ return pos;
+ }
+ }
+ }
+ }
+ }
+
+ /**
+ * Parse this rule : <br>
+ * <p>
+ * <oidPrefix> ::= 'OID.' | 'oid.' | e
+ * </p>
+ *
+ * @param bytes The buffer to parse
+ * @param pos The current position in the byte buffer
+ * @return The new position in the byte buffer, or -1 if the rule does not apply to the byte buffer
+ */
+ protected static int parseOidPrefix( byte[] bytes, int pos )
+ {
+
+ if ( ( StringUtils.areEquals( bytes, pos, OID_LOWER ) == -1 ) &&
+ ( StringUtils.areEquals( bytes, pos, OID_UPPER ) == -1 ) )
+ {
+
+ return -1;
+ }
+ else
+ {
+ pos += 4;
+
+ return pos;
+ }
+ }
+
+ /**
+ * Parse this rule : <br>
+ * <p>
+ * <oidValue> ::= [0-9] <digits> <oids>
+ * </p>
+ *
+ * @param bytes The buffer to parse
+ * @param pos The current position in the byte buffer
+ * @return The new position in the byte buffer, or -1 if the rule does not apply to the byte buffer
+ */
+ protected static int parseOidValue(byte[] bytes, int pos)
+ {
+ // <attributType> ::= [0-9] <digits> <oids>
+ if ( StringUtils.isDigit( bytes, pos ) == false )
+ {
+
+ // Nope... An error
+ return -1;
+ }
+ else
+ {
+
+ // Let's process an oid
+ pos++;
+
+ while ( StringUtils.isDigit( bytes, pos ) )
+ {
+ pos++;
+ }
+
+ // <oids> ::= '.' [0-9] <digits> <oids> | e
+ if ( StringUtils.isCharASCII( bytes, pos, '.' ) == false )
+ {
+
+ return pos;
+ }
+ else
+ {
+
+ do
+ {
+ pos++;
+
+ if ( StringUtils.isDigit( bytes, pos ) == false )
+ {
+
+ return -1;
+ }
+ else
+ {
+ pos++;
+
+ while ( StringUtils.isDigit( bytes, pos ) )
+ {
+ pos++;
+ }
+ }
+ }
+ while ( StringUtils.isCharASCII( bytes, pos, '.' ) );
+
+ return pos;
+ }
+ }
+ }
+
+ /**
+ * Parse this rule : <br>
+ * <p>
+ * <attributType> ::= [a-zA-Z] <keychars> |
+ * <oidPrefix> [0-9] <digits> <oids> | [0-9] <digits> <oids>
+ * </p>
+ *
+ * The string *MUST* be an ASCII string, not an unicode string.
+ *
+ * @param bytes The buffer to parse
+ * @param pos The current position in the byte buffer
+ * @return The new position in the byte buffer, or -1 if the rule does not apply to the byte buffer
+ */
+ protected static int parseAttributeType( byte[] bytes, int pos )
+ {
+
+ // <attributType> ::= [a-zA-Z] <keychars> | <oidPrefix> [0-9] <digits> <oids> | [0-9] <digits> <oids>
+
+ if ( StringUtils.isAlphaASCII( bytes, pos ))
+ {
+ // <attributType> ::= [a-zA-Z] <keychars> | <oidPrefix> [0-9] <digits> <oids>
+
+ // We have got an Alpha char, it may be the begining of an OID ?
+ int oldPos = pos;
+
+ if ( ( pos = parseOidPrefix( bytes, oldPos ) ) != -1 )
+ {
+ return parseOidValue(bytes, pos);
+ }
+ else
+ {
+ // It's not an oid, it's a String (ASCII)
+ // <attributType> ::= [a-zA-Z] <keychars>
+ // <keychars> ::= [a-zA-Z] <keychar> | [0-9] <keychar> | '-' <keychar> | e
+ pos = oldPos + 1;
+
+ while ( StringUtils.isAlphaDigitMinus( bytes, pos ) )
+ {
+ pos++;
+ }
+
+ return pos;
+ }
+ }
+ else
+ {
+
+ // An oid
+ // <attributType> ::= [0-9] <digits> <oids>
+ return parseOidValue(bytes, pos);
+ }
+ }
+
+ /**
+ * Parse this rule : <br>
+ * <p>
+ * <attributeTypeAndValues> ::= <spaces> '+' <spaces> <attributeType> <spaces> '=' <spaces> <attributeValue> <attributeTypeAndValues> | e
+ * </p>
+ *
+ * @param bytes The buffer to parse
+ * @param pos The current position in the byte buffer
+ * @return The new position in the byte buffer, or -1 if the rule does not apply to the byte buffer
+ */
+ protected static int parseAttributeTypeAndValues( byte[] bytes, int pos )
+ {
+
+ while ( true )
+ {
+ pos = parseSpaces( bytes, pos );
+
+ if ( StringUtils.isCharASCII( bytes, pos, '+' ) )
+ {
+ pos++;
+ }
+ else
+ {
+
+ // <attributeTypeAndValues> ::= e
+ return pos;
+ }
+
+ pos = parseSpaces( bytes, pos );
+
+ if ( ( pos = parseAttributeType( bytes, pos ) ) == -1 )
+ {
+
+ return -1;
+ }
+
+ pos = parseSpaces( bytes, pos );
+
+ if ( StringUtils.isCharASCII( bytes, pos, '=' ) )
+ {
+ pos++;
+ }
+ else
+ {
+
+ return -1;
+ }
+
+ pos = parseSpaces( bytes, pos );
+
+ return parseAttributeValue( bytes, pos );
+ }
+ }
+
+ /**
+ * Parse this rule : <br>
+ * <p>
+ * <name-component> ::= <attributeType> <spaces> '=' <spaces> <attributeValue> <attributeTypeAndValues>
+ * </p>
+ *
+ * @param bytes The buffer to parse
+ * @param pos The current position in the buffer
+ * @return The new position in the byte buffer, or -1 if the rule does not apply to the byte buffer
+ */
+ protected static int parseNameComponent( byte[] bytes, int pos )
+ {
+
+ if ( ( pos = parseAttributeType( bytes, pos ) ) == -1 )
+ {
+
+ return -1;
+ }
+
+ pos = parseSpaces( bytes, pos );
+
+ if ( StringUtils.isCharASCII( bytes, pos, '=' ) == false )
+ {
+
+ return -1;
+ }
+ else
+ {
+ pos++;
+ }
+
+ pos = parseSpaces( bytes, pos );
+
+ if ( ( pos = parseAttributeValue( bytes, pos ) ) == -1 )
+ {
+
+ return -1;
+ }
+
+ return parseAttributeTypeAndValues( bytes, pos );
+ }
+
+ /**
+ * Construct an empty LdapDN object
+ */
+ public RelativeLdapDN(int length, boolean isStreamed)
+ {
+ super(length, isStreamed);
+ }
+
+ /**
+ * Construct an empty LdapDN object
+ */
+ public RelativeLdapDN()
+ {
+ super(0, false);
+ }
+
+ /**
+ * Parse a buffer and checks that it is a valid relative DN <br>
+ *
+ * @param bytes The byte buffer that contains the relative DN
+ * @exception A DecoderException is thrown if the buffer does not contains a valid relative DN.
+ */
+ public RelativeLdapDN( byte[] bytes ) throws DecoderException
+ {
+
+ if ( bytes == null || bytes.length == 0)
+ {
+ return;
+ }
+
+ int pos = 0;
+
+ // Parse the name component
+ if ( ( pos = parseNameComponent( bytes, pos ) ) == -1 )
+ {
+ throw new DecoderException( "Bad relative DN : " + new String( bytes) );
+ }
+
+ setData(bytes);
+ }
+}
Added: directory/shared/ldap/branches/new-codec-integration/apache2-provider/src/java/main/org/apache/asn1new/ldap/codec/utils/DNUtils.java
URL: http://svn.apache.org/viewcvs/directory/shared/ldap/branches/new-codec-integration/apache2-provider/src/java/main/org/apache/asn1new/ldap/codec/utils/DNUtils.java?rev=234264&view=auto
==============================================================================
--- directory/shared/ldap/branches/new-codec-integration/apache2-provider/src/java/main/org/apache/asn1new/ldap/codec/utils/DNUtils.java (added)
+++ directory/shared/ldap/branches/new-codec-integration/apache2-provider/src/java/main/org/apache/asn1new/ldap/codec/utils/DNUtils.java Sun Aug 21 09:53:27 2005
@@ -0,0 +1,385 @@
+/*
+ * Copyright 2005 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.asn1new.ldap.codec.utils;
+
+import org.apache.asn1new.util.StringUtils;
+
+/**
+ * Utility class used by the LdapDN Parser.
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ */
+public class DNUtils
+{
+ //~ Static fields/initializers -----------------------------------------------------------------
+
+ /** <safe-init-char> ::= [0x01-0x09] | 0x0B | 0x0C | [0x0E-0x1F] | [0x21-0x39] | 0x3B | [0x3D-0x7F] */
+ private static final boolean[] SAFE_INIT_CHAR =
+ {
+ false, true, true, true, true, true, true, true, true, true, false, true, true, false,
+ true, true, true, true, true, true, true, true, true, true, true, true, true, true, true,
+ true, true, true, false, true, true, true, true, true, true, true, true, true, true, true,
+ true, true, true, true, true, true, true, true, true, true, true, true, true, true, false,
+ true, false, true, true, true, true, true, true, true, true, true, true, true, true, true,
+ true, true, true, true, true, true, true, true, true, true, true, true, true, true, true,
+ true, true, true, true, true, true, true, true, true, true, true, true, true, true, true,
+ true, true, true, true, true, true, true, true, true, true, true, true, true, true, true,
+ true, true, true, true, true, true, true, true, true
+ };
+
+ /** <safe-char> ::= [0x01-0x09] | 0x0B | 0x0C | [0x0E-0x7F] */
+ private static final boolean[] SAFE_CHAR =
+ {
+ false, true, true, true, true, true, true, true, true, true, false, true, true, false,
+ true, true, true, true, true, true, true, true, true, true, true, true, true, true, true,
+ true, true, true, true, true, true, true, true, true, true, true, true, true, true, true,
+ true, true, true, true, true, true, true, true, true, true, true, true, true, true, true,
+ true, true, true, true, true, true, true, true, true, true, true, true, true, true, true,
+ true, true, true, true, true, true, true, true, true, true, true, true, true, true, true,
+ true, true, true, true, true, true, true, true, true, true, true, true, true, true, true,
+ true, true, true, true, true, true, true, true, true, true, true, true, true, true, true,
+ true, true, true, true, true, true, true, true, true
+ };
+
+ /** <base64-char> ::= 0x2B | 0x2F | [0x30-0x39] | 0x3D | [0x41-0x5A] | [0x61-0x7A] */
+ private static final boolean[] BASE64_CHAR =
+ {
+ false, false, false, false, false, false, false, false, false, false, false, false, false,
+ false, false, false, false, false, false, false, false, false, false, false, false, false,
+ false, false, false, false, false, false, false, false, false, false, false, false, false,
+ false, false, false, false, true, false, false, false, true, true, true, true, true, true,
+ true, true, true, true, true, false, false, false, true, false, false, false, true, true,
+ true, true, true, true, true, true, true, true, true, true, true, true, true, true, true,
+ true, true, true, true, true, true, true, true, true, false, false, false, false, false,
+ false, true, true, true, true, true, true, true, true, true, true, true, true, true, true,
+ true, true, true, true, true, true, true, true, true, true, true, true, false, false,
+ false, false, false
+ };
+
+ /** '"' | '#' | '+' | ',' | [0-9] | ';' | '<' | '=' | '>' | [A-F] | '\' | [a-f]
+ * 0x22 | 0x23 | 0x2B | 0x2C | [0x30-0x39] | 0x3B | 0x3C | 0x3D | 0x3E | [0x41-0x46] | 0x5C | [0x61-0x66] */
+ private static final boolean[] PAIR_CHAR =
+ {
+ false, false, false, false, false, false, false, false, false, false, false, false, false,
+ false, false, false, false, false, false, false, false, false, false, false, false, false,
+ false, false, false, false, false, false, false, false, true, true, false, false, false,
+ false, false, false, false, true, true, false, false, false, true, true, true, true, true,
+ true, true, true, true, true, false, true, true, true, true, false, false, true, true,
+ true, true, true, true, false, false, false, false, false, false, false, false, false,
+ false, false, false, false, false, false, false, false, false, false, false, false, true,
+ false, false, false, false, true, true, true, true, true, true, false, false, false, false,
+ false, false, false, false, false, false, false, false, false, false, false, false, false,
+ false, false, false, false, false, false, false, false
+ };
+
+ //~ Methods ------------------------------------------------------------------------------------
+
+ /**
+ * Walk the buffer while characters are Safe String characters :
+ * <safe-string> ::= <safe-init-char> <safe-chars>
+ * <safe-init-char> ::= [0x01-0x09] | 0x0B | 0x0C | [0x0E-0x1F] | [0x21-0x39] | 0x3B | [0x3D-0x7F]
+ * <safe-chars> ::= <safe-char> <safe-chars> |
+ * <safe-char> ::= [0x01-0x09] | 0x0B | 0x0C | [0x0E-0x7F]
+ *
+ * @param byteArray The buffer which contains the data
+ * @param index Current position in the buffer
+ *
+ * @return The position of the first character which is not a Safe Char
+ */
+ public static int parseSafeString( byte[] byteArray, int index )
+ {
+ if ( ( byteArray == null ) || ( byteArray.length == 0 ) || ( index < 0 ) ||
+ ( index >= byteArray.length ) )
+ {
+ return -1;
+ }
+ else
+ {
+ byte c = byteArray[index];
+
+ if ( ( c > 127 ) || ( SAFE_INIT_CHAR[c] == false ) )
+ {
+ return -1;
+ }
+
+ index++;
+
+ while ( index < byteArray.length )
+ {
+ c = byteArray[index];
+
+ if ( ( c > 127 ) || ( SAFE_CHAR[c] == false ) )
+ {
+ break;
+ }
+
+ index++;
+ }
+
+ return index;
+ }
+ }
+
+ /**
+ * Walk the buffer while characters are Alpha characters :
+ * <alpha> ::= [0x41-0x5A] | [0x61-0x7A]
+ *
+ * @param byteArray The buffer which contains the data
+ * @param index Current position in the buffer
+ *
+ * @return The position of the first character which is not an Alpha Char
+ */
+ public static int parseAlphaASCII( byte[] byteArray, int index )
+ {
+ if ( ( byteArray == null ) || ( byteArray.length == 0 ) || ( index < 0 ) ||
+ ( index >= byteArray.length ) )
+ {
+ return -1;
+ }
+ else
+ {
+ byte c = byteArray[index++];
+
+ if ( ( c > 127 ) || ( StringUtils.ALPHA[c] == false ) )
+ {
+ return -1;
+ }
+ else
+ {
+ return index;
+ }
+ }
+ }
+
+ /**
+ * Check if the current character is a Pair Char
+ * <pairchar> ::= ',' | '=' | '+' | '<' | '>' | '#' | ';' | '\' | '"' | [0-9a-fA-F] [0-9a-fA-F]
+ *
+ * @param byteArray The buffer which contains the data
+ * @param index Current position in the buffer
+ *
+ * @return <code>true</code> if the current character is a Pair Char
+ */
+ public static boolean isPairChar( byte[] byteArray, int index )
+ {
+ if ( ( byteArray == null ) || ( byteArray.length == 0 ) || ( index < 0 ) ||
+ ( index >= byteArray.length ) )
+ {
+ return false;
+ }
+ else
+ {
+ byte c = byteArray[index];
+
+ if ( ( c > 127 ) || ( PAIR_CHAR[c] == false ) )
+ {
+ return false;
+ }
+ else
+ {
+ if ( StringUtils.isHex( byteArray, index++ ) )
+ {
+ return StringUtils.isHex( byteArray, index );
+ }
+ else
+ {
+ return true;
+ }
+ }
+ }
+ }
+
+ /**
+ * Check if the current character is a String Char.
+ * Chars are Unicode, not ASCII.
+ * <stringchar> ::= [0x00-0xFFFF] - [,=+<>#;\"\n\r]
+ * @param byteArray The buffer which contains the data
+ * @param index Current position in the buffer
+ *
+ * @return The current char if it is a String Char, or '#' (this is
+ * simpler than throwing an exception :)
+ */
+ public static int isStringChar( byte[] byteArray, int index )
+ {
+ if ( ( byteArray == null ) || ( byteArray.length == 0 ) || ( index < 0 ) ||
+ ( index >= byteArray.length ) )
+ {
+ return -1;
+ }
+ else
+ {
+ byte c = byteArray[index];
+
+ if ( ( c == 0x0A ) ||
+ ( c == 0x0D ) ||
+ ( c == '"' ) ||
+ ( c == '#' ) ||
+ ( c == '+' ) ||
+ ( c == ',' ) ||
+ ( c == ';' ) ||
+ ( c == '<' ) ||
+ ( c == '=' ) ||
+ ( c == '>' ) )
+ {
+ return -1;
+ }
+ else
+ {
+ return StringUtils.countBytesPerChar(byteArray, index);
+ }
+ }
+ }
+
+ /**
+ * Check if the current character is a Quote Char
+ * We are testing Unicode chars
+ * <quotechar> ::= [0x00-0xFFFF] - [\"]
+ *
+ * @param byteArray The buffer which contains the data
+ * @param index Current position in the buffer
+ *
+ * @return <code>true</code> if the current character is a Quote Char
+ */
+ public static int isQuoteChar( byte[] byteArray, int index )
+ {
+ if ( ( byteArray == null ) || ( byteArray.length == 0 ) || ( index < 0 ) ||
+ ( index >= byteArray.length ) )
+ {
+ return -1;
+ }
+ else
+ {
+ byte c = byteArray[index];
+
+ if ( ( c == '\\' ) || ( c == '"' ) )
+ {
+ return -1;
+ }
+ else
+ {
+ return StringUtils.countBytesPerChar(byteArray, index);
+ }
+ }
+ }
+
+ /**
+ * Parse an hex pair
+ * <hexpair> ::= <hex> <hex>
+ *
+ * @param byteArray The buffer which contains the data
+ * @param index Current position in the buffer
+ *
+ * @return The new position, -1 if the buffer does not contain an HexPair, -2 if the
+ * buffer contains an hex byte but not two.
+ */
+ public static int parseHexPair( byte[] byteArray, int index )
+ {
+ if ( StringUtils.isHex( byteArray, index ) )
+ {
+ if ( StringUtils.isHex( byteArray, index + 1 ) )
+ {
+ return index + 2;
+ }
+ else
+ {
+ return -2;
+ }
+ }
+ else
+ {
+ return -1;
+ }
+ }
+
+ /**
+ * Parse an hex string, which is a list of hex pairs
+ * <hexstring> ::= <hexpair> <hexpairs>
+ * <hexpairs> ::= <hexpair> <hexpairs> | e
+ *
+ * @param byteArray The buffer which contains the data
+ * @param index Current position in the buffer
+ *
+ * @return Return the first position which is not an hex pair, or -1 if there is no
+ * hexpair at the beginning or if an hexpair is invalid (if we have only one hex instead of 2)
+ */
+ public static int parseHexString( byte[] byteArray, int index )
+ {
+ int result = parseHexPair( byteArray, index );
+
+ if ( result < 0 )
+ {
+ return -1;
+ }
+ else
+ {
+ index += 2;
+ }
+
+ while ( ( result = parseHexPair( byteArray, index ) ) >= 0 )
+ {
+ index += 2;
+ }
+
+ return ( ( result == -2 ) ? -1 : index );
+ }
+
+ /**
+ * Walk the buffer while characters are Base64 characters :
+ * <base64-string> ::= <base64-char> <base64-chars>
+ * <base64-chars> ::= <base64-char> <base64-chars> |
+ * <base64-char> ::= 0x2B | 0x2F | [0x30-0x39] | 0x3D | [0x41-0x5A] | [0x61-0x7A]
+ *
+ * @param byteArray The buffer which contains the data
+ * @param index Current position in the buffer
+ *
+ * @return The position of the first character which is not a Base64 Char
+ */
+ public static int parseBase64String( byte[] byteArray, int index )
+ {
+ if ( ( byteArray == null ) || ( byteArray.length == 0 ) || ( index < 0 ) ||
+ ( index >= byteArray.length ) )
+ {
+ return -1;
+ }
+ else
+ {
+ byte c = byteArray[index];
+
+ if ( ( c > 127 ) || ( BASE64_CHAR[c] == false ) )
+ {
+ return -1;
+ }
+
+ index++;
+
+ while ( index < byteArray.length )
+ {
+ c = byteArray[index];
+
+ if ( ( c > 127 ) || ( BASE64_CHAR[c] == false ) )
+ {
+ break;
+ }
+
+ index++;
+ }
+
+ return index;
+ }
+ }
+
+}