Changeset 7824 in josm


Ignore:
Timestamp:
2014-12-18T23:47:21+01:00 (10 years ago)
Author:
Don-vip
Message:

fix #10862 - proper validation of IDN (Internationalized Domain Name) URLs, both in their Unicode and ASCII form => patch of Apache DomainValidator routine

Location:
trunk
Files:
1 added
2 edited

Legend:

Unmodified
Added
Removed
  • trunk/src/org/openstreetmap/josm/data/validation/routines/DomainValidator.java

    r7801 r7824  
    4646 *     <li>{@link #isValidGenericTld} - validates generic TLDs
    4747 *         (<code>.com, .org</code>, etc.)</li>
     48 *     <li>{@link #isValidIdnTld} - validates IDN TLDs
     49 *         (<code>.xn--*</code>, etc.)</li>
    4850 *     <li>{@link #isValidCountryCodeTld} - validates country code TLDs
    4951 *         (<code>.us, .uk, .cn</code>, etc.)</li>
     
    6466    private static final String DOMAIN_LABEL_REGEX = "\\p{Alnum}(?>[\\p{Alnum}-]*\\p{Alnum})*";
    6567    private static final String TOP_LABEL_REGEX = "\\p{Alpha}{2,}";
     68    // JOSM PATCH BEGIN
     69    // See #10862 - IDN TLDs in ASCII form
     70    private static final String TOP_LABEL_IDN_REGEX = "(?:xn|XN)--\\p{Alnum}{2,}(?:-\\p{Alpha}{2,})?";
    6671    private static final String DOMAIN_NAME_REGEX =
    67             "^(?:" + DOMAIN_LABEL_REGEX + "\\.)+" + "(" + TOP_LABEL_REGEX + ")$";
     72            "^(?:" + DOMAIN_LABEL_REGEX + "\\.)+" + "(" + TOP_LABEL_REGEX + "|" + TOP_LABEL_IDN_REGEX + ")$";
     73    // JOSM PATCH END
    6874
    6975    private final boolean allowLocal;
     
    152158        return isValidInfrastructureTld(tld)
    153159                || isValidGenericTld(tld)
     160                || isValidIdnTld(tld)
    154161                || isValidCountryCodeTld(tld);
    155162    }
     
    175182    public boolean isValidGenericTld(String gTld) {
    176183        return Arrays.binarySearch(GENERIC_TLDS, chompLeadingDot(gTld.toLowerCase())) >= 0;
     184    }
     185
     186    /**
     187     * Returns true if the specified <code>String</code> matches any
     188     * IANA-defined IDN top-level domain. Leading dots are ignored
     189     * if present. The search is case-sensitive.
     190     * @param iTld the parameter to check for IDN TLD status
     191     * @return true if the parameter is an IDN TLD
     192     */
     193    public boolean isValidIdnTld(String iTld) {
     194        return Arrays.binarySearch(IDN_TLDS, chompLeadingDot(iTld.toUpperCase())) >= 0;
    177195    }
    178196
     
    634652    };
    635653
     654    // JOSM PATCH BEGIN
     655    // see #10862 - list of IDN TLDs taken from IANA on 2014-12-18
     656    private static final String[] IDN_TLDS = new String[] {
     657        "XN--1QQW23A",
     658        "XN--3BST00M",
     659        "XN--3DS443G",
     660        "XN--3E0B707E",
     661        "XN--45BRJ9C",
     662        "XN--45Q11C",
     663        "XN--4GBRIM",
     664        "XN--55QW42G",
     665        "XN--55QX5D",
     666        "XN--6FRZ82G",
     667        "XN--6QQ986B3XL",
     668        "XN--80ADXHKS",
     669        "XN--80AO21A",
     670        "XN--80ASEHDB",
     671        "XN--80ASWG",
     672        "XN--90A3AC",
     673        "XN--C1AVG",
     674        "XN--CG4BKI",
     675        "XN--CLCHC0EA0B2G2A9GCD",
     676        "XN--CZR694B",
     677        "XN--CZRS0T",
     678        "XN--CZRU2D",
     679        "XN--D1ACJ3B",
     680        "XN--D1ALF",
     681        "XN--FIQ228C5HS",
     682        "XN--FIQ64B",
     683        "XN--FIQS8S",
     684        "XN--FIQZ9S",
     685        "XN--FLW351E",
     686        "XN--FPCRJ9C3D",
     687        "XN--FZC2C9E2C",
     688        "XN--GECRJ9C",
     689        "XN--H2BRJ9C",
     690        "XN--HXT814E",
     691        "XN--I1B6B1A6A2E",
     692        "XN--IO0A7I",
     693        "XN--J1AMH",
     694        "XN--J6W193G",
     695        "XN--KPRW13D",
     696        "XN--KPRY57D",
     697        "XN--KPUT3I",
     698        "XN--L1ACC",
     699        "XN--LGBBAT1AD8J",
     700        "XN--MGB9AWBF",
     701        "XN--MGBA3A4F16A",
     702        "XN--MGBAAM7A8H",
     703        "XN--MGBAB2BD",
     704        "XN--MGBAYH7GPA",
     705        "XN--MGBBH1A71E",
     706        "XN--MGBC0A9AZCG",
     707        "XN--MGBERP4A5D4AR",
     708        "XN--MGBX4CD0AB",
     709        "XN--NGBC5AZD",
     710        "XN--NODE",
     711        "XN--NQV7F",
     712        "XN--NQV7FS00EMA",
     713        "XN--O3CW4H",
     714        "XN--OGBPF8FL",
     715        "XN--P1ACF",
     716        "XN--P1AI",
     717        "XN--PGBS0DH",
     718        "XN--Q9JYB4C",
     719        "XN--QCKA1PMC",
     720        "XN--RHQV96G",
     721        "XN--S9BRJ9C",
     722        "XN--SES554G",
     723        "XN--UNUP4Y",
     724        "XN--VERMGENSBERATER-CTB",
     725        "XN--VERMGENSBERATUNG-PWB",
     726        "XN--VHQUV",
     727        "XN--WGBH1C",
     728        "XN--WGBL6A",
     729        "XN--XHQ521B",
     730        "XN--XKC2AL3HYE2A",
     731        "XN--XKC2DL3A5EE0H",
     732        "XN--YFRO4I67O",
     733        "XN--YGBI2AMMX",
     734        "XN--ZFR164B",
     735    };
     736    // END JOSM PATCH
     737
    636738    private static final String[] COUNTRY_CODE_TLDS = new String[] {
    637739        "ac",                 // Ascension Island
     
    897999        Arrays.sort(COUNTRY_CODE_TLDS);
    8981000        Arrays.sort(GENERIC_TLDS);
     1001        Arrays.sort(IDN_TLDS);
    8991002        Arrays.sort(LOCAL_TLDS);
    9001003    }
  • trunk/src/org/openstreetmap/josm/data/validation/tests/InternetTags.java

    r7489 r7824  
    33
    44import static org.openstreetmap.josm.tools.I18n.tr;
     5
     6import java.net.IDN;
     7import java.util.regex.Pattern;
    58
    69import org.openstreetmap.josm.command.ChangePropertyCommand;
     
    2326public class InternetTags extends Test {
    2427
    25     protected static final int INVALID_URL = 3301;
    26     protected static final int INVALID_EMAIL = 3302;
     28    /** Error code for an invalid URL */
     29    public static final int INVALID_URL = 3301;
     30    /** Error code for an invalid e-mail */
     31    public static final int INVALID_EMAIL = 3302;
     32
     33    private static final Pattern ASCII_PATTERN = Pattern.compile("^\\p{ASCII}+$");
    2734
    2835    /**
    2936     * List of keys subject to URL validation.
    3037     */
    31     public static String[] URL_KEYS = new String[] {
     38    private static String[] URL_KEYS = new String[] {
    3239        "url", "source:url",
    3340        "website", "contact:website", "heritage:website", "source:website"
     
    3744     * List of keys subject to email validation.
    3845     */
    39     public static String[] EMAIL_KEYS = new String[] {
     46    private static String[] EMAIL_KEYS = new String[] {
    4047        "email", "contact:email"
    4148    };
     
    4855    }
    4956
     57    /**
     58     * Potentially validates a given primitive key against a given validator.
     59     * @param p The OSM primitive to test
     60     * @param k The key to validate
     61     * @param keys The list of keys to check. If {@code k} is not inside this collection, do nothing
     62     * @param validator The validator to run if {@code k} is inside {@code keys}
     63     * @param code The error code to set if the validation fails
     64     * @return {@code true} if the validation fails. In this case, a new error has been created.
     65     */
    5066    private boolean doTest(OsmPrimitive p, String k, String[] keys, AbstractValidator validator, int code) {
    5167        for (String i : keys) {
    5268            if (i.equals(k)) {
    53                 if (!validator.isValid(p.get(k))) {
    54                     TestError error;
    55                     String msg = tr("''{0}'': {1}", k, validator.getErrorMessage());
    56                     String fix = validator.getFix();
    57                     if (fix != null) {
    58                         error = new FixableTestError(this, Severity.WARNING, msg, code, p,
    59                                 new ChangePropertyCommand(p, k, fix));
    60                     } else {
    61                         error = new TestError(this, Severity.WARNING, msg, code, p);
    62                     }
    63                     return errors.add(error);
     69                TestError error = validateTag(p, k, validator, code);
     70                if (error != null) {
     71                    errors.add(error);
    6472                }
    6573                break;
     
    6977    }
    7078
     79    /**
     80     * Validates a given primitive tag against a given validator.
     81     * @param p The OSM primitive to test
     82     * @param k The key to validate
     83     * @param validator The validator to run
     84     * @param code The error code to set if the validation fails
     85     * @return The error if the validation fails, {@code null} otherwise
     86     * @since 7824
     87     */
     88    public TestError validateTag(OsmPrimitive p, String k, AbstractValidator validator, int code) {
     89        TestError error = doValidateTag(p, k, null, validator, code);
     90        if (error != null) {
     91            // Workaround to https://issues.apache.org/jira/browse/VALIDATOR-290
     92            // Apache Commons Validator 1.4.1-SNAPSHOT does not support yet IDN URLs
     93            // To remove if it gets fixed on Apache side
     94            String v = p.get(k);
     95            if (!ASCII_PATTERN.matcher(v).matches()) {
     96                try {
     97                    String protocol = "";
     98                    if (v.contains("://")) {
     99                        protocol = v.substring(0, v.indexOf("://")+3);
     100                    }
     101                    String domain = !protocol.isEmpty() ? v.substring(protocol.length(), v.length()) : v;
     102                    String ending = "";
     103                    if (domain.contains("/")) {
     104                        int idx = domain.indexOf("/");
     105                        ending = domain.substring(idx, domain.length());
     106                        domain = domain.substring(0, idx);
     107                    }
     108                    // Try to apply ToASCII algorithm
     109                    error = doValidateTag(p, k, protocol+IDN.toASCII(domain)+ending, validator, code);
     110                } catch (IllegalArgumentException e) {
     111                    error.setMessage(error.getMessage() +
     112                            tr(" URL cannot be converted to ASCII: {0}", e.getMessage()));
     113                }
     114            }
     115        }
     116        return error;
     117    }
     118
     119    /**
     120     * Validates a given primitive tag against a given validator.
     121     * @param p The OSM primitive to test
     122     * @param k The key to validate
     123     * @param v The value to validate. May be {@code null} to use {@code p.get(k)}
     124     * @param validator The validator to run
     125     * @param code The error code to set if the validation fails
     126     * @return The error if the validation fails, {@code null} otherwise
     127     */
     128    private TestError doValidateTag(OsmPrimitive p, String k, String v, AbstractValidator validator, int code) {
     129        TestError error = null;
     130        if (!validator.isValid(v != null ? v : p.get(k))) {
     131            String msg = tr("''{0}'': {1}", k, validator.getErrorMessage());
     132            String fix = validator.getFix();
     133            if (fix != null) {
     134                error = new FixableTestError(this, Severity.WARNING, msg, code, p,
     135                        new ChangePropertyCommand(p, k, fix));
     136            } else {
     137                error = new TestError(this, Severity.WARNING, msg, code, p);
     138            }
     139        }
     140        return error;
     141    }
     142
    71143    private void test(OsmPrimitive p) {
    72144        for (String k : p.keySet()) {
     145            // Test key against URL validator
    73146            if (!doTest(p, k, URL_KEYS, UrlValidator.getInstance(), INVALID_URL)) {
     147                // Test key against e-mail validator only if the URL validator did not fail
    74148                doTest(p, k, EMAIL_KEYS, EmailValidator.getInstance(), INVALID_EMAIL);
    75149            }
Note: See TracChangeset for help on using the changeset viewer.