/* ************************************************************************** * $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 = ""; 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* * * separators = "(" | ")" | "<" | ">" | "@" * | "," | ";" | ":" | "\" | <"> * | "/" | "[" | "]" | "?" | "=" * | "{" | "}" | SP | HT * * CTL = * * CHAR = * * @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(); } }