Rev 11 | Go to most recent revision | Only display areas with differences | Regard whitespace | Details | Blame | Last modification | View Log | RSS feed
Rev 11 | Rev 13 | ||
---|---|---|---|
1 | package com.dominicsayers.isemail; |
1 | package com.dominicsayers.isemail; |
2 | 2 | ||
3 | import javax.naming.NamingException; |
3 | import javax.naming.NamingException; |
4 | 4 | ||
5 | /** |
5 | /** |
- | 6 | * This class checks if email addresses are valid or not. |
|
- | 7 | * |
|
6 | * @package isemail |
8 | * @package isemail |
7 | * @author Dominic Sayers <dominic_sayers@hotmail.com>; Translated from PHP into |
9 | * @author Dominic Sayers <dominic_sayers@hotmail.com>; Translated from PHP into |
8 | * Java by Daniel Marschall [www.daniel-marschall.de] |
10 | * Java by Daniel Marschall [www.daniel-marschall.de] |
9 | * @copyright 2010 Dominic Sayers |
11 | * @copyright 2010 Dominic Sayers; Java-Translation 2010 by Daniel Marschall |
10 | * @license http://www.opensource.org/licenses/bsd-license.php BSD License |
12 | * @license http://www.opensource.org/licenses/bsd-license.php BSD License |
11 | * @link http://www.dominicsayers.com/isemail |
13 | * @see http://www.dominicsayers.com/isemail |
12 | * @version 1.17 - Upper length limit corrected to 254 characters; |
14 | * @version 1.17 - Upper length limit corrected to 254 characters; |
13 | * Java-Translation 2010-06-13 |
15 | * Java-Translation 2010-06-13 |
14 | */ |
16 | */ |
15 | 17 | ||
16 | /* |
18 | /* |
17 | * Copyright (c) 2008-2010, Dominic Sayers All rights reserved. |
19 | * Copyright (c) 2008-2010, Dominic Sayers All rights reserved. |
18 | * |
20 | * |
19 | * Redistribution and use in source and binary forms, with or without |
21 | * Redistribution and use in source and binary forms, with or without |
20 | * modification, are permitted provided that the following conditions are met: |
22 | * modification, are permitted provided that the following conditions are met: |
21 | * |
23 | * |
22 | * Redistributions of source code must retain the above copyright notice, this |
24 | * Redistributions of source code must retain the above copyright notice, this |
23 | * list of conditions and the following disclaimer. Redistributions in binary |
25 | * list of conditions and the following disclaimer. Redistributions in binary |
24 | * form must reproduce the above copyright notice, this list of conditions and |
26 | * form must reproduce the above copyright notice, this list of conditions and |
25 | * the following disclaimer in the documentation and/or other materials provided |
27 | * the following disclaimer in the documentation and/or other materials provided |
26 | * with the distribution. Neither the name of Dominic Sayers nor the names of |
28 | * with the distribution. Neither the name of Dominic Sayers nor the names of |
27 | * its contributors may be used to endorse or promote products derived from this |
29 | * its contributors may be used to endorse or promote products derived from this |
28 | * software without specific prior written permission. |
30 | * software without specific prior written permission. |
29 | * |
31 | * |
30 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" |
32 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" |
31 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
33 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
32 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE |
34 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE |
33 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE |
35 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE |
34 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR |
36 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR |
35 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF |
37 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF |
36 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS |
38 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS |
37 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN |
39 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN |
38 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) |
40 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) |
39 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE |
41 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE |
40 | * POSSIBILITY OF SUCH DAMAGE. |
42 | * POSSIBILITY OF SUCH DAMAGE. |
41 | */ |
43 | */ |
42 | 44 | ||
43 | public class IsEMail { |
45 | public class IsEMail { |
44 | 46 | ||
45 | /** |
47 | /** |
46 | * Checks the syntax of an email address without DNS check. |
48 | * Checks the syntax of an email address without DNS check. |
47 | * |
49 | * |
48 | * @param email |
50 | * @param email |
49 | * The email address to be checked. |
51 | * The email address to be checked. |
50 | * @return True if the email address is valid. |
52 | * @return True if the email address is valid. |
51 | */ |
53 | */ |
52 | public static boolean is_email(String email) { |
54 | public static boolean is_email(String email) { |
53 | return (is_email_diagnosis(email, false) == EMailSyntaxDiagnosis.ISEMAIL_VALID); |
55 | return (is_email_diagnosis(email, false) == EMailSyntaxDiagnosis.ISEMAIL_VALID); |
54 | } |
56 | } |
55 | 57 | ||
56 | /** |
58 | /** |
57 | * Checks the syntax of an email address. |
59 | * Checks the syntax of an email address. |
58 | * |
60 | * |
59 | * @param email |
61 | * @param email |
60 | * The email address to be checked. |
62 | * The email address to be checked. |
61 | * @param checkDNS |
63 | * @param checkDNS |
62 | * Whether a DNS check should be performed or not. |
64 | * Whether a DNS check should be performed or not. |
63 | * @return True if the email address is valid. |
65 | * @return True if the email address is valid. |
64 | */ |
66 | */ |
65 | public static boolean is_email(String email, boolean checkDNS) { |
67 | public static boolean is_email(String email, boolean checkDNS) { |
66 | return (is_email_diagnosis(email, checkDNS) == EMailSyntaxDiagnosis.ISEMAIL_VALID); |
68 | return (is_email_diagnosis(email, checkDNS) == EMailSyntaxDiagnosis.ISEMAIL_VALID); |
67 | } |
69 | } |
68 | 70 | ||
69 | /** |
71 | /** |
70 | * Checks the syntax of an email address with diagnosis and without DNS |
72 | * Checks the syntax of an email address with diagnosis and without DNS |
71 | * check. |
73 | * check. |
72 | * |
74 | * |
73 | * @param email |
75 | * @param email |
74 | * The email address to be checked. |
76 | * The email address to be checked. |
75 | * @return A diagnosis of the email syntax. |
77 | * @return A diagnosis of the email syntax. |
76 | */ |
78 | */ |
77 | public static EMailSyntaxDiagnosis is_email_diagnosis(String email) { |
79 | public static EMailSyntaxDiagnosis is_email_diagnosis(String email) { |
78 | return is_email_diagnosis(email, false); |
80 | return is_email_diagnosis(email, false); |
79 | } |
81 | } |
80 | 82 | ||
81 | /** |
83 | /** |
82 | * Checks the syntax of an email address with diagnosis. |
84 | * Checks the syntax of an email address with diagnosis. |
83 | * |
85 | * |
84 | * @param email |
86 | * @param email |
85 | * The email address to be checked. |
87 | * The email address to be checked. |
86 | * @param checkDNS |
88 | * @param checkDNS |
87 | * Whether a DNS check should be performed or not. |
89 | * Whether a DNS check should be performed or not. |
88 | * @return A diagnosis of the email syntax. |
90 | * @return A diagnosis of the email syntax. |
89 | */ |
91 | */ |
90 | public static EMailSyntaxDiagnosis is_email_diagnosis(String email, |
92 | public static EMailSyntaxDiagnosis is_email_diagnosis(String email, |
91 | boolean checkDNS) { |
93 | boolean checkDNS) { |
92 | 94 | ||
93 | if (email == null) |
95 | if (email == null) |
94 | email = ""; |
96 | email = ""; |
95 | 97 | ||
96 | // Check that 'email' is a valid address. Read the following RFCs to |
98 | // Check that 'email' is a valid address. Read the following RFCs to |
97 | // understand the constraints: |
99 | // understand the constraints: |
98 | // (http://tools.ietf.org/html/rfc5322) |
100 | // (http://tools.ietf.org/html/rfc5322) |
99 | // (http://tools.ietf.org/html/rfc3696) |
101 | // (http://tools.ietf.org/html/rfc3696) |
100 | // (http://tools.ietf.org/html/rfc5321) |
102 | // (http://tools.ietf.org/html/rfc5321) |
101 | // (http://tools.ietf.org/html/rfc4291#section-2.2) |
103 | // (http://tools.ietf.org/html/rfc4291#section-2.2) |
102 | // (http://tools.ietf.org/html/rfc1123#section-2.1) |
104 | // (http://tools.ietf.org/html/rfc1123#section-2.1) |
103 | 105 | ||
104 | // the upper limit on address lengths should normally be considered to |
106 | // the upper limit on address lengths should normally be considered to |
105 | // be 254 |
107 | // be 254 |
106 | // (http://www.rfc-editor.org/errata_search.php?rfc=3696) |
108 | // (http://www.rfc-editor.org/errata_search.php?rfc=3696) |
107 | // NB My erratum has now been verified by the IETF so the correct answer |
109 | // NB My erratum has now been verified by the IETF so the correct answer |
108 | // is 254 |
110 | // is 254 |
109 | // |
111 | // |
110 | // The maximum total length of a reverse-path or forward-path is 256 |
112 | // The maximum total length of a reverse-path or forward-path is 256 |
111 | // characters (including the punctuation and element separators) |
113 | // characters (including the punctuation and element separators) |
112 | // (http://tools.ietf.org/html/rfc5321#section-4.5.3.1.3) |
114 | // (http://tools.ietf.org/html/rfc5321#section-4.5.3.1.3) |
113 | // NB There is a mandatory 2-character wrapper round the actual address |
115 | // NB There is a mandatory 2-character wrapper round the actual address |
114 | int emailLength = email.length(); |
116 | int emailLength = email.length(); |
115 | // revision 1.17: Max length reduced to 254 (see above) |
117 | // revision 1.17: Max length reduced to 254 (see above) |
116 | if (emailLength > 254) { |
118 | if (emailLength > 254) { |
117 | return EMailSyntaxDiagnosis.ISEMAIL_TOOLONG; // Too long |
119 | return EMailSyntaxDiagnosis.ISEMAIL_TOOLONG; // Too long |
118 | } |
120 | } |
119 | 121 | ||
120 | // Contemporary email addresses consist of a "local part" separated from |
122 | // Contemporary email addresses consist of a "local part" separated from |
121 | // a "domain part" (a fully-qualified domain name) by an at-sign ("@"). |
123 | // a "domain part" (a fully-qualified domain name) by an at-sign ("@"). |
122 | // (http://tools.ietf.org/html/rfc3696#section-3) |
124 | // (http://tools.ietf.org/html/rfc3696#section-3) |
123 | int atIndex = email.lastIndexOf('@'); |
125 | int atIndex = email.lastIndexOf('@'); |
124 | 126 | ||
125 | if (atIndex == -1) { |
127 | if (atIndex == -1) { |
126 | return EMailSyntaxDiagnosis.ISEMAIL_NOAT; // No at-sign |
128 | return EMailSyntaxDiagnosis.ISEMAIL_NOAT; // No at-sign |
127 | } |
129 | } |
128 | if (atIndex == 0) { |
130 | if (atIndex == 0) { |
129 | return EMailSyntaxDiagnosis.ISEMAIL_NOLOCALPART; // No local part |
131 | return EMailSyntaxDiagnosis.ISEMAIL_NOLOCALPART; // No local part |
130 | } |
132 | } |
131 | if (atIndex == emailLength - 1) { |
133 | if (atIndex == emailLength - 1) { |
132 | // No domain part |
134 | // No domain part |
133 | return EMailSyntaxDiagnosis.ISEMAIL_NODOMAIN; |
135 | return EMailSyntaxDiagnosis.ISEMAIL_NODOMAIN; |
134 | // revision 1.14: Length test bug suggested by Andrew Campbell of |
136 | // revision 1.14: Length test bug suggested by Andrew Campbell of |
135 | // Gloucester, MA |
137 | // Gloucester, MA |
136 | } |
138 | } |
137 | 139 | ||
138 | // Sanitize comments |
140 | // Sanitize comments |
139 | // - remove nested comments, quotes and dots in comments |
141 | // - remove nested comments, quotes and dots in comments |
140 | // - remove parentheses and dots from quoted strings |
142 | // - remove parentheses and dots from quoted strings |
141 | int braceDepth = 0; |
143 | int braceDepth = 0; |
142 | boolean inQuote = false; |
144 | boolean inQuote = false; |
143 | boolean escapeThisChar = false; |
145 | boolean escapeThisChar = false; |
144 | 146 | ||
145 | for (int i = 0; i < emailLength; ++i) { |
147 | for (int i = 0; i < emailLength; ++i) { |
146 | char charX = email.charAt(i); |
148 | char charX = email.charAt(i); |
147 | boolean replaceChar = false; |
149 | boolean replaceChar = false; |
148 | 150 | ||
149 | if (charX == '\\') { |
151 | if (charX == '\\') { |
150 | escapeThisChar = !escapeThisChar; // Escape the next character? |
152 | escapeThisChar = !escapeThisChar; // Escape the next character? |
151 | } else { |
153 | } else { |
152 | switch (charX) { |
154 | switch (charX) { |
153 | case '(': |
155 | case '(': |
154 | if (escapeThisChar) { |
156 | if (escapeThisChar) { |
155 | replaceChar = true; |
157 | replaceChar = true; |
156 | } else { |
158 | } else { |
157 | if (inQuote) { |
159 | if (inQuote) { |
158 | replaceChar = true; |
160 | replaceChar = true; |
159 | } else { |
161 | } else { |
160 | if (braceDepth++ > 0) { |
162 | if (braceDepth++ > 0) { |
161 | replaceChar = true; // Increment brace depth |
163 | replaceChar = true; // Increment brace depth |
162 | } |
164 | } |
163 | } |
165 | } |
164 | } |
166 | } |
165 | 167 | ||
166 | break; |
168 | break; |
167 | case ')': |
169 | case ')': |
168 | if (escapeThisChar) { |
170 | if (escapeThisChar) { |
169 | replaceChar = true; |
171 | replaceChar = true; |
170 | } else { |
172 | } else { |
171 | if (inQuote) { |
173 | if (inQuote) { |
172 | replaceChar = true; |
174 | replaceChar = true; |
173 | } else { |
175 | } else { |
174 | if (--braceDepth > 0) |
176 | if (--braceDepth > 0) |
175 | replaceChar = true; // Decrement brace depth |
177 | replaceChar = true; // Decrement brace depth |
176 | if (braceDepth < 0) { |
178 | if (braceDepth < 0) { |
177 | braceDepth = 0; |
179 | braceDepth = 0; |
178 | } |
180 | } |
179 | } |
181 | } |
180 | } |
182 | } |
181 | 183 | ||
182 | break; |
184 | break; |
183 | case '"': |
185 | case '"': |
184 | if (escapeThisChar) { |
186 | if (escapeThisChar) { |
185 | replaceChar = true; |
187 | replaceChar = true; |
186 | } else { |
188 | } else { |
187 | if (braceDepth == 0) { |
189 | if (braceDepth == 0) { |
188 | // Are we inside a quoted string? |
190 | // Are we inside a quoted string? |
189 | inQuote = !inQuote; |
191 | inQuote = !inQuote; |
190 | } else { |
192 | } else { |
191 | replaceChar = true; |
193 | replaceChar = true; |
192 | } |
194 | } |
193 | } |
195 | } |
194 | 196 | ||
195 | break; |
197 | break; |
196 | case '.': // Dots don't help us either |
198 | case '.': // Dots don't help us either |
197 | if (escapeThisChar) { |
199 | if (escapeThisChar) { |
198 | replaceChar = true; |
200 | replaceChar = true; |
199 | } else { |
201 | } else { |
200 | if (braceDepth > 0) |
202 | if (braceDepth > 0) |
201 | replaceChar = true; |
203 | replaceChar = true; |
202 | } |
204 | } |
203 | 205 | ||
204 | break; |
206 | break; |
205 | default: |
207 | default: |
206 | } |
208 | } |
207 | 209 | ||
208 | escapeThisChar = false; |
210 | escapeThisChar = false; |
209 | if (replaceChar) { |
211 | if (replaceChar) { |
210 | // Replace the offending character with something harmless |
212 | // Replace the offending character with something harmless |
211 | // revision 1.12: Line above replaced because PHPLint |
213 | // revision 1.12: Line above replaced because PHPLint |
212 | // doesn't like that syntax |
214 | // doesn't like that syntax |
213 | email = replaceCharAt(email, i, 'x'); |
215 | email = replaceCharAt(email, i, 'x'); |
214 | } |
216 | } |
215 | 217 | ||
216 | } |
218 | } |
217 | } |
219 | } |
218 | 220 | ||
219 | String localPart = PHPFunctions.substr(email, 0, atIndex); |
221 | String localPart = PHPFunctions.substr(email, 0, atIndex); |
220 | String domain = PHPFunctions.substr(email, atIndex + 1); |
222 | String domain = PHPFunctions.substr(email, atIndex + 1); |
221 | // Folding white space |
223 | // Folding white space |
222 | final String FWS = "(?:(?:(?:[ \\t]*(?:\\r\\n))?[ \\t]+)|(?:[ \\t]+(?:(?:\\r\\n)[ \\t]+)*))"; |
224 | final String FWS = "(?:(?:(?:[ \\t]*(?:\\r\\n))?[ \\t]+)|(?:[ \\t]+(?:(?:\\r\\n)[ \\t]+)*))"; |
223 | // Let's check the local part for RFC compliance... |
225 | // Let's check the local part for RFC compliance... |
224 | // |
226 | // |
225 | // local-part = dot-atom / quoted-string / obs-local-part |
227 | // local-part = dot-atom / quoted-string / obs-local-part |
226 | // obs-local-part = word *("." word) |
228 | // obs-local-part = word *("." word) |
227 | // (http://tools.ietf.org/html/rfc5322#section-3.4.1) |
229 | // (http://tools.ietf.org/html/rfc5322#section-3.4.1) |
228 | // |
230 | // |
229 | // Problem: need to distinguish between "first.last" and "first"."last" |
231 | // Problem: need to distinguish between "first.last" and "first"."last" |
230 | // (i.e. one element or two). And I suck at regexes. |
232 | // (i.e. one element or two). And I suck at regexes. |
231 | 233 | ||
232 | String[] dotArray = PHPFunctions.preg_split( |
234 | String[] dotArray = PHPFunctions.preg_split( |
233 | "(?m)\\.(?=(?:[^\\\"]*\\\"[^\\\"]*\\\")*(?![^\\\"]*\\\"))", |
235 | "(?m)\\.(?=(?:[^\\\"]*\\\"[^\\\"]*\\\")*(?![^\\\"]*\\\"))", |
234 | localPart); |
236 | localPart); |
235 | int partLength = 0; |
237 | int partLength = 0; |
236 | 238 | ||
237 | for (String element : dotArray) { |
239 | for (String element : dotArray) { |
238 | // Remove any leading or trailing FWS |
240 | // Remove any leading or trailing FWS |
239 | element = PHPFunctions.preg_replace("^" + FWS + "|" + FWS + "$", |
241 | element = PHPFunctions.preg_replace("^" + FWS + "|" + FWS + "$", |
240 | "", element); |
242 | "", element); |
241 | int elementLength = element.length(); |
243 | int elementLength = element.length(); |
242 | 244 | ||
243 | if (elementLength == 0) { |
245 | if (elementLength == 0) { |
244 | // Can't have empty element (consecutive dots or |
246 | // Can't have empty element (consecutive dots or |
245 | // dots at the start or end) |
247 | // dots at the start or end) |
246 | return EMailSyntaxDiagnosis.ISEMAIL_ZEROLENGTHELEMENT; |
248 | return EMailSyntaxDiagnosis.ISEMAIL_ZEROLENGTHELEMENT; |
247 | } |
249 | } |
248 | // revision 1.15: Speed up the test and get rid of |
250 | // revision 1.15: Speed up the test and get rid of |
249 | // "unitialized string offset" notices from PHP |
251 | // "unitialized string offset" notices from PHP |
250 | 252 | ||
251 | // We need to remove any valid comments (i.e. those at the start or |
253 | // We need to remove any valid comments (i.e. those at the start or |
252 | // end of the element) |
254 | // end of the element) |
253 | if (element.charAt(0) == '(') { |
255 | if (element.charAt(0) == '(') { |
254 | int indexBrace = element.indexOf(')'); |
256 | int indexBrace = element.indexOf(')'); |
255 | if (indexBrace != -1) { |
257 | if (indexBrace != -1) { |
256 | if (PHPFunctions.preg_match("(?<!\\\\)[\\(\\)]", |
258 | if (PHPFunctions.preg_match("(?<!\\\\)[\\(\\)]", |
257 | PHPFunctions.substr(element, 1, indexBrace - 1)) > 0) { |
259 | PHPFunctions.substr(element, 1, indexBrace - 1)) > 0) { |
258 | // Illegal characters in comment |
260 | // Illegal characters in comment |
259 | return EMailSyntaxDiagnosis.ISEMAIL_BADCOMMENT_START; |
261 | return EMailSyntaxDiagnosis.ISEMAIL_BADCOMMENT_START; |
260 | } |
262 | } |
261 | element = PHPFunctions.substr(element, indexBrace + 1, |
263 | element = PHPFunctions.substr(element, indexBrace + 1, |
262 | elementLength - indexBrace - 1); |
264 | elementLength - indexBrace - 1); |
263 | elementLength = element.length(); |
265 | elementLength = element.length(); |
264 | } |
266 | } |
265 | } |
267 | } |
266 | 268 | ||
267 | if (element.charAt(elementLength - 1) == ')') { |
269 | if (element.charAt(elementLength - 1) == ')') { |
268 | int indexBrace = element.lastIndexOf('('); |
270 | int indexBrace = element.lastIndexOf('('); |
269 | if (indexBrace != -1) { |
271 | if (indexBrace != -1) { |
270 | if (PHPFunctions.preg_match("(?<!\\\\)(?:[\\(\\)])", |
272 | if (PHPFunctions.preg_match("(?<!\\\\)(?:[\\(\\)])", |
271 | PHPFunctions.substr(element, indexBrace + 1, |
273 | PHPFunctions.substr(element, indexBrace + 1, |
272 | elementLength - indexBrace - 2)) > 0) { |
274 | elementLength - indexBrace - 2)) > 0) { |
273 | // Illegal characters in comment |
275 | // Illegal characters in comment |
274 | return EMailSyntaxDiagnosis.ISEMAIL_BADCOMMENT_END; |
276 | return EMailSyntaxDiagnosis.ISEMAIL_BADCOMMENT_END; |
275 | } |
277 | } |
276 | element = PHPFunctions.substr(element, 0, indexBrace); |
278 | element = PHPFunctions.substr(element, 0, indexBrace); |
277 | elementLength = element.length(); |
279 | elementLength = element.length(); |
278 | } |
280 | } |
279 | } |
281 | } |
280 | 282 | ||
281 | // Remove any leading or trailing FWS around the element (inside any |
283 | // Remove any leading or trailing FWS around the element (inside any |
282 | // comments) |
284 | // comments) |
283 | element = PHPFunctions.preg_replace("^" + FWS + "|" + FWS + "$", |
285 | element = PHPFunctions.preg_replace("^" + FWS + "|" + FWS + "$", |
284 | "", element); |
286 | "", element); |
285 | 287 | ||
286 | // What's left counts towards the maximum length for this part |
288 | // What's left counts towards the maximum length for this part |
287 | if (partLength > 0) |
289 | if (partLength > 0) |
288 | partLength++; // for the dot |
290 | partLength++; // for the dot |
289 | partLength += element.length(); |
291 | partLength += element.length(); |
290 | 292 | ||
291 | // Each dot-delimited component can be an atom or a quoted string |
293 | // Each dot-delimited component can be an atom or a quoted string |
292 | // (because of the obs-local-part provision) |
294 | // (because of the obs-local-part provision) |
293 | 295 | ||
294 | if (PHPFunctions.preg_match("(?s)^\"(?:.)*\"$", element) > 0) { |
296 | if (PHPFunctions.preg_match("(?s)^\"(?:.)*\"$", element) > 0) { |
295 | // Quoted-string tests: |
297 | // Quoted-string tests: |
296 | // |
298 | // |
297 | // Remove any FWS |
299 | // Remove any FWS |
298 | element = PHPFunctions.preg_replace("(?<!\\\\)" + FWS, "", |
300 | element = PHPFunctions.preg_replace("(?<!\\\\)" + FWS, "", |
299 | element); |
301 | element); |
300 | // My regex skillz aren't up to distinguishing between \" \\" |
302 | // My regex skillz aren't up to distinguishing between \" \\" |
301 | // \\\" \\\\" etc. |
303 | // \\\" \\\\" etc. |
302 | // So remove all \\ from the string first... |
304 | // So remove all \\ from the string first... |
303 | element = PHPFunctions.preg_replace("\\\\\\\\", " ", element); |
305 | element = PHPFunctions.preg_replace("\\\\\\\\", " ", element); |
304 | if (PHPFunctions |
306 | if (PHPFunctions |
305 | .preg_match( |
307 | .preg_match( |
306 | "(?<!\\\\|^)[\"\\r\\n\\x00](?!$)|\\\\\"$|\"\"", |
308 | "(?<!\\\\|^)[\"\\r\\n\\x00](?!$)|\\\\\"$|\"\"", |
307 | element) > 0) { |
309 | element) > 0) { |
308 | // ", CR, LF and NUL must be escaped, "" is too short |
310 | // ", CR, LF and NUL must be escaped, "" is too short |
309 | return EMailSyntaxDiagnosis.ISEMAIL_UNESCAPEDDELIM; |
311 | return EMailSyntaxDiagnosis.ISEMAIL_UNESCAPEDDELIM; |
310 | } |
312 | } |
311 | } else { |
313 | } else { |
312 | // Unquoted string tests: |
314 | // Unquoted string tests: |
313 | // |
315 | // |
314 | // Period (".") may...appear, but may not be used to start or |
316 | // Period (".") may...appear, but may not be used to start or |
315 | // end the |
317 | // end the |
316 | // local part, nor may two or more consecutive periods appear. |
318 | // local part, nor may two or more consecutive periods appear. |
317 | // (http://tools.ietf.org/html/rfc3696#section-3) |
319 | // (http://tools.ietf.org/html/rfc3696#section-3) |
318 | // |
320 | // |
319 | // A zero-length element implies a period at the beginning or |
321 | // A zero-length element implies a period at the beginning or |
320 | // end of the |
322 | // end of the |
321 | // local part, or two periods together. Either way it's not |
323 | // local part, or two periods together. Either way it's not |
322 | // allowed. |
324 | // allowed. |
323 | if (element.isEmpty()) { |
325 | if (element.isEmpty()) { |
324 | // Dots in wrong place |
326 | // Dots in wrong place |
325 | return EMailSyntaxDiagnosis.ISEMAIL_EMPTYELEMENT; |
327 | return EMailSyntaxDiagnosis.ISEMAIL_EMPTYELEMENT; |
326 | } |
328 | } |
327 | 329 | ||
328 | // Any ASCII graphic (printing) character other than the |
330 | // Any ASCII graphic (printing) character other than the |
329 | // at-sign ("@"), backslash, double quote, comma, or square |
331 | // at-sign ("@"), backslash, double quote, comma, or square |
330 | // brackets may |
332 | // brackets may |
331 | // appear without quoting. If any of that list of excluded |
333 | // appear without quoting. If any of that list of excluded |
332 | // characters |
334 | // characters |
333 | // are to appear, they must be quoted |
335 | // are to appear, they must be quoted |
334 | // (http://tools.ietf.org/html/rfc3696#section-3) |
336 | // (http://tools.ietf.org/html/rfc3696#section-3) |
335 | // |
337 | // |
336 | // Any excluded characters? i.e. 0x00-0x20, (, ), <, >, [, ], :, |
338 | // Any excluded characters? i.e. 0x00-0x20, (, ), <, >, [, ], :, |
337 | // ;, @, \, comma, period, " |
339 | // ;, @, \, comma, period, " |
338 | if (PHPFunctions.preg_match( |
340 | if (PHPFunctions.preg_match( |
339 | "[\\x00-\\x20\\(\\)<>\\[\\]:;@\\\\,\\.\"]", element) > 0) { |
341 | "[\\x00-\\x20\\(\\)<>\\[\\]:;@\\\\,\\.\"]", element) > 0) { |
340 | // These characters must be in a quoted string |
342 | // These characters must be in a quoted string |
341 | return EMailSyntaxDiagnosis.ISEMAIL_UNESCAPEDSPECIAL; |
343 | return EMailSyntaxDiagnosis.ISEMAIL_UNESCAPEDSPECIAL; |
342 | } |
344 | } |
343 | } |
345 | } |
344 | } |
346 | } |
345 | 347 | ||
346 | if (partLength > 64) { |
348 | if (partLength > 64) { |
347 | // Local part must be 64 characters or less |
349 | // Local part must be 64 characters or less |
348 | return EMailSyntaxDiagnosis.ISEMAIL_LOCALTOOLONG; |
350 | return EMailSyntaxDiagnosis.ISEMAIL_LOCALTOOLONG; |
349 | } |
351 | } |
350 | 352 | ||
351 | // Now let's check the domain part... |
353 | // Now let's check the domain part... |
352 | 354 | ||
353 | // The domain name can also be replaced by an IP address in square |
355 | // The domain name can also be replaced by an IP address in square |
354 | // brackets |
356 | // brackets |
355 | // (http://tools.ietf.org/html/rfc3696#section-3) |
357 | // (http://tools.ietf.org/html/rfc3696#section-3) |
356 | // (http://tools.ietf.org/html/rfc5321#section-4.1.3) |
358 | // (http://tools.ietf.org/html/rfc5321#section-4.1.3) |
357 | // (http://tools.ietf.org/html/rfc4291#section-2.2) |
359 | // (http://tools.ietf.org/html/rfc4291#section-2.2) |
358 | 360 | ||
359 | if (PHPFunctions.preg_match("^\\[(.)+]$", domain) == 1) { |
361 | if (PHPFunctions.preg_match("^\\[(.)+]$", domain) == 1) { |
360 | // It's an address-literal |
362 | // It's an address-literal |
361 | String addressLiteral = PHPFunctions.substr(domain, 1, domain |
363 | String addressLiteral = PHPFunctions.substr(domain, 1, domain |
362 | .length() - 2); |
364 | .length() - 2); |
363 | 365 | ||
364 | String IPv6; |
366 | String IPv6; |
365 | int groupMax; |
367 | int groupMax; |
366 | 368 | ||
367 | // Extract IPv4 part from the end of the address-literal (if there |
369 | // Extract IPv4 part from the end of the address-literal (if there |
368 | // is one) |
370 | // is one) |
369 | String[] matchesIP = PHPFunctions |
371 | String[] matchesIP = PHPFunctions |
370 | .preg_match_to_array( |
372 | .preg_match_to_array( |
371 | "\\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]?)$", |
373 | "\\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]?)$", |
372 | addressLiteral); |
374 | addressLiteral); |
373 | if (matchesIP.length > 0) { |
375 | if (matchesIP.length > 0) { |
374 | int index = addressLiteral.lastIndexOf(matchesIP[0]); |
376 | int index = addressLiteral.lastIndexOf(matchesIP[0]); |
375 | 377 | ||
376 | if (index == 0) { |
378 | if (index == 0) { |
377 | // Nothing there except a valid IPv4 address, so... |
379 | // Nothing there except a valid IPv4 address, so... |
378 | return EMailSyntaxDiagnosis.ISEMAIL_VALID; |
380 | return EMailSyntaxDiagnosis.ISEMAIL_VALID; |
379 | } else { |
381 | } else { |
380 | // Assume it's an attempt at a mixed address (IPv6 + IPv4) |
382 | // Assume it's an attempt at a mixed address (IPv6 + IPv4) |
381 | if (addressLiteral.charAt(index - 1) != ':') { |
383 | if (addressLiteral.charAt(index - 1) != ':') { |
382 | // Character preceding IPv4 address must be ':' |
384 | // Character preceding IPv4 address must be ':' |
383 | return EMailSyntaxDiagnosis.ISEMAIL_IPV4BADPREFIX; |
385 | return EMailSyntaxDiagnosis.ISEMAIL_IPV4BADPREFIX; |
384 | } |
386 | } |
385 | if (!addressLiteral.startsWith("IPv6:")) { |
387 | if (!addressLiteral.startsWith("IPv6:")) { |
386 | // RFC5321 section 4.1.3 |
388 | // RFC5321 section 4.1.3 |
387 | return EMailSyntaxDiagnosis.ISEMAIL_IPV6BADPREFIXMIXED; |
389 | return EMailSyntaxDiagnosis.ISEMAIL_IPV6BADPREFIXMIXED; |
388 | } |
390 | } |
389 | 391 | ||
390 | IPv6 = PHPFunctions.substr(addressLiteral, 5, |
392 | IPv6 = PHPFunctions.substr(addressLiteral, 5, |
391 | (index == 7) ? 2 : index - 6); |
393 | (index == 7) ? 2 : index - 6); |
392 | groupMax = 6; |
394 | groupMax = 6; |
393 | } |
395 | } |
394 | } else { |
396 | } else { |
395 | // It must be an attempt at pure IPv6 |
397 | // It must be an attempt at pure IPv6 |
396 | if (!addressLiteral.startsWith("IPv6:")) { |
398 | if (!addressLiteral.startsWith("IPv6:")) { |
397 | // RFC5321 section 4.1.3 |
399 | // RFC5321 section 4.1.3 |
398 | return EMailSyntaxDiagnosis.ISEMAIL_IPV6BADPREFIX; |
400 | return EMailSyntaxDiagnosis.ISEMAIL_IPV6BADPREFIX; |
399 | } |
401 | } |
400 | IPv6 = PHPFunctions.substr(addressLiteral, 5); |
402 | IPv6 = PHPFunctions.substr(addressLiteral, 5); |
401 | groupMax = 8; |
403 | groupMax = 8; |
402 | } |
404 | } |
403 | 405 | ||
404 | String[][] matchesIP6 = PHPFunctions.preg_match_all( |
406 | String[][] matchesIP6 = PHPFunctions.preg_match_all( |
405 | "^[0-9a-fA-F]{0,4}|\\:[0-9a-fA-F]{0,4}|(.)", IPv6); |
407 | "^[0-9a-fA-F]{0,4}|\\:[0-9a-fA-F]{0,4}|(.)", IPv6); |
406 | int groupCount = 0; |
408 | int groupCount = 0; |
407 | if (matchesIP6.length > 0) { |
409 | if (matchesIP6.length > 0) { |
408 | groupCount = matchesIP6[0].length; |
410 | groupCount = matchesIP6[0].length; |
409 | } // else: Undefined state (should never be reached) |
411 | } // else: Undefined state (should never be reached) |
410 | int index = IPv6.indexOf("::"); |
412 | int index = IPv6.indexOf("::"); |
411 | 413 | ||
412 | if (index == -1) { |
414 | if (index == -1) { |
413 | // We need exactly the right number of groups |
415 | // We need exactly the right number of groups |
414 | if (groupCount != groupMax) { |
416 | if (groupCount != groupMax) { |
415 | // RFC5321 section 4.1.3 |
417 | // RFC5321 section 4.1.3 |
416 | return EMailSyntaxDiagnosis.ISEMAIL_IPV6GROUPCOUNT; |
418 | return EMailSyntaxDiagnosis.ISEMAIL_IPV6GROUPCOUNT; |
417 | } |
419 | } |
418 | } else { |
420 | } else { |
419 | if (index != IPv6.lastIndexOf("::")) { |
421 | if (index != IPv6.lastIndexOf("::")) { |
420 | // More than one '::' |
422 | // More than one '::' |
421 | return EMailSyntaxDiagnosis.ISEMAIL_IPV6DOUBLEDOUBLECOLON; |
423 | return EMailSyntaxDiagnosis.ISEMAIL_IPV6DOUBLEDOUBLECOLON; |
422 | } |
424 | } |
423 | groupMax = (index == 0 || index == (IPv6.length() - 2)) ? groupMax |
425 | groupMax = (index == 0 || index == (IPv6.length() - 2)) ? groupMax |
424 | : groupMax - 1; |
426 | : groupMax - 1; |
425 | if (groupCount > groupMax) { |
427 | if (groupCount > groupMax) { |
426 | // Too many IPv6 groups in address |
428 | // Too many IPv6 groups in address |
427 | return EMailSyntaxDiagnosis.ISEMAIL_IPV6TOOMANYGROUPS; |
429 | return EMailSyntaxDiagnosis.ISEMAIL_IPV6TOOMANYGROUPS; |
428 | } |
430 | } |
429 | } |
431 | } |
430 | 432 | ||
431 | // Daniel Marschall: For the Java translation, I optimized |
433 | // Daniel Marschall: For the Java translation, I optimized |
432 | // the process. Instead of sorting the array (which needs |
434 | // the process. Instead of sorting the array (which needs |
433 | // null-pointer checks and array-length checks) and then |
435 | // null-pointer checks and array-length checks) and then |
434 | // checking element [0], I decided to directly check every |
436 | // checking element [0], I decided to directly check every |
435 | // element. |
437 | // element. |
436 | 438 | ||
437 | // Check for unmatched characters |
439 | // Check for unmatched characters |
438 | // array_multisort(matchesIP6[1], SORT_DESC); |
440 | // array_multisort(matchesIP6[1], SORT_DESC); |
439 | // if ($matchesIP6[1][0] !== '')) { |
441 | // if ($matchesIP6[1][0] !== '')) { |
440 | // return EMailResultState.ISEMAIL_IPV6BADCHAR; |
442 | // return EMailResultState.ISEMAIL_IPV6BADCHAR; |
441 | // } |
443 | // } |
442 | 444 | ||
443 | // Check for unmatched characters |
445 | // Check for unmatched characters |
444 | if (matchesIP6.length > 1) { |
446 | if (matchesIP6.length > 1) { |
445 | for (String s : matchesIP6[1]) { |
447 | for (String s : matchesIP6[1]) { |
446 | if ((s != null) && (!s.isEmpty())) { |
448 | if ((s != null) && (!s.isEmpty())) { |
447 | return EMailSyntaxDiagnosis.ISEMAIL_IPV6BADCHAR; |
449 | return EMailSyntaxDiagnosis.ISEMAIL_IPV6BADCHAR; |
448 | } |
450 | } |
449 | } |
451 | } |
450 | } // else: Undefined state (should never be reached) |
452 | } // else: Undefined state (should never be reached) |
451 | 453 | ||
452 | // It's a valid IPv6 address, so... |
454 | // It's a valid IPv6 address, so... |
453 | return EMailSyntaxDiagnosis.ISEMAIL_VALID; |
455 | return EMailSyntaxDiagnosis.ISEMAIL_VALID; |
454 | } else { |
456 | } else { |
455 | // It's a domain name... |
457 | // It's a domain name... |
456 | 458 | ||
457 | // The syntax of a legal Internet host name was specified in RFC-952 |
459 | // The syntax of a legal Internet host name was specified in RFC-952 |
458 | // One aspect of host name syntax is hereby changed: the |
460 | // One aspect of host name syntax is hereby changed: the |
459 | // restriction on the first character is relaxed to allow either a |
461 | // restriction on the first character is relaxed to allow either a |
460 | // letter or a digit. |
462 | // letter or a digit. |
461 | // (http://tools.ietf.org/html/rfc1123#section-2.1) |
463 | // (http://tools.ietf.org/html/rfc1123#section-2.1) |
462 | // |
464 | // |
463 | // NB RFC 1123 updates RFC 1035, but this is not currently apparent |
465 | // NB RFC 1123 updates RFC 1035, but this is not currently apparent |
464 | // from reading RFC 1035. |
466 | // from reading RFC 1035. |
465 | // |
467 | // |
466 | // Most common applications, including email and the Web, will |
468 | // Most common applications, including email and the Web, will |
467 | // generally not |
469 | // generally not |
468 | // permit...escaped strings |
470 | // permit...escaped strings |
469 | // (http://tools.ietf.org/html/rfc3696#section-2) |
471 | // (http://tools.ietf.org/html/rfc3696#section-2) |
470 | // |
472 | // |
471 | // the better strategy has now become to make the |
473 | // the better strategy has now become to make the |
472 | // "at least one period" test, |
474 | // "at least one period" test, |
473 | // to verify LDH conformance (including verification that the |
475 | // to verify LDH conformance (including verification that the |
474 | // apparent TLD name |
476 | // apparent TLD name |
475 | // is not all-numeric) |
477 | // is not all-numeric) |
476 | // (http://tools.ietf.org/html/rfc3696#section-2) |
478 | // (http://tools.ietf.org/html/rfc3696#section-2) |
477 | // |
479 | // |
478 | // Characters outside the set of alphabetic characters, digits, and |
480 | // Characters outside the set of alphabetic characters, digits, and |
479 | // hyphen MUST NOT appear in domain name |
481 | // hyphen MUST NOT appear in domain name |
480 | // labels for SMTP clients or servers |
482 | // labels for SMTP clients or servers |
481 | // (http://tools.ietf.org/html/rfc5321#section-4.1.2) |
483 | // (http://tools.ietf.org/html/rfc5321#section-4.1.2) |
482 | // |
484 | // |
483 | // RFC5321 precludes the use of a trailing dot in a domain name for |
485 | // RFC5321 precludes the use of a trailing dot in a domain name for |
484 | // SMTP purposes |
486 | // SMTP purposes |
485 | // (http://tools.ietf.org/html/rfc5321#section-4.1.2) |
487 | // (http://tools.ietf.org/html/rfc5321#section-4.1.2) |
486 | 488 | ||
487 | dotArray = PHPFunctions.preg_split( |
489 | dotArray = PHPFunctions.preg_split( |
488 | "(?m)\\.(?=(?:[^\\\"]*\\\"[^\\\"]*\\\")*(?![^\\\"]*\\\"))", |
490 | "(?m)\\.(?=(?:[^\\\"]*\\\"[^\\\"]*\\\")*(?![^\\\"]*\\\"))", |
489 | domain); |
491 | domain); |
490 | partLength = 0; |
492 | partLength = 0; |
491 | // Since we use 'element' after the foreach |
493 | // Since we use 'element' after the foreach |
492 | // loop let's make sure it has a value |
494 | // loop let's make sure it has a value |
493 | String lastElement = ""; |
495 | String lastElement = ""; |
494 | // revision 1.13: Line above added because PHPLint now checks for |
496 | // revision 1.13: Line above added because PHPLint now checks for |
495 | // Definitely Assigned Variables |
497 | // Definitely Assigned Variables |
496 | 498 | ||
497 | if (dotArray.length == 1) { |
499 | if (dotArray.length == 1) { |
498 | // Mail host can't be a TLD (cite? What about localhost?) |
500 | // Mail host can't be a TLD (cite? What about localhost?) |
499 | return EMailSyntaxDiagnosis.ISEMAIL_TLD; |
501 | return EMailSyntaxDiagnosis.ISEMAIL_TLD; |
500 | } |
502 | } |
501 | 503 | ||
502 | for (String element : dotArray) { |
504 | for (String element : dotArray) { |
503 | lastElement = element; |
505 | lastElement = element; |
504 | // Remove any leading or trailing FWS |
506 | // Remove any leading or trailing FWS |
505 | element = PHPFunctions.preg_replace( |
507 | element = PHPFunctions.preg_replace( |
506 | "^" + FWS + "|" + FWS + "$", "", element); |
508 | "^" + FWS + "|" + FWS + "$", "", element); |
507 | int elementLength = element.length(); |
509 | int elementLength = element.length(); |
508 | 510 | ||
509 | // Each dot-delimited component must be of type atext |
511 | // Each dot-delimited component must be of type atext |
510 | // A zero-length element implies a period at the beginning or |
512 | // A zero-length element implies a period at the beginning or |
511 | // end of the |
513 | // end of the |
512 | // local part, or two periods together. Either way it's not |
514 | // local part, or two periods together. Either way it's not |
513 | // allowed. |
515 | // allowed. |
514 | if (elementLength == 0) { |
516 | if (elementLength == 0) { |
515 | // Dots in wrong place |
517 | // Dots in wrong place |
516 | return EMailSyntaxDiagnosis.ISEMAIL_DOMAINEMPTYELEMENT; |
518 | return EMailSyntaxDiagnosis.ISEMAIL_DOMAINEMPTYELEMENT; |
517 | } |
519 | } |
518 | // revision 1.15: Speed up the test and get rid of |
520 | // revision 1.15: Speed up the test and get rid of |
519 | // "unitialized string offset" notices from PHP |
521 | // "unitialized string offset" notices from PHP |
520 | 522 | ||
521 | // Then we need to remove all valid comments (i.e. those at the |
523 | // Then we need to remove all valid comments (i.e. those at the |
522 | // start or end of the element |
524 | // start or end of the element |
523 | if (element.charAt(0) == '(') { |
525 | if (element.charAt(0) == '(') { |
524 | int indexBrace = element.indexOf(')'); |
526 | int indexBrace = element.indexOf(')'); |
525 | if (indexBrace != -1) { |
527 | if (indexBrace != -1) { |
526 | if (PHPFunctions |
528 | if (PHPFunctions |
527 | .preg_match("(?<!\\\\)[\\(\\)]", PHPFunctions |
529 | .preg_match("(?<!\\\\)[\\(\\)]", PHPFunctions |
528 | .substr(element, 1, indexBrace - 1)) > 0) { |
530 | .substr(element, 1, indexBrace - 1)) > 0) { |
529 | // revision 1.17: Fixed name of constant (also |
531 | // revision 1.17: Fixed name of constant (also |
530 | // spotted by turboflash - thanks!) |
532 | // spotted by turboflash - thanks!) |
531 | // Illegal characters in comment |
533 | // Illegal characters in comment |
532 | return EMailSyntaxDiagnosis.ISEMAIL_BADCOMMENT_START; |
534 | return EMailSyntaxDiagnosis.ISEMAIL_BADCOMMENT_START; |
533 | } |
535 | } |
534 | element = PHPFunctions.substr(element, indexBrace + 1, |
536 | element = PHPFunctions.substr(element, indexBrace + 1, |
535 | elementLength - indexBrace - 1); |
537 | elementLength - indexBrace - 1); |
536 | elementLength = element.length(); |
538 | elementLength = element.length(); |
537 | } |
539 | } |
538 | } |
540 | } |
539 | 541 | ||
540 | if (element.charAt(elementLength - 1) == ')') { |
542 | if (element.charAt(elementLength - 1) == ')') { |
541 | int indexBrace = element.lastIndexOf('('); |
543 | int indexBrace = element.lastIndexOf('('); |
542 | if (indexBrace != -1) { |
544 | if (indexBrace != -1) { |
543 | if (PHPFunctions.preg_match("(?<!\\\\)(?:[\\(\\)])", |
545 | if (PHPFunctions.preg_match("(?<!\\\\)(?:[\\(\\)])", |
544 | PHPFunctions.substr(element, indexBrace + 1, |
546 | PHPFunctions.substr(element, indexBrace + 1, |
545 | elementLength - indexBrace - 2)) > 0) { |
547 | elementLength - indexBrace - 2)) > 0) { |
546 | // revision 1.17: Fixed name of constant (also |
548 | // revision 1.17: Fixed name of constant (also |
547 | // spotted by turboflash - thanks!) |
549 | // spotted by turboflash - thanks!) |
548 | // Illegal characters in comment |
550 | // Illegal characters in comment |
549 | return EMailSyntaxDiagnosis.ISEMAIL_BADCOMMENT_END; |
551 | return EMailSyntaxDiagnosis.ISEMAIL_BADCOMMENT_END; |
550 | } |
552 | } |
551 | 553 | ||
552 | element = PHPFunctions.substr(element, 0, indexBrace); |
554 | element = PHPFunctions.substr(element, 0, indexBrace); |
553 | elementLength = element.length(); |
555 | elementLength = element.length(); |
554 | } |
556 | } |
555 | } |
557 | } |
556 | 558 | ||
557 | // Remove any leading or trailing FWS around the element (inside |
559 | // Remove any leading or trailing FWS around the element (inside |
558 | // any comments) |
560 | // any comments) |
559 | element = PHPFunctions.preg_replace( |
561 | element = PHPFunctions.preg_replace( |
560 | "^" + FWS + "|" + FWS + "$", "", element); |
562 | "^" + FWS + "|" + FWS + "$", "", element); |
561 | 563 | ||
562 | // What's left counts towards the maximum length for this part |
564 | // What's left counts towards the maximum length for this part |
563 | if (partLength > 0) |
565 | if (partLength > 0) |
564 | partLength++; // for the dot |
566 | partLength++; // for the dot |
565 | partLength += element.length(); |
567 | partLength += element.length(); |
566 | 568 | ||
567 | // The DNS defines domain name syntax very generally -- a |
569 | // The DNS defines domain name syntax very generally -- a |
568 | // string of labels each containing up to 63 8-bit octets, |
570 | // string of labels each containing up to 63 8-bit octets, |
569 | // separated by dots, and with a maximum total of 255 |
571 | // separated by dots, and with a maximum total of 255 |
570 | // octets. |
572 | // octets. |
571 | // (http://tools.ietf.org/html/rfc1123#section-6.1.3.5) |
573 | // (http://tools.ietf.org/html/rfc1123#section-6.1.3.5) |
572 | if (elementLength > 63) { |
574 | if (elementLength > 63) { |
573 | // Label must be 63 characters or less |
575 | // Label must be 63 characters or less |
574 | return EMailSyntaxDiagnosis.ISEMAIL_DOMAINELEMENTTOOLONG; |
576 | return EMailSyntaxDiagnosis.ISEMAIL_DOMAINELEMENTTOOLONG; |
575 | } |
577 | } |
576 | 578 | ||
577 | // Any ASCII graphic (printing) character other than the |
579 | // Any ASCII graphic (printing) character other than the |
578 | // at-sign ("@"), backslash, double quote, comma, or square |
580 | // at-sign ("@"), backslash, double quote, comma, or square |
579 | // brackets may |
581 | // brackets may |
580 | // appear without quoting. If any of that list of excluded |
582 | // appear without quoting. If any of that list of excluded |
581 | // characters |
583 | // characters |
582 | // are to appear, they must be quoted |
584 | // are to appear, they must be quoted |
583 | // (http://tools.ietf.org/html/rfc3696#section-3) |
585 | // (http://tools.ietf.org/html/rfc3696#section-3) |
584 | // |
586 | // |
585 | // If the hyphen is used, it is not permitted to appear at |
587 | // If the hyphen is used, it is not permitted to appear at |
586 | // either the beginning or end of a label. |
588 | // either the beginning or end of a label. |
587 | // (http://tools.ietf.org/html/rfc3696#section-2) |
589 | // (http://tools.ietf.org/html/rfc3696#section-2) |
588 | // |
590 | // |
589 | // Any excluded characters? i.e. 0x00-0x20, (, ), <, >, [, ], :, |
591 | // Any excluded characters? i.e. 0x00-0x20, (, ), <, >, [, ], :, |
590 | // ;, @, \, comma, period, " |
592 | // ;, @, \, comma, period, " |
591 | 593 | ||
592 | if (PHPFunctions.preg_match( |
594 | if (PHPFunctions.preg_match( |
593 | "[\\x00-\\x20\\(\\)<>\\[\\]:;@\\\\,\\.\"]|^-|-$", |
595 | "[\\x00-\\x20\\(\\)<>\\[\\]:;@\\\\,\\.\"]|^-|-$", |
594 | element) > 0) { |
596 | element) > 0) { |
595 | return EMailSyntaxDiagnosis.ISEMAIL_DOMAINBADCHAR; |
597 | return EMailSyntaxDiagnosis.ISEMAIL_DOMAINBADCHAR; |
596 | } |
598 | } |
597 | } |
599 | } |
598 | 600 | ||
599 | if (partLength > 255) { |
601 | if (partLength > 255) { |
600 | // Domain part must be 255 characters or less |
602 | // Domain part must be 255 characters or less |
601 | // (http://tools.ietf.org/html/rfc1123#section-6.1.3.5) |
603 | // (http://tools.ietf.org/html/rfc1123#section-6.1.3.5) |
602 | return EMailSyntaxDiagnosis.ISEMAIL_DOMAINTOOLONG; |
604 | return EMailSyntaxDiagnosis.ISEMAIL_DOMAINTOOLONG; |
603 | } |
605 | } |
604 | 606 | ||
605 | if (PHPFunctions.preg_match("^[0-9]+$", lastElement) > 0) { |
607 | if (PHPFunctions.preg_match("^[0-9]+$", lastElement) > 0) { |
606 | // TLD can't be all-numeric |
608 | // TLD can't be all-numeric |
607 | // (http://www.apps.ietf.org/rfc/rfc3696.html#sec-2) |
609 | // (http://www.apps.ietf.org/rfc/rfc3696.html#sec-2) |
608 | return EMailSyntaxDiagnosis.ISEMAIL_TLDNUMERIC; |
610 | return EMailSyntaxDiagnosis.ISEMAIL_TLDNUMERIC; |
609 | } |
611 | } |
610 | 612 | ||
611 | // Check DNS? |
613 | // Check DNS? |
612 | if (checkDNS) { |
614 | if (checkDNS) { |
613 | try { |
615 | try { |
614 | if (!((DNSLookup.doLookup(domain, DNSType.A) > 0) || (DNSLookup |
616 | if (!((DNSLookup.doLookup(domain, DNSType.A) > 0) || (DNSLookup |
615 | .doLookup(domain, DNSType.MX) > 0))) { |
617 | .doLookup(domain, DNSType.MX) > 0))) { |
616 | // Domain doesn't actually exist |
618 | // Domain doesn't actually exist |
617 | return EMailSyntaxDiagnosis.ISEMAIL_DOMAINNOTFOUND; |
619 | return EMailSyntaxDiagnosis.ISEMAIL_DOMAINNOTFOUND; |
618 | } |
620 | } |
619 | } catch (NamingException e) { |
621 | } catch (NamingException e) { |
- | 622 | // Resp.: Internal error |
|
620 | return EMailSyntaxDiagnosis.ISEMAIL_DOMAINNOTFOUND; |
623 | return EMailSyntaxDiagnosis.ISEMAIL_DOMAINNOTFOUND; |
621 | } |
624 | } |
622 | } |
625 | } |
623 | } |
626 | } |
624 | 627 | ||
625 | // Eliminate all other factors, and the one which remains must be the |
628 | // Eliminate all other factors, and the one which remains must be the |
626 | // truth. (Sherlock Holmes, The Sign of Four) |
629 | // truth. (Sherlock Holmes, The Sign of Four) |
627 | return EMailSyntaxDiagnosis.ISEMAIL_VALID; |
630 | return EMailSyntaxDiagnosis.ISEMAIL_VALID; |
628 | } |
631 | } |
629 | 632 | ||
630 | /** |
633 | /** |
631 | * Replaces a char in a String |
634 | * Replaces a char in a String |
632 | * |
635 | * |
633 | * @param s |
636 | * @param s |
634 | * The input string |
637 | * The input string |
635 | * @param pos |
638 | * @param pos |
636 | * The position of the char to be replaced |
639 | * The position of the char to be replaced |
637 | * @param c |
640 | * @param c |
638 | * The new char |
641 | * The new char |
639 | * @return The new String |
642 | * @return The new String |
640 | * @see http://www.rgagnon.com/javadetails/java-0030.html |
643 | * @see Source: http://www.rgagnon.com/javadetails/java-0030.html |
641 | */ |
644 | */ |
642 | public static String replaceCharAt(String s, int pos, char c) { |
645 | public static String replaceCharAt(String s, int pos, char c) { |
643 | return s.substring(0, pos) + c + s.substring(pos + 1); |
646 | return s.substring(0, pos) + c + s.substring(pos + 1); |
644 | } |
647 | } |
645 | 648 | ||
646 | private IsEMail() { |
649 | private IsEMail() { |
647 | } |
650 | } |
648 | } |
651 | } |
649 | 652 |