| /* ************************************************************************** |
| * $OpenLDAP: /com/novell/sasl/client/DirectiveList.java,v 1.4 2005/01/17 15:00:54 sunilk Exp $ |
| * |
| * Copyright (C) 2002 Novell, Inc. All Rights Reserved. |
| * |
| * THIS WORK IS SUBJECT TO U.S. AND INTERNATIONAL COPYRIGHT LAWS AND |
| * TREATIES. USE, MODIFICATION, AND REDISTRIBUTION OF THIS WORK IS SUBJECT |
| * TO VERSION 2.0.1 OF THE OPENLDAP PUBLIC LICENSE, A COPY OF WHICH IS |
| * AVAILABLE AT HTTP://WWW.OPENLDAP.ORG/LICENSE.HTML OR IN THE FILE "LICENSE" |
| * IN THE TOP-LEVEL DIRECTORY OF THE DISTRIBUTION. ANY USE OR EXPLOITATION |
| * OF THIS WORK OTHER THAN AS AUTHORIZED IN VERSION 2.0.1 OF THE OPENLDAP |
| * PUBLIC LICENSE, OR OTHER PRIOR WRITTEN CONSENT FROM NOVELL, COULD SUBJECT |
| * THE PERPETRATOR TO CRIMINAL AND CIVIL LIABILITY. |
| ******************************************************************************/ |
| package com.novell.sasl.client; |
| |
| import java.util.*; |
| import org.apache.harmony.javax.security.sasl.*; |
| import java.io.UnsupportedEncodingException; |
| |
| /** |
| * Implements the DirectiveList class whihc will be used by the |
| * DigestMD5SaslClient class |
| */ |
| class DirectiveList extends Object |
| { |
| private static final int STATE_LOOKING_FOR_FIRST_DIRECTIVE = 1; |
| private static final int STATE_LOOKING_FOR_DIRECTIVE = 2; |
| private static final int STATE_SCANNING_NAME = 3; |
| private static final int STATE_LOOKING_FOR_EQUALS = 4; |
| private static final int STATE_LOOKING_FOR_VALUE = 5; |
| private static final int STATE_LOOKING_FOR_COMMA = 6; |
| private static final int STATE_SCANNING_QUOTED_STRING_VALUE = 7; |
| private static final int STATE_SCANNING_TOKEN_VALUE = 8; |
| private static final int STATE_NO_UTF8_SUPPORT = 9; |
| |
| private int m_curPos; |
| private int m_errorPos; |
| private String m_directives; |
| private int m_state; |
| private ArrayList m_directiveList; |
| private String m_curName; |
| private int m_scanStart; |
| |
| /** |
| * Constructs a new DirectiveList. |
| */ |
| DirectiveList( |
| byte[] directives) |
| { |
| m_curPos = 0; |
| m_state = STATE_LOOKING_FOR_FIRST_DIRECTIVE; |
| m_directiveList = new ArrayList(10); |
| m_scanStart = 0; |
| m_errorPos = -1; |
| try |
| { |
| m_directives = new String(directives, "UTF-8"); |
| } |
| catch(UnsupportedEncodingException e) |
| { |
| m_state = STATE_NO_UTF8_SUPPORT; |
| } |
| } |
| |
| /** |
| * This function takes a US-ASCII character string containing a list of comma |
| * separated directives, and parses the string into the individual directives |
| * and their values. A directive consists of a token specifying the directive |
| * name followed by an equal sign (=) and the directive value. The value is |
| * either a token or a quoted string |
| * |
| * @exception SaslException If an error Occurs |
| */ |
| void parseDirectives() throws SaslException |
| { |
| char prevChar; |
| char currChar; |
| int rc = 0; |
| boolean haveQuotedPair = false; |
| String currentName = "<no name>"; |
| |
| if (m_state == STATE_NO_UTF8_SUPPORT) |
| throw new SaslException("No UTF-8 support on platform"); |
| |
| prevChar = 0; |
| |
| while (m_curPos < m_directives.length()) |
| { |
| currChar = m_directives.charAt(m_curPos); |
| switch (m_state) |
| { |
| case STATE_LOOKING_FOR_FIRST_DIRECTIVE: |
| case STATE_LOOKING_FOR_DIRECTIVE: |
| if (isWhiteSpace(currChar)) |
| { |
| break; |
| } |
| else if (isValidTokenChar(currChar)) |
| { |
| m_scanStart = m_curPos; |
| m_state = STATE_SCANNING_NAME; |
| } |
| else |
| { |
| m_errorPos = m_curPos; |
| throw new SaslException("Parse error: Invalid name character"); |
| } |
| break; |
| |
| case STATE_SCANNING_NAME: |
| if (isValidTokenChar(currChar)) |
| { |
| break; |
| } |
| else if (isWhiteSpace(currChar)) |
| { |
| currentName = m_directives.substring(m_scanStart, m_curPos); |
| m_state = STATE_LOOKING_FOR_EQUALS; |
| } |
| else if ('=' == currChar) |
| { |
| currentName = m_directives.substring(m_scanStart, m_curPos); |
| m_state = STATE_LOOKING_FOR_VALUE; |
| } |
| else |
| { |
| m_errorPos = m_curPos; |
| throw new SaslException("Parse error: Invalid name character"); |
| } |
| break; |
| |
| case STATE_LOOKING_FOR_EQUALS: |
| if (isWhiteSpace(currChar)) |
| { |
| break; |
| } |
| else if ('=' == currChar) |
| { |
| m_state = STATE_LOOKING_FOR_VALUE; |
| } |
| else |
| { |
| m_errorPos = m_curPos; |
| throw new SaslException("Parse error: Expected equals sign '='."); |
| } |
| break; |
| |
| case STATE_LOOKING_FOR_VALUE: |
| if (isWhiteSpace(currChar)) |
| { |
| break; |
| } |
| else if ('"' == currChar) |
| { |
| m_scanStart = m_curPos+1; /* don't include the quote */ |
| m_state = STATE_SCANNING_QUOTED_STRING_VALUE; |
| } |
| else if (isValidTokenChar(currChar)) |
| { |
| m_scanStart = m_curPos; |
| m_state = STATE_SCANNING_TOKEN_VALUE; |
| } |
| else |
| { |
| m_errorPos = m_curPos; |
| throw new SaslException("Parse error: Unexpected character"); |
| } |
| break; |
| |
| case STATE_SCANNING_TOKEN_VALUE: |
| if (isValidTokenChar(currChar)) |
| { |
| break; |
| } |
| else if (isWhiteSpace(currChar)) |
| { |
| addDirective(currentName, false); |
| m_state = STATE_LOOKING_FOR_COMMA; |
| } |
| else if (',' == currChar) |
| { |
| addDirective(currentName, false); |
| m_state = STATE_LOOKING_FOR_DIRECTIVE; |
| } |
| else |
| { |
| m_errorPos = m_curPos; |
| throw new SaslException("Parse error: Invalid value character"); |
| } |
| break; |
| |
| case STATE_SCANNING_QUOTED_STRING_VALUE: |
| if ('\\' == currChar) |
| haveQuotedPair = true; |
| if ( ('"' == currChar) && |
| ('\\' != prevChar) ) |
| { |
| addDirective(currentName, haveQuotedPair); |
| haveQuotedPair = false; |
| m_state = STATE_LOOKING_FOR_COMMA; |
| } |
| break; |
| |
| case STATE_LOOKING_FOR_COMMA: |
| if (isWhiteSpace(currChar)) |
| break; |
| else if (currChar == ',') |
| m_state = STATE_LOOKING_FOR_DIRECTIVE; |
| else |
| { |
| m_errorPos = m_curPos; |
| throw new SaslException("Parse error: Expected a comma."); |
| } |
| break; |
| } |
| if (0 != rc) |
| break; |
| prevChar = currChar; |
| m_curPos++; |
| } /* end while loop */ |
| |
| |
| if (rc == 0) |
| { |
| /* check the ending state */ |
| switch (m_state) |
| { |
| case STATE_SCANNING_TOKEN_VALUE: |
| addDirective(currentName, false); |
| break; |
| |
| case STATE_LOOKING_FOR_FIRST_DIRECTIVE: |
| case STATE_LOOKING_FOR_COMMA: |
| break; |
| |
| case STATE_LOOKING_FOR_DIRECTIVE: |
| throw new SaslException("Parse error: Trailing comma."); |
| |
| case STATE_SCANNING_NAME: |
| case STATE_LOOKING_FOR_EQUALS: |
| case STATE_LOOKING_FOR_VALUE: |
| throw new SaslException("Parse error: Missing value."); |
| |
| case STATE_SCANNING_QUOTED_STRING_VALUE: |
| throw new SaslException("Parse error: Missing closing quote."); |
| } |
| } |
| |
| } |
| |
| /** |
| * This function returns TRUE if the character is a valid token character. |
| * |
| * token = 1*<any CHAR except CTLs or separators> |
| * |
| * separators = "(" | ")" | "<" | ">" | "@" |
| * | "," | ";" | ":" | "\" | <"> |
| * | "/" | "[" | "]" | "?" | "=" |
| * | "{" | "}" | SP | HT |
| * |
| * CTL = <any US-ASCII control character |
| * (octets 0 - 31) and DEL (127)> |
| * |
| * CHAR = <any US-ASCII character (octets 0 - 127)> |
| * |
| * @param c character to be tested |
| * |
| * @return Returns TRUE if the character is a valid token character. |
| */ |
| boolean isValidTokenChar( |
| char c) |
| { |
| if ( ( (c >= '\u0000') && (c <='\u0020') ) || |
| ( (c >= '\u003a') && (c <= '\u0040') ) || |
| ( (c >= '\u005b') && (c <= '\u005d') ) || |
| ('\u002c' == c) || |
| ('\u0025' == c) || |
| ('\u0028' == c) || |
| ('\u0029' == c) || |
| ('\u007b' == c) || |
| ('\u007d' == c) || |
| ('\u007f' == c) ) |
| return false; |
| |
| return true; |
| } |
| |
| /** |
| * This function returns TRUE if the character is linear white space (LWS). |
| * LWS = [CRLF] 1*( SP | HT ) |
| * @param c Input charcter to be tested |
| * |
| * @return Returns TRUE if the character is linear white space (LWS) |
| */ |
| boolean isWhiteSpace( |
| char c) |
| { |
| if ( ('\t' == c) || // HORIZONTAL TABULATION. |
| ('\n' == c) || // LINE FEED. |
| ('\r' == c) || // CARRIAGE RETURN. |
| ('\u0020' == c) ) |
| return true; |
| |
| return false; |
| } |
| |
| /** |
| * This function creates a directive record and adds it to the list, the |
| * value will be added later after it is parsed. |
| * |
| * @param name Name |
| * @param haveQuotedPair true if quoted pair is there else false |
| */ |
| void addDirective( |
| String name, |
| boolean haveQuotedPair) |
| { |
| String value; |
| int inputIndex; |
| int valueIndex; |
| char valueChar; |
| int type; |
| |
| if (!haveQuotedPair) |
| { |
| value = m_directives.substring(m_scanStart, m_curPos); |
| } |
| else |
| { //copy one character at a time skipping backslash excapes. |
| StringBuffer valueBuf = new StringBuffer(m_curPos - m_scanStart); |
| valueIndex = 0; |
| inputIndex = m_scanStart; |
| while (inputIndex < m_curPos) |
| { |
| if ('\\' == (valueChar = m_directives.charAt(inputIndex))) |
| inputIndex++; |
| valueBuf.setCharAt(valueIndex, m_directives.charAt(inputIndex)); |
| valueIndex++; |
| inputIndex++; |
| } |
| value = new String(valueBuf); |
| } |
| |
| if (m_state == STATE_SCANNING_QUOTED_STRING_VALUE) |
| type = ParsedDirective.QUOTED_STRING_VALUE; |
| else |
| type = ParsedDirective.TOKEN_VALUE; |
| m_directiveList.add(new ParsedDirective(name, value, type)); |
| } |
| |
| |
| /** |
| * Returns the List iterator. |
| * |
| * @return Returns the Iterator Object for the List. |
| */ |
| Iterator getIterator() |
| { |
| return m_directiveList.iterator(); |
| } |
| } |
| |