email-bounce-parser 1.0.15 → 1.0.17

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -39,7 +39,7 @@ console.log(result);
39
39
  ### Parse a bounce email
40
40
 
41
41
  `read(body)` parses the bounce email and extracts available information (error code and type, server, original recipient, etc.):
42
- * `body` must be a string representing the bounce email body (as returned by [mailparser](https://github.com/nodemailer/mailparser), for example)
42
+ * `body` must be a string representing the bounce email **text** body (not HTML body). You can use [mailparser](https://github.com/nodemailer/mailparser) for example.
43
43
 
44
44
  ```js
45
45
  const EmailBounceParse = require("email-bounce-parser");
package/lib/parser.js CHANGED
@@ -8,6 +8,7 @@ var ERROR_CODES_TYPES = {
8
8
  "421" : "service_not_available",
9
9
  "421 4.3.0" : "temporary_system_problem",
10
10
  "421 4.4.5" : "server_busy",
11
+ "421 4.7.28" : "ip_blocked_spam",
11
12
  "432 4.7.12" : "password_needed",
12
13
  "450" : "action_not_taken_mailbox_unavailable",
13
14
  "450 4.2.1" : "action_not_taken_mailbox_rate_limited",
@@ -64,8 +65,8 @@ var REGEXES = {
64
65
  /\(in reply to end of ([A-Z\s]+) command\)$/m, // "in reply to end of DATA command"
65
66
  /\(in reply to ([A-Z\s]+) command\)$/m, // "in reply to RCPT TO command"
66
67
  /timed out while sending ([A-Z\s]+)$/m, // "timed out while sending (RCPT TO)", "timed out while sending RCPT TO"
67
- /error after ([A-Z\s]+)/m, // "LMTP error after RCPT TO"
68
- /while sending end of ([a-z\s]+)/m // "while sending end of data"
68
+ /error after ([A-Z\s]+)/i, // "LMTP error after RCPT TO"
69
+ /while sending end of ([a-z\s]+)/i // "while sending end of data"
69
70
  ],
70
71
 
71
72
  email_server : [
@@ -84,7 +85,7 @@ var REGEXES = {
84
85
  /said\: (\d{3}(?:[\s\-]\d\.\d\.\d\d?)?) (.+) \(in reply to/,
85
86
  /said\: (\d{3}(?:[\s\-]\d\.\d\.\d\d?)?)-(.+)/,
86
87
  /(\d{3}\-\'\d\.\d\.\d\d?) (.+)/,
87
- /LMTP error after (?:.+?)\:\<.+?\>\: (\d{3}(?:[\s\-]\d\.\d\.\d\d?)?) <.+?\>(.+)$/m,
88
+ /LMTP error after .+?\:\<.+?\>\: (\d{3}(?:[\s\-]\d\.\d\.\d\d?)?) <.+?\>(.+)$/m,
88
89
  /remote server returned \'(\d{3}(?:[\s\-]\d\.\d\.\d\d?)?) (.+)\'/i,
89
90
  /The response from the remote server was:\s(\d{3}(?:[\s\-]\d\.\d\.\d\d?)?) (.+)/,
90
91
  /refused to talk to me\: (\d{3}(?:[\s\-]\d\.\d\.\d\d?)?) (.+)/,
@@ -94,18 +95,29 @@ var REGEXES = {
94
95
  ],
95
96
 
96
97
  error_lax : [
97
- /(^\<.+?\>:.+)/m
98
+ /^(?:\<.+?\> \(expanded from \<.+?\>\)\:)(.+)/m, // "<john@acme.com> (expanded from <john@digiflex.com>): Domain name not found"
99
+ /^(?:\<.+?\>\:)(.+)/m, // "<john@acme.com>: Domain name not found"
100
+ ],
101
+
102
+ error_label_cleanup_recipient : [
103
+ /^\<.+?\> \(expanded from \<.+?\>\)\:/m, // "<john@acme.com> (expanded from <john@digiflex.com>): Domain name not found"
104
+ /^\<.+?\>\:/m, // "<john@acme.com>: Domain name not found"
105
+ ],
106
+
107
+ error_label_cleanup_command : [
108
+ /\(in reply to (?:end of)?[A-Z\s]+ command\)$/m // "in reply to DATA command" or "in reply to end of DATA command"
98
109
  ],
99
110
 
100
111
  recipient : [
101
- /The following addresses had permanent fatal errors (?:.*?)\s\<(.+?)\>/,
102
- /The following message to \<(.+?)\> was undeliverable/,
103
- /to the following addresses:\s*(.+)/,
112
+ /the following addresses had permanent fatal errors (?:.*?)\s\<(.+?)\>/i,
113
+ /the following message to \<(.+?)\> was undeliverable/i,
114
+ /to the following addresses:\s*(.+)/i,
104
115
  /^\<(.+?)\>:/m,
105
- /^(.+?)\s?\n?\s*[\[|<]mailto\:(.+?)[\]|>]/m,
106
- /^Your message wasn't delivered to (.+?)\s/m,
107
- /^Your message to (.+?) couldn\'t/m,
108
- /\<(.+?)\>:/
116
+ /^(.+?)\s?\n?\s*[\[|<]mailto\:(.+?)[\]|>]/mi, // "john@acme.com<mailto:john@acme.com>"
117
+ /^your message wasn't delivered to (.+?)\s/mi, // "Your message wasn't delivered to john@acme.com"
118
+ /^your message to (.+?) couldn\'t/mi, // "Your message to john@acme.com couldn't"
119
+ /\<(.+?)\> \(expanded from \<.+?\>\)\:/, // "<john@acme.com> (expanded from <john@digiflex.com>): "
120
+ /\<(.+?)\>:/ // "<john@acme.com>:"
109
121
  ],
110
122
 
111
123
  temporary_safe : /(temporary|temporarily)/i,
@@ -122,7 +134,9 @@ var REGEXES = {
122
134
  ],
123
135
 
124
136
  enhanced_types_server_unknown : [
125
- /(does not accept mail)/i
137
+ /(does not accept mail)/i,
138
+ /(domain name not found)/i,
139
+ /(no route to host)/i
126
140
  ],
127
141
 
128
142
  enhanced_types_server_timeout : [
@@ -134,8 +148,8 @@ var REGEXES = {
134
148
  ],
135
149
 
136
150
  error_code : [
137
- /(\d{3})[\s\-](\d\.\d\.\d)/, // "550 5.4.1" or "550-5.4.1"
138
- /(\d{3})\-\'(\d\.\d\.\d)/, // "550-'5.4.1"
151
+ /(\d{3})[\s\-](\d\.\d\.\d\d?)/, // "550 5.4.1" or "550-5.4.1" or "550-5.4.11"
152
+ /(\d{3})\-\'(\d\.\d\.\d\d?)/, // "550-'5.4.1" or "550-'5.4.11"
139
153
  /(\d{3})/ // "550"
140
154
  ]
141
155
  };
@@ -276,7 +290,7 @@ class Parser {
276
290
 
277
291
  if (_match && _match.length > 1) {
278
292
  _code = this.__parseErrorCode(_match[1]);
279
- _label = trimString(_match[2]);
293
+ _label = this.__cleanErrorLabel(_match[2]);
280
294
 
281
295
  // Check temporary / permanent
282
296
  if (_code.basic[0] === "4") {
@@ -306,7 +320,7 @@ class Parser {
306
320
  var _match = loopRegexes(this.__regexes.error_lax, str);
307
321
 
308
322
  if (_match && _match.length > 1) {
309
- _label = trimString(_match[1]);
323
+ _label = this.__cleanErrorLabel(_match[1]);
310
324
  }
311
325
  }
312
326
 
@@ -550,6 +564,50 @@ class Parser {
550
564
 
551
565
  return _str;
552
566
  }
567
+
568
+
569
+ /**
570
+ * Cleans error label
571
+ * @private
572
+ * @param {string} [label]
573
+ * @return {string} Unquoted text
574
+ */
575
+ __cleanErrorLabel(label="") {
576
+ var _label = this.__unquoteText(label);
577
+
578
+ _label = trimString(_label);
579
+
580
+ // Strip recipient (at the beginning) and command (at the end), in case \
581
+ // they are still present
582
+ _label = loopRegexes(this.__regexes.error_label_cleanup_recipient, _label, "replace");
583
+ _label = loopRegexes(this.__regexes.error_label_cleanup_command, _label, "replace");
584
+
585
+ _label = trimString(_label);
586
+
587
+ return _label;
588
+ }
589
+
590
+
591
+ /**
592
+ * Unquotes text
593
+ * @private
594
+ * @param {string} [str]
595
+ * @return {string} Unquoted text
596
+ */
597
+ __unquoteText(str="") {
598
+ var _str = str;
599
+
600
+ _str = trimString(_str);
601
+
602
+ if (
603
+ (_str.startsWith("'") && _str.endsWith("'")) ||
604
+ (_str.startsWith("\"") && _str.endsWith("\""))
605
+ ) {
606
+ return _str.slice(1, -1);
607
+ }
608
+
609
+ return _str;
610
+ }
553
611
  }
554
612
 
555
613
 
package/lib/utils.js CHANGED
@@ -23,6 +23,8 @@ var loopRegexes = function(
23
23
  regexes, str, mode = "match", highestPosition = true
24
24
  ) {
25
25
  var _match;
26
+ var _minLength = mode === "split" ? 1 : 0;
27
+ var _maxLength = str.length;
26
28
 
27
29
  for (var _i = 0; _i < regexes.length; _i++) {
28
30
  var _regex = regexes[_i];
@@ -40,15 +42,12 @@ var loopRegexes = function(
40
42
  _match = _currentMatch;
41
43
 
42
44
  // A successfull replace must return a smaller string than the input
43
- var _maxLength = str.length;
44
-
45
45
  if (_match.length < _maxLength) {
46
46
  break;
47
47
  }
48
48
  } else if (mode === "split" || mode === "match") {
49
- // A successfull split must return an array with at least two items
50
- var _minLength = mode === "split" ? 1 : 0;
51
49
 
50
+ // A successfull split must return an array with at least two items
52
51
  if ((_currentMatch || []).length > _minLength) {
53
52
  if (highestPosition === true) {
54
53
  // No previous match?
@@ -80,7 +79,7 @@ var loopRegexes = function(
80
79
  }
81
80
  }
82
81
 
83
- return (_match || []);
82
+ return (mode === "replace") ? (_match || "") : (_match || []);
84
83
  }
85
84
 
86
85
 
@@ -91,7 +90,7 @@ var loopRegexes = function(
91
90
  * @return {string} The trimed string
92
91
  */
93
92
  var trimString = function(str) {
94
- return ((str || "").trim() || null)
93
+ return ((str || "").trim() || "");
95
94
  }
96
95
 
97
96
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "email-bounce-parser",
3
- "version": "1.0.15",
3
+ "version": "1.0.17",
4
4
  "description": "Parses bounce emails and extract errors",
5
5
  "author": "Eliott Vincent <eliott@crisp.chat>",
6
6
  "main": "lib/index.js",
@@ -30,7 +30,9 @@
30
30
  "mail",
31
31
  "email",
32
32
  "bounce",
33
- "bounces"
33
+ "bounces",
34
+ "smtp",
35
+ "rfc"
34
36
  ],
35
37
  "license": "MIT"
36
38
  }