146972b2d825a2368706e7d2210559ca18fc2b332Sergio Giro/*
246972b2d825a2368706e7d2210559ca18fc2b332Sergio Giro *  Licensed to the Apache Software Foundation (ASF) under one or more
346972b2d825a2368706e7d2210559ca18fc2b332Sergio Giro *  contributor license agreements.  See the NOTICE file distributed with
446972b2d825a2368706e7d2210559ca18fc2b332Sergio Giro *  this work for additional information regarding copyright ownership.
546972b2d825a2368706e7d2210559ca18fc2b332Sergio Giro *  The ASF licenses this file to You under the Apache License, Version 2.0
646972b2d825a2368706e7d2210559ca18fc2b332Sergio Giro *  (the "License"); you may not use this file except in compliance with
746972b2d825a2368706e7d2210559ca18fc2b332Sergio Giro *  the License.  You may obtain a copy of the License at
846972b2d825a2368706e7d2210559ca18fc2b332Sergio Giro *
946972b2d825a2368706e7d2210559ca18fc2b332Sergio Giro *     http://www.apache.org/licenses/LICENSE-2.0
1046972b2d825a2368706e7d2210559ca18fc2b332Sergio Giro *
1146972b2d825a2368706e7d2210559ca18fc2b332Sergio Giro *  Unless required by applicable law or agreed to in writing, software
1246972b2d825a2368706e7d2210559ca18fc2b332Sergio Giro *  distributed under the License is distributed on an "AS IS" BASIS,
1346972b2d825a2368706e7d2210559ca18fc2b332Sergio Giro *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
1446972b2d825a2368706e7d2210559ca18fc2b332Sergio Giro *  See the License for the specific language governing permissions and
1546972b2d825a2368706e7d2210559ca18fc2b332Sergio Giro *  limitations under the License.
1646972b2d825a2368706e7d2210559ca18fc2b332Sergio Giro */
1746972b2d825a2368706e7d2210559ca18fc2b332Sergio Giro
1846972b2d825a2368706e7d2210559ca18fc2b332Sergio Giropackage org.apache.http.conn.ssl;
1946972b2d825a2368706e7d2210559ca18fc2b332Sergio Giro
2046972b2d825a2368706e7d2210559ca18fc2b332Sergio Giroimport java.util.ArrayList;
2146972b2d825a2368706e7d2210559ca18fc2b332Sergio Giroimport java.util.Collections;
2246972b2d825a2368706e7d2210559ca18fc2b332Sergio Giroimport java.util.List;
2346972b2d825a2368706e7d2210559ca18fc2b332Sergio Giroimport javax.security.auth.x500.X500Principal;
2446972b2d825a2368706e7d2210559ca18fc2b332Sergio Giro
2546972b2d825a2368706e7d2210559ca18fc2b332Sergio Giro/**
2646972b2d825a2368706e7d2210559ca18fc2b332Sergio Giro * A distinguished name (DN) parser. This parser only supports extracting a
2746972b2d825a2368706e7d2210559ca18fc2b332Sergio Giro * string value from a DN. It doesn't support values in the hex-string style.
2846972b2d825a2368706e7d2210559ca18fc2b332Sergio Giro *
2946972b2d825a2368706e7d2210559ca18fc2b332Sergio Giro * @hide
3046972b2d825a2368706e7d2210559ca18fc2b332Sergio Giro */
3146972b2d825a2368706e7d2210559ca18fc2b332Sergio Giro@Deprecated
32ab72367506bdc9ce97db4ce3714ff65cedfa5e05Sergio Girofinal class AndroidDistinguishedNameParser {
3346972b2d825a2368706e7d2210559ca18fc2b332Sergio Giro    private final String dn;
3446972b2d825a2368706e7d2210559ca18fc2b332Sergio Giro    private final int length;
3546972b2d825a2368706e7d2210559ca18fc2b332Sergio Giro    private int pos;
3646972b2d825a2368706e7d2210559ca18fc2b332Sergio Giro    private int beg;
3746972b2d825a2368706e7d2210559ca18fc2b332Sergio Giro    private int end;
3846972b2d825a2368706e7d2210559ca18fc2b332Sergio Giro
3946972b2d825a2368706e7d2210559ca18fc2b332Sergio Giro    /** tmp vars to store positions of the currently parsed item */
4046972b2d825a2368706e7d2210559ca18fc2b332Sergio Giro    private int cur;
4146972b2d825a2368706e7d2210559ca18fc2b332Sergio Giro
4246972b2d825a2368706e7d2210559ca18fc2b332Sergio Giro    /** distinguished name chars */
4346972b2d825a2368706e7d2210559ca18fc2b332Sergio Giro    private char[] chars;
4446972b2d825a2368706e7d2210559ca18fc2b332Sergio Giro
45ab72367506bdc9ce97db4ce3714ff65cedfa5e05Sergio Giro    public AndroidDistinguishedNameParser(X500Principal principal) {
4646972b2d825a2368706e7d2210559ca18fc2b332Sergio Giro        // RFC2253 is used to ensure we get attributes in the reverse
4746972b2d825a2368706e7d2210559ca18fc2b332Sergio Giro        // order of the underlying ASN.1 encoding, so that the most
4846972b2d825a2368706e7d2210559ca18fc2b332Sergio Giro        // significant values of repeated attributes occur first.
4946972b2d825a2368706e7d2210559ca18fc2b332Sergio Giro        this.dn = principal.getName(X500Principal.RFC2253);
5046972b2d825a2368706e7d2210559ca18fc2b332Sergio Giro        this.length = this.dn.length();
5146972b2d825a2368706e7d2210559ca18fc2b332Sergio Giro    }
5246972b2d825a2368706e7d2210559ca18fc2b332Sergio Giro
5346972b2d825a2368706e7d2210559ca18fc2b332Sergio Giro    // gets next attribute type: (ALPHA 1*keychar) / oid
5446972b2d825a2368706e7d2210559ca18fc2b332Sergio Giro    private String nextAT() {
5546972b2d825a2368706e7d2210559ca18fc2b332Sergio Giro        // skip preceding space chars, they can present after
5646972b2d825a2368706e7d2210559ca18fc2b332Sergio Giro        // comma or semicolon (compatibility with RFC 1779)
5746972b2d825a2368706e7d2210559ca18fc2b332Sergio Giro        for (; pos < length && chars[pos] == ' '; pos++) {
5846972b2d825a2368706e7d2210559ca18fc2b332Sergio Giro        }
5946972b2d825a2368706e7d2210559ca18fc2b332Sergio Giro        if (pos == length) {
6046972b2d825a2368706e7d2210559ca18fc2b332Sergio Giro            return null; // reached the end of DN
6146972b2d825a2368706e7d2210559ca18fc2b332Sergio Giro        }
6246972b2d825a2368706e7d2210559ca18fc2b332Sergio Giro
6346972b2d825a2368706e7d2210559ca18fc2b332Sergio Giro        // mark the beginning of attribute type
6446972b2d825a2368706e7d2210559ca18fc2b332Sergio Giro        beg = pos;
6546972b2d825a2368706e7d2210559ca18fc2b332Sergio Giro
6646972b2d825a2368706e7d2210559ca18fc2b332Sergio Giro        // attribute type chars
6746972b2d825a2368706e7d2210559ca18fc2b332Sergio Giro        pos++;
6846972b2d825a2368706e7d2210559ca18fc2b332Sergio Giro        for (; pos < length && chars[pos] != '=' && chars[pos] != ' '; pos++) {
6946972b2d825a2368706e7d2210559ca18fc2b332Sergio Giro            // we don't follow exact BNF syntax here:
7046972b2d825a2368706e7d2210559ca18fc2b332Sergio Giro            // accept any char except space and '='
7146972b2d825a2368706e7d2210559ca18fc2b332Sergio Giro        }
7246972b2d825a2368706e7d2210559ca18fc2b332Sergio Giro        if (pos >= length) {
7346972b2d825a2368706e7d2210559ca18fc2b332Sergio Giro            throw new IllegalStateException("Unexpected end of DN: " + dn);
7446972b2d825a2368706e7d2210559ca18fc2b332Sergio Giro        }
7546972b2d825a2368706e7d2210559ca18fc2b332Sergio Giro
7646972b2d825a2368706e7d2210559ca18fc2b332Sergio Giro        // mark the end of attribute type
7746972b2d825a2368706e7d2210559ca18fc2b332Sergio Giro        end = pos;
7846972b2d825a2368706e7d2210559ca18fc2b332Sergio Giro
7946972b2d825a2368706e7d2210559ca18fc2b332Sergio Giro        // skip trailing space chars between attribute type and '='
8046972b2d825a2368706e7d2210559ca18fc2b332Sergio Giro        // (compatibility with RFC 1779)
8146972b2d825a2368706e7d2210559ca18fc2b332Sergio Giro        if (chars[pos] == ' ') {
8246972b2d825a2368706e7d2210559ca18fc2b332Sergio Giro            for (; pos < length && chars[pos] != '=' && chars[pos] == ' '; pos++) {
8346972b2d825a2368706e7d2210559ca18fc2b332Sergio Giro            }
8446972b2d825a2368706e7d2210559ca18fc2b332Sergio Giro
8546972b2d825a2368706e7d2210559ca18fc2b332Sergio Giro            if (chars[pos] != '=' || pos == length) {
8646972b2d825a2368706e7d2210559ca18fc2b332Sergio Giro                throw new IllegalStateException("Unexpected end of DN: " + dn);
8746972b2d825a2368706e7d2210559ca18fc2b332Sergio Giro            }
8846972b2d825a2368706e7d2210559ca18fc2b332Sergio Giro        }
8946972b2d825a2368706e7d2210559ca18fc2b332Sergio Giro
9046972b2d825a2368706e7d2210559ca18fc2b332Sergio Giro        pos++; //skip '=' char
9146972b2d825a2368706e7d2210559ca18fc2b332Sergio Giro
9246972b2d825a2368706e7d2210559ca18fc2b332Sergio Giro        // skip space chars between '=' and attribute value
9346972b2d825a2368706e7d2210559ca18fc2b332Sergio Giro        // (compatibility with RFC 1779)
9446972b2d825a2368706e7d2210559ca18fc2b332Sergio Giro        for (; pos < length && chars[pos] == ' '; pos++) {
9546972b2d825a2368706e7d2210559ca18fc2b332Sergio Giro        }
9646972b2d825a2368706e7d2210559ca18fc2b332Sergio Giro
9746972b2d825a2368706e7d2210559ca18fc2b332Sergio Giro        // in case of oid attribute type skip its prefix: "oid." or "OID."
9846972b2d825a2368706e7d2210559ca18fc2b332Sergio Giro        // (compatibility with RFC 1779)
9946972b2d825a2368706e7d2210559ca18fc2b332Sergio Giro        if ((end - beg > 4) && (chars[beg + 3] == '.')
10046972b2d825a2368706e7d2210559ca18fc2b332Sergio Giro                && (chars[beg] == 'O' || chars[beg] == 'o')
10146972b2d825a2368706e7d2210559ca18fc2b332Sergio Giro                && (chars[beg + 1] == 'I' || chars[beg + 1] == 'i')
10246972b2d825a2368706e7d2210559ca18fc2b332Sergio Giro                && (chars[beg + 2] == 'D' || chars[beg + 2] == 'd')) {
10346972b2d825a2368706e7d2210559ca18fc2b332Sergio Giro            beg += 4;
10446972b2d825a2368706e7d2210559ca18fc2b332Sergio Giro        }
10546972b2d825a2368706e7d2210559ca18fc2b332Sergio Giro
10646972b2d825a2368706e7d2210559ca18fc2b332Sergio Giro        return new String(chars, beg, end - beg);
10746972b2d825a2368706e7d2210559ca18fc2b332Sergio Giro    }
10846972b2d825a2368706e7d2210559ca18fc2b332Sergio Giro
10946972b2d825a2368706e7d2210559ca18fc2b332Sergio Giro    // gets quoted attribute value: QUOTATION *( quotechar / pair ) QUOTATION
11046972b2d825a2368706e7d2210559ca18fc2b332Sergio Giro    private String quotedAV() {
11146972b2d825a2368706e7d2210559ca18fc2b332Sergio Giro        pos++;
11246972b2d825a2368706e7d2210559ca18fc2b332Sergio Giro        beg = pos;
11346972b2d825a2368706e7d2210559ca18fc2b332Sergio Giro        end = beg;
11446972b2d825a2368706e7d2210559ca18fc2b332Sergio Giro        while (true) {
11546972b2d825a2368706e7d2210559ca18fc2b332Sergio Giro
11646972b2d825a2368706e7d2210559ca18fc2b332Sergio Giro            if (pos == length) {
11746972b2d825a2368706e7d2210559ca18fc2b332Sergio Giro                throw new IllegalStateException("Unexpected end of DN: " + dn);
11846972b2d825a2368706e7d2210559ca18fc2b332Sergio Giro            }
11946972b2d825a2368706e7d2210559ca18fc2b332Sergio Giro
12046972b2d825a2368706e7d2210559ca18fc2b332Sergio Giro            if (chars[pos] == '"') {
12146972b2d825a2368706e7d2210559ca18fc2b332Sergio Giro                // enclosing quotation was found
12246972b2d825a2368706e7d2210559ca18fc2b332Sergio Giro                pos++;
12346972b2d825a2368706e7d2210559ca18fc2b332Sergio Giro                break;
12446972b2d825a2368706e7d2210559ca18fc2b332Sergio Giro            } else if (chars[pos] == '\\') {
12546972b2d825a2368706e7d2210559ca18fc2b332Sergio Giro                chars[end] = getEscaped();
12646972b2d825a2368706e7d2210559ca18fc2b332Sergio Giro            } else {
12746972b2d825a2368706e7d2210559ca18fc2b332Sergio Giro                // shift char: required for string with escaped chars
12846972b2d825a2368706e7d2210559ca18fc2b332Sergio Giro                chars[end] = chars[pos];
12946972b2d825a2368706e7d2210559ca18fc2b332Sergio Giro            }
13046972b2d825a2368706e7d2210559ca18fc2b332Sergio Giro            pos++;
13146972b2d825a2368706e7d2210559ca18fc2b332Sergio Giro            end++;
13246972b2d825a2368706e7d2210559ca18fc2b332Sergio Giro        }
13346972b2d825a2368706e7d2210559ca18fc2b332Sergio Giro
13446972b2d825a2368706e7d2210559ca18fc2b332Sergio Giro        // skip trailing space chars before comma or semicolon.
13546972b2d825a2368706e7d2210559ca18fc2b332Sergio Giro        // (compatibility with RFC 1779)
13646972b2d825a2368706e7d2210559ca18fc2b332Sergio Giro        for (; pos < length && chars[pos] == ' '; pos++) {
13746972b2d825a2368706e7d2210559ca18fc2b332Sergio Giro        }
13846972b2d825a2368706e7d2210559ca18fc2b332Sergio Giro
13946972b2d825a2368706e7d2210559ca18fc2b332Sergio Giro        return new String(chars, beg, end - beg);
14046972b2d825a2368706e7d2210559ca18fc2b332Sergio Giro    }
14146972b2d825a2368706e7d2210559ca18fc2b332Sergio Giro
14246972b2d825a2368706e7d2210559ca18fc2b332Sergio Giro    // gets hex string attribute value: "#" hexstring
14346972b2d825a2368706e7d2210559ca18fc2b332Sergio Giro    private String hexAV() {
14446972b2d825a2368706e7d2210559ca18fc2b332Sergio Giro        if (pos + 4 >= length) {
14546972b2d825a2368706e7d2210559ca18fc2b332Sergio Giro            // encoded byte array  must be not less then 4 c
14646972b2d825a2368706e7d2210559ca18fc2b332Sergio Giro            throw new IllegalStateException("Unexpected end of DN: " + dn);
14746972b2d825a2368706e7d2210559ca18fc2b332Sergio Giro        }
14846972b2d825a2368706e7d2210559ca18fc2b332Sergio Giro
14946972b2d825a2368706e7d2210559ca18fc2b332Sergio Giro        beg = pos; // store '#' position
15046972b2d825a2368706e7d2210559ca18fc2b332Sergio Giro        pos++;
15146972b2d825a2368706e7d2210559ca18fc2b332Sergio Giro        while (true) {
15246972b2d825a2368706e7d2210559ca18fc2b332Sergio Giro
15346972b2d825a2368706e7d2210559ca18fc2b332Sergio Giro            // check for end of attribute value
15446972b2d825a2368706e7d2210559ca18fc2b332Sergio Giro            // looks for space and component separators
15546972b2d825a2368706e7d2210559ca18fc2b332Sergio Giro            if (pos == length || chars[pos] == '+' || chars[pos] == ','
15646972b2d825a2368706e7d2210559ca18fc2b332Sergio Giro                    || chars[pos] == ';') {
15746972b2d825a2368706e7d2210559ca18fc2b332Sergio Giro                end = pos;
15846972b2d825a2368706e7d2210559ca18fc2b332Sergio Giro                break;
15946972b2d825a2368706e7d2210559ca18fc2b332Sergio Giro            }
16046972b2d825a2368706e7d2210559ca18fc2b332Sergio Giro
16146972b2d825a2368706e7d2210559ca18fc2b332Sergio Giro            if (chars[pos] == ' ') {
16246972b2d825a2368706e7d2210559ca18fc2b332Sergio Giro                end = pos;
16346972b2d825a2368706e7d2210559ca18fc2b332Sergio Giro                pos++;
16446972b2d825a2368706e7d2210559ca18fc2b332Sergio Giro                // skip trailing space chars before comma or semicolon.
16546972b2d825a2368706e7d2210559ca18fc2b332Sergio Giro                // (compatibility with RFC 1779)
16646972b2d825a2368706e7d2210559ca18fc2b332Sergio Giro                for (; pos < length && chars[pos] == ' '; pos++) {
16746972b2d825a2368706e7d2210559ca18fc2b332Sergio Giro                }
16846972b2d825a2368706e7d2210559ca18fc2b332Sergio Giro                break;
16946972b2d825a2368706e7d2210559ca18fc2b332Sergio Giro            } else if (chars[pos] >= 'A' && chars[pos] <= 'F') {
17046972b2d825a2368706e7d2210559ca18fc2b332Sergio Giro                chars[pos] += 32; //to low case
17146972b2d825a2368706e7d2210559ca18fc2b332Sergio Giro            }
17246972b2d825a2368706e7d2210559ca18fc2b332Sergio Giro
17346972b2d825a2368706e7d2210559ca18fc2b332Sergio Giro            pos++;
17446972b2d825a2368706e7d2210559ca18fc2b332Sergio Giro        }
17546972b2d825a2368706e7d2210559ca18fc2b332Sergio Giro
17646972b2d825a2368706e7d2210559ca18fc2b332Sergio Giro        // verify length of hex string
17746972b2d825a2368706e7d2210559ca18fc2b332Sergio Giro        // encoded byte array  must be not less then 4 and must be even number
17846972b2d825a2368706e7d2210559ca18fc2b332Sergio Giro        int hexLen = end - beg; // skip first '#' char
17946972b2d825a2368706e7d2210559ca18fc2b332Sergio Giro        if (hexLen < 5 || (hexLen & 1) == 0) {
18046972b2d825a2368706e7d2210559ca18fc2b332Sergio Giro            throw new IllegalStateException("Unexpected end of DN: " + dn);
18146972b2d825a2368706e7d2210559ca18fc2b332Sergio Giro        }
18246972b2d825a2368706e7d2210559ca18fc2b332Sergio Giro
18346972b2d825a2368706e7d2210559ca18fc2b332Sergio Giro        // get byte encoding from string representation
18446972b2d825a2368706e7d2210559ca18fc2b332Sergio Giro        byte[] encoded = new byte[hexLen / 2];
18546972b2d825a2368706e7d2210559ca18fc2b332Sergio Giro        for (int i = 0, p = beg + 1; i < encoded.length; p += 2, i++) {
18646972b2d825a2368706e7d2210559ca18fc2b332Sergio Giro            encoded[i] = (byte) getByte(p);
18746972b2d825a2368706e7d2210559ca18fc2b332Sergio Giro        }
18846972b2d825a2368706e7d2210559ca18fc2b332Sergio Giro
18946972b2d825a2368706e7d2210559ca18fc2b332Sergio Giro        return new String(chars, beg, hexLen);
19046972b2d825a2368706e7d2210559ca18fc2b332Sergio Giro    }
19146972b2d825a2368706e7d2210559ca18fc2b332Sergio Giro
19246972b2d825a2368706e7d2210559ca18fc2b332Sergio Giro    // gets string attribute value: *( stringchar / pair )
19346972b2d825a2368706e7d2210559ca18fc2b332Sergio Giro    private String escapedAV() {
19446972b2d825a2368706e7d2210559ca18fc2b332Sergio Giro        beg = pos;
19546972b2d825a2368706e7d2210559ca18fc2b332Sergio Giro        end = pos;
19646972b2d825a2368706e7d2210559ca18fc2b332Sergio Giro        while (true) {
19746972b2d825a2368706e7d2210559ca18fc2b332Sergio Giro            if (pos >= length) {
19846972b2d825a2368706e7d2210559ca18fc2b332Sergio Giro                // the end of DN has been found
19946972b2d825a2368706e7d2210559ca18fc2b332Sergio Giro                return new String(chars, beg, end - beg);
20046972b2d825a2368706e7d2210559ca18fc2b332Sergio Giro            }
20146972b2d825a2368706e7d2210559ca18fc2b332Sergio Giro
20246972b2d825a2368706e7d2210559ca18fc2b332Sergio Giro            switch (chars[pos]) {
20346972b2d825a2368706e7d2210559ca18fc2b332Sergio Giro            case '+':
20446972b2d825a2368706e7d2210559ca18fc2b332Sergio Giro            case ',':
20546972b2d825a2368706e7d2210559ca18fc2b332Sergio Giro            case ';':
20646972b2d825a2368706e7d2210559ca18fc2b332Sergio Giro                // separator char has been found
20746972b2d825a2368706e7d2210559ca18fc2b332Sergio Giro                return new String(chars, beg, end - beg);
20846972b2d825a2368706e7d2210559ca18fc2b332Sergio Giro            case '\\':
20946972b2d825a2368706e7d2210559ca18fc2b332Sergio Giro                // escaped char
21046972b2d825a2368706e7d2210559ca18fc2b332Sergio Giro                chars[end++] = getEscaped();
21146972b2d825a2368706e7d2210559ca18fc2b332Sergio Giro                pos++;
21246972b2d825a2368706e7d2210559ca18fc2b332Sergio Giro                break;
21346972b2d825a2368706e7d2210559ca18fc2b332Sergio Giro            case ' ':
21446972b2d825a2368706e7d2210559ca18fc2b332Sergio Giro                // need to figure out whether space defines
21546972b2d825a2368706e7d2210559ca18fc2b332Sergio Giro                // the end of attribute value or not
21646972b2d825a2368706e7d2210559ca18fc2b332Sergio Giro                cur = end;
21746972b2d825a2368706e7d2210559ca18fc2b332Sergio Giro
21846972b2d825a2368706e7d2210559ca18fc2b332Sergio Giro                pos++;
21946972b2d825a2368706e7d2210559ca18fc2b332Sergio Giro                chars[end++] = ' ';
22046972b2d825a2368706e7d2210559ca18fc2b332Sergio Giro
22146972b2d825a2368706e7d2210559ca18fc2b332Sergio Giro                for (; pos < length && chars[pos] == ' '; pos++) {
22246972b2d825a2368706e7d2210559ca18fc2b332Sergio Giro                    chars[end++] = ' ';
22346972b2d825a2368706e7d2210559ca18fc2b332Sergio Giro                }
22446972b2d825a2368706e7d2210559ca18fc2b332Sergio Giro                if (pos == length || chars[pos] == ',' || chars[pos] == '+'
22546972b2d825a2368706e7d2210559ca18fc2b332Sergio Giro                        || chars[pos] == ';') {
22646972b2d825a2368706e7d2210559ca18fc2b332Sergio Giro                    // separator char or the end of DN has been found
22746972b2d825a2368706e7d2210559ca18fc2b332Sergio Giro                    return new String(chars, beg, cur - beg);
22846972b2d825a2368706e7d2210559ca18fc2b332Sergio Giro                }
22946972b2d825a2368706e7d2210559ca18fc2b332Sergio Giro                break;
23046972b2d825a2368706e7d2210559ca18fc2b332Sergio Giro            default:
23146972b2d825a2368706e7d2210559ca18fc2b332Sergio Giro                chars[end++] = chars[pos];
23246972b2d825a2368706e7d2210559ca18fc2b332Sergio Giro                pos++;
23346972b2d825a2368706e7d2210559ca18fc2b332Sergio Giro            }
23446972b2d825a2368706e7d2210559ca18fc2b332Sergio Giro        }
23546972b2d825a2368706e7d2210559ca18fc2b332Sergio Giro    }
23646972b2d825a2368706e7d2210559ca18fc2b332Sergio Giro
23746972b2d825a2368706e7d2210559ca18fc2b332Sergio Giro    // returns escaped char
23846972b2d825a2368706e7d2210559ca18fc2b332Sergio Giro    private char getEscaped() {
23946972b2d825a2368706e7d2210559ca18fc2b332Sergio Giro        pos++;
24046972b2d825a2368706e7d2210559ca18fc2b332Sergio Giro        if (pos == length) {
24146972b2d825a2368706e7d2210559ca18fc2b332Sergio Giro            throw new IllegalStateException("Unexpected end of DN: " + dn);
24246972b2d825a2368706e7d2210559ca18fc2b332Sergio Giro        }
24346972b2d825a2368706e7d2210559ca18fc2b332Sergio Giro
24446972b2d825a2368706e7d2210559ca18fc2b332Sergio Giro        switch (chars[pos]) {
24546972b2d825a2368706e7d2210559ca18fc2b332Sergio Giro        case '"':
24646972b2d825a2368706e7d2210559ca18fc2b332Sergio Giro        case '\\':
24746972b2d825a2368706e7d2210559ca18fc2b332Sergio Giro        case ',':
24846972b2d825a2368706e7d2210559ca18fc2b332Sergio Giro        case '=':
24946972b2d825a2368706e7d2210559ca18fc2b332Sergio Giro        case '+':
25046972b2d825a2368706e7d2210559ca18fc2b332Sergio Giro        case '<':
25146972b2d825a2368706e7d2210559ca18fc2b332Sergio Giro        case '>':
25246972b2d825a2368706e7d2210559ca18fc2b332Sergio Giro        case '#':
25346972b2d825a2368706e7d2210559ca18fc2b332Sergio Giro        case ';':
25446972b2d825a2368706e7d2210559ca18fc2b332Sergio Giro        case ' ':
25546972b2d825a2368706e7d2210559ca18fc2b332Sergio Giro        case '*':
25646972b2d825a2368706e7d2210559ca18fc2b332Sergio Giro        case '%':
25746972b2d825a2368706e7d2210559ca18fc2b332Sergio Giro        case '_':
25846972b2d825a2368706e7d2210559ca18fc2b332Sergio Giro            //FIXME: escaping is allowed only for leading or trailing space char
25946972b2d825a2368706e7d2210559ca18fc2b332Sergio Giro            return chars[pos];
26046972b2d825a2368706e7d2210559ca18fc2b332Sergio Giro        default:
26146972b2d825a2368706e7d2210559ca18fc2b332Sergio Giro            // RFC doesn't explicitly say that escaped hex pair is
26246972b2d825a2368706e7d2210559ca18fc2b332Sergio Giro            // interpreted as UTF-8 char. It only contains an example of such DN.
26346972b2d825a2368706e7d2210559ca18fc2b332Sergio Giro            return getUTF8();
26446972b2d825a2368706e7d2210559ca18fc2b332Sergio Giro        }
26546972b2d825a2368706e7d2210559ca18fc2b332Sergio Giro    }
26646972b2d825a2368706e7d2210559ca18fc2b332Sergio Giro
26746972b2d825a2368706e7d2210559ca18fc2b332Sergio Giro    // decodes UTF-8 char
26846972b2d825a2368706e7d2210559ca18fc2b332Sergio Giro    // see http://www.unicode.org for UTF-8 bit distribution table
26946972b2d825a2368706e7d2210559ca18fc2b332Sergio Giro    private char getUTF8() {
27046972b2d825a2368706e7d2210559ca18fc2b332Sergio Giro        int res = getByte(pos);
27146972b2d825a2368706e7d2210559ca18fc2b332Sergio Giro        pos++; //FIXME tmp
27246972b2d825a2368706e7d2210559ca18fc2b332Sergio Giro
27346972b2d825a2368706e7d2210559ca18fc2b332Sergio Giro        if (res < 128) { // one byte: 0-7F
27446972b2d825a2368706e7d2210559ca18fc2b332Sergio Giro            return (char) res;
27546972b2d825a2368706e7d2210559ca18fc2b332Sergio Giro        } else if (res >= 192 && res <= 247) {
27646972b2d825a2368706e7d2210559ca18fc2b332Sergio Giro
27746972b2d825a2368706e7d2210559ca18fc2b332Sergio Giro            int count;
27846972b2d825a2368706e7d2210559ca18fc2b332Sergio Giro            if (res <= 223) { // two bytes: C0-DF
27946972b2d825a2368706e7d2210559ca18fc2b332Sergio Giro                count = 1;
28046972b2d825a2368706e7d2210559ca18fc2b332Sergio Giro                res = res & 0x1F;
28146972b2d825a2368706e7d2210559ca18fc2b332Sergio Giro            } else if (res <= 239) { // three bytes: E0-EF
28246972b2d825a2368706e7d2210559ca18fc2b332Sergio Giro                count = 2;
28346972b2d825a2368706e7d2210559ca18fc2b332Sergio Giro                res = res & 0x0F;
28446972b2d825a2368706e7d2210559ca18fc2b332Sergio Giro            } else { // four bytes: F0-F7
28546972b2d825a2368706e7d2210559ca18fc2b332Sergio Giro                count = 3;
28646972b2d825a2368706e7d2210559ca18fc2b332Sergio Giro                res = res & 0x07;
28746972b2d825a2368706e7d2210559ca18fc2b332Sergio Giro            }
28846972b2d825a2368706e7d2210559ca18fc2b332Sergio Giro
28946972b2d825a2368706e7d2210559ca18fc2b332Sergio Giro            int b;
29046972b2d825a2368706e7d2210559ca18fc2b332Sergio Giro            for (int i = 0; i < count; i++) {
29146972b2d825a2368706e7d2210559ca18fc2b332Sergio Giro                pos++;
29246972b2d825a2368706e7d2210559ca18fc2b332Sergio Giro                if (pos == length || chars[pos] != '\\') {
29346972b2d825a2368706e7d2210559ca18fc2b332Sergio Giro                    return 0x3F; //FIXME failed to decode UTF-8 char - return '?'
29446972b2d825a2368706e7d2210559ca18fc2b332Sergio Giro                }
29546972b2d825a2368706e7d2210559ca18fc2b332Sergio Giro                pos++;
29646972b2d825a2368706e7d2210559ca18fc2b332Sergio Giro
29746972b2d825a2368706e7d2210559ca18fc2b332Sergio Giro                b = getByte(pos);
29846972b2d825a2368706e7d2210559ca18fc2b332Sergio Giro                pos++; //FIXME tmp
29946972b2d825a2368706e7d2210559ca18fc2b332Sergio Giro                if ((b & 0xC0) != 0x80) {
30046972b2d825a2368706e7d2210559ca18fc2b332Sergio Giro                    return 0x3F; //FIXME failed to decode UTF-8 char - return '?'
30146972b2d825a2368706e7d2210559ca18fc2b332Sergio Giro                }
30246972b2d825a2368706e7d2210559ca18fc2b332Sergio Giro
30346972b2d825a2368706e7d2210559ca18fc2b332Sergio Giro                res = (res << 6) + (b & 0x3F);
30446972b2d825a2368706e7d2210559ca18fc2b332Sergio Giro            }
30546972b2d825a2368706e7d2210559ca18fc2b332Sergio Giro            return (char) res;
30646972b2d825a2368706e7d2210559ca18fc2b332Sergio Giro        } else {
30746972b2d825a2368706e7d2210559ca18fc2b332Sergio Giro            return 0x3F; //FIXME failed to decode UTF-8 char - return '?'
30846972b2d825a2368706e7d2210559ca18fc2b332Sergio Giro        }
30946972b2d825a2368706e7d2210559ca18fc2b332Sergio Giro    }
31046972b2d825a2368706e7d2210559ca18fc2b332Sergio Giro
31146972b2d825a2368706e7d2210559ca18fc2b332Sergio Giro    // Returns byte representation of a char pair
31246972b2d825a2368706e7d2210559ca18fc2b332Sergio Giro    // The char pair is composed of DN char in
31346972b2d825a2368706e7d2210559ca18fc2b332Sergio Giro    // specified 'position' and the next char
31446972b2d825a2368706e7d2210559ca18fc2b332Sergio Giro    // According to BNF syntax:
31546972b2d825a2368706e7d2210559ca18fc2b332Sergio Giro    // hexchar    = DIGIT / "A" / "B" / "C" / "D" / "E" / "F"
31646972b2d825a2368706e7d2210559ca18fc2b332Sergio Giro    //                    / "a" / "b" / "c" / "d" / "e" / "f"
31746972b2d825a2368706e7d2210559ca18fc2b332Sergio Giro    private int getByte(int position) {
31846972b2d825a2368706e7d2210559ca18fc2b332Sergio Giro        if (position + 1 >= length) {
31946972b2d825a2368706e7d2210559ca18fc2b332Sergio Giro            throw new IllegalStateException("Malformed DN: " + dn);
32046972b2d825a2368706e7d2210559ca18fc2b332Sergio Giro        }
32146972b2d825a2368706e7d2210559ca18fc2b332Sergio Giro
32246972b2d825a2368706e7d2210559ca18fc2b332Sergio Giro        int b1, b2;
32346972b2d825a2368706e7d2210559ca18fc2b332Sergio Giro
32446972b2d825a2368706e7d2210559ca18fc2b332Sergio Giro        b1 = chars[position];
32546972b2d825a2368706e7d2210559ca18fc2b332Sergio Giro        if (b1 >= '0' && b1 <= '9') {
32646972b2d825a2368706e7d2210559ca18fc2b332Sergio Giro            b1 = b1 - '0';
32746972b2d825a2368706e7d2210559ca18fc2b332Sergio Giro        } else if (b1 >= 'a' && b1 <= 'f') {
32846972b2d825a2368706e7d2210559ca18fc2b332Sergio Giro            b1 = b1 - 87; // 87 = 'a' - 10
32946972b2d825a2368706e7d2210559ca18fc2b332Sergio Giro        } else if (b1 >= 'A' && b1 <= 'F') {
33046972b2d825a2368706e7d2210559ca18fc2b332Sergio Giro            b1 = b1 - 55; // 55 = 'A' - 10
33146972b2d825a2368706e7d2210559ca18fc2b332Sergio Giro        } else {
33246972b2d825a2368706e7d2210559ca18fc2b332Sergio Giro            throw new IllegalStateException("Malformed DN: " + dn);
33346972b2d825a2368706e7d2210559ca18fc2b332Sergio Giro        }
33446972b2d825a2368706e7d2210559ca18fc2b332Sergio Giro
33546972b2d825a2368706e7d2210559ca18fc2b332Sergio Giro        b2 = chars[position + 1];
33646972b2d825a2368706e7d2210559ca18fc2b332Sergio Giro        if (b2 >= '0' && b2 <= '9') {
33746972b2d825a2368706e7d2210559ca18fc2b332Sergio Giro            b2 = b2 - '0';
33846972b2d825a2368706e7d2210559ca18fc2b332Sergio Giro        } else if (b2 >= 'a' && b2 <= 'f') {
33946972b2d825a2368706e7d2210559ca18fc2b332Sergio Giro            b2 = b2 - 87; // 87 = 'a' - 10
34046972b2d825a2368706e7d2210559ca18fc2b332Sergio Giro        } else if (b2 >= 'A' && b2 <= 'F') {
34146972b2d825a2368706e7d2210559ca18fc2b332Sergio Giro            b2 = b2 - 55; // 55 = 'A' - 10
34246972b2d825a2368706e7d2210559ca18fc2b332Sergio Giro        } else {
34346972b2d825a2368706e7d2210559ca18fc2b332Sergio Giro            throw new IllegalStateException("Malformed DN: " + dn);
34446972b2d825a2368706e7d2210559ca18fc2b332Sergio Giro        }
34546972b2d825a2368706e7d2210559ca18fc2b332Sergio Giro
34646972b2d825a2368706e7d2210559ca18fc2b332Sergio Giro        return (b1 << 4) + b2;
34746972b2d825a2368706e7d2210559ca18fc2b332Sergio Giro    }
34846972b2d825a2368706e7d2210559ca18fc2b332Sergio Giro
34946972b2d825a2368706e7d2210559ca18fc2b332Sergio Giro    /**
35046972b2d825a2368706e7d2210559ca18fc2b332Sergio Giro     * Parses the DN and returns the most significant attribute value
35146972b2d825a2368706e7d2210559ca18fc2b332Sergio Giro     * for an attribute type, or null if none found.
35246972b2d825a2368706e7d2210559ca18fc2b332Sergio Giro     *
35346972b2d825a2368706e7d2210559ca18fc2b332Sergio Giro     * @param attributeType attribute type to look for (e.g. "ca")
35446972b2d825a2368706e7d2210559ca18fc2b332Sergio Giro     */
35546972b2d825a2368706e7d2210559ca18fc2b332Sergio Giro    public String findMostSpecific(String attributeType) {
35646972b2d825a2368706e7d2210559ca18fc2b332Sergio Giro        // Initialize internal state.
35746972b2d825a2368706e7d2210559ca18fc2b332Sergio Giro        pos = 0;
35846972b2d825a2368706e7d2210559ca18fc2b332Sergio Giro        beg = 0;
35946972b2d825a2368706e7d2210559ca18fc2b332Sergio Giro        end = 0;
36046972b2d825a2368706e7d2210559ca18fc2b332Sergio Giro        cur = 0;
36146972b2d825a2368706e7d2210559ca18fc2b332Sergio Giro        chars = dn.toCharArray();
36246972b2d825a2368706e7d2210559ca18fc2b332Sergio Giro
36346972b2d825a2368706e7d2210559ca18fc2b332Sergio Giro        String attType = nextAT();
36446972b2d825a2368706e7d2210559ca18fc2b332Sergio Giro        if (attType == null) {
36546972b2d825a2368706e7d2210559ca18fc2b332Sergio Giro            return null;
36646972b2d825a2368706e7d2210559ca18fc2b332Sergio Giro        }
36746972b2d825a2368706e7d2210559ca18fc2b332Sergio Giro        while (true) {
36846972b2d825a2368706e7d2210559ca18fc2b332Sergio Giro            String attValue = "";
36946972b2d825a2368706e7d2210559ca18fc2b332Sergio Giro
37046972b2d825a2368706e7d2210559ca18fc2b332Sergio Giro            if (pos == length) {
37146972b2d825a2368706e7d2210559ca18fc2b332Sergio Giro                return null;
37246972b2d825a2368706e7d2210559ca18fc2b332Sergio Giro            }
37346972b2d825a2368706e7d2210559ca18fc2b332Sergio Giro
37446972b2d825a2368706e7d2210559ca18fc2b332Sergio Giro            switch (chars[pos]) {
37546972b2d825a2368706e7d2210559ca18fc2b332Sergio Giro            case '"':
37646972b2d825a2368706e7d2210559ca18fc2b332Sergio Giro                attValue = quotedAV();
37746972b2d825a2368706e7d2210559ca18fc2b332Sergio Giro                break;
37846972b2d825a2368706e7d2210559ca18fc2b332Sergio Giro            case '#':
37946972b2d825a2368706e7d2210559ca18fc2b332Sergio Giro                attValue = hexAV();
38046972b2d825a2368706e7d2210559ca18fc2b332Sergio Giro                break;
38146972b2d825a2368706e7d2210559ca18fc2b332Sergio Giro            case '+':
38246972b2d825a2368706e7d2210559ca18fc2b332Sergio Giro            case ',':
38346972b2d825a2368706e7d2210559ca18fc2b332Sergio Giro            case ';': // compatibility with RFC 1779: semicolon can separate RDNs
38446972b2d825a2368706e7d2210559ca18fc2b332Sergio Giro                //empty attribute value
38546972b2d825a2368706e7d2210559ca18fc2b332Sergio Giro                break;
38646972b2d825a2368706e7d2210559ca18fc2b332Sergio Giro            default:
38746972b2d825a2368706e7d2210559ca18fc2b332Sergio Giro                attValue = escapedAV();
38846972b2d825a2368706e7d2210559ca18fc2b332Sergio Giro            }
38946972b2d825a2368706e7d2210559ca18fc2b332Sergio Giro
39046972b2d825a2368706e7d2210559ca18fc2b332Sergio Giro            // Values are ordered from most specific to least specific
39146972b2d825a2368706e7d2210559ca18fc2b332Sergio Giro            // due to the RFC2253 formatting. So take the first match
39246972b2d825a2368706e7d2210559ca18fc2b332Sergio Giro            // we see.
39346972b2d825a2368706e7d2210559ca18fc2b332Sergio Giro            if (attributeType.equalsIgnoreCase(attType)) {
39446972b2d825a2368706e7d2210559ca18fc2b332Sergio Giro                return attValue;
39546972b2d825a2368706e7d2210559ca18fc2b332Sergio Giro            }
39646972b2d825a2368706e7d2210559ca18fc2b332Sergio Giro
39746972b2d825a2368706e7d2210559ca18fc2b332Sergio Giro            if (pos >= length) {
39846972b2d825a2368706e7d2210559ca18fc2b332Sergio Giro                return null;
39946972b2d825a2368706e7d2210559ca18fc2b332Sergio Giro            }
40046972b2d825a2368706e7d2210559ca18fc2b332Sergio Giro
40146972b2d825a2368706e7d2210559ca18fc2b332Sergio Giro            if (chars[pos] == ',' || chars[pos] == ';') {
40246972b2d825a2368706e7d2210559ca18fc2b332Sergio Giro            } else if (chars[pos] != '+') {
40346972b2d825a2368706e7d2210559ca18fc2b332Sergio Giro                throw new IllegalStateException("Malformed DN: " + dn);
40446972b2d825a2368706e7d2210559ca18fc2b332Sergio Giro            }
40546972b2d825a2368706e7d2210559ca18fc2b332Sergio Giro
40646972b2d825a2368706e7d2210559ca18fc2b332Sergio Giro            pos++;
40746972b2d825a2368706e7d2210559ca18fc2b332Sergio Giro            attType = nextAT();
40846972b2d825a2368706e7d2210559ca18fc2b332Sergio Giro            if (attType == null) {
40946972b2d825a2368706e7d2210559ca18fc2b332Sergio Giro                throw new IllegalStateException("Malformed DN: " + dn);
41046972b2d825a2368706e7d2210559ca18fc2b332Sergio Giro            }
41146972b2d825a2368706e7d2210559ca18fc2b332Sergio Giro        }
41246972b2d825a2368706e7d2210559ca18fc2b332Sergio Giro    }
41346972b2d825a2368706e7d2210559ca18fc2b332Sergio Giro
41446972b2d825a2368706e7d2210559ca18fc2b332Sergio Giro    /**
41546972b2d825a2368706e7d2210559ca18fc2b332Sergio Giro     * Parses the DN and returns all values for an attribute type, in
41646972b2d825a2368706e7d2210559ca18fc2b332Sergio Giro     * the order of decreasing significance (most significant first).
41746972b2d825a2368706e7d2210559ca18fc2b332Sergio Giro     *
41846972b2d825a2368706e7d2210559ca18fc2b332Sergio Giro     * @param attributeType attribute type to look for (e.g. "ca")
41946972b2d825a2368706e7d2210559ca18fc2b332Sergio Giro     */
42046972b2d825a2368706e7d2210559ca18fc2b332Sergio Giro    public List<String> getAllMostSpecificFirst(String attributeType) {
42146972b2d825a2368706e7d2210559ca18fc2b332Sergio Giro        // Initialize internal state.
42246972b2d825a2368706e7d2210559ca18fc2b332Sergio Giro        pos = 0;
42346972b2d825a2368706e7d2210559ca18fc2b332Sergio Giro        beg = 0;
42446972b2d825a2368706e7d2210559ca18fc2b332Sergio Giro        end = 0;
42546972b2d825a2368706e7d2210559ca18fc2b332Sergio Giro        cur = 0;
42646972b2d825a2368706e7d2210559ca18fc2b332Sergio Giro        chars = dn.toCharArray();
42746972b2d825a2368706e7d2210559ca18fc2b332Sergio Giro        List<String> result = Collections.emptyList();
42846972b2d825a2368706e7d2210559ca18fc2b332Sergio Giro
42946972b2d825a2368706e7d2210559ca18fc2b332Sergio Giro        String attType = nextAT();
43046972b2d825a2368706e7d2210559ca18fc2b332Sergio Giro        if (attType == null) {
43146972b2d825a2368706e7d2210559ca18fc2b332Sergio Giro            return result;
43246972b2d825a2368706e7d2210559ca18fc2b332Sergio Giro        }
43346972b2d825a2368706e7d2210559ca18fc2b332Sergio Giro        while (pos < length) {
43446972b2d825a2368706e7d2210559ca18fc2b332Sergio Giro            String attValue = "";
43546972b2d825a2368706e7d2210559ca18fc2b332Sergio Giro
43646972b2d825a2368706e7d2210559ca18fc2b332Sergio Giro            switch (chars[pos]) {
43746972b2d825a2368706e7d2210559ca18fc2b332Sergio Giro            case '"':
43846972b2d825a2368706e7d2210559ca18fc2b332Sergio Giro                attValue = quotedAV();
43946972b2d825a2368706e7d2210559ca18fc2b332Sergio Giro                break;
44046972b2d825a2368706e7d2210559ca18fc2b332Sergio Giro            case '#':
44146972b2d825a2368706e7d2210559ca18fc2b332Sergio Giro                attValue = hexAV();
44246972b2d825a2368706e7d2210559ca18fc2b332Sergio Giro                break;
44346972b2d825a2368706e7d2210559ca18fc2b332Sergio Giro            case '+':
44446972b2d825a2368706e7d2210559ca18fc2b332Sergio Giro            case ',':
44546972b2d825a2368706e7d2210559ca18fc2b332Sergio Giro            case ';': // compatibility with RFC 1779: semicolon can separate RDNs
44646972b2d825a2368706e7d2210559ca18fc2b332Sergio Giro                //empty attribute value
44746972b2d825a2368706e7d2210559ca18fc2b332Sergio Giro                break;
44846972b2d825a2368706e7d2210559ca18fc2b332Sergio Giro            default:
44946972b2d825a2368706e7d2210559ca18fc2b332Sergio Giro                attValue = escapedAV();
45046972b2d825a2368706e7d2210559ca18fc2b332Sergio Giro            }
45146972b2d825a2368706e7d2210559ca18fc2b332Sergio Giro
45246972b2d825a2368706e7d2210559ca18fc2b332Sergio Giro            // Values are ordered from most specific to least specific
45346972b2d825a2368706e7d2210559ca18fc2b332Sergio Giro            // due to the RFC2253 formatting. So take the first match
45446972b2d825a2368706e7d2210559ca18fc2b332Sergio Giro            // we see.
45546972b2d825a2368706e7d2210559ca18fc2b332Sergio Giro            if (attributeType.equalsIgnoreCase(attType)) {
45646972b2d825a2368706e7d2210559ca18fc2b332Sergio Giro                if (result.isEmpty()) {
45746972b2d825a2368706e7d2210559ca18fc2b332Sergio Giro                    result = new ArrayList<String>();
45846972b2d825a2368706e7d2210559ca18fc2b332Sergio Giro                }
45946972b2d825a2368706e7d2210559ca18fc2b332Sergio Giro                result.add(attValue);
46046972b2d825a2368706e7d2210559ca18fc2b332Sergio Giro            }
46146972b2d825a2368706e7d2210559ca18fc2b332Sergio Giro
46246972b2d825a2368706e7d2210559ca18fc2b332Sergio Giro            if (pos >= length) {
46346972b2d825a2368706e7d2210559ca18fc2b332Sergio Giro                break;
46446972b2d825a2368706e7d2210559ca18fc2b332Sergio Giro            }
46546972b2d825a2368706e7d2210559ca18fc2b332Sergio Giro
46646972b2d825a2368706e7d2210559ca18fc2b332Sergio Giro            if (chars[pos] == ',' || chars[pos] == ';') {
46746972b2d825a2368706e7d2210559ca18fc2b332Sergio Giro            } else if (chars[pos] != '+') {
46846972b2d825a2368706e7d2210559ca18fc2b332Sergio Giro                throw new IllegalStateException("Malformed DN: " + dn);
46946972b2d825a2368706e7d2210559ca18fc2b332Sergio Giro            }
47046972b2d825a2368706e7d2210559ca18fc2b332Sergio Giro
47146972b2d825a2368706e7d2210559ca18fc2b332Sergio Giro            pos++;
47246972b2d825a2368706e7d2210559ca18fc2b332Sergio Giro            attType = nextAT();
47346972b2d825a2368706e7d2210559ca18fc2b332Sergio Giro            if (attType == null) {
47446972b2d825a2368706e7d2210559ca18fc2b332Sergio Giro                throw new IllegalStateException("Malformed DN: " + dn);
47546972b2d825a2368706e7d2210559ca18fc2b332Sergio Giro            }
47646972b2d825a2368706e7d2210559ca18fc2b332Sergio Giro        }
47746972b2d825a2368706e7d2210559ca18fc2b332Sergio Giro
47846972b2d825a2368706e7d2210559ca18fc2b332Sergio Giro        return result;
47946972b2d825a2368706e7d2210559ca18fc2b332Sergio Giro    }
48046972b2d825a2368706e7d2210559ca18fc2b332Sergio Giro}
481