Socket
Socket
Sign inDemoInstall

email-addresses

Package Overview
Dependencies
Maintainers
1
Versions
14
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

email-addresses - npm Package Compare versions

Comparing version 1.1.2 to 2.0.0

179

lib/email-addresses.js
// email-addresses.js - RFC 5322 email address parser
// v 1.1.2
// v 2.0.0
//

@@ -153,2 +153,12 @@ // http://tools.ietf.org/html/rfc5322

function colwsp(prod) {
return function collapseSemanticWhitespace() {
var result = prod();
if (result !== null && result.semantic.length > 0) {
result.semantic = " ";
}
return result;
};
}
function star(prod, minimum) {

@@ -175,2 +185,52 @@ return function starFunc() {

// One expects names to get normalized like this:
// " First Last " -> "First Last"
// "First Last" -> "First Last"
// "First Last" -> "First Last"
function collapseWhitespace(s) {
function isWhitespace(c) {
return c === ' ' ||
c === '\t' ||
c === '\r' ||
c === '\n';
}
var i, str;
str = "";
for (i = 0; i < s.length; i += 1) {
if (!isWhitespace(s[i]) || !isWhitespace(s[i + 1])) {
str += s[i];
}
}
if (isWhitespace(str[0])) {
str = str.substring(1);
}
if (isWhitespace(str[str.length - 1])) {
str = str.substring(0, str.length - 1);
}
return str;
}
// UTF-8 pseudo-production (RFC 6532)
// RFC 6532 extends RFC 5322 productions to include UTF-8
// using the following productions:
// UTF8-non-ascii = UTF8-2 / UTF8-3 / UTF8-4
// UTF8-2 = <Defined in Section 4 of RFC3629>
// UTF8-3 = <Defined in Section 4 of RFC3629>
// UTF8-4 = <Defined in Section 4 of RFC3629>
//
// For reference, the extended RFC 5322 productions are:
// VCHAR =/ UTF8-non-ascii
// ctext =/ UTF8-non-ascii
// atext =/ UTF8-non-ascii
// qtext =/ UTF8-non-ascii
// dtext =/ UTF8-non-ascii
function isUTF8NonAscii(tok) {
// In JavaScript, we just deal directly with Unicode code points,
// so we aren't checking individual bytes for UTF-8 encoding.
// Just check that the character is non-ascii.
return tok.charCodeAt(0) >= 128;
}
// common productions (RFC 5234)

@@ -208,3 +268,7 @@ // http://tools.ietf.org/html/rfc5234

var code = tok.charCodeAt(0);
return (0x21 <= code && code <= 0x7E);
var accept = (0x21 <= code && code <= 0x7E);
if (opts.rfc6532) {
accept = accept || isUTF8NonAscii(tok);
}
return accept;
}));

@@ -262,6 +326,10 @@ }

var code = tok.charCodeAt(0);
return ((33 <= code && code <= 39) ||
(42 <= code && code <= 91) ||
(93 <= code && code <= 126)
);
var accept =
(33 <= code && code <= 39) ||
(42 <= code && code <= 91) ||
(93 <= code && code <= 126);
if (opts.rfc6532) {
accept = accept || isUTF8NonAscii(tok);
}
return accept;
});

@@ -317,8 +385,12 @@ },

return wrap('atext', compareToken(function atextFunc(tok) {
return (('a' <= tok && tok <= 'z') ||
('A' <= tok && tok <= 'Z') ||
('0' <= tok && tok <= '9') ||
(['!', '#', '$', '%', '&', '\'', '*', '+', '-', '/',
'=', '?', '^', '_', '`', '{', '|', '}', '~'].indexOf(tok) >= 0)
);
var accept =
('a' <= tok && tok <= 'z') ||
('A' <= tok && tok <= 'Z') ||
('0' <= tok && tok <= '9') ||
(['!', '#', '$', '%', '&', '\'', '*', '+', '-', '/',
'=', '?', '^', '_', '`', '{', '|', '}', '~'].indexOf(tok) >= 0);
if (opts.rfc6532) {
accept = accept || isUTF8NonAscii(tok);
}
return accept;
}));

@@ -329,3 +401,3 @@ }

