001/* 002 * The contents of this file are subject to the terms of the Common Development and 003 * Distribution License (the License). You may not use this file except in compliance with the 004 * License. 005 * 006 * You can obtain a copy of the License at legal/CDDLv1.0.txt. See the License for the 007 * specific language governing permission and limitations under the License. 008 * 009 * When distributing Covered Software, include this CDDL Header Notice in each file and include 010 * the License file at legal/CDDLv1.0.txt. If applicable, add the following below the CDDL 011 * Header, with the fields enclosed by brackets [] replaced by your own identifying 012 * information: "Portions Copyright [year] [name of copyright owner]". 013 * 014 * Copyright 2013-2014 Manuel Gaupp 015 * Portions Copyright 2014-2016 ForgeRock AS. 016 */ 017package org.opends.server.protocols.asn1; 018 019import static org.forgerock.util.Reject.*; 020import static org.opends.messages.ProtocolMessages.*; 021 022import java.math.BigInteger; 023import java.util.regex.Matcher; 024import java.util.regex.Pattern; 025 026import org.forgerock.i18n.LocalizableMessage; 027 028/** 029 * This class implements a parser for strings which are encoded using the 030 * Generic String Encoding Rules (GSER) defined in RFC 3641. 031 * 032 * @see <a href="http://tools.ietf.org/html/rfc3641">RFC 3641 - 033 * Generic String Encoding Rules (GSER) for ASN.1 Types 034 * </a> 035 */ 036public class GSERParser 037{ 038 039 private String gserValue; 040 private int pos; 041 private int length; 042 043 /** 044 * Pattern to match an identifier defined in RFC 3641, section 3.4. 045 * <pre> 046 * An <identifier> conforms to the definition of an identifier in ASN.1 047 * notation (Clause 11.3 of X.680 [8]). It begins with a lowercase 048 * letter and is followed by zero or more letters, digits, and hyphens. 049 * A hyphen is not permitted to be the last character, nor is it to be 050 * followed by another hyphen. The case of letters in an identifier is 051 * always significant. 052 * 053 * identifier = lowercase *alphanumeric *(hyphen 1*alphanumeric) 054 * alphanumeric = uppercase / lowercase / decimal-digit 055 * uppercase = %x41-5A ; "A" to "Z" 056 * lowercase = %x61-7A ; "a" to "z" 057 * decimal-digit = %x30-39 ; "0" to "9" 058 * hyphen = "-" 059 * </pre> 060 */ 061 private static Pattern GSER_IDENTIFIER = Pattern 062 .compile("^([a-z]([A-Za-z0-9]|(-[A-Za-z0-9]))*)"); 063 064 /** 065 * Pattern to match the identifier part (including the colon) of an 066 * IdentifiedChoiceValue defined in RFC 3641, section 3.12. 067 * <pre> 068 * IdentifiedChoiceValue = identifier ":" Value 069 * </pre> 070 */ 071 private static Pattern GSER_CHOICE_IDENTIFIER = Pattern 072 .compile("^([a-z]([A-Za-z0-9]|(-[A-Za-z0-9]))*:)"); 073 074 075 076 /** 077 * Pattern to match "sp", containing zero, one or more space characters. 078 * <pre> 079 * sp = *%x20 ; zero, one or more space characters 080 * </pre> 081 */ 082 private static Pattern GSER_SP = Pattern.compile("^( *)"); 083 084 085 086 /** 087 * Pattern to match "msp", containing at least one space character. 088 * <pre> 089 * msp = 1*%x20 ; one or more space characters 090 * </pre> 091 */ 092 private static Pattern GSER_MSP = Pattern.compile("^( +)"); 093 094 095 096 /** 097 * Pattern to match an Integer value. 098 */ 099 private static Pattern GSER_INTEGER = Pattern.compile("^(\\d+)"); 100 101 102 103 /** 104 * Pattern to match a GSER StringValue, defined in RFC 3641, section 3.2: 105 * <pre> 106 * Any embedded double quotes in the resulting UTF-8 character string 107 * are escaped by repeating the double quote characters. 108 * 109 * [...] 110 * 111 * StringValue = dquote *SafeUTF8Character dquote 112 * dquote = %x22 ; " (double quote) 113 * </pre> 114 */ 115 private static Pattern GSER_STRING = Pattern 116 .compile("^(\"([^\"]|(\"\"))*\")"); 117 118 119 120 /** 121 * Pattern to match the beginning of a GSER encoded Sequence. 122 * <pre> 123 * SequenceValue = ComponentList 124 * ComponentList = "{" [ sp NamedValue *( "," sp NamedValue) ] sp "}" 125 * </pre> 126 */ 127 private static Pattern GSER_SEQUENCE_START = Pattern.compile("^(\\{)"); 128 129 130 131 /** 132 * Pattern to match the end of a GSER encoded Sequence. 133 * <pre> 134 * SequenceValue = ComponentList 135 * ComponentList = "{" [ sp NamedValue *( "," sp NamedValue) ] sp "}" 136 * </pre> 137 */ 138 private static Pattern GSER_SEQUENCE_END = Pattern.compile("^(\\})"); 139 140 141 142 /** 143 * Pattern to match the separator used in GSER encoded sequences. 144 */ 145 private static Pattern GSER_SEP = Pattern.compile("^(,)"); 146 147 148 149 /** 150 * Creates a new GSER Parser. 151 * 152 * @param value the GSER encoded String value 153 */ 154 public GSERParser(String value) 155 { 156 ifNull(value); 157 this.gserValue = value; 158 this.pos = 0; 159 this.length = value.length(); 160 } 161 162 163 164 /** 165 * Determines if the GSER String contains at least one character to be read. 166 * 167 * @return <code>true</code> if there is at least one remaining character or 168 * <code>false</code> otherwise. 169 */ 170 public boolean hasNext() 171 { 172 return pos < length; 173 } 174 175 176 177 /** 178 * Determines if the remaining GSER String matches the provided pattern. 179 * 180 * @param pattern the pattern to search for 181 * 182 * @return <code>true</code> if the remaining string matches the pattern or 183 * <code>false</code> otherwise. 184 */ 185 private boolean hasNext(Pattern pattern) 186 { 187 if (!hasNext()) 188 { 189 return false; 190 } 191 192 Matcher matcher = pattern.matcher(gserValue.substring(pos,length)); 193 194 return matcher.find(); 195 } 196 197 198 199 /** 200 * Returns the String matched by the first capturing group of the pattern. 201 * The parser advances past the input matched by the first capturing group. 202 * 203 * @param pattern the pattern to search for 204 * 205 * @return the String matched by the first capturing group of the pattern 206 * 207 * @throws GSERException 208 * If no match could be found 209 */ 210 private String next(Pattern pattern) throws GSERException 211 { 212 Matcher matcher = pattern.matcher(gserValue.substring(pos,length)); 213 if (matcher.find() && matcher.groupCount() >= 1) 214 { 215 pos += matcher.end(1); 216 return matcher.group(1); 217 } 218 else 219 { 220 LocalizableMessage msg = ERR_GSER_PATTERN_NO_MATCH.get(pattern.pattern(), 221 gserValue.substring(pos,length)); 222 throw new GSERException(msg); 223 } 224 } 225 226 227 228 /** 229 * Skips the input matched by the first capturing group. 230 * 231 * @param pattern the pattern to search for 232 * 233 * @throws GSERException 234 * If no match could be found 235 */ 236 private void skip(Pattern pattern) throws GSERException 237 { 238 Matcher matcher = pattern.matcher(gserValue.substring(pos,length)); 239 240 if (matcher.find() && matcher.groupCount() >= 1) 241 { 242 pos += matcher.end(1); 243 } 244 else 245 { 246 LocalizableMessage msg = ERR_GSER_PATTERN_NO_MATCH.get(pattern.pattern(), 247 gserValue.substring(pos,length)); 248 throw new GSERException(msg); 249 } 250 } 251 252 253 254 /** 255 * Skips the input matching zero, one or more space characters. 256 * 257 * @return reference to this GSERParser 258 * 259 * @throws GSERException 260 * If no match could be found 261 */ 262 public GSERParser skipSP() throws GSERException 263 { 264 skip(GSER_SP); 265 return this; 266 } 267 268 269 270 /** 271 * Skips the input matching one or more space characters. 272 * 273 * @return reference to this GSERParser 274 * 275 * @throws GSERException 276 * If no match could be found 277 */ 278 public GSERParser skipMSP() throws GSERException 279 { 280 skip(GSER_MSP); 281 return this; 282 } 283 284 285 286 /** 287 * Skips the input matching the start of a sequence and subsequent space 288 * characters. 289 * 290 * @return reference to this GSERParser 291 * 292 * @throws GSERException 293 * If the input does not match the start of a sequence 294 */ 295 public GSERParser readStartSequence() throws GSERException 296 { 297 next(GSER_SEQUENCE_START); 298 skip(GSER_SP); 299 return this; 300 } 301 302 303 304 /** 305 * Skips the input matching the end of a sequence and preceding space 306 * characters. 307 * 308 * @return reference to this GSERParser 309 * 310 * @throws GSERException 311 * If the input does not match the end of a sequence 312 */ 313 public GSERParser readEndSequence() throws GSERException 314 { 315 skip(GSER_SP); 316 next(GSER_SEQUENCE_END); 317 return this; 318 } 319 320 321 /** 322 * Skips the input matching the separator pattern (",") and subsequenct space 323 * characters. 324 * 325 * @return reference to this GSERParser 326 * 327 * @throws GSERException 328 * If the input does not match the separator pattern. 329 */ 330 public GSERParser skipSeparator() throws GSERException 331 { 332 if (!hasNext(GSER_SEP)) 333 { 334 LocalizableMessage msg = ERR_GSER_NO_VALID_SEPARATOR.get(gserValue 335 .substring(pos,length)); 336 throw new GSERException(msg); 337 } 338 skip(GSER_SEP); 339 skip(GSER_SP); 340 return this; 341 } 342 343 344 345 /** 346 * Returns the next element as a String. 347 * 348 * @return the input matching the String pattern 349 * 350 * @throws GSERException 351 * If the input does not match the string pattern. 352 */ 353 public String nextString() throws GSERException 354 { 355 if (!hasNext(GSER_STRING)) 356 { 357 LocalizableMessage msg = ERR_GSER_NO_VALID_STRING.get(gserValue 358 .substring(pos,length)); 359 throw new GSERException(msg); 360 } 361 362 String str = next(GSER_STRING); 363 364 // Strip leading and trailing dquotes; unescape double dquotes 365 return str.substring(1, str.length() - 1).replace("\"\"","\""); 366 } 367 368 369 /** 370 * Returns the next element as an Integer. 371 * 372 * @return the input matching the integer pattern 373 * 374 * @throws GSERException 375 * If the input does not match the integer pattern 376 */ 377 public int nextInteger() throws GSERException 378 { 379 if (!hasNext(GSER_INTEGER)) 380 { 381 LocalizableMessage msg = ERR_GSER_NO_VALID_INTEGER.get(gserValue 382 .substring(pos,length)); 383 throw new GSERException(msg); 384 } 385 return Integer.valueOf(next(GSER_INTEGER)).intValue(); 386 } 387 388 389 390 /** 391 * Returns the next element as a BigInteger. 392 * 393 * @return the input matching the integer pattern 394 * 395 * @throws GSERException 396 * If the input does not match the integer pattern 397 */ 398 public BigInteger nextBigInteger() throws GSERException 399 { 400 if (!hasNext(GSER_INTEGER)) 401 { 402 LocalizableMessage msg = ERR_GSER_NO_VALID_INTEGER.get(gserValue 403 .substring(pos,length)); 404 throw new GSERException(msg); 405 } 406 return new BigInteger(next(GSER_INTEGER)); 407 } 408 409 410 /** 411 * Returns the identifier of the next NamedValue element. 412 * 413 * @return the identifier of the NamedValue element 414 * 415 * @throws GSERException 416 * If the input does not match the identifier pattern of a 417 * NamedValue 418 */ 419 public String nextNamedValueIdentifier() throws GSERException 420 { 421 if (!hasNext(GSER_IDENTIFIER)) 422 { 423 LocalizableMessage msg = ERR_GSER_NO_VALID_IDENTIFIER.get(gserValue 424 .substring(pos,length)); 425 throw new GSERException(msg); 426 } 427 String identifier = next(GSER_IDENTIFIER); 428 if (!hasNext(GSER_MSP)) 429 { 430 LocalizableMessage msg = ERR_GSER_SPACE_CHAR_EXPECTED.get(gserValue 431 .substring(pos,length)); 432 throw new GSERException(msg); 433 } 434 skipMSP(); 435 return identifier; 436 } 437 438 439 /** 440 * Return the identifier of the next IdentifiedChoiceValue element. 441 * 442 * @return the identifier of the IdentifiedChoiceValue element 443 * 444 * @throws GSERException 445 * If the input does not match the identifier pattern of an 446 * IdentifiedChoiceValue 447 */ 448 public String nextChoiceValueIdentifier() throws GSERException 449 { 450 if (!hasNext(GSER_CHOICE_IDENTIFIER)) 451 { 452 LocalizableMessage msg = ERR_GSER_NO_VALID_IDENTIFIEDCHOICE.get(gserValue 453 .substring(pos,length)); 454 throw new GSERException(msg); 455 } 456 String identifier = next(GSER_CHOICE_IDENTIFIER); 457 458 // Remove the colon at the end of the identifier 459 return identifier.substring(0, identifier.length() - 1); 460 } 461 462 463}