001    /*
002     *  Licensed to the Apache Software Foundation (ASF) under one
003     *  or more contributor license agreements.  See the NOTICE file
004     *  distributed with this work for additional information
005     *  regarding copyright ownership.  The ASF licenses this file
006     *  to you under the Apache License, Version 2.0 (the
007     *  "License"); you may not use this file except in compliance
008     *  with the License.  You may obtain a copy of the License at
009     *  
010     *    http://www.apache.org/licenses/LICENSE-2.0
011     *  
012     *  Unless required by applicable law or agreed to in writing,
013     *  software distributed under the License is distributed on an
014     *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
015     *  KIND, either express or implied.  See the License for the
016     *  specific language governing permissions and limitations
017     *  under the License. 
018     *  
019     */
020    package org.apache.directory.shared.ldap.schema.syntaxes;
021    
022    
023    import org.apache.directory.shared.ldap.constants.SchemaConstants;
024    import org.apache.directory.shared.ldap.schema.AbstractSyntaxChecker;
025    import org.apache.directory.shared.ldap.util.StringTools;
026    
027    
028    /**
029     * A SyntaxChecker which verifies that a value is a TeletexTerminalIdentifier according to 
030     * RFC 4517 :
031     * 
032     * teletex-id = ttx-term *(DOLLAR ttx-param)
033     * ttx-term   = PrintableString          ; terminal identifier
034     * ttx-param  = ttx-key COLON ttx-value  ; parameter
035     * ttx-key    = "graphic" | "control" | "misc" | "page" | "private"
036     * ttx-value  = *ttx-value-octet
037     *
038     * ttx-value-octet = %x00-23 | (%x5C "24") | %x25-5B | (%x5C "5C") | %x5D-FF
039     * 
040     * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
041     * @version $Rev$
042     */
043    public class TeletexTerminalIdentifierSyntaxChecker extends AbstractSyntaxChecker
044    {
045        /**
046         * 
047         * Creates a new instance of TeletexTerminalIdentifier.
048         *
049         */
050        public TeletexTerminalIdentifierSyntaxChecker()
051        {
052            super( SchemaConstants.TELETEX_TERMINAL_IDENTIFIER_SYNTAX );
053        }
054        
055        /**
056         * 
057         * Creates a new instance of TeletexTerminalIdentifier.
058         * 
059         * @param oid the oid to associate with this new SyntaxChecker
060         *
061         */
062        protected TeletexTerminalIdentifierSyntaxChecker( String oid )
063        {
064            super( oid );
065        }
066        
067        
068        /* (non-Javadoc)
069         * @see org.apache.directory.shared.ldap.schema.SyntaxChecker#isValidSyntax(java.lang.Object)
070         */
071        public boolean isValidSyntax( Object value )
072        {
073            String strValue = null;
074    
075            if ( value == null )
076            {
077                return false;
078            }
079            
080            if ( value instanceof String )
081            {
082                strValue = ( String ) value;
083            }
084            else if ( value instanceof byte[] )
085            {
086                strValue = StringTools.utf8ToString( ( byte[] ) value ); 
087            }
088            else
089            {
090                strValue = value.toString();
091            }
092    
093            if ( strValue.length() == 0 )
094            {
095                return false;
096            }
097    
098            // Search for the first '$' separator
099            int dollar = strValue.indexOf( '$' );
100            
101            String terminalIdentifier = ( ( dollar == -1 ) ? strValue : strValue.substring( 0, dollar ) );
102            
103            if ( terminalIdentifier.length() == 0 )
104            {
105                // It should not be null
106                return false;
107            }
108            
109            if ( !StringTools.isPrintableString( terminalIdentifier ) )
110            {
111                // It's not a valid PrintableString 
112                return false;
113            }
114            
115            if ( dollar == -1 )
116            {
117                // No ttx-param : let's get out
118                return true;
119            }
120            
121            // Ok, now let's deal withh optional ttx-params
122            String[] ttxParams = strValue.substring( dollar + 1 ).split( "\\$" );
123            
124            if ( ttxParams.length == 0 )
125            {
126                return false;
127            }
128            
129            for ( String ttxParam:ttxParams )
130            {
131                int colon = ttxParam.indexOf( ':' );
132                
133                if ( colon == -1 )
134                {
135                    // we must have a ':' separator
136                    return false;
137                }
138                
139                String key = ttxParam.substring( 0, colon );
140                
141                if ( key.startsWith( "graphic" ) ||
142                     key.startsWith( "control" ) ||
143                     key.startsWith( "misc" ) ||
144                     key.startsWith( "page" ) ||
145                     key.startsWith( "private" ) )
146                {
147                    if ( colon + 1 == ttxParam.length() )
148                    {
149                        return false;
150                    }
151                    
152                    boolean hasEsc = false;
153                    
154                    for ( byte b:StringTools.getBytesUtf8( ttxParam ) )
155                    {
156                        switch ( b )
157                        {
158                            case 0x24 :
159                                // '$' is not accepted
160                                return false;
161                                
162                            case 0x5c :
163                                if ( hasEsc )
164                                {
165                                    // two following \ are not accepted
166                                    return false;
167                                }
168                                else
169                                {
170                                    hasEsc = true;
171                                }
172                                
173                                continue;
174                            
175                            case '2' :
176                                continue;
177    
178                            case '4' :
179                                // We have found a "\24"
180                                hasEsc = false;
181                                continue;
182                                
183                            case '5' :
184                                continue;
185    
186                            case 'c' :
187                            case 'C' :
188                                // We have found a "\5c" or a "\5C"
189                                hasEsc = false;
190                                continue;
191                                
192                            default :
193                                if ( hasEsc )
194                                {
195                                    // A \ should be followed by "24" or "5c" or "5C"
196                                    return false;
197                                }
198                                
199                            continue;
200                        }
201                    }
202                }
203                else
204                {
205                    return false;
206                }
207            }
208            
209            return true;
210        }
211    }