mirror of
https://github.com/dlang/phobos.git
synced 2025-05-04 09:00:22 +03:00
1371 lines
No EOL
66 KiB
D
1371 lines
No EOL
66 KiB
D
/**
|
||
* To validate an email address according to RFCs 5321, 5322 and others
|
||
*
|
||
* Copyright © 2008-2011, Dominic Sayers$(BR)
|
||
* Test schema documentation Copyright © 2011, Daniel Marschall$(BR)
|
||
* All rights reserved.
|
||
*
|
||
* Permission is hereby granted, free of charge, to any person or organization
|
||
* obtaining a copy of the software and accompanying documentation covered by
|
||
* this license (the "Software") to use, reproduce, display, distribute,
|
||
* execute, and transmit the Software, and to prepare derivative works of the
|
||
* Software, and to permit third-parties to whom the Software is furnished to
|
||
* do so, all subject to the following:
|
||
*
|
||
* The copyright notices in the Software and this entire statement, including
|
||
* the above license grant, this restriction and the following disclaimer,
|
||
* must be included in all copies of the Software, in whole or in part, and
|
||
* all derivative works of the Software, unless such copies or derivative
|
||
* works are solely in the form of machine-executable object code generated by
|
||
* a source language processor.
|
||
*
|
||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||
* FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
|
||
* SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
|
||
* FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
|
||
* ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||
* DEALINGS IN THE SOFTWARE.
|
||
*
|
||
* Authors: Dominic Sayers <dominic@sayers.cc>, Jacob Carlborg
|
||
* Copyright: Copyright (c) 2008-2011 Dominic Sayers. All rights reserved.
|
||
* License: $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost Software License 1.0)
|
||
* Link: $(LINK http://www.dominicsayers.com/isemail)
|
||
* Version: 3.0.13 - Version 3.0
|
||
*
|
||
* Port to the D programming language:
|
||
* Jacob Carlborg
|
||
*/
|
||
//module std.net.isemail;
|
||
|
||
static import std.algorithm;
|
||
import std.array;
|
||
import std.regex;
|
||
import std.stdio;
|
||
import std.string;
|
||
import std.traits;
|
||
import std.conv;
|
||
import std.utf;
|
||
|
||
alias std.algorithm.canFind contains;
|
||
|
||
// This $(D_KEYWORD template) will use the buildSelector $(D_KEYWORD template) to build
|
||
|
||
/**
|
||
* Returns the integer value of the first character in the given string.
|
||
*
|
||
* Examples:
|
||
* ---
|
||
* assert("abcde".firstChar == 71);
|
||
* ---
|
||
*
|
||
* Params:
|
||
* str = the string to get the first character from
|
||
*
|
||
* Returns: the first character as an integer
|
||
*/
|
||
int firstChar (Char) (in Char[] str) if (isSomeChar!(Char))
|
||
{
|
||
return cast(int) str.first;
|
||
}
|
||
|
||
/**
|
||
* Returns the maximum of the values in the given array.
|
||
*
|
||
* Examples:
|
||
* ---
|
||
* assert([1, 2, 3, 4] == 4);
|
||
* assert([3, 5, 9, 2, 5] == 9);
|
||
* assert([7, 13, 9, 12, 0] == 13);
|
||
* ---
|
||
*
|
||
* Params:
|
||
* arr = the array containing the values to return the maximum of
|
||
*
|
||
* Returns: the maximum value
|
||
*/
|
||
T max (T) (T[] arr)
|
||
{
|
||
auto max = arr.first;
|
||
|
||
foreach (i ; 0 .. arr.length - 1)
|
||
max = std.algorithm.max(max, arr[i + 1]);
|
||
|
||
return max;
|
||
}
|
||
|
||
/**
|
||
* Returns the portion of string specified by the $(D_PARAM start) and
|
||
* $(D_PARAM length) parameters.
|
||
*
|
||
* Examples:
|
||
* ---
|
||
* assert("abcdef".substr(-1) == "f");
|
||
* assert("abcdef".substr(-2) == "ef");
|
||
* assert("abcdef".substr(-3, 1) == "d");
|
||
* ---
|
||
*
|
||
* Params:
|
||
* str = the input string. Must be one character or longer.
|
||
* start = if $(D_PARAM start) is non-negative, the returned string will start at the
|
||
* $(D_PARAM start)'th position in $(D_PARAM str), counting from zero.
|
||
* For instance, in the string "abcdef", the character at position 0 is 'a',
|
||
* the character at position 2 is 'c', and so forth.
|
||
*
|
||
* If $(D_PARAM start) is negative, the returned string will start at the
|
||
* $(D_PARAM start)'th character from the end of $(D_PARAM str).
|
||
*
|
||
* If $(D_PARAM str) is less than or equal to $(D_PARAM start) characters long,
|
||
* $(D_KEYWORD true) will be returned.
|
||
*
|
||
* length = if $(D_PARAM length) is given and is positive, the string returned will
|
||
* contain at most $(D_PARAM length) characters beginning from $(D_PARAM start)
|
||
* (depending on the length of string).
|
||
*
|
||
* If $(D_PARAM length) is given and is negative, then that many characters
|
||
* will be omitted from the end of string (after the start position has been
|
||
* calculated when a $(D_PARAM start) is negative). If $(D_PARAM start)
|
||
* denotes the position of this truncation or beyond, $(D_KEYWORD false)
|
||
* will be returned.
|
||
*
|
||
* If $(D_PARAM length) is given and is 0, an empty string will be returned.
|
||
*
|
||
* If $(D_PARAM length) is omitted, the substring starting from $(D_PARAM start)
|
||
* until the end of the string will be returned.
|
||
*
|
||
* Returns: the extracted part of string, or an empty string.
|
||
*/
|
||
T[] substr (T) (T[] str, sizediff_t start = 0, sizediff_t length = sizediff_t.min)
|
||
{
|
||
sizediff_t end = length;
|
||
|
||
if (start < 0)
|
||
{
|
||
start = str.length + start;
|
||
|
||
if (end < 0)
|
||
{
|
||
if (end == sizediff_t.min)
|
||
end = 0;
|
||
|
||
end = str.length + end;
|
||
}
|
||
|
||
|
||
else
|
||
end = start + end;
|
||
}
|
||
|
||
else
|
||
{
|
||
if (end == sizediff_t.min)
|
||
end = str.length;
|
||
|
||
if (end < 0)
|
||
end = str.length + end;
|
||
}
|
||
|
||
return str[start .. end];
|
||
}
|
||
|
||
/**
|
||
* Compare the two given strings lexicographically. An upper limit of the number of
|
||
* characters, that will be used in the comparison, can be specified. Supports both
|
||
* case-sensitive and case-insensitive comparison.
|
||
*
|
||
* Examples:
|
||
* ---
|
||
* assert("abc".compareFirstN("abcdef", 3) == 0);
|
||
* assert("abc".compareFirstN("Abc", 3, true) == 0);
|
||
* assert("abc".compareFirstN("abcdef", 6) < 0);
|
||
* assert("abcdef".compareFirstN("abc", 6) > 0);
|
||
* ---
|
||
*
|
||
* Params:
|
||
* s1 = the first string to be compared
|
||
* s2 = the second string to be compared
|
||
* length = the length of strings to be used in the comparison.
|
||
* caseInsensitive = if true, a case-insensitive comparison will be made,
|
||
* otherwise a case-sensitive comparison will be made
|
||
*
|
||
* Returns: (for $(D pred = "a < b")):
|
||
*
|
||
* $(BOOKTABLE,
|
||
* $(TR $(TD $(D < 0)) $(TD $(D s1 < s2) ))
|
||
* $(TR $(TD $(D = 0)) $(TD $(D s1 == s2)))
|
||
* $(TR $(TD $(D > 0)) $(TD $(D s1 > s2)))
|
||
* )
|
||
*/
|
||
int compareFirstN (alias pred = "a < b", S1, S2) (S1 s1, S2 s2, size_t length, bool caseInsensitive = false) if (is(Unqual!(std.algorithm.ElementType!S1) == dchar) && is(Unqual!(std.algorithm.ElementType!S2) == dchar))
|
||
{
|
||
auto s1End = length <= s1.length ? length : s1.length;
|
||
auto s2End = length <= s2.length ? length : s2.length;
|
||
|
||
auto slice1 = s1[0 .. s1End];
|
||
auto slice2 = s2[0 .. s2End];
|
||
|
||
return caseInsensitive ? slice1.icmp(slice2) : slice1.cmp(slice2);
|
||
}
|
||
|
||
/**
|
||
* Returns a range consisting of the elements of the $(D_PARAM input) range that
|
||
* matches the given $(D_PARAM pattern).
|
||
*
|
||
* Examples:
|
||
* ---
|
||
* assert(equal(["ab", "0a", "cd", "1b"].grep(regexp("\d\w")), ["0a", "1b"]));
|
||
* assert(equal(["abc", "0123", "defg", "4567"].grep(regexp("(\w+)"), true), ["0123", "4567"]));
|
||
* ---
|
||
*
|
||
* Params:
|
||
* input = the input range
|
||
* pattern = the regular expression pattern to search for
|
||
* invert = if $(D_KEYWORD true), this function returns the elements of the
|
||
* input range that do $(B not) match the given $(D_PARAM pattern).
|
||
*
|
||
* Returns: a range containing the matched elements
|
||
*/
|
||
auto grep (Range, Regex) (Range input, Regex pattern, bool invert = false)
|
||
{
|
||
auto dg = invert ? (std.algorithm.ElementType!(Range) e) { return e.match(pattern).empty; } :
|
||
(std.algorithm.ElementType!(Range) e) { return !e.match(pattern).empty; };
|
||
|
||
return std.algorithm.filter!(dg)(input);
|
||
}
|
||
|
||
/**
|
||
* Pops the last element of the given range and returns the element.
|
||
*
|
||
* Examples:
|
||
* ---
|
||
* auto array = [0, 1, 2, 3];
|
||
* auto result = array.pop;
|
||
*
|
||
* assert(array == [0, 1, 2]);
|
||
* assert(result == 3);
|
||
* ---
|
||
*
|
||
* Params:
|
||
* range = the range to pop the element from
|
||
*
|
||
* Returns: the popped element
|
||
*/
|
||
std.algorithm.ElementType!(A) pop (A) (ref A a) if (isDynamicArray!A && !isNarrowString!A && isMutable!A && !is(A == void[]))
|
||
{
|
||
auto e = a.last;
|
||
a.popBack;
|
||
return e;
|
||
}
|
||
|
||
/**
|
||
* Returns the character at the given index as a string. The returned string will be a
|
||
* slice of the original string.
|
||
*
|
||
* Examples:
|
||
* ---
|
||
* assert("abc".get(1, 'b') == "b");
|
||
* assert("löv".get(1, 'ö') == "ö");
|
||
* ---
|
||
*
|
||
* Params:
|
||
* str = the string to get the character from
|
||
* index = the index of the character to get
|
||
* c = the character to return, or any other of the same length
|
||
*
|
||
* Returns: the character at the given index as a string
|
||
*/
|
||
T[] get (T) (T[] str, size_t index, dchar c)
|
||
{
|
||
return str[index .. index + codeLength!(T)(c)];
|
||
}
|
||
|
||
// issue 4673
|
||
bool isNumeric (dchar c)
|
||
{
|
||
switch (c)
|
||
{
|
||
case 'i':
|
||
case '.':
|
||
case '-':
|
||
case '+':
|
||
case 'u':
|
||
case 'l':
|
||
case 'L':
|
||
case 'U':
|
||
case 'I':
|
||
return false;
|
||
|
||
default:
|
||
}
|
||
|
||
return std.string.isNumeric(c);
|
||
}
|
||
|
||
alias writeln println;
|
||
alias front first;
|
||
alias back last;
|
||
alias popFront shift;
|
||
|
||
/**
|
||
* An email status code, indicating if an email address is valid or not.
|
||
* If it is invalid it also indicates why.
|
||
*/
|
||
enum EmailStatusCode
|
||
{
|
||
// Categories
|
||
ValidCategory = 1,
|
||
DnsWarning = 7,
|
||
Rfc5321 = 15,
|
||
CFoldingWhitespace = 31,
|
||
Deprecated = 63,
|
||
Rfc5322 = 127,
|
||
Error = 255,
|
||
|
||
// Diagnoses
|
||
// Address is valid
|
||
Valid = 0,
|
||
|
||
// Address is valid but a DNS check was not successful
|
||
DnsWarningNoMXRecord = 5,
|
||
DnsWarningNoRecord = 6,
|
||
|
||
// Address is valid for SMTP but has unusual elements
|
||
Rfc5321TopLevelDomain = 9,
|
||
Rfc5321TopLevelDomainNumeric = 10,
|
||
Rfc5321QuotedString = 11,
|
||
Rfc5321AddressLiteral = 12,
|
||
Rfc5321IpV6Deprecated = 13,
|
||
|
||
// Address is valid within the message but cannot be used unmodified for the envelope
|
||
Comment = 17,
|
||
FoldingWhitespace = 18,
|
||
|
||
// Address contains deprecated elements but may still be valid in restricted contexts
|
||
DeprecatedLocalPart = 33,
|
||
DeprecatedFoldingWhitespace = 34,
|
||
DeprecatedQuotedText = 35,
|
||
DeprecatedQuotedPair = 36,
|
||
DeprecatedComment = 37,
|
||
DeprecatedCommentText = 38,
|
||
DeprecatedCommentFoldingWhitespaceNearAt = 49,
|
||
|
||
// The address is only valid according to the broad definition of RFC 5322. It is otherwise invalid.
|
||
Rfc5322Domain = 65,
|
||
Rfc5322TooLong = 66,
|
||
Rfc5322LocalTooLong = 67,
|
||
Rfc5322DomainTooLong = 68,
|
||
Rfc5322LabelTooLong = 69,
|
||
Rfc5322DomainLiteral = 70,
|
||
Rfc5322DomainLiteralObsoleteText = 71,
|
||
Rfc5322IpV6GroupCount = 72,
|
||
Rfc5322IpV6TooManyDoubleColons = 73,
|
||
Rfc5322IpV6BadChar = 74,
|
||
Rfc5322IpV6MaxGroups = 75,
|
||
Rfc5322IpV6ColonStart = 76,
|
||
Rfc5322IpV6ColonEnd = 77,
|
||
|
||
// Address is invalid for any purpose
|
||
ErrorExpectingDomainText = 129,
|
||
ErrorNoLocalPart = 130,
|
||
ErrorNoDomain = 131,
|
||
ErrorConsecutiveDots = 132,
|
||
ErrorTextAfterCommentFoldingWhitespace = 133,
|
||
ErrorTextAfterQuotedString = 134,
|
||
ErrorTextAfterDomainLiteral = 135,
|
||
ErrorExpectingQuotedPair = 136,
|
||
ErrorExpectingText = 137,
|
||
ErrorExpectingQuotedText = 138,
|
||
ErrorExpectingCommentText = 139,
|
||
ErrorBackslashEnd = 140,
|
||
ErrorDotStart = 141,
|
||
ErrorDotEnd = 142,
|
||
ErrorDomainHyphenStart = 143,
|
||
ErrorDomainHyphenEnd = 144,
|
||
ErrorUnclosedQuotedString = 145,
|
||
ErrorUnclosedComment = 146,
|
||
ErrorUnclosedDomainLiteral = 147,
|
||
ErrorFoldingWhitespaceCrflX2 = 148,
|
||
ErrorFoldingWhitespaceCrLfEnd = 149,
|
||
ErrorCrNoLf = 150,
|
||
|
||
Threshold = 16
|
||
}
|
||
|
||
/// Error levels for the isEmail function
|
||
enum ErrorLevel
|
||
{
|
||
Off,
|
||
On,
|
||
Warning,
|
||
Error
|
||
}
|
||
|
||
/// Email parts for the isEmail function
|
||
enum EmailPart
|
||
{
|
||
ComponentLocalPart,
|
||
ComponentDomain,
|
||
ComponentLiteral,
|
||
ContextComment,
|
||
ContextFoldingWhitespace,
|
||
ContextQuotedString,
|
||
ContextQuotedPair,
|
||
Status
|
||
}
|
||
|
||
private
|
||
{
|
||
// Miscellaneous string constants
|
||
struct Token
|
||
{
|
||
static:
|
||
|
||
enum
|
||
{
|
||
At = "@",
|
||
Backslash = `\`,
|
||
Dot = ".",
|
||
DoubleQuote = `"`,
|
||
OpenParenthesis = "(",
|
||
CloseParenthesis = ")",
|
||
OpenBracket = "[",
|
||
CloseBracket = "]",
|
||
Hyphen = "-",
|
||
Colon = ":",
|
||
DoubleColon = "::",
|
||
Space = " ",
|
||
Tab = "\t",
|
||
Cr = "\r",
|
||
Lf = "\n",
|
||
IpV6Tag = "IPV6:",
|
||
|
||
// US-ASCII visible characters not valid for atext (http://tools.ietf.org/html/rfc5322#section-3.2.3)
|
||
Specials = `()<>[]:;@\\,."`
|
||
}
|
||
}
|
||
}
|
||
|
||
// This struct represents the status of an email address
|
||
struct EmailStatus
|
||
{
|
||
/// Indicates if the email address is valid or not.
|
||
const bool valid;
|
||
|
||
/// The local part of the email address, that is, the part before the at sign.
|
||
const string localPart;
|
||
|
||
/// The domain part of the email address, that is, the part after the at sign.
|
||
const string domainPart;
|
||
|
||
/// The email status code
|
||
const EmailStatusCode statusCode;
|
||
|
||
alias valid this;
|
||
|
||
/**
|
||
* Params:
|
||
* valid = indicates if the email address is valid or not
|
||
* localPart = the local part of the email address
|
||
* domainPart = the domain part of the email address
|
||
* statusCode = the status code
|
||
*/
|
||
this (bool valid, string localPart, string domainPart, EmailStatusCode statusCode)
|
||
{
|
||
this.valid = valid;
|
||
this.localPart = localPart;
|
||
this.domainPart = domainPart;
|
||
this.statusCode = statusCode;
|
||
}
|
||
|
||
/// Returns the email status as a string
|
||
string status ()
|
||
{
|
||
return "";
|
||
}
|
||
|
||
/// Returns a textual representation of the email status
|
||
string toString ()
|
||
{
|
||
return format("EmailStatus\n{\n\tvalid: %s\n\tlocalPart: %s\n\tdomainPart: %s\n\tstatusCode: %s\n}", valid, localPart, domainPart, statusCode);
|
||
}
|
||
}
|
||
|
||
/**
|
||
* Check that an email address conforms to RFCs 5321, 5322 and others
|
||
*
|
||
* As of Version 3.0, we are now distinguishing clearly between a Mailbox as defined
|
||
* by RFC 5321 and an addr-spec as defined by RFC 5322. Depending on the context,
|
||
* either can be regarded as a valid email address. The RFC 5321 Mailbox specification
|
||
* is more restrictive (comments, white space and obsolete forms are not allowed)
|
||
*
|
||
* Params:
|
||
* email = The email address to check
|
||
* checkDNS = If true then a DNS check for MX records will be made
|
||
* errorLevel = Determines the boundary between valid and invalid addresses.
|
||
* Status codes above this number will be returned as-is,
|
||
* status codes below will be returned as ISEMAIL_VALID. Thus the
|
||
* calling program can simply look for ISEMAIL_VALID if it is
|
||
* only interested in whether an address is valid or not. The
|
||
* errorlevel will determine how "picky" is_email() is about
|
||
* the address.
|
||
*
|
||
* If omitted or passed as false then isEmail() will return
|
||
* true or false rather than an integer error or warning.
|
||
*
|
||
* NB Note the difference between $(D_PARAM errorLevel) = false and
|
||
* $(D_PARAM errorLevel) = 0
|
||
*/
|
||
EmailStatus isEmail (string email, bool checkDNS = false, ErrorLevel errorLevel = ErrorLevel.Off)
|
||
{
|
||
int threshold;
|
||
bool diagnose;
|
||
|
||
if (errorLevel == ErrorLevel.On || errorLevel == ErrorLevel.Off)
|
||
{
|
||
threshold = EmailStatusCode.Valid;
|
||
diagnose = cast(bool) errorLevel;
|
||
}
|
||
|
||
else
|
||
{
|
||
diagnose = true;
|
||
|
||
switch (errorLevel)
|
||
{
|
||
case ErrorLevel.Warning: threshold = EmailStatusCode.Threshold; break;
|
||
case ErrorLevel.Error: threshold = EmailStatusCode.Valid; break;
|
||
default: threshold = errorLevel;
|
||
}
|
||
}
|
||
|
||
auto returnStatus = [EmailStatusCode.Valid];
|
||
auto context = EmailPart.ComponentLocalPart;
|
||
auto contextStack = [context];
|
||
auto contextPrior = context;
|
||
auto token = "";
|
||
auto tokenPrior = "";
|
||
auto parseData = [EmailPart.ComponentLocalPart : "", EmailPart.ComponentDomain : ""];
|
||
auto atomList = [EmailPart.ComponentLocalPart : [""], EmailPart.ComponentDomain : [""]];
|
||
auto elementCount = 0;
|
||
auto elementLength = 0;
|
||
auto hyphenFlag = false;
|
||
auto endOrDie = false;
|
||
auto crlfCount = int.min; // int.min == not defined
|
||
|
||
foreach (i, e ; email)
|
||
{
|
||
token = email.get(i, e);
|
||
|
||
switch (context)
|
||
{
|
||
case EmailPart.ComponentLocalPart:
|
||
switch (token)
|
||
{
|
||
case Token.OpenParenthesis:
|
||
if (elementLength == 0)
|
||
returnStatus ~= elementCount == 0 ? EmailStatusCode.Comment : EmailStatusCode.DeprecatedComment;
|
||
|
||
else
|
||
{
|
||
returnStatus ~= EmailStatusCode.Comment;
|
||
endOrDie = true;
|
||
}
|
||
|
||
contextStack ~= context;
|
||
context = EmailPart.ContextComment;
|
||
break;
|
||
|
||
case Token.Dot:
|
||
if (elementLength == 0)
|
||
returnStatus ~= elementCount == 0 ? EmailStatusCode.ErrorDotStart : EmailStatusCode.ErrorConsecutiveDots;
|
||
|
||
else
|
||
{
|
||
if (endOrDie)
|
||
returnStatus ~= EmailStatusCode.DeprecatedLocalPart;
|
||
}
|
||
|
||
endOrDie = false;
|
||
elementLength = 0;
|
||
elementCount++;
|
||
parseData[EmailPart.ComponentLocalPart] ~= token;
|
||
|
||
if (elementCount >= atomList[EmailPart.ComponentLocalPart].length)
|
||
atomList[EmailPart.ComponentLocalPart] ~= "";
|
||
|
||
else
|
||
atomList[EmailPart.ComponentLocalPart][elementCount] = "";
|
||
break;
|
||
|
||
case Token.DoubleQuote:
|
||
if (elementLength == 0)
|
||
{
|
||
returnStatus ~= elementCount == 0 ? EmailStatusCode.Rfc5321QuotedString : EmailStatusCode.DeprecatedLocalPart;
|
||
|
||
parseData[EmailPart.ComponentLocalPart] ~= token;
|
||
atomList[EmailPart.ComponentLocalPart][elementCount] ~= token;
|
||
elementLength++;
|
||
endOrDie = true;
|
||
contextStack ~= context;
|
||
context = EmailPart.ContextQuotedString;
|
||
}
|
||
|
||
else
|
||
returnStatus ~= EmailStatusCode.ErrorExpectingText;
|
||
break;
|
||
|
||
case Token.Cr:
|
||
case Token.Space:
|
||
case Token.Tab:
|
||
if ((token == Token.Cr) && ((++i == email.length) || (email.get(i, e) != Token.Lf)))
|
||
{
|
||
returnStatus ~= EmailStatusCode.ErrorCrNoLf;
|
||
break;
|
||
}
|
||
|
||
if (elementLength == 0)
|
||
returnStatus ~= elementCount == 0 ? EmailStatusCode.FoldingWhitespace : EmailStatusCode.DeprecatedFoldingWhitespace;
|
||
|
||
else
|
||
endOrDie = true;
|
||
|
||
contextStack ~= context;
|
||
context = EmailPart.ContextFoldingWhitespace;
|
||
tokenPrior = token;
|
||
break;
|
||
|
||
case Token.At:
|
||
if (contextStack.length != 1)
|
||
throw new Exception("Unexpected item on context stack");
|
||
|
||
if (parseData[EmailPart.ComponentLocalPart] == "")
|
||
returnStatus ~= EmailStatusCode.ErrorNoLocalPart;
|
||
|
||
else if (elementLength == 0)
|
||
returnStatus ~= EmailStatusCode.ErrorDotEnd;
|
||
|
||
else if (parseData[EmailPart.ComponentLocalPart].length > 64)
|
||
returnStatus ~= EmailStatusCode.Rfc5322LocalTooLong;
|
||
|
||
else if (contextPrior == EmailPart.ContextComment || contextPrior == EmailPart.ContextFoldingWhitespace)
|
||
returnStatus ~= EmailStatusCode.DeprecatedCommentFoldingWhitespaceNearAt;
|
||
|
||
context = EmailPart.ComponentDomain;
|
||
contextStack = [context];
|
||
elementCount = 0;
|
||
elementLength = 0;
|
||
endOrDie = false;
|
||
break;
|
||
|
||
default:
|
||
if (endOrDie)
|
||
{
|
||
switch (contextPrior)
|
||
{
|
||
case EmailPart.ContextComment:
|
||
case EmailPart.ContextFoldingWhitespace:
|
||
returnStatus ~= EmailStatusCode.ErrorTextAfterCommentFoldingWhitespace;
|
||
break;
|
||
|
||
case EmailPart.ContextQuotedString:
|
||
returnStatus ~= EmailStatusCode.ErrorTextAfterQuotedString;
|
||
break;
|
||
|
||
default:
|
||
throw new Exception("More text found where none is allowed, but unrecognised prior context: " ~ to!(string)(contextPrior));
|
||
}
|
||
}
|
||
|
||
else
|
||
{
|
||
contextPrior = context;
|
||
auto ord = token.firstChar;
|
||
|
||
if (ord < 33 || ord > 126 || ord == 10 || Token.Specials.contains(token))
|
||
returnStatus ~= EmailStatusCode.ErrorExpectingText;
|
||
|
||
parseData[EmailPart.ComponentLocalPart] ~= token;
|
||
atomList[EmailPart.ComponentLocalPart][elementCount] ~= token;
|
||
elementLength++;
|
||
}
|
||
}
|
||
break;
|
||
|
||
case EmailPart.ComponentDomain:
|
||
switch (token)
|
||
{
|
||
case Token.OpenParenthesis:
|
||
if (elementLength == 0)
|
||
returnStatus ~= elementCount == 0 ? EmailStatusCode.DeprecatedCommentFoldingWhitespaceNearAt : EmailStatusCode.DeprecatedComment;
|
||
|
||
else
|
||
{
|
||
returnStatus ~= EmailStatusCode.Comment;
|
||
endOrDie = true;
|
||
}
|
||
|
||
contextStack ~= context;
|
||
context = EmailPart.ContextComment;
|
||
break;
|
||
|
||
case Token.Dot:
|
||
if (elementLength == 0)
|
||
returnStatus ~= elementCount == 0 ? EmailStatusCode.ErrorDotStart : EmailStatusCode.ErrorConsecutiveDots;
|
||
|
||
else if (hyphenFlag)
|
||
returnStatus ~= EmailStatusCode.ErrorDomainHyphenEnd;
|
||
|
||
else
|
||
{
|
||
if (elementLength > 63)
|
||
returnStatus ~= EmailStatusCode.Rfc5322LabelTooLong;
|
||
}
|
||
|
||
endOrDie = false;
|
||
elementLength = 0,
|
||
elementCount++;
|
||
|
||
//atomList[EmailPart.ComponentDomain][elementCount] = "";
|
||
atomList[EmailPart.ComponentDomain] ~= "";
|
||
parseData[EmailPart.ComponentDomain] ~= token;
|
||
break;
|
||
|
||
case Token.OpenBracket:
|
||
if (parseData[EmailPart.ComponentDomain] == "")
|
||
{
|
||
endOrDie = true;
|
||
elementLength++;
|
||
contextStack ~= context;
|
||
context = EmailPart.ComponentLiteral;
|
||
parseData[EmailPart.ComponentDomain] ~= token;
|
||
atomList[EmailPart.ComponentDomain][elementCount] ~= token;
|
||
parseData[EmailPart.ComponentLiteral] = "";
|
||
}
|
||
|
||
else
|
||
returnStatus ~= EmailStatusCode.ErrorExpectingText;
|
||
break;
|
||
|
||
case Token.Cr:
|
||
case Token.Space:
|
||
case Token.Tab:
|
||
if (token == Token.Cr && (++i == email.length || email.get(i, e) != Token.Lf))
|
||
{
|
||
returnStatus ~= EmailStatusCode.ErrorCrNoLf;
|
||
break;
|
||
}
|
||
|
||
if (elementLength == 0)
|
||
returnStatus ~= elementCount == 0 ? EmailStatusCode.DeprecatedCommentFoldingWhitespaceNearAt : EmailStatusCode.DeprecatedFoldingWhitespace;
|
||
|
||
else
|
||
{
|
||
returnStatus ~= EmailStatusCode.FoldingWhitespace;
|
||
endOrDie = true;
|
||
}
|
||
|
||
contextStack ~= context;
|
||
context = EmailPart.ContextFoldingWhitespace;
|
||
tokenPrior = token;
|
||
break;
|
||
|
||
default:
|
||
if (endOrDie)
|
||
{
|
||
switch (contextPrior)
|
||
{
|
||
case EmailPart.ContextComment:
|
||
case EmailPart.ContextFoldingWhitespace:
|
||
returnStatus ~= EmailStatusCode.ErrorTextAfterCommentFoldingWhitespace;
|
||
break;
|
||
|
||
case EmailPart.ComponentLiteral:
|
||
returnStatus ~= EmailStatusCode.ErrorTextAfterDomainLiteral;
|
||
break;
|
||
|
||
default:
|
||
throw new Exception("More text found where none is allowed, but unrecognised prior context: " ~ to!(string)(contextPrior));
|
||
}
|
||
|
||
}
|
||
|
||
auto ord = token.firstChar;
|
||
hyphenFlag = false;
|
||
|
||
if (ord < 33 || ord > 126 || Token.Specials.contains(token))
|
||
returnStatus ~= EmailStatusCode.ErrorExpectingText;
|
||
|
||
else if (token == Token.Hyphen)
|
||
{
|
||
if (elementLength == 0)
|
||
returnStatus ~= EmailStatusCode.ErrorDomainHyphenStart;
|
||
|
||
hyphenFlag = true;
|
||
}
|
||
|
||
else if (!((ord > 47 && ord < 58) || (ord > 64 && ord < 91) || (ord > 96 && ord < 123)))
|
||
returnStatus ~= EmailStatusCode.Rfc5322Domain;
|
||
|
||
parseData[EmailPart.ComponentDomain] ~= token;
|
||
atomList[EmailPart.ComponentDomain][elementCount] ~= token;
|
||
elementLength++;
|
||
}
|
||
break;
|
||
|
||
case EmailPart.ComponentLiteral:
|
||
switch (token)
|
||
{
|
||
case Token.CloseBracket:
|
||
if (returnStatus.max < EmailStatusCode.Deprecated)
|
||
{
|
||
auto maxGroups = 8;
|
||
auto index = -1;
|
||
auto addressLiteral = parseData[EmailPart.ComponentLiteral];
|
||
auto matchesIp = array(addressLiteral.match(regex(`\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]?)$`)).captures);
|
||
|
||
if (!matchesIp.empty)
|
||
{
|
||
index = addressLiteral.lastIndexOf(matchesIp.first);
|
||
|
||
if (index != 0)
|
||
addressLiteral = addressLiteral.substr(0, index) ~ "0:0";
|
||
}
|
||
|
||
if (index == 0)
|
||
returnStatus ~= EmailStatusCode.Rfc5321AddressLiteral;
|
||
|
||
else if (addressLiteral.compareFirstN(Token.IpV6Tag, 5, true))
|
||
returnStatus ~= EmailStatusCode.Rfc5322DomainLiteral;
|
||
|
||
else
|
||
{
|
||
auto ipV6 = addressLiteral.substr(5);
|
||
matchesIp = ipV6.split(Token.Colon);
|
||
auto groupCount = matchesIp.length;
|
||
index = ipV6.indexOf(Token.DoubleColon);
|
||
|
||
if (index == -1)
|
||
{
|
||
if (groupCount != maxGroups)
|
||
returnStatus ~= EmailStatusCode.Rfc5322IpV6GroupCount;
|
||
}
|
||
|
||
else
|
||
{
|
||
if (index != ipV6.lastIndexOf(Token.DoubleColon))
|
||
returnStatus ~= EmailStatusCode.Rfc5322IpV6TooManyDoubleColons;
|
||
|
||
else
|
||
{
|
||
if (index == 0 || index == (ipV6.length - 2))
|
||
maxGroups++;
|
||
|
||
if (groupCount > maxGroups)
|
||
returnStatus ~= EmailStatusCode.Rfc5322IpV6MaxGroups;
|
||
|
||
else if (groupCount == maxGroups)
|
||
returnStatus ~= EmailStatusCode.Rfc5321IpV6Deprecated;
|
||
}
|
||
}
|
||
|
||
if (ipV6.substr(0, 1) == Token.Colon && ipV6.substr(1, 1) != Token.Colon)
|
||
returnStatus ~= EmailStatusCode.Rfc5322IpV6ColonStart;
|
||
|
||
else if (ipV6.substr(-1) == Token.Colon && ipV6.substr(-2, -1) != Token.Colon)
|
||
returnStatus ~= EmailStatusCode.Rfc5322IpV6ColonEnd;
|
||
|
||
else if (!matchesIp.grep(regex(`^[0-9A-Fa-f]{0,4}$`), true).empty)
|
||
returnStatus ~= EmailStatusCode.Rfc5322IpV6BadChar;
|
||
|
||
else
|
||
returnStatus ~= EmailStatusCode.Rfc5321AddressLiteral;
|
||
}
|
||
}
|
||
|
||
else
|
||
returnStatus ~= EmailStatusCode.Rfc5322DomainLiteral;
|
||
|
||
parseData[EmailPart.ComponentDomain] ~= token;
|
||
atomList[EmailPart.ComponentDomain][elementCount] ~= token;
|
||
elementLength++;
|
||
contextPrior = context;
|
||
context = contextStack.pop;
|
||
break;
|
||
|
||
case Token.Backslash:
|
||
returnStatus ~= EmailStatusCode.Rfc5322DomainLiteralObsoleteText;
|
||
contextStack ~= context;
|
||
context = EmailPart.ContextQuotedPair;
|
||
break;
|
||
|
||
case Token.Cr:
|
||
case Token.Space:
|
||
case Token.Tab:
|
||
if (token == Token.Cr && (++i == email.length || email.get(i, e) != Token.Lf))
|
||
{
|
||
returnStatus ~= EmailStatusCode.ErrorCrNoLf;
|
||
break;
|
||
}
|
||
|
||
returnStatus ~= EmailStatusCode.FoldingWhitespace;
|
||
contextStack ~= context;
|
||
context = EmailPart.ContextFoldingWhitespace;
|
||
tokenPrior = token;
|
||
break;
|
||
|
||
default:
|
||
auto ord = token.firstChar;
|
||
|
||
if (ord > 127 || ord == 0 || token == Token.OpenBracket)
|
||
{
|
||
returnStatus ~= EmailStatusCode.ErrorExpectingDomainText;
|
||
break;
|
||
}
|
||
|
||
else if (ord < 33 || ord == 127)
|
||
returnStatus ~= EmailStatusCode.Rfc5322DomainLiteralObsoleteText;
|
||
|
||
parseData[EmailPart.ComponentLiteral] ~= token;
|
||
parseData[EmailPart.ComponentDomain] ~= token;
|
||
atomList[EmailPart.ComponentDomain][elementCount] ~= token;
|
||
elementLength++;
|
||
}
|
||
break;
|
||
|
||
case EmailPart.ContextQuotedString:
|
||
switch (token)
|
||
{
|
||
case Token.Backslash:
|
||
contextStack ~= context;
|
||
context = EmailPart.ContextQuotedPair;
|
||
break;
|
||
|
||
case Token.Cr:
|
||
case Token.Tab:
|
||
if (token == Token.Cr && (++i == email.length || email.get(i, e) != Token.Lf))
|
||
{
|
||
returnStatus ~= EmailStatusCode.ErrorCrNoLf;
|
||
break;
|
||
}
|
||
|
||
parseData[EmailPart.ComponentLocalPart] ~= Token.Space;
|
||
atomList[EmailPart.ComponentLocalPart][elementCount] ~= Token.Space;
|
||
elementLength++;
|
||
|
||
returnStatus ~= EmailStatusCode.FoldingWhitespace;
|
||
contextStack ~= context;
|
||
context = EmailPart.ContextFoldingWhitespace;
|
||
tokenPrior = token;
|
||
break;
|
||
|
||
case Token.DoubleQuote:
|
||
parseData[EmailPart.ComponentLocalPart] ~= token;
|
||
atomList[EmailPart.ComponentLocalPart][elementCount] ~= token;
|
||
elementLength++;
|
||
contextPrior = context;
|
||
context = contextStack.pop;
|
||
break;
|
||
|
||
default:
|
||
auto ord = token.firstChar;
|
||
|
||
if (ord > 127 || ord == 0 || ord == 10)
|
||
returnStatus ~= EmailStatusCode.ErrorExpectingQuotedText;
|
||
|
||
else if (ord < 32 || ord == 127)
|
||
returnStatus ~= EmailStatusCode.DeprecatedQuotedText;
|
||
|
||
parseData[EmailPart.ComponentLocalPart] ~= token;
|
||
atomList[EmailPart.ComponentLocalPart][elementCount] ~= token;
|
||
elementLength++;
|
||
}
|
||
break;
|
||
|
||
case EmailPart.ContextQuotedPair:
|
||
auto ord = token.firstChar;
|
||
|
||
if (ord > 127)
|
||
returnStatus ~= EmailStatusCode.ErrorExpectingQuotedPair;
|
||
|
||
else if (ord < 31 && ord != 9 || ord == 127)
|
||
returnStatus ~= EmailStatusCode.DeprecatedQuotedPair;
|
||
|
||
contextPrior = context;
|
||
context = contextStack.pop;
|
||
token = Token.Backslash ~ token;
|
||
|
||
switch (context)
|
||
{
|
||
case EmailPart.ContextComment: break;
|
||
|
||
case EmailPart.ContextQuotedString:
|
||
parseData[EmailPart.ComponentLocalPart] ~= token;
|
||
atomList[EmailPart.ComponentLocalPart][elementCount] ~= token;
|
||
elementLength += 2;
|
||
break;
|
||
|
||
case EmailPart.ComponentLiteral:
|
||
parseData[EmailPart.ComponentDomain] ~= token;
|
||
atomList[EmailPart.ComponentDomain][elementCount] ~= token;
|
||
elementLength += 2;
|
||
break;
|
||
|
||
default:
|
||
throw new Exception("Quoted pair logic invoked in an invalid context: " ~ to!(string)(context));
|
||
}
|
||
break;
|
||
|
||
case EmailPart.ContextComment:
|
||
switch (token)
|
||
{
|
||
case Token.OpenParenthesis:
|
||
contextStack ~= context;
|
||
context = EmailPart.ContextComment;
|
||
break;
|
||
|
||
case Token.CloseParenthesis:
|
||
contextPrior = context;
|
||
context = contextStack.pop;
|
||
break;
|
||
|
||
case Token.Backslash:
|
||
contextStack ~= context;
|
||
context = EmailPart.ContextQuotedPair;
|
||
break;
|
||
|
||
case Token.Cr:
|
||
case Token.Space:
|
||
case Token.Tab:
|
||
if (token == Token.Cr && (i++ == email.length || email.get(i, e) != Token.Lf))
|
||
{
|
||
returnStatus ~= EmailStatusCode.ErrorCrNoLf;
|
||
break;
|
||
}
|
||
|
||
returnStatus ~= EmailStatusCode.FoldingWhitespace;
|
||
|
||
contextStack ~= context;
|
||
context = EmailPart.ContextFoldingWhitespace;
|
||
tokenPrior = token;
|
||
break;
|
||
|
||
default:
|
||
auto ord = token.firstChar;
|
||
|
||
if (ord > 127 || ord == 0 || ord == 10)
|
||
{
|
||
returnStatus ~= EmailStatusCode.ErrorExpectingCommentText;
|
||
break;
|
||
}
|
||
|
||
else if (ord < 32 || ord == 127)
|
||
returnStatus ~= EmailStatusCode.DeprecatedCommentText;
|
||
}
|
||
break;
|
||
|
||
case EmailPart.ContextFoldingWhitespace:
|
||
if (tokenPrior == Token.Cr)
|
||
{
|
||
if (token == Token.Cr)
|
||
{
|
||
returnStatus ~= EmailStatusCode.ErrorFoldingWhitespaceCrflX2;
|
||
break;
|
||
}
|
||
|
||
if (crlfCount != int.min) // int.min == not defined
|
||
{
|
||
if (++crlfCount > 1)
|
||
returnStatus ~= EmailStatusCode.DeprecatedFoldingWhitespace;
|
||
}
|
||
|
||
else
|
||
crlfCount = 1;
|
||
}
|
||
|
||
switch (token)
|
||
{
|
||
case Token.Cr:
|
||
if (++i == email.length || email.get(i, e) != Token.Lf)
|
||
returnStatus ~= EmailStatusCode.ErrorCrNoLf;
|
||
break;
|
||
|
||
case Token.Space:
|
||
case Token.Tab:
|
||
break;
|
||
|
||
default:
|
||
if (tokenPrior == Token.Cr)
|
||
{
|
||
returnStatus ~= EmailStatusCode.ErrorFoldingWhitespaceCrLfEnd;
|
||
break;
|
||
}
|
||
|
||
crlfCount = int.min; // int.min == not defined
|
||
contextPrior = context;
|
||
context = contextStack.pop;
|
||
i--;
|
||
break;
|
||
}
|
||
|
||
tokenPrior = token;
|
||
break;
|
||
|
||
default:
|
||
throw new Exception("Unkown context: " ~ to!(string)(context));
|
||
}
|
||
|
||
if (returnStatus.max > EmailStatusCode.Rfc5322)
|
||
break;
|
||
}
|
||
|
||
if (returnStatus.max < EmailStatusCode.Rfc5322)
|
||
{
|
||
if (context == EmailPart.ContextQuotedString)
|
||
returnStatus ~= EmailStatusCode.ErrorUnclosedQuotedString;
|
||
|
||
else if (context == EmailPart.ContextQuotedPair)
|
||
returnStatus ~= EmailStatusCode.ErrorBackslashEnd;
|
||
|
||
else if (context == EmailPart.ContextComment)
|
||
returnStatus ~= EmailStatusCode.ErrorUnclosedComment;
|
||
|
||
else if (context == EmailPart.ComponentLiteral)
|
||
returnStatus ~= EmailStatusCode.ErrorUnclosedDomainLiteral;
|
||
|
||
else if (token == Token.Cr)
|
||
returnStatus ~= EmailStatusCode.ErrorFoldingWhitespaceCrLfEnd;
|
||
|
||
else if (parseData[EmailPart.ComponentDomain] == "")
|
||
returnStatus ~= EmailStatusCode.ErrorNoDomain;
|
||
|
||
else if (elementLength == 0)
|
||
returnStatus ~= EmailStatusCode.ErrorDotEnd;
|
||
|
||
else if (hyphenFlag)
|
||
returnStatus ~= EmailStatusCode.ErrorDomainHyphenEnd;
|
||
|
||
else if (parseData[EmailPart.ComponentDomain].length > 255)
|
||
returnStatus ~= EmailStatusCode.Rfc5322DomainTooLong;
|
||
|
||
else if ((parseData[EmailPart.ComponentLocalPart] ~ Token.At ~ parseData[EmailPart.ComponentDomain]).length > 254)
|
||
returnStatus ~= EmailStatusCode.Rfc5322TooLong;
|
||
|
||
else if (elementLength > 63)
|
||
returnStatus ~= EmailStatusCode.Rfc5322LabelTooLong;
|
||
}
|
||
|
||
auto dnsChecked = false;
|
||
|
||
if (checkDNS && returnStatus.max < EmailStatusCode.DnsWarning)
|
||
{
|
||
assert(false, "DNS check is currently not implemented");
|
||
}
|
||
|
||
if (!dnsChecked && returnStatus.max < EmailStatusCode.DnsWarning)
|
||
{
|
||
if (elementCount == 0)
|
||
returnStatus ~= EmailStatusCode.Rfc5321TopLevelDomain;
|
||
|
||
if (isNumeric(atomList[EmailPart.ComponentDomain][elementCount].first))
|
||
returnStatus ~= EmailStatusCode.Rfc5321TopLevelDomainNumeric;
|
||
}
|
||
|
||
returnStatus = array(std.algorithm.uniq(returnStatus));
|
||
auto finalStatus = returnStatus.max;
|
||
|
||
if (returnStatus.length != 1)
|
||
returnStatus.shift;
|
||
|
||
parseData[EmailPart.Status] = to!(string)(returnStatus);
|
||
|
||
if (finalStatus < threshold)
|
||
finalStatus = EmailStatusCode.Valid;
|
||
|
||
if (!diagnose)
|
||
finalStatus = finalStatus < EmailStatusCode.Threshold ? EmailStatusCode.Valid : EmailStatusCode.Error;
|
||
|
||
auto valid = finalStatus == EmailStatusCode.Valid;
|
||
auto localPart = "";
|
||
auto domainPart = "";
|
||
|
||
if (auto value = EmailPart.ComponentLocalPart in parseData)
|
||
localPart = *value;
|
||
|
||
if (auto value = EmailPart.ComponentDomain in parseData)
|
||
domainPart = *value;
|
||
|
||
return EmailStatus(valid, localPart, domainPart, finalStatus);
|
||
}
|
||
|
||
unittest
|
||
{
|
||
assert(``.isEmail(false, ErrorLevel.On).statusCode == EmailStatusCode.ErrorNoDomain);
|
||
assert(`test`.isEmail(false, ErrorLevel.On).statusCode == EmailStatusCode.ErrorNoDomain);
|
||
assert(`@`.isEmail(false, ErrorLevel.On).statusCode == EmailStatusCode.ErrorNoLocalPart);
|
||
assert(`test@`.isEmail(false, ErrorLevel.On).statusCode == EmailStatusCode.ErrorNoDomain);
|
||
//assert(`test@io`.isEmail(false, ErrorLevel.On).statusCode == EmailStatusCode.Valid, `io. currently has an MX-record (Feb 2011). Some DNS setups seem to find it, some don't. If you don't see the MX for io. then try setting your DNS server to 8.8.8.8 (the Google DNS server)`);
|
||
assert(`@io`.isEmail(false, ErrorLevel.On).statusCode == EmailStatusCode.ErrorNoLocalPart, `io. currently has an MX-record (Feb 2011)`);
|
||
assert(`@iana.org`.isEmail(false, ErrorLevel.On).statusCode == EmailStatusCode.ErrorNoLocalPart);
|
||
assert(`test@iana.org`.isEmail(false, ErrorLevel.On).statusCode == EmailStatusCode.Valid);
|
||
assert(`test@nominet.org.uk`.isEmail(false, ErrorLevel.On).statusCode == EmailStatusCode.Valid);
|
||
assert(`test@about.museum`.isEmail(false, ErrorLevel.On).statusCode == EmailStatusCode.Valid);
|
||
assert(`a@iana.org`.isEmail(false, ErrorLevel.On).statusCode == EmailStatusCode.Valid);
|
||
//assert(`test@e.com`.isEmail(false, ErrorLevel.On).statusCode == EmailStatusCode.DnsWarningNoRecord); // DNS check is currently not implemented
|
||
//assert(`test@iana.a`.isEmail(false, ErrorLevel.On).statusCode == EmailStatusCode.DnsWarningNoRecord); // DNS check is currently not implemented
|
||
assert(`test.test@iana.org`.isEmail(false, ErrorLevel.On).statusCode == EmailStatusCode.Valid);
|
||
assert(`.test@iana.org`.isEmail(false, ErrorLevel.On).statusCode == EmailStatusCode.ErrorDotStart);
|
||
assert(`test.@iana.org`.isEmail(false, ErrorLevel.On).statusCode == EmailStatusCode.ErrorDotEnd);
|
||
assert(`test..iana.org`.isEmail(false, ErrorLevel.On).statusCode == EmailStatusCode.ErrorConsecutiveDots);
|
||
assert(`test_exa-mple.com`.isEmail(false, ErrorLevel.On).statusCode == EmailStatusCode.ErrorNoDomain);
|
||
assert("!#$%&`*+/=?^`{|}~@iana.org".isEmail(false, ErrorLevel.On).statusCode == EmailStatusCode.Valid);
|
||
assert(`test\@test@iana.org`.isEmail(false, ErrorLevel.On).statusCode == EmailStatusCode.ErrorExpectingText);
|
||
assert(`123@iana.org`.isEmail(false, ErrorLevel.On).statusCode == EmailStatusCode.Valid);
|
||
assert(`test@123.com`.isEmail(false, ErrorLevel.On).statusCode == EmailStatusCode.Valid);
|
||
assert(`test@iana.123`.isEmail(false, ErrorLevel.On).statusCode == EmailStatusCode.Rfc5321TopLevelDomainNumeric);
|
||
assert(`test@255.255.255.255`.isEmail(false, ErrorLevel.On).statusCode == EmailStatusCode.Rfc5321TopLevelDomainNumeric);
|
||
assert(`abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghiklm@iana.org`.isEmail(false, ErrorLevel.On).statusCode == EmailStatusCode.Valid);
|
||
assert(`abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghiklmn@iana.org`.isEmail(false, ErrorLevel.On).statusCode == EmailStatusCode.Rfc5322LocalTooLong);
|
||
//assert(`test@abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghikl.com`.isEmail(false, ErrorLevel.On).statusCode == EmailStatusCode.DnsWarningNoRecord); // DNS check is currently not implemented
|
||
assert(`test@abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghiklm.com`.isEmail(false, ErrorLevel.On).statusCode == EmailStatusCode.Rfc5322LabelTooLong);
|
||
assert(`test@mason-dixon.com`.isEmail(false, ErrorLevel.On).statusCode == EmailStatusCode.Valid);
|
||
assert(`test@-iana.org`.isEmail(false, ErrorLevel.On).statusCode == EmailStatusCode.ErrorDomainHyphenStart);
|
||
assert(`test@iana-.com`.isEmail(false, ErrorLevel.On).statusCode == EmailStatusCode.ErrorDomainHyphenEnd);
|
||
assert(`test@g--a.com`.isEmail(false, ErrorLevel.On).statusCode == EmailStatusCode.Valid);
|
||
//assert(`test@iana.co-uk`.isEmail(false, ErrorLevel.On).statusCode == EmailStatusCode.DnsWarningNoRecord); // DNS check is currently not implemented
|
||
assert(`test@.iana.org`.isEmail(false, ErrorLevel.On).statusCode == EmailStatusCode.ErrorDotStart);
|
||
assert(`test@iana.org.`.isEmail(false, ErrorLevel.On).statusCode == EmailStatusCode.ErrorDotEnd);
|
||
assert(`test@iana..com`.isEmail(false, ErrorLevel.On).statusCode == EmailStatusCode.ErrorConsecutiveDots);
|
||
//assert(`a@a.b.c.d.e.f.g.h.i.j.k.l.m.n.o.p.q.r.s.t.u.v.w.x.y.z.a.b.c.d.e.f.g.h.i.j.k.l.m.n.o.p.q.r.s.t.u.v.w.x.y.z.a.b.c.d.e.f.g.h.i.j.k.l.m.n.o.p.q.r.s.t.u.v.w.x.y.z.a.b.c.d.e.f.g.h.i.j.k.l.m.n.o.p.q.r.s.t.u.v.w.x.y.z.a.b.c.d.e.f.g.h.i.j.k.l.m.n.o.p.q.r.s.t.u.v`.isEmail(false, ErrorLevel.On).statusCode == EmailStatusCode.DnsWarningNoRecord); // DNS check is currently not implemented
|
||
//assert(`abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghiklm@abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghikl.abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghikl.abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghi`.isEmail(false, ErrorLevel.On).statusCode == EmailStatusCode.DnsWarningNoRecord); // DNS check is currently not implemented
|
||
assert(`abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghiklm@abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghikl.abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghikl.abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghij`.isEmail(false, ErrorLevel.On).statusCode == EmailStatusCode.Rfc5322TooLong);
|
||
assert(`a@abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghikl.abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghikl.abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghikl.abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefg.hij`.isEmail(false, ErrorLevel.On).statusCode == EmailStatusCode.Rfc5322TooLong);
|
||
assert(`a@abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghikl.abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghikl.abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghikl.abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefg.hijk`.isEmail(false, ErrorLevel.On).statusCode == EmailStatusCode.Rfc5322DomainTooLong);
|
||
assert(`"test"@iana.org`.isEmail(false, ErrorLevel.On).statusCode == EmailStatusCode.Rfc5321QuotedString);
|
||
assert(`""@iana.org`.isEmail(false, ErrorLevel.On).statusCode == EmailStatusCode.Rfc5321QuotedString);
|
||
assert(`"""@iana.org`.isEmail(false, ErrorLevel.On).statusCode == EmailStatusCode.ErrorExpectingText);
|
||
assert(`"\a"@iana.org`.isEmail(false, ErrorLevel.On).statusCode == EmailStatusCode.Rfc5321QuotedString);
|
||
assert(`"\""@iana.org`.isEmail(false, ErrorLevel.On).statusCode == EmailStatusCode.Rfc5321QuotedString);
|
||
assert(`"\"@iana.org`.isEmail(false, ErrorLevel.On).statusCode == EmailStatusCode.ErrorUnclosedQuotedString);
|
||
assert(`"\\"@iana.org`.isEmail(false, ErrorLevel.On).statusCode == EmailStatusCode.Rfc5321QuotedString);
|
||
assert(`test"@iana.org`.isEmail(false, ErrorLevel.On).statusCode == EmailStatusCode.ErrorExpectingText);
|
||
assert(`"test@iana.org`.isEmail(false, ErrorLevel.On).statusCode == EmailStatusCode.ErrorUnclosedQuotedString);
|
||
assert(`"test"test@iana.org`.isEmail(false, ErrorLevel.On).statusCode == EmailStatusCode.ErrorTextAfterQuotedString);
|
||
assert(`test"text"@iana.org`.isEmail(false, ErrorLevel.On).statusCode == EmailStatusCode.ErrorExpectingText);
|
||
assert(`"test""test"@iana.org`.isEmail(false, ErrorLevel.On).statusCode == EmailStatusCode.ErrorExpectingText);
|
||
assert(`"test"."test"@iana.org`.isEmail(false, ErrorLevel.On).statusCode == EmailStatusCode.DeprecatedLocalPart);
|
||
assert(`"test\ test"@iana.org`.isEmail(false, ErrorLevel.On).statusCode == EmailStatusCode.Rfc5321QuotedString);
|
||
assert(`"test".test@iana.org`.isEmail(false, ErrorLevel.On).statusCode == EmailStatusCode.DeprecatedLocalPart);
|
||
assert("\"test\u0000\"@iana.org".isEmail(false, ErrorLevel.On).statusCode == EmailStatusCode.ErrorExpectingQuotedText);
|
||
assert("\"test\\\u0000\"@iana.org".isEmail(false, ErrorLevel.On).statusCode == EmailStatusCode.DeprecatedQuotedPair);
|
||
assert(`"abcdefghijklmnopqrstuvwxyz abcdefghijklmnopqrstuvwxyz abcdefghj"@iana.org`.isEmail(false, ErrorLevel.On).statusCode == EmailStatusCode.Rfc5322LocalTooLong, `Quotes are still part of the length restriction`);
|
||
assert(`"abcdefghijklmnopqrstuvwxyz abcdefghijklmnopqrstuvwxyz abcdefg\h"@iana.org`.isEmail(false, ErrorLevel.On).statusCode == EmailStatusCode.Rfc5322LocalTooLong, `Quoted pair is still part of the length restriction`);
|
||
//assert(`test@[255.255.255.255]`.isEmail(false, ErrorLevel.On).statusCode == EmailStatusCode.Rfc5321AddressLiteral); // std.regex bug: *+? not allowed in atom
|
||
assert(`test@a[255.255.255.255]`.isEmail(false, ErrorLevel.On).statusCode == EmailStatusCode.ErrorExpectingText);
|
||
// assert(`test@[255.255.255]`.isEmail(false, ErrorLevel.On).statusCode == EmailStatusCode.Rfc5322DomainLiteral); // std.regex bug: *+? not allowed in atom
|
||
// assert(`test@[255.255.255.255.255]`.isEmail(false, ErrorLevel.On).statusCode == EmailStatusCode.Rfc5322DomainLiteral); // std.regex bug: *+? not allowed in atom
|
||
// assert(`test@[255.255.255.256]`.isEmail(false, ErrorLevel.On).statusCode == EmailStatusCode.Rfc5322DomainLiteral); // std.regex bug: *+? not allowed in atom
|
||
// assert(`test@[1111:2222:3333:4444:5555:6666:7777:8888]`.isEmail(false, ErrorLevel.On).statusCode == EmailStatusCode.Rfc5322DomainLiteral); // std.regex bug: *+? not allowed in atom
|
||
// assert(`test@[IPv6:1111:2222:3333:4444:5555:6666:7777]`.isEmail(false, ErrorLevel.On).statusCode == EmailStatusCode.Rfc5322IpV6GroupCount); // std.regex bug: *+? not allowed in atom
|
||
// assert(`test@[IPv6:1111:2222:3333:4444:5555:6666:7777:8888]`.isEmail(false, ErrorLevel.On).statusCode == EmailStatusCode.Rfc5321AddressLiteral); // std.regex bug: *+? not allowed in atom
|
||
// assert(`test@[IPv6:1111:2222:3333:4444:5555:6666:7777:8888:9999]`.isEmail(false, ErrorLevel.On).statusCode == EmailStatusCode.Rfc5322IpV6GroupCount); // std.regex bug: *+? not allowed in atom
|
||
// assert(`test@[IPv6:1111:2222:3333:4444:5555:6666:7777:888G]`.isEmail(false, ErrorLevel.On).statusCode == EmailStatusCode.Rfc5322IpV6BadChar); // std.regex bug: *+? not allowed in atom
|
||
// assert(`test@[IPv6:1111:2222:3333:4444:5555:6666::8888]`.isEmail(false, ErrorLevel.On).statusCode == EmailStatusCode.Rfc5321IpV6Deprecated); // std.regex bug: *+? not allowed in atom
|
||
// assert(`test@[IPv6:1111:2222:3333:4444:5555::8888]`.isEmail(false, ErrorLevel.On).statusCode == EmailStatusCode.Rfc5321AddressLiteral); // std.regex bug: *+? not allowed in atom
|
||
// assert(`test@[IPv6:1111:2222:3333:4444:5555:6666::7777:8888]`.isEmail(false, ErrorLevel.On).statusCode == EmailStatusCode.Rfc5322IpV6MaxGroups); // std.regex bug: *+? not allowed in atom
|
||
// assert(`test@[IPv6::3333:4444:5555:6666:7777:8888]`.isEmail(false, ErrorLevel.On).statusCode == EmailStatusCode.Rfc5322IpV6ColonStart); // std.regex bug: *+? not allowed in atom
|
||
// assert(`test@[IPv6:::3333:4444:5555:6666:7777:8888]`.isEmail(false, ErrorLevel.On).statusCode == EmailStatusCode.Rfc5321AddressLiteral); // std.regex bug: *+? not allowed in atom
|
||
// assert(`test@[IPv6:1111::4444:5555::8888]`.isEmail(false, ErrorLevel.On).statusCode == EmailStatusCode.Rfc5322IpV6TooManyDoubleColons); // std.regex bug: *+? not allowed in atom
|
||
// assert(`test@[IPv6:::]`.isEmail(false, ErrorLevel.On).statusCode == EmailStatusCode.Rfc5321AddressLiteral); // std.regex bug: *+? not allowed in atom
|
||
// assert(`test@[IPv6:1111:2222:3333:4444:5555:255.255.255.255]`.isEmail(false, ErrorLevel.On).statusCode == EmailStatusCode.Rfc5322IpV6GroupCount); // std.regex bug: *+? not allowed in atom
|
||
// assert(`test@[IPv6:1111:2222:3333:4444:5555:6666:255.255.255.255]`.isEmail(false, ErrorLevel.On).statusCode == EmailStatusCode.Rfc5321AddressLiteral); // std.regex bug: *+? not allowed in atom
|
||
// assert(`test@[IPv6:1111:2222:3333:4444:5555:6666:7777:255.255.255.255]`.isEmail(false, ErrorLevel.On).statusCode == EmailStatusCode.Rfc5322IpV6GroupCount); // std.regex bug: *+? not allowed in atom
|
||
// assert(`test@[IPv6:1111:2222:3333:4444::255.255.255.255]`.isEmail(false, ErrorLevel.On).statusCode == EmailStatusCode.Rfc5321AddressLiteral); // std.regex bug: *+? not allowed in atom
|
||
// assert(`test@[IPv6:1111:2222:3333:4444:5555:6666::255.255.255.255]`.isEmail(false, ErrorLevel.On).statusCode == EmailStatusCode.Rfc5322IpV6MaxGroups); // std.regex bug: *+? not allowed in atom
|
||
// assert(`test@[IPv6:1111:2222:3333:4444:::255.255.255.255]`.isEmail(false, ErrorLevel.On).statusCode == EmailStatusCode.Rfc5322IpV6TooManyDoubleColons); // std.regex bug: *+? not allowed in atom
|
||
// assert(`test@[IPv6::255.255.255.255]`.isEmail(false, ErrorLevel.On).statusCode == EmailStatusCode.Rfc5322IpV6ColonStart); // std.regex bug: *+? not allowed in atom
|
||
assert(` test @iana.org`.isEmail(false, ErrorLevel.On).statusCode == EmailStatusCode.DeprecatedCommentFoldingWhitespaceNearAt);
|
||
assert(`test@ iana .com`.isEmail(false, ErrorLevel.On).statusCode == EmailStatusCode.DeprecatedCommentFoldingWhitespaceNearAt);
|
||
assert(`test . test@iana.org`.isEmail(false, ErrorLevel.On).statusCode == EmailStatusCode.DeprecatedFoldingWhitespace);
|
||
assert("\u000D\u000A test@iana.org".isEmail(false, ErrorLevel.On).statusCode == EmailStatusCode.FoldingWhitespace, `Folding whitespace`);
|
||
assert("\u000D\u000A \u000D\u000A test@iana.org".isEmail(false, ErrorLevel.On).statusCode == EmailStatusCode.DeprecatedFoldingWhitespace, `FWS with one line composed entirely of WSP -- only allowed as obsolete FWS (someone might allow only non-obsolete FWS)`);
|
||
assert(`(comment)test@iana.org`.isEmail(false, ErrorLevel.On).statusCode == EmailStatusCode.Comment);
|
||
assert(`((comment)test@iana.org`.isEmail(false, ErrorLevel.On).statusCode == EmailStatusCode.ErrorUnclosedComment);
|
||
assert(`(comment(comment))test@iana.org`.isEmail(false, ErrorLevel.On).statusCode == EmailStatusCode.Comment);
|
||
assert(`test@(comment)iana.org`.isEmail(false, ErrorLevel.On).statusCode == EmailStatusCode.DeprecatedCommentFoldingWhitespaceNearAt);
|
||
assert(`test(comment)test@iana.org`.isEmail(false, ErrorLevel.On).statusCode == EmailStatusCode.ErrorTextAfterCommentFoldingWhitespace);
|
||
// assert(`test@(comment)[255.255.255.255]`.isEmail(false, ErrorLevel.On).statusCode == EmailStatusCode.DeprecatedCommentFoldingWhitespaceNearAt); // std.regex bug: *+? not allowed in atom
|
||
assert(`(comment)abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghiklm@iana.org`.isEmail(false, ErrorLevel.On).statusCode == EmailStatusCode.Comment);
|
||
assert(`test@(comment)abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghikl.com`.isEmail(false, ErrorLevel.On).statusCode == EmailStatusCode.DeprecatedCommentFoldingWhitespaceNearAt);
|
||
assert(`(comment)test@abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghik.abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghik.abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijk.abcdefghijklmnopqrstuvwxyzabcdefghijk.abcdefghijklmnopqrstu`.isEmail(false, ErrorLevel.On).statusCode == EmailStatusCode.Comment);
|
||
assert("test@iana.org\u000A".isEmail(false, ErrorLevel.On).statusCode == EmailStatusCode.ErrorExpectingText);
|
||
assert(`test@xn--hxajbheg2az3al.xn--jxalpdlp`.isEmail(false, ErrorLevel.On).statusCode == EmailStatusCode.Valid, `A valid IDN from ICANN's <a href="http://idn.icann.org/#The_example.test_names">IDN TLD evaluation gateway</a>`);
|
||
assert(`xn--test@iana.org`.isEmail(false, ErrorLevel.On).statusCode == EmailStatusCode.Valid, `RFC 3490: "unless the email standards are revised to invite the use of IDNA for local parts, a domain label that holds the local part of an email address SHOULD NOT begin with the ACE prefix, and even if it does, it is to be interpreted literally as a local part that happens to begin with the ACE prefix"`);
|
||
assert(`test@iana.org-`.isEmail(false, ErrorLevel.On).statusCode == EmailStatusCode.ErrorDomainHyphenEnd);
|
||
assert(`"test@iana.org`.isEmail(false, ErrorLevel.On).statusCode == EmailStatusCode.ErrorUnclosedQuotedString);
|
||
assert(`(test@iana.org`.isEmail(false, ErrorLevel.On).statusCode == EmailStatusCode.ErrorUnclosedComment);
|
||
assert(`test@(iana.org`.isEmail(false, ErrorLevel.On).statusCode == EmailStatusCode.ErrorUnclosedComment);
|
||
assert(`test@[1.2.3.4`.isEmail(false, ErrorLevel.On).statusCode == EmailStatusCode.ErrorUnclosedDomainLiteral);
|
||
assert(`"test\"@iana.org`.isEmail(false, ErrorLevel.On).statusCode == EmailStatusCode.ErrorUnclosedQuotedString);
|
||
assert(`(comment\)test@iana.org`.isEmail(false, ErrorLevel.On).statusCode == EmailStatusCode.ErrorUnclosedComment);
|
||
assert(`test@iana.org(comment\)`.isEmail(false, ErrorLevel.On).statusCode == EmailStatusCode.ErrorUnclosedComment);
|
||
assert(`test@iana.org(comment\`.isEmail(false, ErrorLevel.On).statusCode == EmailStatusCode.ErrorBackslashEnd);
|
||
// assert(`test@[RFC-5322-domain-literal]`.isEmail(false, ErrorLevel.On).statusCode == EmailStatusCode.Rfc5322DomainLiteral); // std.regex bug: *+? not allowed in atom
|
||
// assert(`test@[RFC-5322]-domain-literal]`.isEmail(false, ErrorLevel.On).statusCode == EmailStatusCode.ErrorTextAfterDomainLiteral); // std.regex bug: *+? not allowed in atom
|
||
// assert(`test@[RFC-5322-[domain-literal]`.isEmail(false, ErrorLevel.On).statusCode == EmailStatusCode.ErrorExpectingDomainText); // std.regex bug: *+? not allowed in atom
|
||
// assert("test@[RFC-5322-\\\u0007-domain-literal]".isEmail(false, ErrorLevel.On).statusCode == EmailStatusCode.Rfc5322DomainLiteralObsoleteText, `obs-dtext <strong>and</strong> obs-qp`); // std.regex bug: *+? not allowed in atom
|
||
// assert("test@[RFC-5322-\\\u0009-domain-literal]".isEmail(false, ErrorLevel.On).statusCode == EmailStatusCode.Rfc5322DomainLiteralObsoleteText); // std.regex bug: *+? not allowed in atom
|
||
// assert(`test@[RFC-5322-\]-domain-literal]`.isEmail(false, ErrorLevel.On).statusCode == EmailStatusCode.Rfc5322DomainLiteralObsoleteText); // std.regex bug: *+? not allowed in atom
|
||
// assert(`test@[RFC-5322-domain-literal\]`.isEmail(false, ErrorLevel.On).statusCode == EmailStatusCode.ErrorUnclosedDomainLiteral); // std.regex bug: *+? not allowed in atom
|
||
// assert(`test@[RFC-5322-domain-literal\`.isEmail(false, ErrorLevel.On).statusCode == EmailStatusCode.ErrorBackslashEnd); // std.regex bug: *+? not allowed in atom
|
||
// assert(`test@[RFC 5322 domain literal]`.isEmail(false, ErrorLevel.On).statusCode == EmailStatusCode.Rfc5322DomainLiteral, `Spaces are FWS in a domain literal`); // std.regex bug: *+? not allowed in atom
|
||
// assert(`test@[RFC-5322-domain-literal] (comment)`.isEmail(false, ErrorLevel.On).statusCode == EmailStatusCode.Rfc5322DomainLiteral); // std.regex bug: *+? not allowed in atom
|
||
assert(`@iana.org`.isEmail(false, ErrorLevel.On).statusCode == EmailStatusCode.ErrorExpectingText);
|
||
assert(`test@.org`.isEmail(false, ErrorLevel.On).statusCode == EmailStatusCode.ErrorExpectingText);
|
||
assert(`""@iana.org`.isEmail(false, ErrorLevel.On).statusCode == EmailStatusCode.DeprecatedQuotedText);
|
||
assert(`"\"@iana.org`.isEmail(false, ErrorLevel.On).statusCode == EmailStatusCode.DeprecatedQuotedPair);
|
||
assert(`()test@iana.org`.isEmail(false, ErrorLevel.On).statusCode == EmailStatusCode.DeprecatedCommentText);
|
||
assert("test@iana.org\u000D".isEmail(false, ErrorLevel.On).statusCode == EmailStatusCode.ErrorCrNoLf, `No LF after the CR`);
|
||
assert("\u000Dtest@iana.org".isEmail(false, ErrorLevel.On).statusCode == EmailStatusCode.ErrorCrNoLf, `No LF after the CR`);
|
||
assert("\"\u000Dtest\"@iana.org".isEmail(false, ErrorLevel.On).statusCode == EmailStatusCode.ErrorCrNoLf, `No LF after the CR`);
|
||
assert("(\u000D)test@iana.org".isEmail(false, ErrorLevel.On).statusCode == EmailStatusCode.ErrorCrNoLf, `No LF after the CR`);
|
||
assert("test@iana.org(\u000D)".isEmail(false, ErrorLevel.On).statusCode == EmailStatusCode.ErrorCrNoLf, `No LF after the CR`);
|
||
assert("\u000Atest@iana.org".isEmail(false, ErrorLevel.On).statusCode == EmailStatusCode.ErrorExpectingText);
|
||
assert("\"\u000A\"@iana.org".isEmail(false, ErrorLevel.On).statusCode == EmailStatusCode.ErrorExpectingQuotedText);
|
||
assert("\"\\\u000A\"@iana.org".isEmail(false, ErrorLevel.On).statusCode == EmailStatusCode.DeprecatedQuotedPair);
|
||
assert("(\u000A)test@iana.org".isEmail(false, ErrorLevel.On).statusCode == EmailStatusCode.ErrorExpectingCommentText);
|
||
assert("\u0007@iana.org".isEmail(false, ErrorLevel.On).statusCode == EmailStatusCode.ErrorExpectingText);
|
||
assert("test@\u0007.org".isEmail(false, ErrorLevel.On).statusCode == EmailStatusCode.ErrorExpectingText);
|
||
assert("\"\u0007\"@iana.org".isEmail(false, ErrorLevel.On).statusCode == EmailStatusCode.DeprecatedQuotedText);
|
||
assert("\"\\\u0007\"@iana.org".isEmail(false, ErrorLevel.On).statusCode == EmailStatusCode.DeprecatedQuotedPair);
|
||
assert("(\u0007)test@iana.org".isEmail(false, ErrorLevel.On).statusCode == EmailStatusCode.DeprecatedCommentText);
|
||
assert("\u000D\u000Atest@iana.org".isEmail(false, ErrorLevel.On).statusCode == EmailStatusCode.ErrorFoldingWhitespaceCrLfEnd, `Not FWS because no actual white space`);
|
||
assert("\u000D\u000A \u000D\u000Atest@iana.org".isEmail(false, ErrorLevel.On).statusCode == EmailStatusCode.ErrorFoldingWhitespaceCrLfEnd, `Not obs-FWS because there must be white space on each "fold"`);
|
||
assert(" \u000D\u000Atest@iana.org".isEmail(false, ErrorLevel.On).statusCode == EmailStatusCode.ErrorFoldingWhitespaceCrLfEnd, `Not FWS because no white space after the fold`);
|
||
assert(" \u000D\u000A test@iana.org".isEmail(false, ErrorLevel.On).statusCode == EmailStatusCode.FoldingWhitespace, `FWS`);
|
||
assert(" \u000D\u000A \u000D\u000Atest@iana.org".isEmail(false, ErrorLevel.On).statusCode == EmailStatusCode.ErrorFoldingWhitespaceCrLfEnd, `Not FWS because no white space after the second fold`);
|
||
assert(" \u000D\u000A\u000D\u000Atest@iana.org".isEmail(false, ErrorLevel.On).statusCode == EmailStatusCode.ErrorFoldingWhitespaceCrflX2, `Not FWS because no white space after either fold`);
|
||
assert(" \u000D\u000A\u000D\u000A test@iana.org".isEmail(false, ErrorLevel.On).statusCode == EmailStatusCode.ErrorFoldingWhitespaceCrflX2, `Not FWS because no white space after the first fold`);
|
||
assert("test@iana.org\u000D\u000A ".isEmail(false, ErrorLevel.On).statusCode == EmailStatusCode.FoldingWhitespace, `FWS`);
|
||
assert("test@iana.org\u000D\u000A \u000D\u000A ".isEmail(false, ErrorLevel.On).statusCode == EmailStatusCode.DeprecatedFoldingWhitespace, `FWS with one line composed entirely of WSP -- only allowed as obsolete FWS (someone might allow only non-obsolete FWS)`);
|
||
assert("test@iana.org\u000D\u000A".isEmail(false, ErrorLevel.On).statusCode == EmailStatusCode.ErrorFoldingWhitespaceCrLfEnd, `Not FWS because no actual white space`);
|
||
assert("test@iana.org\u000D\u000A \u000D\u000A".isEmail(false, ErrorLevel.On).statusCode == EmailStatusCode.ErrorFoldingWhitespaceCrLfEnd, `Not obs-FWS because there must be white space on each "fold"`);
|
||
assert("test@iana.org \u000D\u000A".isEmail(false, ErrorLevel.On).statusCode == EmailStatusCode.ErrorFoldingWhitespaceCrLfEnd, `Not FWS because no white space after the fold`);
|
||
assert("test@iana.org \u000D\u000A ".isEmail(false, ErrorLevel.On).statusCode == EmailStatusCode.FoldingWhitespace, `FWS`);
|
||
assert("test@iana.org \u000D\u000A \u000D\u000A".isEmail(false, ErrorLevel.On).statusCode == EmailStatusCode.ErrorFoldingWhitespaceCrLfEnd, `Not FWS because no white space after the second fold`);
|
||
assert("test@iana.org \u000D\u000A\u000D\u000A".isEmail(false, ErrorLevel.On).statusCode == EmailStatusCode.ErrorFoldingWhitespaceCrflX2, `Not FWS because no white space after either fold`);
|
||
assert("test@iana.org \u000D\u000A\u000D\u000A ".isEmail(false, ErrorLevel.On).statusCode == EmailStatusCode.ErrorFoldingWhitespaceCrflX2, `Not FWS because no white space after the first fold`);
|
||
assert(" test@iana.org".isEmail(false, ErrorLevel.On).statusCode == EmailStatusCode.FoldingWhitespace);
|
||
assert(`test@iana.org `.isEmail(false, ErrorLevel.On).statusCode == EmailStatusCode.FoldingWhitespace);
|
||
// assert(`test@[IPv6:1::2:]`.isEmail(false, ErrorLevel.On).statusCode == EmailStatusCode.Rfc5322IpV6ColonEnd); // std.regex bug: *+? not allowed in atom
|
||
assert("\"test\\\u00A9\"@iana.org".isEmail(false, ErrorLevel.On).statusCode == EmailStatusCode.ErrorExpectingQuotedPair);
|
||
assert(`test@iana/icann.org`.isEmail(false, ErrorLevel.On).statusCode == EmailStatusCode.Rfc5322Domain);
|
||
assert(`test.(comment)test@iana.org`.isEmail(false, ErrorLevel.On).statusCode == EmailStatusCode.DeprecatedComment);
|
||
assert(`test@org`.isEmail(false, ErrorLevel.On).statusCode == EmailStatusCode.Rfc5321TopLevelDomain);
|
||
// assert(`test@test.com`.isEmail(false, ErrorLevel.On).statusCode == EmailStatusCode.DnsWarningNoMXRecord, `test.com has an A-record but not an MX-record`); // DNS check is currently not implemented
|
||
// assert(`test@nic.no`.isEmail(false, ErrorLevel.On).statusCode == EmailStatusCode.DnsWarningNoRecord, `nic.no currently has no MX-records or A-records (Feb 2011). If you are seeing an A-record for nic.io then try setting your DNS server to 8.8.8.8 (the Google DNS server) - your DNS server may be faking an A-record (OpenDNS does this, for instance).`); // DNS check is currently not implemented
|
||
}
|
||
|
||
void main ()
|
||
{
|
||
println(`test@iana.123`.isEmail(false, ErrorLevel.Warning).toString);
|
||
} |