Subversion Repositories javautils

Rev

Rev 4 | Go to most recent revision | Details | Compare with Previous | Last modification | View Log | RSS feed

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