001 /* 002 * CDDL HEADER START 003 * 004 * The contents of this file are subject to the terms of the 005 * Common Development and Distribution License, Version 1.0 only 006 * (the "License"). You may not use this file except in compliance 007 * with the License. 008 * 009 * You can obtain a copy of the license at 010 * trunk/opends/resource/legal-notices/OpenDS.LICENSE 011 * or https://OpenDS.dev.java.net/OpenDS.LICENSE. 012 * See the License for the specific language governing permissions 013 * and limitations under the License. 014 * 015 * When distributing Covered Code, include this CDDL HEADER in each 016 * file and include the License file at 017 * trunk/opends/resource/legal-notices/OpenDS.LICENSE. If applicable, 018 * add the following below this CDDL HEADER, with the fields enclosed 019 * by brackets "[]" replaced with your own identifying information: 020 * Portions Copyright [yyyy] [name of copyright owner] 021 * 022 * CDDL HEADER END 023 * 024 * 025 * Copyright 2006-2008 Sun Microsystems, Inc. 026 */ 027 package org.opends.server.schema; 028 import org.opends.messages.Message; 029 030 031 032 import java.util.Arrays; 033 034 import org.opends.server.admin.std.server.EqualityMatchingRuleCfg; 035 import org.opends.server.api.EqualityMatchingRule; 036 import org.opends.server.config.ConfigException; 037 import org.opends.server.core.DirectoryServer; 038 import org.opends.server.protocols.asn1.ASN1OctetString; 039 import org.opends.server.types.AcceptRejectWarn; 040 import org.opends.server.types.ByteString; 041 import org.opends.server.types.DirectoryException; 042 import org.opends.server.types.DN; 043 import org.opends.server.types.InitializationException; 044 import org.opends.server.types.ResultCode; 045 046 import static org.opends.server.loggers.debug.DebugLogger.*; 047 import org.opends.server.loggers.debug.DebugTracer; 048 import org.opends.server.types.DebugLogLevel; 049 import static org.opends.messages.SchemaMessages.*; 050 import static org.opends.server.schema.SchemaConstants.*; 051 import static org.opends.server.util.StaticUtils.*; 052 053 054 055 /** 056 * This class defines the distinguishedNameMatch matching rule defined in X.520 057 * and referenced in RFC 2252. 058 */ 059 public class DistinguishedNameEqualityMatchingRule 060 extends EqualityMatchingRule 061 { 062 /** 063 * The tracer object for the debug logger. 064 */ 065 private static final DebugTracer TRACER = getTracer(); 066 067 068 069 070 /** 071 * Creates a new instance of this caseExactMatch matching rule. 072 */ 073 public DistinguishedNameEqualityMatchingRule() 074 { 075 super(); 076 } 077 078 079 080 /** 081 * {@inheritDoc} 082 */ 083 public void initializeMatchingRule(EqualityMatchingRuleCfg configuration) 084 throws ConfigException, InitializationException 085 { 086 // No initialization is required. 087 } 088 089 090 091 /** 092 * Retrieves the common name for this matching rule. 093 * 094 * @return The common name for this matching rule, or <CODE>null</CODE> if 095 * it does not have a name. 096 */ 097 public String getName() 098 { 099 return EMR_DN_NAME; 100 } 101 102 103 104 /** 105 * Retrieves the OID for this matching rule. 106 * 107 * @return The OID for this matching rule. 108 */ 109 public String getOID() 110 { 111 return EMR_DN_OID; 112 } 113 114 115 116 /** 117 * Retrieves the description for this matching rule. 118 * 119 * @return The description for this matching rule, or <CODE>null</CODE> if 120 * there is none. 121 */ 122 public String getDescription() 123 { 124 // There is no standard description for this matching rule. 125 return null; 126 } 127 128 129 130 /** 131 * Retrieves the OID of the syntax with which this matching rule is 132 * associated. 133 * 134 * @return The OID of the syntax with which this matching rule is associated. 135 */ 136 public String getSyntaxOID() 137 { 138 return SYNTAX_DN_OID; 139 } 140 141 142 143 /** 144 * Retrieves the normalized form of the provided value, which is best suited 145 * for efficiently performing matching operations on that value. 146 * 147 * @param value The value to be normalized. 148 * 149 * @return The normalized version of the provided value. 150 * 151 * @throws DirectoryException If the provided value is invalid according to 152 * the associated attribute syntax. 153 */ 154 public ByteString normalizeValue(ByteString value) 155 throws DirectoryException 156 { 157 // Since the normalization for DNs is so complex, it will be handled 158 // elsewhere. 159 DN dn; 160 try 161 { 162 dn = DN.decode(value.stringValue()); 163 } 164 catch (DirectoryException de) 165 { 166 if (debugEnabled()) 167 { 168 TRACER.debugCaught(DebugLogLevel.ERROR, de); 169 } 170 171 // See if we should try to proceed anyway with a bare-bones normalization. 172 if (DirectoryServer.getSyntaxEnforcementPolicy() == 173 AcceptRejectWarn.REJECT) 174 { 175 throw de; 176 } 177 178 return bestEffortNormalize(toLowerCase(value.stringValue())); 179 } 180 catch (Exception e) 181 { 182 if (debugEnabled()) 183 { 184 TRACER.debugCaught(DebugLogLevel.ERROR, e); 185 } 186 187 if (DirectoryServer.getSyntaxEnforcementPolicy() == 188 AcceptRejectWarn.REJECT) 189 { 190 Message message = ERR_ATTR_SYNTAX_DN_INVALID.get( 191 value.stringValue(), String.valueOf(e)); 192 throw new DirectoryException(ResultCode.INVALID_ATTRIBUTE_SYNTAX, 193 message); 194 } 195 else 196 { 197 return bestEffortNormalize(toLowerCase(value.stringValue())); 198 } 199 } 200 201 return new ASN1OctetString(dn.toNormalizedString()); 202 } 203 204 205 206 /** 207 * Performs "best-effort" normalization on the provided string in the event 208 * that the real DN normalization code rejected the value. It will simply 209 * attempt to strip out any spaces that it thinks might be unnecessary. 210 * 211 * @param lowerString The all-lowercase version of the string to normalize. 212 * 213 * @return A best-effort normalized version of the provided value. 214 */ 215 private ByteString bestEffortNormalize(String lowerString) 216 { 217 int length = lowerString.length(); 218 StringBuilder buffer = new StringBuilder(length); 219 220 for (int i=0; i < length; i++) 221 { 222 char c = lowerString.charAt(i); 223 if (c == ' ') 224 { 225 if (i == 0) 226 { 227 // A space at the beginning of the value will be ignored. 228 continue; 229 } 230 else 231 { 232 // Look at the previous character. If it was a backslash, then keep 233 // the space. If it was a comma, then skip the space. Otherwise, keep 234 // processing. 235 char previous = lowerString.charAt(i-1); 236 if (previous == '\\') 237 { 238 buffer.append(' '); 239 continue; 240 } 241 else if (previous == ',') 242 { 243 continue; 244 } 245 } 246 247 248 if (i == (length-1)) 249 { 250 // A space at the end of the value will be ignored. 251 break; 252 } 253 else 254 { 255 // Look at the next character. If it is a space or a comma, then skip 256 // the space. Otherwise, include it. 257 char next = lowerString.charAt(i+1); 258 if ((next == ' ') || (next == ',')) 259 { 260 continue; 261 } 262 else 263 { 264 buffer.append(' '); 265 } 266 } 267 } 268 else 269 { 270 // It's not a space, so we'll include it. 271 buffer.append(c); 272 } 273 } 274 275 return new ASN1OctetString(buffer.toString()); 276 } 277 278 279 280 /** 281 * Indicates whether the two provided normalized values are equal to each 282 * other. 283 * 284 * @param value1 The normalized form of the first value to compare. 285 * @param value2 The normalized form of the second value to compare. 286 * 287 * @return <CODE>true</CODE> if the provided values are equal, or 288 * <CODE>false</CODE> if not. 289 */ 290 public boolean areEqual(ByteString value1, ByteString value2) 291 { 292 // Since the values are already normalized, we just need to compare the 293 // associated byte arrays. 294 return Arrays.equals(value1.value(), value2.value()); 295 } 296 } 297