Subversion Repositories javautils

Rev

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