function atom() {
return wrap('atom', and(invis(opt(cfws)), star(atext, 1), invis(opt(cfws)))());
return wrap('atom', and(colwsp(opt(cfws)), star(atext, 1), colwsp(opt(cfws)))());
}

@@ -365,7 +437,4 @@

(93 <= code && code <= 126);
// characters outside of ASCII are not technically allowed in RFC 5322
// .. but having them is within reason
// http://stackoverflow.com/questions/3467777/is-this-a-valid-email-address
if (opts.extendedASCII) {
accept = accept || (code >= 128);
if (opts.rfc6532) {
accept = accept || isUTF8NonAscii(tok);
}

@@ -390,3 +459,3 @@ return accept;

invis(opt(cfws)),
dquote, star(and(opt(fws), qcontent)), opt(fws), dquote,
invis(dquote), star(and(opt(colwsp(fws)), qcontent)), opt(invis(fws)), invis(dquote),
invis(opt(cfws))

@@ -452,3 +521,9 @@ )());

function displayName() {
return wrap('display-name', phrase());
return wrap('display-name', function phraseFixedSemantic() {
var result = phrase();
if (result !== null) {
result.semantic = collapseWhitespace(result.semantic);
}
return result;
}());
}

@@ -504,5 +579,9 @@

var code = tok.charCodeAt(0);
return ((33 <= code && code <= 90) ||
(94 <= code && code <= 126)
);
var accept =
(33 <= code && code <= 90) ||
(94 <= code && code <= 126);
if (opts.rfc6532) {
accept = accept || isUTF8NonAscii(tok);
}
return accept;
});

@@ -529,3 +608,11 @@ },

function domain() {
return wrap('domain', or(obsDomain, dotAtom, domainLiteral)());
return wrap('domain', function domainCheckTLD() {
var result = or(obsDomain, dotAtom, domainLiteral)();
if (opts.rejectTLD) {
if (result.semantic.indexOf('.') < 0) {
return null;
}
}
return result;
}());
}

@@ -575,3 +662,3 @@

word,
star(or(word, literal('.'), invis(cfws)))
star(or(word, literal('.'), colwsp(cfws)))
)());

@@ -727,30 +814,2 @@ }

