email-bounce-parser 1.0.14 → 1.0.16

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.
Files changed (2) hide show
  1. package/lib/parser.js +75 -31
  2. package/package.json +4 -2
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",
@@ -61,48 +62,53 @@ var REGEXES = {
61
62
  ],
62
63
 
63
64
  command : [
64
- /\(in reply to end of ([A-Z\s]+) command\)$/m, // 'end of' command
65
- /\(in reply to ([A-Z\s]+) command\)$/m, // default
66
- /timed out while sending ([A-Z\s]+)$/m, // command timed out
67
- /error after ([A-Z\s]+)/m // 'after' command
65
+ /\(in reply to end of ([A-Z\s]+) command\)$/m, // "in reply to end of DATA command"
66
+ /\(in reply to ([A-Z\s]+) command\)$/m, // "in reply to RCPT TO command"
67
+ /timed out while sending ([A-Z\s]+)$/m, // "timed out while sending (RCPT TO)", "timed out while sending RCPT TO"
68
+ /error after ([A-Z\s]+)/m, // "LMTP error after RCPT TO"
69
+ /while sending end of ([a-z\s]+)/m // "while sending end of data"
68
70
  ],
69
71
 
70
72
  email_server : [
71
73
  /host (\S+)\[([\d\.]+)\](?:\:(\d+)\:)? (?:said|refused to talk)/m,
72
74
  /^(\S+) rejected your message/m,
73
- /\: connect to (.+)\[(.+)\](?:\:(\d+)\:)?/m,
75
+ /\: connect to (.+)\[(.+)\](?:\:(\d+)\:)?/m, // "connect to acme.com[132.145.223.95]:25:"
74
76
  /Name service error for name\=(\S+)\s/m,
75
77
  /RCPT TO\:\<(.+?)\>/m,
76
78
  /Message rejected by\:\s+([\S\.]+?)$/m, // Outlook error
77
- /from (\S+) \[([\d\.]+)\](?:\:(\d+)\:)?/m
79
+ /from (\S+) \[([\d\.]+)\](?:\:(\d+)\:)?/m,
80
+ /conversation with (\S+)\[([\d\.]+)\](?:\:(\d+)\:)? timed out/m,
81
+ /Generating server: (\S+)(?:\[([\d\.]+)\])?(?:\:(\d+)\:)?/m // "Generating server: PH7PR02MB9549495.namprd02.prod.outlook.com"
78
82
  ],
79
83
 
