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 */
23package com.sun.org.apache.xml.internal.security.utils;
24
25import java.io.IOException;
26import java.io.StringReader;
27
28public 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}
475