Subversion Repositories javautils

Rev

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