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 */
020package org.apache.directory.api.ldap.model.schema.syntaxCheckers;
021
022
023import org.apache.directory.api.ldap.model.constants.SchemaConstants;
024import org.apache.directory.api.ldap.model.schema.SyntaxChecker;
025import org.apache.directory.api.util.Strings;
026import org.slf4j.Logger;
027import org.slf4j.LoggerFactory;
028
029
030/**
031 * A SyntaxChecker which verifies that a value is a TeletexTerminalIdentifier according to 
032 * RFC 4517 :
033 * 
034 * teletex-id = ttx-term *(DOLLAR ttx-param)
035 * ttx-term   = PrintableString          ; terminal identifier
036 * ttx-param  = ttx-key COLON ttx-value  ; parameter
037 * ttx-key    = "graphic" | "control" | "misc" | "page" | "private"
038 * ttx-value  = *ttx-value-octet
039 *
040 * ttx-value-octet = %x00-23 | (%x5C "24") | %x25-5B | (%x5C "5C") | %x5D-FF
041 * 
042 * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
043 */
044@SuppressWarnings("serial")
045public class TeletexTerminalIdentifierSyntaxChecker extends SyntaxChecker
046{
047    /** A logger for this class */
048    private static final Logger LOG = LoggerFactory.getLogger( TeletexTerminalIdentifierSyntaxChecker.class );
049
050
051    /**
052     * Creates a new instance of TeletexTerminalIdentifier.
053     */
054    public TeletexTerminalIdentifierSyntaxChecker()
055    {
056        super( SchemaConstants.TELETEX_TERMINAL_IDENTIFIER_SYNTAX );
057    }
058
059
060    /**
061     * {@inheritDoc}
062     */
063    public boolean isValidSyntax( Object value )
064    {
065        String strValue = null;
066
067        if ( value == null )
068        {
069            LOG.debug( "Syntax invalid for 'null'" );
070            return false;
071        }
072
073        if ( value instanceof String )
074        {
075            strValue = ( String ) value;
076        }
077        else if ( value instanceof byte[] )
078        {
079            strValue = Strings.utf8ToString( ( byte[] ) value );
080        }
081        else
082        {
083            strValue = value.toString();
084        }
085
086        if ( strValue.length() == 0 )
087        {
088            LOG.debug( "Syntax invalid for '{}'", value );
089            return false;
090        }
091
092        // Search for the first '$' separator
093        int dollar = strValue.indexOf( '$' );
094
095        String terminalIdentifier = ( ( dollar == -1 ) ? strValue : strValue.substring( 0, dollar ) );
096
097        if ( terminalIdentifier.length() == 0 )
098        {
099            // It should not be null
100            LOG.debug( "Syntax invalid for '{}'", value );
101            return false;
102        }
103
104        if ( !Strings.isPrintableString( terminalIdentifier ) )
105        {
106            // It's not a valid PrintableString 
107            LOG.debug( "Syntax invalid for '{}'", value );
108            return false;
109        }
110
111        if ( dollar == -1 )
112        {
113            // No ttx-param : let's get out
114            LOG.debug( "Syntax valid for '{}'", value );
115            return true;
116        }
117
118        // Ok, now let's deal withh optional ttx-params
119        String[] ttxParams = strValue.substring( dollar + 1 ).split( "\\$" );
120
121        if ( ttxParams.length == 0 )
122        {
123            LOG.debug( "Syntax invalid for '{}'", value );
124            return false;
125        }
126
127        for ( String ttxParam : ttxParams )
128        {
129            int colon = ttxParam.indexOf( ':' );
130
131            if ( colon == -1 )
132            {
133                // we must have a ':' separator
134                LOG.debug( "Syntax invalid for '{}'", value );
135                return false;
136            }
137
138            String key = ttxParam.substring( 0, colon );
139
140            if ( key.startsWith( "graphic" )
141                || key.startsWith( "control" )
142                || key.startsWith( "misc" )
143                || key.startsWith( "page" )
144                || key.startsWith( "private" ) )
145            {
146                if ( colon + 1 == ttxParam.length() )
147                {
148                    LOG.debug( "Syntax invalid for '{}'", value );
149                    return false;
150                }
151
152                boolean hasEsc = false;
153
154                for ( byte b : Strings.getBytesUtf8( ttxParam ) )
155                {
156                    switch ( b )
157                    {
158                        case 0x24:
159                            // '$' is not accepted
160                            LOG.debug( "Syntax invalid for '{}'", value );
161                            return false;
162
163                        case 0x5c:
164                            if ( hasEsc )
165                            {
166                                // two following \ are not accepted
167                                LOG.debug( "Syntax invalid for '{}'", value );
168                                return false;
169                            }
170                            else
171                            {
172                                hasEsc = true;
173                            }
174
175                            continue;
176
177                        case '2':
178                            continue;
179
180                        case '4':
181                            // We have found a "\24"
182                            hasEsc = false;
183                            continue;
184
185                        case '5':
186                            continue;
187
188                        case 'c':
189                        case 'C':
190                            // We have found a "\5c" or a "\5C"
191                            hasEsc = false;
192                            continue;
193
194                        default:
195                            if ( hasEsc )
196                            {
197                                // A \ should be followed by "24" or "5c" or "5C"
198                                return false;
199                            }
200
201                            continue;
202                    }
203                }
204            }
205            else
206            {
207                LOG.debug( "Syntax invalid for '{}'", value );
208                return false;
209            }
210        }
211
212        LOG.debug( "Syntax valid for '{}'", value );
213        return true;
214    }
215}