1 /*
   2  * reserved comment block
   3  * DO NOT REMOVE OR ALTER!
   4  */
   5 /**
   6  * Licensed to the Apache Software Foundation (ASF) under one
   7  * or more contributor license agreements. See the NOTICE file
   8  * distributed with this work for additional information
   9  * regarding copyright ownership. The ASF licenses this file
  10  * to you under the Apache License, Version 2.0 (the
  11  * "License"); you may not use this file except in compliance
  12  * with the License. You may obtain a copy of the License at
  13  *
  14  * http://www.apache.org/licenses/LICENSE-2.0
  15  *
  16  * Unless required by applicable law or agreed to in writing,
  17  * software distributed under the License is distributed on an
  18  * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
  19  * KIND, either express or implied. See the License for the
  20  * specific language governing permissions and limitations
  21  * under the License.
  22  */
  23 package com.sun.org.apache.xml.internal.security.utils;
  24 
  25 import java.io.IOException;
  26 import java.io.StringReader;
  27 
  28 public class RFC2253Parser {
  29 
  30     /**
  31      * Method rfc2253toXMLdsig
  32      *
  33      * @param dn
  34      * @return normalized string
  35      */
  36     public static String rfc2253toXMLdsig(String dn) {
  37         // Transform from RFC1779 to RFC2253
  38         String normalized = normalize(dn, true);
  39 
  40         return rfctoXML(normalized);
  41     }
  42 
  43     /**
  44      * Method xmldsigtoRFC2253
  45      *
  46      * @param dn
  47      * @return normalized string
  48      */
  49     public static String xmldsigtoRFC2253(String dn) {
  50         // Transform from RFC1779 to RFC2253
  51         String normalized = normalize(dn, false);
  52 
  53         return xmltoRFC(normalized);
  54     }
  55 
  56     /**
  57      * Method normalize
  58      *
  59      * @param dn
  60      * @return normalized string
  61      */
  62     public static String normalize(String dn) {
  63         return normalize(dn, true);
  64     }
  65 
  66     /**
  67      * Method normalize
  68      *
  69      * @param dn
  70      * @param toXml
  71      * @return normalized string
  72      */
  73     public static String normalize(String dn, boolean toXml) {
  74         //if empty string
  75         if ((dn == null) || dn.equals("")) {
  76             return "";
  77         }
  78 
  79         try {
  80             String DN = semicolonToComma(dn);
  81             StringBuilder sb = new StringBuilder();
  82             int i = 0;
  83             int l = 0;
  84             int k;
  85 
  86             //for name component
  87             for (int j = 0; (k = DN.indexOf(',', j)) >= 0; j = k + 1) {
  88                 l += countQuotes(DN, j, k);
  89 
  90                 if ((k > 0) && (DN.charAt(k - 1) != '\\') && (l % 2) == 0) {
  91                     sb.append(parseRDN(DN.substring(i, k).trim(), toXml)).append(',');
  92 
  93                     i = k + 1;
  94                     l = 0;
  95                 }
  96             }
  97 
  98             sb.append(parseRDN(trim(DN.substring(i)), toXml));
  99 
 100             return sb.toString();
 101         } catch (IOException ex) {
 102             return dn;
 103         }
 104     }
 105 
 106     /**
 107      * Method parseRDN
 108      *
 109      * @param str
 110      * @param toXml
 111      * @return normalized string
 112      * @throws IOException
 113      */
 114     static String parseRDN(String str, boolean toXml) throws IOException {
 115         StringBuilder sb = new StringBuilder();
 116         int i = 0;
 117         int l = 0;
 118         int k;
 119 
 120         for (int j = 0; (k = str.indexOf('+', j)) >= 0; j = k + 1) {
 121             l += countQuotes(str, j, k);
 122 
 123             if ((k > 0) && (str.charAt(k - 1) != '\\') && (l % 2) == 0) {
 124                 sb.append(parseATAV(trim(str.substring(i, k)), toXml)).append('+');
 125 
 126                 i = k + 1;
 127                 l = 0;
 128             }
 129         }
 130 
 131         sb.append(parseATAV(trim(str.substring(i)), toXml));
 132 
 133         return sb.toString();
 134     }
 135 
 136     /**
 137      * Method parseATAV
 138      *
 139      * @param str
 140      * @param toXml
 141      * @return normalized string
 142      * @throws IOException
 143      */
 144     static String parseATAV(String str, boolean toXml) throws IOException {
 145         int i = str.indexOf('=');
 146 
 147         if ((i == -1) || ((i > 0) && (str.charAt(i - 1) == '\\'))) {
 148             return str;
 149         }
 150         String attrType = normalizeAT(str.substring(0, i));
 151         // only normalize if value is a String
 152         String attrValue = null;
 153         if (attrType.charAt(0) >= '0' && attrType.charAt(0) <= '9') {
 154             attrValue = str.substring(i + 1);
 155         } else {
 156             attrValue = normalizeV(str.substring(i + 1), toXml);
 157         }
 158 
 159         return attrType + "=" + attrValue;
 160 
 161     }
 162 
 163     /**
 164      * Method normalizeAT
 165      *
 166      * @param str
 167      * @return normalized string
 168      */
 169     static String normalizeAT(String str) {
 170 
 171         String at = str.toUpperCase().trim();
 172 
 173         if (at.startsWith("OID")) {
 174             at = at.substring(3);
 175         }
 176 
 177         return at;
 178     }
 179 
 180     /**
 181      * Method normalizeV
 182      *
 183      * @param str
 184      * @param toXml
 185      * @return normalized string
 186      * @throws IOException
 187      */
 188     static String normalizeV(String str, boolean toXml) throws IOException {
 189         String value = trim(str);
 190 
 191         if (value.startsWith("\"")) {
 192             StringBuilder sb = new StringBuilder();
 193             StringReader sr = new StringReader(value.substring(1, value.length() - 1));
 194             int i = 0;
 195             char c;
 196 
 197             while ((i = sr.read()) > -1) {
 198                 c = (char) i;
 199 
 200                 //the following char is defined at 4.Relationship with RFC1779 and LDAPv2 inrfc2253
 201                 if ((c == ',') || (c == '=') || (c == '+') || (c == '<')
 202                     || (c == '>') || (c == '#') || (c == ';')) {
 203                     sb.append('\\');
 204                 }
 205 
 206                 sb.append(c);
 207             }
 208 
 209             value = trim(sb.toString());
 210         }
 211 
 212         if (toXml) {
 213             if (value.startsWith("#")) {
 214                 value = '\\' + value;
 215             }
 216         } else {
 217             if (value.startsWith("\\#")) {
 218                 value = value.substring(1);
 219             }
 220         }
 221 
 222         return value;
 223     }
 224 
 225     /**
 226      * Method rfctoXML
 227      *
 228      * @param string
 229      * @return normalized string
 230      */
 231     static String rfctoXML(String string) {
 232         try {
 233             String s = changeLess32toXML(string);
 234 
 235             return changeWStoXML(s);
 236         } catch (Exception e) {
 237             return string;
 238         }
 239     }
 240 
 241     /**
 242      * Method xmltoRFC
 243      *
 244      * @param string
 245      * @return normalized string
 246      */
 247     static String xmltoRFC(String string) {
 248         try {
 249             String s = changeLess32toRFC(string);
 250 
 251             return changeWStoRFC(s);
 252         } catch (Exception e) {
 253             return string;
 254         }
 255     }
 256 
 257     /**
 258      * Method changeLess32toRFC
 259      *
 260      * @param string
 261      * @return normalized string
 262      * @throws IOException
 263      */
 264     static String changeLess32toRFC(String string) throws IOException {
 265         StringBuilder sb = new StringBuilder();
 266         StringReader sr = new StringReader(string);
 267         int i = 0;
 268         char c;
 269 
 270         while ((i = sr.read()) > -1) {
 271             c = (char) i;
 272 
 273             if (c == '\\') {
 274                 sb.append(c);
 275 
 276                 char c1 = (char) sr.read();
 277                 char c2 = (char) sr.read();
 278 
 279                 //65 (A) 97 (a)
 280                 if ((((c1 >= 48) && (c1 <= 57)) || ((c1 >= 65) && (c1 <= 70)) || ((c1 >= 97) && (c1 <= 102)))
 281                     && (((c2 >= 48) && (c2 <= 57))
 282                         || ((c2 >= 65) && (c2 <= 70))
 283                         || ((c2 >= 97) && (c2 <= 102)))) {
 284                     char ch = (char) Byte.parseByte("" + c1 + c2, 16);
 285 
 286                     sb.append(ch);
 287                 } else {
 288                     sb.append(c1);
 289                     sb.append(c2);
 290                 }
 291             } else {
 292                 sb.append(c);
 293             }
 294         }
 295 
 296         return sb.toString();
 297     }
 298 
 299     /**
 300      * Method changeLess32toXML
 301      *
 302      * @param string
 303      * @return normalized string
 304      * @throws IOException
 305      */
 306     static String changeLess32toXML(String string) throws IOException {
 307         StringBuilder sb = new StringBuilder();
 308         StringReader sr = new StringReader(string);
 309         int i = 0;
 310 
 311         while ((i = sr.read()) > -1) {
 312             if (i < 32) {
 313                 sb.append('\\');
 314                 sb.append(Integer.toHexString(i));
 315             } else {
 316                 sb.append((char) i);
 317             }
 318         }
 319 
 320         return sb.toString();
 321     }
 322 
 323     /**
 324      * Method changeWStoXML
 325      *
 326      * @param string
 327      * @return normalized string
 328      * @throws IOException
 329      */
 330     static String changeWStoXML(String string) throws IOException {
 331         StringBuilder sb = new StringBuilder();
 332         StringReader sr = new StringReader(string);
 333         int i = 0;
 334         char c;
 335 
 336         while ((i = sr.read()) > -1) {
 337             c = (char) i;
 338 
 339             if (c == '\\') {
 340                 char c1 = (char) sr.read();
 341 
 342                 if (c1 == ' ') {
 343                     sb.append('\\');
 344 
 345                     String s = "20";
 346 
 347                     sb.append(s);
 348                 } else {
 349                     sb.append('\\');
 350                     sb.append(c1);
 351                 }
 352             } else {
 353                 sb.append(c);
 354             }
 355         }
 356 
 357         return sb.toString();
 358     }
 359 
 360     /**
 361      * Method changeWStoRFC
 362      *
 363      * @param string
 364      * @return normalized string
 365      */
 366     static String changeWStoRFC(String string) {
 367         StringBuilder sb = new StringBuilder();
 368         int i = 0;
 369         int k;
 370 
 371         for (int j = 0; (k = string.indexOf("\\20", j)) >= 0; j = k + 3) {
 372             sb.append(trim(string.substring(i, k))).append("\\ ");
 373 
 374             i = k + 3;
 375         }
 376 
 377         sb.append(string.substring(i));
 378 
 379         return sb.toString();
 380     }
 381 
 382     /**
 383      * Method semicolonToComma
 384      *
 385      * @param str
 386      * @return normalized string
 387      */
 388     static String semicolonToComma(String str) {
 389         return removeWSandReplace(str, ";", ",");
 390     }
 391 
 392     /**
 393      * Method removeWhiteSpace
 394      *
 395      * @param str
 396      * @param symbol
 397      * @return normalized string
 398      */
 399     static String removeWhiteSpace(String str, String symbol) {
 400         return removeWSandReplace(str, symbol, symbol);
 401     }
 402 
 403     /**
 404      * Method removeWSandReplace
 405      *
 406      * @param str
 407      * @param symbol
 408      * @param replace
 409      * @return normalized string
 410      */
 411     static String removeWSandReplace(String str, String symbol, String replace) {
 412         StringBuilder sb = new StringBuilder();
 413         int i = 0;
 414         int l = 0;
 415         int k;
 416 
 417         for (int j = 0; (k = str.indexOf(symbol, j)) >= 0; j = k + 1) {
 418             l += countQuotes(str, j, k);
 419 
 420             if ((k > 0) && (str.charAt(k - 1) != '\\') && (l % 2) == 0) {
 421                 sb.append(trim(str.substring(i, k))).append(replace);
 422 
 423                 i = k + 1;
 424                 l = 0;
 425             }
 426         }
 427 
 428         sb.append(trim(str.substring(i)));
 429 
 430         return sb.toString();
 431     }
 432 
 433     /**
 434      * Returns the number of Quotation from i to j
 435      *
 436      * @param s
 437      * @param i
 438      * @param j
 439      * @return number of quotes
 440      */
 441     private static int countQuotes(String s, int i, int j) {
 442         int k = 0;
 443 
 444         for (int l = i; l < j; l++) {
 445             if (s.charAt(l) == '"') {
 446                 k++;
 447             }
 448         }
 449 
 450         return k;
 451     }
 452 
 453     //only for the end of a space character occurring at the end of the string from rfc2253
 454 
 455     /**
 456      * Method trim
 457      *
 458      * @param str
 459      * @return the string
 460      */
 461     static String trim(String str) {
 462 
 463         String trimed = str.trim();
 464         int i = str.indexOf(trimed) + trimed.length();
 465 
 466         if ((str.length() > i) && trimed.endsWith("\\")
 467             && !trimed.endsWith("\\\\") && (str.charAt(i) == ' ')) {
 468             trimed = trimed + " ";
 469         }
 470 
 471         return trimed;
 472     }
 473 
 474 }