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 * https://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.i18n.I18n; 024import org.apache.directory.api.ldap.model.constants.SchemaConstants; 025import org.apache.directory.api.ldap.model.schema.SyntaxChecker; 026import org.apache.directory.api.util.Strings; 027 028 029/** 030 * A SyntaxChecker which verifies that a value is a TeletexTerminalIdentifier according to 031 * RFC 4517 : 032 * <pre> 033 * teletex-id = ttx-term *(DOLLAR ttx-param) 034 * ttx-term = PrintableString ; terminal identifier 035 * ttx-param = ttx-key COLON ttx-value ; parameter 036 * ttx-key = "graphic" | "control" | "misc" | "page" | "private" 037 * ttx-value = *ttx-value-octet 038 * 039 * ttx-value-octet = %x00-23 | (%x5C "24") | %x25-5B | (%x5C "5C") | %x5D-FF 040 * </pre> 041 * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a> 042 */ 043@SuppressWarnings("serial") 044public final class TeletexTerminalIdentifierSyntaxChecker extends SyntaxChecker 045{ 046 /** 047 * A static instance of TeletexTerminalIdentifierSyntaxChecker 048 */ 049 public static final TeletexTerminalIdentifierSyntaxChecker INSTANCE = 050 new TeletexTerminalIdentifierSyntaxChecker( SchemaConstants.TELETEX_TERMINAL_IDENTIFIER_SYNTAX ); 051 052 /** 053 * A static Builder for this class 054 */ 055 public static final class Builder extends SCBuilder<TeletexTerminalIdentifierSyntaxChecker> 056 { 057 /** 058 * The Builder constructor 059 */ 060 private Builder() 061 { 062 super( SchemaConstants.TELETEX_TERMINAL_IDENTIFIER_SYNTAX ); 063 } 064 065 066 /** 067 * Create a new instance of TeletexTerminalIdentifierSyntaxChecker 068 * @return A new instance of TeletexTerminalIdentifierSyntaxChecker 069 */ 070 @Override 071 public TeletexTerminalIdentifierSyntaxChecker build() 072 { 073 return new TeletexTerminalIdentifierSyntaxChecker( oid ); 074 } 075 } 076 077 078 /** 079 * Creates a new instance of TeletexTerminalIdentifier. 080 * 081 * @param oid the child's OID 082 */ 083 private TeletexTerminalIdentifierSyntaxChecker( String oid ) 084 { 085 super( oid ); 086 } 087 088 089 /** 090 * @return An instance of the Builder for this class 091 */ 092 public static Builder builder() 093 { 094 return new Builder(); 095 } 096 097 098 /** 099 * {@inheritDoc} 100 */ 101 @Override 102 public boolean isValidSyntax( Object value ) 103 { 104 String strValue; 105 106 if ( value == null ) 107 { 108 if ( LOG.isDebugEnabled() ) 109 { 110 LOG.debug( I18n.err( I18n.ERR_13210_SYNTAX_INVALID, "null" ) ); 111 } 112 113 return false; 114 } 115 116 if ( value instanceof String ) 117 { 118 strValue = ( String ) value; 119 } 120 else if ( value instanceof byte[] ) 121 { 122 strValue = Strings.utf8ToString( ( byte[] ) value ); 123 } 124 else 125 { 126 strValue = value.toString(); 127 } 128 129 if ( strValue.length() == 0 ) 130 { 131 if ( LOG.isDebugEnabled() ) 132 { 133 LOG.debug( I18n.err( I18n.ERR_13210_SYNTAX_INVALID, value ) ); 134 } 135 136 return false; 137 } 138 139 // Search for the first '$' separator 140 int dollar = strValue.indexOf( '$' ); 141 142 String terminalIdentifier = ( dollar == -1 ) ? strValue : strValue.substring( 0, dollar ); 143 144 if ( terminalIdentifier.length() == 0 ) 145 { 146 // It should not be null 147 if ( LOG.isDebugEnabled() ) 148 { 149 LOG.debug( I18n.err( I18n.ERR_13210_SYNTAX_INVALID, value ) ); 150 } 151 152 return false; 153 } 154 155 if ( !Strings.isPrintableString( terminalIdentifier ) ) 156 { 157 // It's not a valid PrintableString 158 if ( LOG.isDebugEnabled() ) 159 { 160 LOG.debug( I18n.err( I18n.ERR_13210_SYNTAX_INVALID, value ) ); 161 } 162 163 return false; 164 } 165 166 if ( dollar == -1 ) 167 { 168 // No ttx-param : let's get out 169 if ( LOG.isDebugEnabled() ) 170 { 171 LOG.debug( I18n.msg( I18n.MSG_13701_SYNTAX_VALID, value ) ); 172 } 173 174 return true; 175 } 176 177 // Ok, now let's deal with optional ttx-params 178 String[] ttxParams = strValue.substring( dollar + 1 ).split( "\\$" ); 179 180 if ( ttxParams.length == 0 ) 181 { 182 if ( LOG.isDebugEnabled() ) 183 { 184 LOG.debug( I18n.err( I18n.ERR_13210_SYNTAX_INVALID, value ) ); 185 } 186 187 return false; 188 } 189 190 for ( String ttxParam : ttxParams ) 191 { 192 int colon = ttxParam.indexOf( ':' ); 193 194 if ( colon == -1 ) 195 { 196 // we must have a ':' separator 197 if ( LOG.isDebugEnabled() ) 198 { 199 LOG.debug( I18n.err( I18n.ERR_13210_SYNTAX_INVALID, value ) ); 200 } 201 202 return false; 203 } 204 205 String key = ttxParam.substring( 0, colon ); 206 207 if ( key.startsWith( "graphic" ) 208 || key.startsWith( "control" ) 209 || key.startsWith( "misc" ) 210 || key.startsWith( "page" ) 211 || key.startsWith( "private" ) ) 212 { 213 if ( colon + 1 == ttxParam.length() ) 214 { 215 if ( LOG.isDebugEnabled() ) 216 { 217 LOG.debug( I18n.err( I18n.ERR_13210_SYNTAX_INVALID, value ) ); 218 } 219 220 return false; 221 } 222 223 boolean hasEsc = false; 224 225 for ( byte b : Strings.getBytesUtf8( ttxParam ) ) 226 { 227 switch ( b ) 228 { 229 case 0x24: 230 // '$' is not accepted 231 if ( LOG.isDebugEnabled() ) 232 { 233 LOG.debug( I18n.err( I18n.ERR_13210_SYNTAX_INVALID, value ) ); 234 } 235 236 return false; 237 238 case 0x5c: 239 if ( hasEsc ) 240 { 241 // two following \ are not accepted 242 if ( LOG.isDebugEnabled() ) 243 { 244 LOG.debug( I18n.err( I18n.ERR_13210_SYNTAX_INVALID, value ) ); 245 } 246 247 return false; 248 } 249 else 250 { 251 hasEsc = true; 252 } 253 254 continue; 255 256 case '2': 257 continue; 258 259 case '4': 260 // We have found a "\24" 261 hasEsc = false; 262 continue; 263 264 case '5': 265 continue; 266 267 case 'c': 268 case 'C': 269 // We have found a "\5c" or a "\5C" 270 hasEsc = false; 271 continue; 272 273 default: 274 if ( hasEsc ) 275 { 276 // A \ should be followed by "24" or "5c" or "5C" 277 return false; 278 } 279 280 continue; 281 } 282 } 283 } 284 else 285 { 286 if ( LOG.isDebugEnabled() ) 287 { 288 LOG.debug( I18n.err( I18n.ERR_13210_SYNTAX_INVALID, value ) ); 289 } 290 291 return false; 292 } 293 } 294 295 if ( LOG.isDebugEnabled() ) 296 { 297 LOG.debug( I18n.msg( I18n.MSG_13701_SYNTAX_VALID, value ) ); 298 } 299 300 return true; 301 } 302}