// One expects names to get normalized like this:
// " First Last " -> "First Last"
// "First Last" -> "First Last"
// "First Last" -> "First Last"
function collapseWhitespace(s) {
function isWhitespace(c) {
return c === ' ' ||
c === '\t' ||
c === '\r' ||
c === '\n';
}
var i, str;
str = "";
for (i = 0; i < s.length; i += 1) {
if (!isWhitespace(s[i]) || !isWhitespace(s[i + 1])) {
str += s[i];
}
}
if (isWhitespace(str[0])) {
str = str.substring(1);
}
if (isWhitespace(str[str.length - 1])) {
str = str.substring(0, str.length - 1);
}
return str;
}
function giveResult(ast) {

@@ -760,5 +819,2 @@ function grabSemantic(n) {

}
function tokensNoWhitespace(n) {
return n !== null ? collapseWhitespace(n.tokens) : null;
}
var i, ret, addresses, addr, name, aspec, local, domain;

@@ -785,3 +841,3 @@ if (ast === null) {

},
name: tokensNoWhitespace(name),
name: grabSemantic(name),
address: grabSemantic(aspec),

@@ -831,3 +887,3 @@ local: grabSemantic(local),

opts = handleOpts(opts, {
extendedASCII: true,
rfc6532: true,
simple: true

@@ -855,3 +911,3 @@ });

opts = handleOpts(opts, {
extendedASCII: true,
rfc6532: true,
simple: true

@@ -891,6 +947,7 @@ });

defaults = {
extendedASCII: false,
rfc6532: false,
partial: false,
simple: false,
strict: false
strict: false,
rejectTLD: false
};

@@ -897,0 +954,0 @@

{
"version": "1.1.2",
"version": "2.0.0",
"name": "email-addresses",

@@ -4,0 +4,0 @@ "description": "An email address parser based on rfc5322",

@@ -6,3 +6,3 @@ email-addresses.js

v 1.1.2
v 2.0.0

@@ -41,3 +41,3 @@ What?

domain: [Object] },
name: '"Jack Bowman"',
name: 'Jack Bowman',
address: 'jack@fogcreek.com',

@@ -86,3 +86,3 @@ local: 'jack',

If you want to examine the parsed address, for example to extract a name or address, you have some options. The object returned by ```parseOneAddress``` has four helper values on it: ```name```, ```address```, ```local```, and ```domain```. See the example above to understand is actually returned. The ```name``` helper field attempts to helpfully collapse whitespace for the field, since that is what we generally expect from names. The ```address```, ```local```, and ```domain``` parts return the "semantic" values for those fields, i.e. they exclude whitespace and RFC 5322 comments. If you desire, you can also obtain the raw parsed tokens or semantic tokens for those fields. The ```parts``` value is an object referencing nodes in the AST generated. Nodes in the AST have two values of interest here, ```tokens``` and ```semantic```.
If you want to examine the parsed address, for example to extract a name or address, you have some options. The object returned by ```parseOneAddress``` has four helper values on it: ```name```, ```address```, ```local```, and ```domain```. See the example above to understand is actually returned. (These are equivalent to ```parts.name.semantic```, ```parts.address.semantic```, etc.) These values try to be smart about collapsing whitespace, quotations, and excluding RFC 5322 comments. If you desire, you can also obtain the raw parsed tokens or semantic tokens for those fields. The ```parts``` value is an object referencing nodes in the AST generated. Nodes in the AST have two values of interest here, ```tokens``` and ```semantic```.

@@ -93,12 +93,12 @@ ```

'Jack Bowman '
> a.parts.name.semantic
'JackBowman'
> a.name
'Jack Bowman'
> a.parts.name.semantic
'Jack Bowman '
> a.parts.address.tokens
'jack@fogcreek.com '
> a.address
'jack@fogcreek.com'
> a.parts.address.semantic
'jack@fogcreek.com'
> a.address
'jack@fogcreek.com'
```

@@ -105,0 +105,0 @@

@@ -29,3 +29,3 @@ var fs = require("fs"),

result = fxn("\"Françoise Lefèvre\"@example.com");
t.ok(result, "extended ascii is enabled by default");
t.ok(result, "RFC 6532 (Unicode support) is enabled by default");

@@ -50,3 +50,3 @@ result = fxn("First Last <first@last.com>");

t.equal(result[0].address, "a@b.c", "full address, semantic only");
t.equal(result[0].name, "\"A B C\"", "display name");
t.equal(result[0].name, "A B C", "display name");
t.equal(result[0].local, "a", "local part");

@@ -65,3 +65,3 @@ t.equal(result[0].domain, "b.c", "domain");

result = fxn("\"Françoise Lefèvre\"@example.com");
t.ok(result, "extended ascii is enabled by default");
t.ok(result, "RFC 6532 (Unicode support) is enabled by default");

@@ -82,3 +82,3 @@ t.end();

t.equal(result[0].address, "a@b.c", "full address, semantic only");
t.equal(result[0].name, "\"A B C\"", "display name");
t.equal(result[0].name, "A B C", "display name");
t.equal(result[0].local, "a", "local part");

@@ -102,4 +102,4 @@ t.equal(result[0].domain, "b.c", "domain");

result = fxn({ input: "\"Françoise Lefèvre\"@example.com", extendedASCII: true });
t.ok(result, "extended ascii support can be turned on");
result = fxn({ input: "\"Françoise Lefèvre\"@example.com", rfc6532: true });
t.ok(result, "but extended ascii is allowed starting with RFC 6532");

@@ -109,2 +109,153 @@ t.end();

test("display-name semantic interpretation", function (t) {
var fxn, result;
fxn = addrs.parseOneAddress;
function check(s, comment, expected) {
t.equal(fxn(s).name, expected || "First Last", comment);
}
check(
"First<foo@bar.com>",
"single basic name is ok",
"First");
check(
"First Last<foo@bar.com>",
"no extra whitespace is ok");
check(
" First Last <foo@bar.com>",
"single whitespace at beginning and end is removed");
check(
"First Last<foo@bar.com>",
"whitespace in the middle is collapsed");
check(
" First Last <foo@bar.com>",
"extra whitespace everywhere is collapsed");
check(
" First Middle Last <foo@bar.com>",
"extra whitespace everywhere is collapsed, with more than 2 names",
"First Middle Last");
check(
"\tFirst \t Last\t<foo@bar.com>",
"extra whitespace everywhere is collapsed with a mix of tabs and spaces");
check(
"\"First Last\"<foo@bar.com>",
"surrounding quotes are not semantic");
check(
" \t \"First Last\" <foo@bar.com>",
"surrounding quotes are not semantic and whitespace is collapsed");
check(
" \t \"First \\\"The\t\tNickname\\\" Last\" <foo@bar.com>",
"surrounding quotes are not semantic, but inner quotes are, and whitespace is collapsed",
"First \"The Nickname\" Last");
t.end();
});
test("address semantic interpretation", function (t) {
var fxn, result;
fxn = addrs.parseOneAddress;
function check(s, comment, expected) {
t.equal(fxn(s).address, expected || "foo@bar.com", comment);
}
check(
"foo@bar.com",
"plain address is ok");
check(
" foo@bar.com ",
"plain address with whitespace at beginning and end");
check(
"foo @bar.com",
"plain address with whitespace left of @ sign");
check(
"foo@ bar.com",
"plain address with whitespace right of @ sign");
// Technically, we should also be able to handle removing CFWS in
// a dot-atom (or more importantly, obs-domain), but I don't think anyone cares.
check(
"\t foo\t\t@ \t bar.com \t ",
"plain address with whitespace everywhere");
check(
"Bob <\t foo\t\t@ \t bar.com \t >",
"angle-addr with whitespace everywhere");
check(
"\"foo\"@bar.com",
"plain address with quoted-string local-part");
check(
"\"foo baz\"@bar.com",
"plain address with quoted-string local-part including spaces" +
" (Note: This is a confusing situation for 'semantic' local-parts, and" +
" in this case we don't return a valid address. Don't use this. Just" +
" take the raw tokens used for the address if you always want it to be equivalent.)",
"foo baz@bar.com");
t.end();
});
test("unicode support", function (t) {
var fxn, result;
fxn = addrs.parseOneAddress;
result = fxn("\"Françoise Lefèvre\"@example.com");
t.ok(result, "extended ascii characters are allowed");
result = fxn("杨孝宇 <xiaoyu@example.com>");
t.ok(result, "unicode support includes chinese characters (display-name, no quoted string)");
result = fxn("\"杨孝宇\" <xiaoyu@example.com>");
t.ok(result, "unicode support includes chinese characters (display-name, quoted-string)");
t.end();
});
test("rejectTLD option", function (t) {
var fxn, result;
fxn = addrs.parseOneAddress;
result = fxn({ input: "foo@bar.com", rejectTLD: false });
t.ok(result, "a simple address is ok (rejectTLD false)");
result = fxn({ input: "foo@bar.com", rejectTLD: true });
t.ok(result, "a simple address is ok (rejectTLD true)");
result = fxn({ input: "\"Foo Bar\" <foo@bar.com>", rejectTLD: false });
t.ok(result, "a more complicated address is ok (rejectTLD false)");
result = fxn({ input: "\"Foo Bar\" <foo@bar.com>", rejectTLD: true });
t.ok(result, "a more complicated address is ok (rejectTLD true)");
result = fxn({ input: "foo@bar", rejectTLD: false });
t.ok(result, "an address with a TLD for its domain is allowed by rfc 5322");
result = fxn({ input: "foo@bar", rejectTLD: true });
t.notOk(result, "an address with a TLD for its domain is rejected when the option is set");
result = fxn({ input: "\"Foo Bar\" <foo@bar>", rejectTLD: false });
t.ok(result, "a more complicated address with a TLD for its domain is allowed by rfc 5322");
result = fxn({ input: "\"Foo Bar\" <foo@bar>", rejectTLD: true });
t.notOk(result, "a more complicated address with a TLD for its domain is rejected when the option is set");
t.end();
});
function isEmailTest(t, data) {

@@ -111,0 +262,0 @@ var nodes = getNodes(data, "//test");

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc