1/* **************************************************************************
2 * $OpenLDAP: /com/novell/sasl/client/TokenParser.java,v 1.3 2005/01/17 15:00:54 sunilk Exp $
3 *
4 * Copyright (C) 2002 Novell, Inc. All Rights Reserved.
5 *
6 * THIS WORK IS SUBJECT TO U.S. AND INTERNATIONAL COPYRIGHT LAWS AND
7 * TREATIES. USE, MODIFICATION, AND REDISTRIBUTION OF THIS WORK IS SUBJECT
8 * TO VERSION 2.0.1 OF THE OPENLDAP PUBLIC LICENSE, A COPY OF WHICH IS
9 * AVAILABLE AT HTTP://WWW.OPENLDAP.ORG/LICENSE.HTML OR IN THE FILE "LICENSE"
10 * IN THE TOP-LEVEL DIRECTORY OF THE DISTRIBUTION. ANY USE OR EXPLOITATION
11 * OF THIS WORK OTHER THAN AS AUTHORIZED IN VERSION 2.0.1 OF THE OPENLDAP
12 * PUBLIC LICENSE, OR OTHER PRIOR WRITTEN CONSENT FROM NOVELL, COULD SUBJECT
13 * THE PERPETRATOR TO CRIMINAL AND CIVIL LIABILITY.
14 ******************************************************************************/
15package com.novell.sasl.client;
16
17import org.apache.harmony.javax.security.sasl.*;
18/**
19 * The TokenParser class will parse individual tokens from a list of tokens that
20 * are a directive value for a DigestMD5 authentication.The tokens are separated
21 * commas.
22 */
23class TokenParser extends Object
24{
25    private static final int STATE_LOOKING_FOR_FIRST_TOKEN = 1;
26    private static final int STATE_LOOKING_FOR_TOKEN       = 2;
27    private static final int STATE_SCANNING_TOKEN          = 3;
28    private static final int STATE_LOOKING_FOR_COMMA       = 4;
29    private static final int STATE_PARSING_ERROR           = 5;
30    private static final int STATE_DONE                    = 6;
31
32    private int        m_curPos;
33    private int     m_scanStart;
34    private int     m_state;
35    private String  m_tokens;
36
37
38    TokenParser(
39        String tokens)
40    {
41        m_tokens = tokens;
42        m_curPos = 0;
43        m_scanStart = 0;
44        m_state =  STATE_LOOKING_FOR_FIRST_TOKEN;
45    }
46
47    /**
48     * This function parses the next token from the tokens string and returns
49     * it as a string. If there are no more tokens a null reference is returned.
50     *
51     * @return  the parsed token or a null reference if there are no more
52     * tokens
53     *
54     * @exception  SASLException if an error occurs while parsing
55     */
56    String parseToken() throws SaslException
57    {
58        char    currChar;
59        String  token = null;
60
61
62        if (m_state == STATE_DONE)
63            return null;
64
65        while (m_curPos < m_tokens.length() && (token == null))
66        {
67            currChar = m_tokens.charAt(m_curPos);
68            switch (m_state)
69            {
70            case STATE_LOOKING_FOR_FIRST_TOKEN:
71            case STATE_LOOKING_FOR_TOKEN:
72                if (isWhiteSpace(currChar))
73                {
74                    break;
75                }
76                else if (isValidTokenChar(currChar))
77                {
78                    m_scanStart = m_curPos;
79                    m_state = STATE_SCANNING_TOKEN;
80                }
81                else
82                {
83                    m_state = STATE_PARSING_ERROR;
84                    throw new SaslException("Invalid token character at position " + m_curPos);
85                }
86                break;
87
88            case STATE_SCANNING_TOKEN:
89                if (isValidTokenChar(currChar))
90                {
91                    break;
92                }
93                else if (isWhiteSpace(currChar))
94                {
95                    token = m_tokens.substring(m_scanStart, m_curPos);
96                    m_state = STATE_LOOKING_FOR_COMMA;
97                }
98                else if (',' == currChar)
99                {
100                    token = m_tokens.substring(m_scanStart, m_curPos);
101                    m_state = STATE_LOOKING_FOR_TOKEN;
102                }
103                else
104                {
105                    m_state = STATE_PARSING_ERROR;
106                    throw new SaslException("Invalid token character at position " + m_curPos);
107                }
108                break;
109
110
111            case STATE_LOOKING_FOR_COMMA:
112                if (isWhiteSpace(currChar))
113                    break;
114                else if (currChar == ',')
115                    m_state = STATE_LOOKING_FOR_TOKEN;
116                else
117                {
118                    m_state = STATE_PARSING_ERROR;
119                    throw new SaslException("Expected a comma, found '" +
120                                            currChar + "' at postion " +
121                                            m_curPos);
122                }
123                break;
124            }
125            m_curPos++;
126        } /* end while loop */
127
128        if (token == null)
129        {    /* check the ending state */
130            switch (m_state)
131            {
132            case STATE_SCANNING_TOKEN:
133                token = m_tokens.substring(m_scanStart);
134                m_state = STATE_DONE;
135                break;
136
137            case STATE_LOOKING_FOR_FIRST_TOKEN:
138            case STATE_LOOKING_FOR_COMMA:
139                break;
140
141            case STATE_LOOKING_FOR_TOKEN:
142                throw new SaslException("Trialing comma");
143            }
144        }
145
146        return token;
147    }
148
149    /**
150     * This function returns TRUE if the character is a valid token character.
151     *
152     *     token          = 1*<any CHAR except CTLs or separators>
153     *
154     *      separators     = "(" | ")" | "<" | ">" | "@"
155     *                     | "," | ";" | ":" | "\" | <">
156     *                     | "/" | "[" | "]" | "?" | "="
157     *                     | "{" | "}" | SP | HT
158     *
159     *      CTL            = <any US-ASCII control character
160     *                       (octets 0 - 31) and DEL (127)>
161     *
162     *      CHAR           = <any US-ASCII character (octets 0 - 127)>
163     *
164     * @param c  character to be validated
165     *
166     * @return True if character is valid Token character else it returns
167     * false
168     */
169    boolean isValidTokenChar(
170        char c)
171    {
172        if ( ( (c >= '\u0000') && (c <='\u0020') ) ||
173             ( (c >= '\u003a') && (c <= '\u0040') ) ||
174             ( (c >= '\u005b') && (c <= '\u005d') ) ||
175             ('\u002c' == c) ||
176             ('\u0025' == c) ||
177             ('\u0028' == c) ||
178             ('\u0029' == c) ||
179             ('\u007b' == c) ||
180             ('\u007d' == c) ||
181             ('\u007f' == c) )
182            return false;
183
184        return true;
185    }
186
187    /**
188     * This function returns TRUE if the character is linear white space (LWS).
189     *         LWS = [CRLF] 1*( SP | HT )
190     *
191     * @param c  character to be validated
192     *
193     * @return True if character is liner whitespace else it returns false
194     */
195    boolean isWhiteSpace(
196        char c)
197    {
198        if ( ('\t' == c) || // HORIZONTAL TABULATION.
199             ('\n' == c) || // LINE FEED.
200             ('\r' == c) || // CARRIAGE RETURN.
201             ('\u0020' == c) )
202            return true;
203
204        return false;
205    }
206
207}
208
209