Subversion Repositories javautils

Rev

Rev 17 | Details | Compare with Previous | Last modification | View Log | RSS feed

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