blob: fc26a6b0ee0a36a5727d712d8494730509c45b61 [file] [log] [blame]
/* **************************************************************************
* $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();
}
}