80
84
  error : [
81
- /said\: (\d{3}(?:[\s\-]\d\.\d\.\d\d?)?) (.+) \(in reply to/m,
82
- /said\: (\d{3}(?:[\s\-]\d\.\d\.\d\d?)?)-(.+)/m,
83
- /(\d{3}\-\'\d\.\d\.\d\d?) (.+)/m,
84
- /LMTP error after (?:.+?)\:\<.+?\>\: (\d{3}(?:[\s\-]\d\.\d\.\d\d?)?) <.+?\>(.+)$/m,
85
- /Remote Server returned \'(\d{3}(?:[\s\-]\d\.\d\.\d\d?)?) (.+)\'/m,
86
- /The response from the remote server was:\s(\d{3}(?:[\s\-]\d\.\d\.\d\d?)?) (.+)/m,
87
- /refused to talk to me\: (\d{3}(?:[\s\-]\d\.\d\.\d\d?)?) (.+)/m,
88
- /(?:error|reason)\:\s*?(\d{3}(?:[\s\-]\d\.\d\.\d\d?)?) (.+)/mi,
89
- /(\d{3}[\s\-]\d\.\d\.\d\d?)/m,
90
- /(\d{3}\-\'\d\.\d\.\d\d?)/m
85
+ /said\: (\d{3}(?:[\s\-]\d\.\d\.\d\d?)?) (.+) \(in reply to/,
86
+ /said\: (\d{3}(?:[\s\-]\d\.\d\.\d\d?)?)-(.+)/,
87
+ /(\d{3}\-\'\d\.\d\.\d\d?) (.+)/,
88
+ /LMTP error after .+?\:\<.+?\>\: (\d{3}(?:[\s\-]\d\.\d\.\d\d?)?) <.+?\>(.+)$/m,
89
+ /remote server returned \'(\d{3}(?:[\s\-]\d\.\d\.\d\d?)?) (.+)\'/i,
90
+ /The response from the remote server was:\s(\d{3}(?:[\s\-]\d\.\d\.\d\d?)?) (.+)/,
91
+ /refused to talk to me\: (\d{3}(?:[\s\-]\d\.\d\.\d\d?)?) (.+)/,
92
+ /(?:error|reason)\:\s*?(\d{3}(?:[\s\-]\d\.\d\.\d\d?)?) (.+)/i,
93
+ /(\d{3}[\s\-]\d\.\d\.\d\d?)/,
94
+ /(\d{3}\-\'\d\.\d\.\d\d?)/
91
95
  ],
92
96
 
93
97
  error_lax : [
94
- /(^\<.+?\>:.+)/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"
95
100
  ],
96
101
 
97
102
  recipient : [
98
- /The following addresses had permanent fatal errors (?:.*?)\s\<(.+?)\>/,
99
- /The following message to \<(.+?)\> was undeliverable/,
100
- /to the following addresses:\s*(.+)/,
103
+ /the following addresses had permanent fatal errors (?:.*?)\s\<(.+?)\>/i,
104
+ /the following message to \<(.+?)\> was undeliverable/i,
105
+ /to the following addresses:\s*(.+)/i,
101
106
  /^\<(.+?)\>:/m,
102
- /^(.+?)\s?\n?\s*[\[|<]mailto\:(.+?)[\]|>]/m,
103
- /^Your message wasn't delivered to (.+?)\s/m,
104
- /^Your message to (.+?) couldn\'t/m,
105
- /\<(.+?)\>:/
107
+ /^(.+?)\s?\n?\s*[\[|<]mailto\:(.+?)[\]|>]/mi, // "john@acme.com<mailto:john@acme.com>"
108
+ /^your message wasn't delivered to (.+?)\s/mi, // "Your message wasn't delivered to john@acme.com"
109
+ /^your message to (.+?) couldn\'t/mi, // "Your message to john@acme.com couldn't"
110
+ /\<(.+?)\> \(expanded from \<.+?\>\)\:/, // "<john@acme.com> (expanded from <john@digiflex.com>): "
111
+ /\<(.+?)\>:/ // "<john@acme.com>:"
106
112
  ],
107
113
 
108
114
  temporary_safe : /(temporary|temporarily)/i,
@@ -119,7 +125,8 @@ var REGEXES = {
119
125
  ],
120
126
 
121
127
  enhanced_types_server_unknown : [
122
- /(does not accept mail)/i
128
+ /(does not accept mail)/i,
129
+ /(domain name not found)/i
123
130
  ],
124
131
 
125
132
  enhanced_types_server_timeout : [
@@ -127,12 +134,12 @@ var REGEXES = {
127
134
  ],
128
135
 
129
136
  enhanced_types_mailbox_full : [
130
- /(out of storage|(?:mailbox (?:is )?full)|(?:user (?:is )?over quota))/i
137
+ /(out of storage|(?:mailbox (?:is )?full)|(?:user (?:is )?over quota))/i // "user is over quota" (iCloud)
131
138
  ],
132
139
 
133
140
  error_code : [
134
- /(\d{3})[\s\-](\d\.\d\.\d)/, // "550 5.4.1" or "550-5.4.1"
135
- /(\d{3})\-\'(\d\.\d\.\d)/, // "550-'5.4.1"
141
+ /(\d{3})[\s\-](\d\.\d\.\d\d?)/, // "550 5.4.1" or "550-5.4.1" or "550-5.4.11"
142
+ /(\d{3})\-\'(\d\.\d\.\d\d?)/, // "550-'5.4.1" or "550-'5.4.11"
136
143
  /(\d{3})/ // "550"
137
144
  ]
138
145
  };
@@ -273,7 +280,7 @@ class Parser {
273
280
 
274
281
  if (_match && _match.length > 1) {
275
282
  _code = this.__parseErrorCode(_match[1]);
276
- _label = trimString(_match[2]);
283
+ _label = this.__cleanErrorLabel(_match[2]);
277
284
 
278
285
  // Check temporary / permanent
279
286
  if (_code.basic[0] === "4") {
@@ -303,7 +310,7 @@ class Parser {
303
310
  var _match = loopRegexes(this.__regexes.error_lax, str);
304
311
 
305
312
  if (_match && _match.length > 1) {
306
- _label = trimString(_match[1]);
313
+ _label = this.__cleanErrorLabel(_match[1]);
307
314
  }
308
315
  }
309
316
 
@@ -494,7 +501,7 @@ class Parser {
494
501
  var _match = loopRegexes(this.__regexes.command, str);
495
502
 
496
503
  if (_match && _match.length > 1) {
497
- _command = _match[1];
504
+ _command = trimString(_match[1]).toUpperCase();
498
505
  }
499
506
 
500
507
  return _command;
@@ -547,6 +554,43 @@ class Parser {
547
554
 
548
555
  return _str;
549
556
  }
557
+
558
+
559
+ /**
560
+ * Cleans error label
561
+ * @private
562
+ * @param {string} [label]
563
+ * @return {string} Unquoted text
564
+ */
565
+ __cleanErrorLabel(label="") {
566
+ var _label = this.__unquoteText(label);
567
+
568
+ _label = trimString(_label);
569
+
570
+ return _label;
571
+ }
572
+
573
+
574
+ /**
575
+ * Unquotes text
576
+ * @private
577
+ * @param {string} [str]
578
+ * @return {string} Unquoted text
579
+ */
580
+ __unquoteText(str="") {
581
+ var _str = str;
582
+
583
+ _str = trimString(_str);
584
+
585
+ if (
586
+ (_str.startsWith("'") && _str.endsWith("'")) ||
587
+ (_str.startsWith("\"") && _str.endsWith("\""))
588
+ ) {
589
+ return _str.slice(1, -1);
590
+ }
591
+
592
+ return _str;
593
+ }
550
594
  }
551
595
 
552
596
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "email-bounce-parser",
3
- "version": "1.0.14",
3
+ "version": "1.0.16",
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
  }