Home | History | Annotate | Download | only in client
      1 /* **************************************************************************
      2  * $OpenLDAP: /com/novell/sasl/client/DirectiveList.java,v 1.4 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  ******************************************************************************/
     15 package com.novell.sasl.client;
     16 
     17 import java.util.*;
     18 import org.apache.harmony.javax.security.sasl.*;
     19 import java.io.UnsupportedEncodingException;
     20 
     21 /**
     22  * Implements the DirectiveList class whihc will be used by the
     23  * DigestMD5SaslClient class
     24  */
     25 class DirectiveList extends Object
     26 {
     27     private static final int STATE_LOOKING_FOR_FIRST_DIRECTIVE  = 1;
     28     private static final int STATE_LOOKING_FOR_DIRECTIVE        = 2;
     29     private static final int STATE_SCANNING_NAME                = 3;
     30     private static final int STATE_LOOKING_FOR_EQUALS            = 4;
     31     private static final int STATE_LOOKING_FOR_VALUE            = 5;
     32     private static final int STATE_LOOKING_FOR_COMMA            = 6;
     33     private static final int STATE_SCANNING_QUOTED_STRING_VALUE    = 7;
     34     private static final int STATE_SCANNING_TOKEN_VALUE            = 8;
     35     private static final int STATE_NO_UTF8_SUPPORT              = 9;
     36 
     37     private int        m_curPos;
     38     private int        m_errorPos;
     39     private String     m_directives;
     40     private int        m_state;
     41     private ArrayList  m_directiveList;
     42     private String     m_curName;
     43     private int        m_scanStart;
     44 
     45     /**
     46      *  Constructs a new DirectiveList.
     47      */
     48      DirectiveList(
     49         byte[] directives)
     50     {
     51         m_curPos = 0;
     52         m_state = STATE_LOOKING_FOR_FIRST_DIRECTIVE;
     53         m_directiveList = new ArrayList(10);
     54         m_scanStart = 0;
     55         m_errorPos = -1;
     56         try
     57         {
     58             m_directives = new String(directives, "UTF-8");
     59         }
     60         catch(UnsupportedEncodingException e)
     61         {
     62             m_state = STATE_NO_UTF8_SUPPORT;
     63         }
     64     }
     65 
     66     /**
     67      * This function takes a US-ASCII character string containing a list of comma
     68      * separated directives, and parses the string into the individual directives
     69      * and their values. A directive consists of a token specifying the directive
     70      * name followed by an equal sign (=) and the directive value. The value is
     71      * either a token or a quoted string
     72      *
     73      * @exception SaslException  If an error Occurs
     74      */
     75     void parseDirectives() throws SaslException
     76     {
     77         char        prevChar;
     78         char        currChar;
     79         int            rc = 0;
     80         boolean        haveQuotedPair = false;
     81         String      currentName = "<no name>";
     82 
     83         if (m_state == STATE_NO_UTF8_SUPPORT)
     84             throw new SaslException("No UTF-8 support on platform");
     85 
     86         prevChar = 0;
     87 
     88         while (m_curPos < m_directives.length())
     89         {
     90             currChar = m_directives.charAt(m_curPos);
     91             switch (m_state)
     92             {
     93             case STATE_LOOKING_FOR_FIRST_DIRECTIVE:
     94             case STATE_LOOKING_FOR_DIRECTIVE:
     95                 if (isWhiteSpace(currChar))
     96                 {
     97                     break;
     98                 }
     99                 else if (isValidTokenChar(currChar))
    100                 {
    101                     m_scanStart = m_curPos;
    102                     m_state = STATE_SCANNING_NAME;
    103                 }
    104                 else
    105                 {
    106                      m_errorPos = m_curPos;
    107                     throw new SaslException("Parse error: Invalid name character");
    108                 }
    109                 break;
    110 
    111             case STATE_SCANNING_NAME:
    112                 if (isValidTokenChar(currChar))
    113                 {
    114                     break;
    115                 }
    116                 else if (isWhiteSpace(currChar))
    117                 {
    118                     currentName = m_directives.substring(m_scanStart, m_curPos);
    119                     m_state = STATE_LOOKING_FOR_EQUALS;
    120                 }
    121                 else if ('=' == currChar)
    122                 {
    123                     currentName = m_directives.substring(m_scanStart, m_curPos);
    124                     m_state = STATE_LOOKING_FOR_VALUE;
    125                 }
    126                 else
    127                 {
    128                      m_errorPos = m_curPos;
    129                     throw new SaslException("Parse error: Invalid name character");
    130                 }
    131                 break;
    132 
    133             case STATE_LOOKING_FOR_EQUALS:
    134                 if (isWhiteSpace(currChar))
    135                 {
    136                     break;
    137                 }
    138                 else if ('=' == currChar)
    139                 {
    140                     m_state = STATE_LOOKING_FOR_VALUE;
    141                 }
    142                 else
    143                 {
    144                     m_errorPos = m_curPos;
    145                     throw new SaslException("Parse error: Expected equals sign '='.");
    146                 }
    147                 break;
    148 
    149             case STATE_LOOKING_FOR_VALUE:
    150                 if (isWhiteSpace(currChar))
    151                 {
    152                     break;
    153                 }
    154                 else if ('"' == currChar)
    155                 {
    156                     m_scanStart = m_curPos+1; /* don't include the quote */
    157                     m_state = STATE_SCANNING_QUOTED_STRING_VALUE;
    158                 }
    159                 else if (isValidTokenChar(currChar))
    160                 {
    161                     m_scanStart = m_curPos;
    162                     m_state = STATE_SCANNING_TOKEN_VALUE;
    163                 }
    164                 else
    165                 {
    166                     m_errorPos = m_curPos;
    167                     throw new SaslException("Parse error: Unexpected character");
    168                 }
    169                 break;
    170 
    171             case STATE_SCANNING_TOKEN_VALUE:
    172                 if (isValidTokenChar(currChar))
    173                 {
    174                     break;
    175                 }
    176                 else if (isWhiteSpace(currChar))
    177                 {
    178                     addDirective(currentName, false);
    179                     m_state = STATE_LOOKING_FOR_COMMA;
    180                 }
    181                 else if (',' == currChar)
    182                 {
    183                     addDirective(currentName, false);
    184                     m_state = STATE_LOOKING_FOR_DIRECTIVE;
    185                 }
    186                 else
    187                 {
    188                      m_errorPos = m_curPos;
    189                     throw new SaslException("Parse error: Invalid value character");
    190                 }
    191                 break;
    192 
    193             case STATE_SCANNING_QUOTED_STRING_VALUE:
    194                 if ('\\' == currChar)
    195                     haveQuotedPair = true;
    196                 if ( ('"' == currChar) &&
    197                      ('\\' != prevChar) )
    198                 {
    199                     addDirective(currentName, haveQuotedPair);
    200                     haveQuotedPair = false;
    201                     m_state = STATE_LOOKING_FOR_COMMA;
    202                 }
    203                 break;
    204 
    205             case STATE_LOOKING_FOR_COMMA:
    206                 if (isWhiteSpace(currChar))
    207                     break;
    208                 else if (currChar == ',')
    209                     m_state = STATE_LOOKING_FOR_DIRECTIVE;
    210                 else
    211                 {
    212                     m_errorPos = m_curPos;
    213                     throw new SaslException("Parse error: Expected a comma.");
    214                 }
    215                 break;
    216             }
    217             if (0 != rc)
    218                 break;
    219             prevChar = currChar;
    220             m_curPos++;
    221         } /* end while loop */
    222 
    223 
    224         if (rc == 0)
    225         {
    226             /* check the ending state */
    227             switch (m_state)
    228             {
    229             case STATE_SCANNING_TOKEN_VALUE:
    230                 addDirective(currentName, false);
    231                 break;
    232 
    233             case STATE_LOOKING_FOR_FIRST_DIRECTIVE:
    234             case STATE_LOOKING_FOR_COMMA:
    235                 break;
    236 
    237             case STATE_LOOKING_FOR_DIRECTIVE:
    238                     throw new SaslException("Parse error: Trailing comma.");
    239 
    240             case STATE_SCANNING_NAME:
    241             case STATE_LOOKING_FOR_EQUALS:
    242             case STATE_LOOKING_FOR_VALUE:
    243                     throw new SaslException("Parse error: Missing value.");
    244 
    245             case STATE_SCANNING_QUOTED_STRING_VALUE:
    246                     throw new SaslException("Parse error: Missing closing quote.");
    247             }
    248         }
    249 
    250     }
    251 
    252     /**
    253      * This function returns TRUE if the character is a valid token character.
    254      *
    255      *     token          = 1*<any CHAR except CTLs or separators>
    256      *
    257      *      separators     = "(" | ")" | "<" | ">" | "@"
    258      *                     | "," | ";" | ":" | "\" | <">
    259      *                     | "/" | "[" | "]" | "?" | "="
    260      *                     | "{" | "}" | SP | HT
    261      *
    262      *      CTL            = <any US-ASCII control character
    263      *                       (octets 0 - 31) and DEL (127)>
    264      *
    265      *      CHAR           = <any US-ASCII character (octets 0 - 127)>
    266      *
    267      * @param c  character to be tested
    268      *
    269      * @return Returns TRUE if the character is a valid token character.
    270      */
    271     boolean isValidTokenChar(
    272         char c)
    273     {
    274         if ( ( (c >= '\u0000') && (c <='\u0020') ) ||
    275              ( (c >= '\u003a') && (c <= '\u0040') ) ||
    276              ( (c >= '\u005b') && (c <= '\u005d') ) ||
    277              ('\u002c' == c) ||
    278              ('\u0025' == c) ||
    279              ('\u0028' == c) ||
    280              ('\u0029' == c) ||
    281              ('\u007b' == c) ||
    282              ('\u007d' == c) ||
    283              ('\u007f' == c) )
    284             return false;
    285 
    286         return true;
    287     }
    288 
    289     /**
    290      * This function returns TRUE if the character is linear white space (LWS).
    291      *         LWS = [CRLF] 1*( SP | HT )
    292      * @param c  Input charcter to be tested
    293      *
    294      * @return Returns TRUE if the character is linear white space (LWS)
    295      */
    296     boolean isWhiteSpace(
    297         char c)
    298     {
    299         if ( ('\t' == c) ||  // HORIZONTAL TABULATION.
    300              ('\n' == c) ||  // LINE FEED.
    301              ('\r' == c) ||  // CARRIAGE RETURN.
    302              ('\u0020' == c) )
    303             return true;
    304 
    305         return false;
    306     }
    307 
    308     /**
    309      * This function creates a directive record and adds it to the list, the
    310      * value will be added later after it is parsed.
    311      *
    312      * @param name  Name
    313      * @param haveQuotedPair true if quoted pair is there else false
    314      */
    315     void addDirective(
    316         String    name,
    317         boolean   haveQuotedPair)
    318     {
    319         String value;
    320         int    inputIndex;
    321         int    valueIndex;
    322         char   valueChar;
    323         int    type;
    324 
    325         if (!haveQuotedPair)
    326         {
    327             value = m_directives.substring(m_scanStart, m_curPos);
    328         }
    329         else
    330         { //copy one character at a time skipping backslash excapes.
    331             StringBuffer valueBuf = new StringBuffer(m_curPos - m_scanStart);
    332             valueIndex = 0;
    333             inputIndex = m_scanStart;
    334             while (inputIndex < m_curPos)
    335             {
    336                 if ('\\' == (valueChar = m_directives.charAt(inputIndex)))
    337                     inputIndex++;
    338                 valueBuf.setCharAt(valueIndex, m_directives.charAt(inputIndex));
    339                 valueIndex++;
    340                 inputIndex++;
    341             }
    342             value = new String(valueBuf);
    343         }
    344 
    345         if (m_state == STATE_SCANNING_QUOTED_STRING_VALUE)
    346             type = ParsedDirective.QUOTED_STRING_VALUE;
    347         else
    348             type = ParsedDirective.TOKEN_VALUE;
    349         m_directiveList.add(new ParsedDirective(name, value, type));
    350     }
    351 
    352 
    353     /**
    354      * Returns the List iterator.
    355      *
    356      * @return     Returns the Iterator Object for the List.
    357      */
    358     Iterator getIterator()
    359     {
    360         return m_directiveList.iterator();
    361     }
    362 }
    363 
    364