Subversion Repositories javautils

Rev

Rev 3 | Rev 17 | Go to most recent revision | Blame | Compare with Previous | Last modification | View Log | RSS feed

  1. package de.viathinksoft.utils.mail.syntaxchecker;
  2.  
  3. import java.util.Arrays;
  4. import java.util.HashSet;
  5. import java.util.regex.Pattern;
  6.  
  7. import de.viathinksoft.utils.mail.EMailAddress;
  8.  
  9. /**
  10.  * This class is not stable. For a good syntax check, please use the classes of
  11.  * Dominic Sayers or Cal Henderson.
  12.  *
  13.  * @author Daniel Marschall
  14.  * @version 0.1
  15.  *
  16.  */
  17. public class MailSyntaxChecker {
  18.  
  19.         private static final String REGEX_IP = "\\b(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\b";
  20.  
  21.         // Führt eine Prüfung der E-Mail-Adresse gemäß SMTP-Spezifikation RFC 5321
  22.         // aus
  23.         private static final boolean CHECK_SMTP_SIZE_LIMITS = false;
  24.  
  25.         // Führt eine Prüfung der TLD gemäß IANA-Daten aus
  26.         private static final boolean CHECK_TLD_RECOGNIZED = true;
  27.  
  28.         // Führt eine DNS-Prüfung durch
  29.         private static final boolean CHECK_DNS = true;
  30.  
  31.         // http://data.iana.org/TLD/tlds-alpha-by-domain.txt
  32.         // Version 2010052500, Last Updated Tue May 25 14:07:02 2010 UTC
  33.         private static final HashSet<String> RECOGNIZED_TLDS_PUNYCODE = hmaker(new String[] {
  34.                         "AC", "AD", "AE", "AERO", "AF", "AG", "AI", "AL", "AM", "AN", "AO",
  35.                         "AQ", "AR", "ARPA", "AS", "ASIA", "AT", "AU", "AW", "AX", "AZ",
  36.                         "BA", "BB", "BD", "BE", "BF", "BG", "BH", "BI", "BIZ", "BJ", "BM",
  37.                         "BN", "BO", "BR", "BS", "BT", "BV", "BW", "BY", "BZ", "CA", "CAT",
  38.                         "CC", "CD", "CF", "CG", "CH", "CI", "CK", "CL", "CM", "CN", "CO",
  39.                         "COM", "COOP", "CR", "CU", "CV", "CX", "CY", "CZ", "DE", "DJ",
  40.                         "DK", "DM", "DO", "DZ", "EC", "EDU", "EE", "EG", "ER", "ES", "ET",
  41.                         "EU", "FI", "FJ", "FK", "FM", "FO", "FR", "GA", "GB", "GD", "GE",
  42.                         "GF", "GG", "GH", "GI", "GL", "GM", "GN", "GOV", "GP", "GQ", "GR",
  43.                         "GS", "GT", "GU", "GW", "GY", "HK", "HM", "HN", "HR", "HT", "HU",
  44.                         "ID", "IE", "IL", "IM", "IN", "INFO", "INT", "IO", "IQ", "IR",
  45.                         "IS", "IT", "JE", "JM", "JO", "JOBS", "JP", "KE", "KG", "KH", "KI",
  46.                         "KM", "KN", "KP", "KR", "KW", "KY", "KZ", "LA", "LB", "LC", "LI",
  47.                         "LK", "LR", "LS", "LT", "LU", "LV", "LY", "MA", "MC", "MD", "ME",
  48.                         "MG", "MH", "MIL", "MK", "ML", "MM", "MN", "MO", "MOBI", "MP",
  49.                         "MQ", "MR", "MS", "MT", "MU", "MUSEUM", "MV", "MW", "MX", "MY",
  50.                         "MZ", "NA", "NAME", "NC", "NE", "NET", "NF", "NG", "NI", "NL",
  51.                         "NO", "NP", "NR", "NU", "NZ", "OM", "ORG", "PA", "PE", "PF", "PG",
  52.                         "PH", "PK", "PL", "PM", "PN", "PR", "PRO", "PS", "PT", "PW", "PY",
  53.                         "QA", "RE", "RO", "RS", "RU", "RW", "SA", "SB", "SC", "SD", "SE",
  54.                         "SG", "SH", "SI", "SJ", "SK", "SL", "SM", "SN", "SO", "SR", "ST",
  55.                         "SU", "SV", "SY", "SZ", "TC", "TD", "TEL", "TF", "TG", "TH", "TJ",
  56.                         "TK", "TL", "TM", "TN", "TO", "TP", "TR", "TRAVEL", "TT", "TV",
  57.                         "TW", "TZ", "UA", "UG", "UK", "US", "UY", "UZ", "VA", "VC", "VE",
  58.                         "VG", "VI", "VN", "VU", "WF", "WS", "XN--0ZWM56D",
  59.                         "XN--11B5BS3A9AJ6G", "XN--80AKHBYKNJ4F", "XN--9T4B11YI5A",
  60.                         "XN--DEBA0AD", "XN--G6W251D", "XN--HGBK6AJ7F53BBA",
  61.                         "XN--HLCJ6AYA9ESC7A", "XN--JXALPDLP", "XN--KGBECHTV",
  62.                         "XN--MGBAAM7A8H", "XN--MGBERP4A5D4AR", "XN--P1AI", "XN--WGBH1C",
  63.                         "XN--ZCKZAH", "YE", "YT", "ZA", "ZM", "ZW", });
  64.  
  65.         private static boolean checkSmtpSizeLimits(EMailAddress email) {
  66.                 // RFC 5321: 4.5.3.1.1. Local-part Längenbegrenzung bei SMTP: 64
  67.                 // Byte
  68.                 // QUE: Soll das auch als Punicode-Variante geprüft werden?
  69.                 if ((email.getLocalPart().length() > 64)
  70.                                 || (email.getLocalPart().length() < 1)) {
  71.                         return false;
  72.                 }
  73.  
  74.                 // RFC 5321: 4.5.3.1.2. Domain-part Längenbegrenzung bei SMTP: 255
  75.                 // Byte
  76.                 if ((email.getDomainPartPunycode().length() > 255)
  77.                                 || (email.getDomainPartPunycode().length() < 1)) {
  78.                         return false;
  79.                 }
  80.  
  81.                 // RFC 5321: 4.5.3.1.5. Reply-Line Längenbegrenzung bei SMTP: 512
  82.                 // Byte. Laut
  83.                 // http://de.wikipedia.org/wiki/E-Mail-Adresse#L.C3.A4nge_der_E-Mail-Adresse
  84.                 // folgt daraus: Länge der MailAddresse ist 254 Bytes.
  85.                 if (email.getMailAddressPunycodedDomain().length() > 254) {
  86.                         return false;
  87.                 }
  88.  
  89.                 return true;
  90.         }
  91.  
  92.         private static boolean checkTldRecognized(EMailAddress email) {
  93.                 // TODO: Mailadressen sind aber auch als ...@[IP] gültig. Dann keine
  94.                 // TLD!
  95.                 return RECOGNIZED_TLDS_PUNYCODE.contains(email.getTldPunycode()
  96.                                 .toUpperCase());
  97.         }
  98.  
  99.         private static boolean preg_match(String regex, String data) {
  100.                 return Pattern.compile(regex).matcher(data).matches();
  101.         }
  102.  
  103.         private static boolean checkDns(String domainOrIP) {
  104.                 // TODO
  105.  
  106.                 return true;
  107.         }
  108.  
  109.         public static boolean isMailValid(String email) {
  110.                 return isMailValid(new EMailAddress(email));
  111.         }
  112.  
  113.         /**
  114.          * Checks if an E-Mail-Address is valid
  115.          *
  116.          * @param email
  117.          * @return
  118.          */
  119.         public static boolean isMailValid(EMailAddress email) {
  120.                 if (CHECK_SMTP_SIZE_LIMITS) {
  121.                         if (!checkSmtpSizeLimits(email))
  122.                                 return false;
  123.                 }
  124.  
  125.                 // Begin RFC-Checks
  126.  
  127.                 final String address = email.getMailAddressUnicode();
  128.                 final String localPart = email.getLocalPart();
  129.                 final String domainPart = email.getDomainPartPunycode();
  130.  
  131.                 // Weder localPart noch domainPart dürfen zwei aufeinanderfolgende
  132.                 // Punkte besitzen.
  133.  
  134.                 if (address.contains("..")) {
  135.                         return false;
  136.                 }
  137.  
  138.                 // localPart darf keine Punkte am Anfang oder Ende besitzen
  139.                
  140.                 if (localPart.length() == 0) {
  141.                         return false;
  142.                 }
  143.                 if (localPart.startsWith(".") || localPart.endsWith(".")) {
  144.                         return false;
  145.                 }
  146.  
  147.                 // domainPart darf keine Punkte am Anfang oder Ende besitzen
  148.  
  149.                 if (domainPart.startsWith(".") || domainPart.endsWith(".")) {
  150.                         return false;
  151.                 }
  152.  
  153.                 // domainPart prüfen
  154.  
  155.                 if (preg_match("^" + REGEX_IP + "$", domainPart)) {
  156.                         // domainPart is <IP>
  157.                         // QUE: Ist das überhaupt gemäß RFC gültig?
  158.  
  159.                         String ip = ""; // TODO
  160.  
  161.                         if (CHECK_DNS) {
  162.                                 if (!checkDns(ip))
  163.                                         return false;
  164.                         }
  165.                 } else if (preg_match("^\\[" + REGEX_IP + "\\]$", domainPart)) {
  166.                         // domainPart is [<IP>]
  167.  
  168.                         String ip = ""; // TODO
  169.  
  170.                         if (CHECK_DNS) {
  171.                                 if (!checkDns(ip))
  172.                                         return false;
  173.                         }
  174.                 } else {
  175.                         if (!preg_match("^[A-Za-z0-9\\-\\.]+$", domainPart)) {
  176.                                 return false;
  177.                         }
  178.  
  179.                         if (CHECK_TLD_RECOGNIZED) {
  180.                                 if (!checkTldRecognized(email))
  181.                                         return false;
  182.                         }
  183.  
  184.                         if (CHECK_DNS) {
  185.                                 if (!checkDns(domainPart))
  186.                                         return false;
  187.                         }
  188.                 }
  189.  
  190.                 // localPart prüfen
  191.  
  192.                 if (!preg_match("^(\\\\.|[A-Za-z0-9!#%&`_=\\/$\'*+?^{}|~.-])+$",
  193.                                 localPart.replaceAll("\\\\", "").replaceAll("@", ""))) {
  194.                         // character not valid in local part unless
  195.                         // local part is quoted
  196.                         if (!preg_match("^\"(\\\\\"|[^\"])+\"$", localPart.replaceAll(
  197.                                         "\\\\", "").replaceAll("@", ""))) {
  198.                                 return false;
  199.                         }
  200.                 }
  201.  
  202.                 // TODO: Weitere Tests gemäß RFC?
  203.  
  204.                 return true;
  205.         }
  206.  
  207.         /**
  208.          * build a HashSet from a array of String literals.
  209.          *
  210.          * @param list
  211.          *            array of strings
  212.          *
  213.          * @return HashSet you can use to test if a string is in the set.
  214.          */
  215.         private static HashSet<String> hmaker(String[] list) {
  216.                 HashSet<String> map = new HashSet<String>(Math.max(
  217.                                 (int) (list.length / .75f) + 1, 16));
  218.                 map.addAll(Arrays.asList(list));
  219.                 return map;
  220.         }
  221. }
  222.