sisimai 5.6.0 → 5.7.0
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.
- checksums.yaml +4 -4
- data/.github/workflows/rake-test.yml +0 -4
- data/ChangeLog.md +32 -0
- data/README-JA.md +19 -16
- data/README.md +23 -21
- data/lib/sisimai/address.rb +8 -31
- data/lib/sisimai/arf.rb +3 -5
- data/lib/sisimai/fact.rb +12 -37
- data/lib/sisimai/lhost/activehunter.rb +0 -2
- data/lib/sisimai/lhost/amazonses.rb +6 -8
- data/lib/sisimai/lhost/apachejames.rb +0 -1
- data/lib/sisimai/lhost/biglobe.rb +0 -16
- data/lib/sisimai/lhost/courier.rb +5 -4
- data/lib/sisimai/lhost/deutschetelekom.rb +120 -0
- data/lib/sisimai/lhost/domino.rb +0 -3
- data/lib/sisimai/lhost/dragonfly.rb +0 -27
- data/lib/sisimai/lhost/einsundeins.rb +1 -10
- data/lib/sisimai/lhost/exchange2003.rb +4 -4
- data/lib/sisimai/lhost/exchange2007.rb +3 -4
- data/lib/sisimai/lhost/exim.rb +30 -78
- data/lib/sisimai/lhost/ezweb.rb +12 -49
- data/lib/sisimai/lhost/fml.rb +4 -29
- data/lib/sisimai/lhost/gmail.rb +0 -23
- data/lib/sisimai/lhost/gmx.rb +7 -24
- data/lib/sisimai/lhost/googlegroups.rb +3 -3
- data/lib/sisimai/lhost/googleworkspace.rb +0 -4
- data/lib/sisimai/lhost/imailserver.rb +3 -9
- data/lib/sisimai/lhost/kddi.rb +6 -20
- data/lib/sisimai/lhost/mailfoundry.rb +0 -2
- data/lib/sisimai/lhost/mailmarshal.rb +1 -3
- data/lib/sisimai/lhost/messagingserver.rb +4 -15
- data/lib/sisimai/lhost/mfilter.rb +0 -1
- data/lib/sisimai/lhost/mimecast.rb +0 -1
- data/lib/sisimai/lhost/notes.rb +1 -2
- data/lib/sisimai/lhost/opensmtpd.rb +0 -40
- data/lib/sisimai/lhost/postfix.rb +10 -11
- data/lib/sisimai/lhost/qmail.rb +14 -81
- data/lib/sisimai/lhost/sendmail.rb +4 -4
- data/lib/sisimai/lhost/trendmicro.rb +3 -3
- data/lib/sisimai/lhost/v5sendmail.rb +0 -1
- data/lib/sisimai/lhost/verizon.rb +1 -2
- data/lib/sisimai/lhost/x1.rb +1 -2
- data/lib/sisimai/lhost/x2.rb +0 -2
- data/lib/sisimai/lhost/x3.rb +4 -9
- data/lib/sisimai/lhost/x6.rb +0 -1
- data/lib/sisimai/lhost/zoho.rb +0 -12
- data/lib/sisimai/lhost.rb +38 -19
- data/lib/sisimai/message.rb +1 -1
- data/lib/sisimai/order.rb +4 -1
- data/lib/sisimai/reason/authfailure.rb +2 -2
- data/lib/sisimai/reason/contenterror.rb +2 -0
- data/lib/sisimai/reason/emailtoolarge.rb +1 -1
- data/lib/sisimai/reason/expired.rb +13 -2
- data/lib/sisimai/reason/hostunknown.rb +9 -0
- data/lib/sisimai/reason/mailboxfull.rb +3 -2
- data/lib/sisimai/reason/networkerror.rb +13 -1
- data/lib/sisimai/reason/norelaying.rb +4 -3
- data/lib/sisimai/reason/notaccept.rb +10 -3
- data/lib/sisimai/reason/notcompliantrfc.rb +1 -0
- data/lib/sisimai/reason/policyviolation.rb +6 -1
- data/lib/sisimai/reason/rejected.rb +6 -0
- data/lib/sisimai/reason/securityerror.rb +1 -0
- data/lib/sisimai/reason/suspend.rb +4 -0
- data/lib/sisimai/reason/syntaxerror.rb +1 -8
- data/lib/sisimai/reason/systemerror.rb +18 -0
- data/lib/sisimai/reason/userunknown.rb +2 -0
- data/lib/sisimai/reason.rb +7 -7
- data/lib/sisimai/rfc1123.rb +1 -1
- data/lib/sisimai/rfc1894.rb +7 -6
- data/lib/sisimai/rfc2045.rb +2 -2
- data/lib/sisimai/rfc3464/thirdparty.rb +1 -1
- data/lib/sisimai/rfc3464.rb +10 -14
- data/lib/sisimai/rfc3834.rb +3 -4
- data/lib/sisimai/rfc791.rb +3 -38
- data/lib/sisimai/rhost/microsoft.rb +4 -0
- data/lib/sisimai/rhost.rb +1 -1
- data/lib/sisimai/smtp/status.rb +23 -19
- data/lib/sisimai/string.rb +0 -12
- data/lib/sisimai/version.rb +1 -1
- data/set-of-emails/maildir/bsd/lhost-deutschetelekom-01.eml +66 -0
- data/set-of-emails/maildir/bsd/lhost-deutschetelekom-02.eml +68 -0
- data/set-of-emails/maildir/bsd/lhost-deutschetelekom-03.eml +50 -0
- data/set-of-emails/should-not-crash/p5-664-iomart-mail-filter.eml +258 -0
- data/set-of-emails/to-be-debugged-because/sisimai-cannot-parse-yet/.gitkeep +0 -0
- metadata +8 -2
|
@@ -72,7 +72,8 @@ module Sisimai::Lhost
|
|
|
72
72
|
next unless o = Sisimai::RFC1894.field(e)
|
|
73
73
|
v = dscontents[-1]
|
|
74
74
|
|
|
75
|
-
|
|
75
|
+
case o[3]
|
|
76
|
+
when "addr"
|
|
76
77
|
# Final-Recipient: rfc822; kijitora@example.jp
|
|
77
78
|
# X-Actual-Recipient: rfc822; kijitora@example.co.jp
|
|
78
79
|
if o[0] == 'final-recipient'
|
|
@@ -88,7 +89,7 @@ module Sisimai::Lhost
|
|
|
88
89
|
# X-Actual-Recipient: rfc822; kijitora@example.co.jp
|
|
89
90
|
v['alias'] = o[2]
|
|
90
91
|
end
|
|
91
|
-
|
|
92
|
+
when "code"
|
|
92
93
|
# Diagnostic-Code: SMTP; 550 5.1.1 <userunknown@example.jp>... User Unknown
|
|
93
94
|
v['spec'] = o[1]
|
|
94
95
|
v['diagnosis'] = o[2]
|
|
@@ -116,7 +117,7 @@ module Sisimai::Lhost
|
|
|
116
117
|
# Continued line of the value of Diagnostic-Code field
|
|
117
118
|
next if readslices[-2].start_with?('Diagnostic-Code:') == false
|
|
118
119
|
next if e.start_with?(' ') == false
|
|
119
|
-
v['diagnosis'] += "
|
|
120
|
+
v['diagnosis'] += " " + e
|
|
120
121
|
readslices[-1] = "Diagnostic-Code: #{e}"
|
|
121
122
|
end
|
|
122
123
|
end
|
|
@@ -127,7 +128,7 @@ module Sisimai::Lhost
|
|
|
127
128
|
# Set default values if each value is empty.
|
|
128
129
|
permessage.each_key { |a| e[a] ||= permessage[a] || '' }
|
|
129
130
|
e['command'] = thecommand if e["command"].empty?
|
|
130
|
-
e['diagnosis'] =
|
|
131
|
+
e['diagnosis'] = e['diagnosis'] || ''
|
|
131
132
|
|
|
132
133
|
MessagesOf.each_key do |r|
|
|
133
134
|
# Verify each regular expression of session errors
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
module Sisimai::Lhost
|
|
2
|
+
# Sisimai::Lhost::DeutscheTelekom decodes a bounce email which created by Deutsche Telekom or
|
|
3
|
+
# T-Online. Methods in the module are called from only Sisimai::Message.
|
|
4
|
+
module DeutscheTelekom
|
|
5
|
+
class << self
|
|
6
|
+
require 'sisimai/lhost'
|
|
7
|
+
|
|
8
|
+
Indicators = Sisimai::Lhost.INDICATORS
|
|
9
|
+
BannerDTAG = Sisimai::Lhost.BannerDTAG
|
|
10
|
+
StartingOf = { message: [BannerDTAG[1]] }.freeze
|
|
11
|
+
|
|
12
|
+
# @abstract Decodes the bounce message from DeutscheTelekom
|
|
13
|
+
# @param [Hash] mhead Message headers of a bounce email
|
|
14
|
+
# @param [String] mbody Message body of a bounce email
|
|
15
|
+
# @return [Hash] Bounce data list and message/rfc822 part
|
|
16
|
+
# @return [Nil] it failed to decode or the arguments are missing
|
|
17
|
+
def inquire(mhead, mbody)
|
|
18
|
+
# - T-Online: https://www.t-online.de/, @t-online.de, @magenta.de
|
|
19
|
+
# - DeutscheTelekom: https://www.telekom.com/
|
|
20
|
+
# - Based on the bounce format of Smail 3, the original design model for Exim
|
|
21
|
+
# - Tailored for Deutsche Telekom's internal Smail 3 fork with custom banners
|
|
22
|
+
# - Module name follows the infrastructure owner for cross-language compatibility
|
|
23
|
+
# - Smail 3: http://www.weird.com/~woods/projects/smail.html
|
|
24
|
+
return nil unless BannerDTAG.any? { |a| mbody.include?(a) }
|
|
25
|
+
|
|
26
|
+
# smail-3.2.0.108/src/
|
|
27
|
+
# notify.c:1052|(void) fprintf(f, "Subject: mail failed, %s\nReference: <%s@%s>\n\n",
|
|
28
|
+
# notify.c:1053| subject_to, message_id, primary_name);
|
|
29
|
+
#
|
|
30
|
+
# T-Online specific headers
|
|
31
|
+
# Received: from mailin42.aul.t-online.de (mailin42.aul.t-online.de [192.51.100.1])
|
|
32
|
+
# by mailout11.t-online.de (Postfix) with SMTP id 05E5A1CAC0
|
|
33
|
+
# From: Mail Delivery System <Mailer-Daemon@t-online.de>
|
|
34
|
+
# X-TOI-MSGID: c9412855-531f-497b-b007-5ffc033877a0
|
|
35
|
+
dscontents = [Sisimai::Lhost.DELIVERYSTATUS]; v = nil
|
|
36
|
+
emailparts = Sisimai::RFC5322.part(mbody, [BannerDTAG[3], BannerDTAG[2]])
|
|
37
|
+
bodyslices = emailparts[0].split("\n")
|
|
38
|
+
messagelog = ''
|
|
39
|
+
readcursor = 0 # (Integer) Points the current cursor position
|
|
40
|
+
recipients = 0 # (Integer) The number of 'Final-Recipient' header
|
|
41
|
+
|
|
42
|
+
while e = bodyslices.shift do
|
|
43
|
+
# Read error messages and delivery status lines from the head of the email to the previous
|
|
44
|
+
# line of the beginning of the original message.
|
|
45
|
+
if readcursor == 0
|
|
46
|
+
# Beginning of the bounce message or delivery status part
|
|
47
|
+
if e.start_with?(StartingOf[:message][0])
|
|
48
|
+
# |------------------------- Failed addresses follow: ---------------------|
|
|
49
|
+
readcursor |= Indicators[:deliverystatus]
|
|
50
|
+
else
|
|
51
|
+
# |------------------------- Message log follows: -------------------------|
|
|
52
|
+
# The line above may appears only in Smail 3.
|
|
53
|
+
#
|
|
54
|
+
# smail-3.2.0.108/src/
|
|
55
|
+
# models.c:787| if (deliver == NULL && defer == NULL) {
|
|
56
|
+
# models.c:788| write_log(WRITE_LOG_MLOG, "no valid recipients were found for this message");
|
|
57
|
+
# models.c:789| return_to_sender = TRUE;
|
|
58
|
+
# models.c:879| }
|
|
59
|
+
messagelog += ' ' + e if e != "" && e.include?(BannerDTAG[0]) == false
|
|
60
|
+
end
|
|
61
|
+
next
|
|
62
|
+
end
|
|
63
|
+
next if (readcursor & Indicators[:deliverystatus]) == 0 || e.empty?
|
|
64
|
+
|
|
65
|
+
# |------------------------- Failed addresses follow: ---------------------|
|
|
66
|
+
# <example@t-online.de>
|
|
67
|
+
# 552 5.2.2 <example@t-online.de> Quota exceeded (mailbox for user is full)
|
|
68
|
+
#
|
|
69
|
+
# |------------------------- Message header follows: ----------------------|
|
|
70
|
+
# Received: from mail.fragdenstaat.de ([94.130.55.89]) by mailin41.mgt.mul.t-online.de.example.com
|
|
71
|
+
# with (TLSv1.3:TLS_AES_256_GCM_SHA384 encrypted)
|
|
72
|
+
# ...
|
|
73
|
+
v = dscontents[-1]
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
if e.start_with?(' <') && e.end_with?('>') && e.count(' ') == 1
|
|
77
|
+
# Deutsche Telekom: The recipient address is enclosed in angle brackets.
|
|
78
|
+
# |------------------------- Failed addresses follow: ---------------------|
|
|
79
|
+
if v["recipient"] != ""
|
|
80
|
+
# There are multiple recipient addresses in the message body.
|
|
81
|
+
dscontents << Sisimai::Lhost.DELIVERYSTATUS
|
|
82
|
+
v = dscontents[-1]
|
|
83
|
+
end
|
|
84
|
+
v['recipient'] = e[2, e.size]
|
|
85
|
+
recipients += 1
|
|
86
|
+
|
|
87
|
+
elsif Sisimai::String.aligned(e, [' ', '@', '.', ' ... '])
|
|
88
|
+
# Smail 3:
|
|
89
|
+
# - The recipient address is not enclosed in angle brackets.
|
|
90
|
+
# - Error message begins with " ... failed:"
|
|
91
|
+
# smail-3.2.0.108/src/
|
|
92
|
+
# notify.c:845| if (cur->error) {
|
|
93
|
+
# notify.c:846| (void) fprintf(f, " %s ... failed: %s\n",
|
|
94
|
+
# notify.c:847| cur->in_addr ? cur->in_addr : "(unknown)",
|
|
95
|
+
# notify.c:848| cur->error->message);
|
|
96
|
+
# notify.c:849| }
|
|
97
|
+
# |------------------------- Failed addresses follow: ---------------------|
|
|
98
|
+
# kijitora@neko.nyaan.example.com ... unknown host
|
|
99
|
+
if v["recipient"] != ""
|
|
100
|
+
# There are multiple recipient addresses in the message body.
|
|
101
|
+
dscontents << Sisimai::Lhost.DELIVERYSTATUS
|
|
102
|
+
v = dscontents[-1]
|
|
103
|
+
end
|
|
104
|
+
v['recipient'] = e[1, e.index(' ... ')]
|
|
105
|
+
v['diagnosis'] = messagelog << ' ' + e
|
|
106
|
+
recipients += 1
|
|
107
|
+
|
|
108
|
+
else
|
|
109
|
+
# 552 5.2.2 <example@t-online.de> Quota exceeded (mailbox for user is full)
|
|
110
|
+
v['diagnosis'] = e
|
|
111
|
+
end
|
|
112
|
+
end
|
|
113
|
+
return nil if recipients == 0
|
|
114
|
+
return { 'ds' => dscontents, 'rfc822' => emailparts[1] }
|
|
115
|
+
end
|
|
116
|
+
def description; return 'DeutscheTelekom'; end
|
|
117
|
+
end
|
|
118
|
+
end
|
|
119
|
+
end
|
|
120
|
+
|
data/lib/sisimai/lhost/domino.rb
CHANGED
|
@@ -9,8 +9,6 @@ module Sisimai::Lhost
|
|
|
9
9
|
Boundaries = ['Content-Type: message/rfc822'].freeze
|
|
10
10
|
StartingOf = {message: ['Your message']}.freeze
|
|
11
11
|
MessagesOf = {
|
|
12
|
-
"filtered" => ["Cannot route mail to user"],
|
|
13
|
-
"systemerror" => ["Several matches found in Domino Directory"],
|
|
14
12
|
"userunknown" => [
|
|
15
13
|
"not listed in Domino Directory",
|
|
16
14
|
"not listed in public Name & Address Book",
|
|
@@ -120,7 +118,6 @@ module Sisimai::Lhost
|
|
|
120
118
|
return nil if recipients == 0
|
|
121
119
|
|
|
122
120
|
dscontents.each do |e|
|
|
123
|
-
e['diagnosis'] = Sisimai::String.sweep(e['diagnosis'])
|
|
124
121
|
e['recipient'] = Sisimai::Address.s3s4(e['recipient'])
|
|
125
122
|
permessage.each_key { |a| e[a] ||= permessage[a] || '' }
|
|
126
123
|
|
|
@@ -14,23 +14,6 @@ module Sisimai::Lhost
|
|
|
14
14
|
# https://github.com/corecode/dma/blob/ffad280aa40c242aa9a2cb9ca5b1b6e8efedd17e/mail.c#L84
|
|
15
15
|
message: ['This is the DragonFly Mail Agent '],
|
|
16
16
|
}.freeze
|
|
17
|
-
MessagesOf = {
|
|
18
|
-
'expired' => [
|
|
19
|
-
# https://github.com/corecode/dma/blob/master/dma.c#L370C1-L374C19
|
|
20
|
-
# dma.c:370| if (gettimeofday(&now, NULL) == 0 &&
|
|
21
|
-
# dma.c:371| (now.tv_sec - st.st_mtim.tv_sec > MAX_TIMEOUT)) {
|
|
22
|
-
# dma.c:372| snprintf(errmsg, sizeof(errmsg),
|
|
23
|
-
# dma.c:373| "Could not deliver for the last %d seconds. Giving up.",
|
|
24
|
-
# dma.c:374| MAX_TIMEOUT);
|
|
25
|
-
# dma.c:375| goto bounce;
|
|
26
|
-
# dma.c:376| }
|
|
27
|
-
'Could not deliver for the last ',
|
|
28
|
-
],
|
|
29
|
-
'hostunknown' => [
|
|
30
|
-
# net.c:663| snprintf(errmsg, sizeof(errmsg), "DNS lookup failure: host %s not found", host);
|
|
31
|
-
'DNS lookup failure: host ',
|
|
32
|
-
],
|
|
33
|
-
}.freeze
|
|
34
17
|
|
|
35
18
|
# @abstract Decodes the bounce message from DMA: DragonFly Mail Agent
|
|
36
19
|
# @param [Hash] mhead Message headers of a bounce email
|
|
@@ -92,16 +75,6 @@ module Sisimai::Lhost
|
|
|
92
75
|
end
|
|
93
76
|
end
|
|
94
77
|
return nil if recipients == 0
|
|
95
|
-
|
|
96
|
-
dscontents.each do |e|
|
|
97
|
-
e['diagnosis'] = Sisimai::String.sweep(e['diagnosis'])
|
|
98
|
-
MessagesOf.each_key do |r|
|
|
99
|
-
# Verify each regular expression of session errors
|
|
100
|
-
next if MessagesOf[r].none? { |a| e['diagnosis'].include?(a) }
|
|
101
|
-
e['reason'] = r
|
|
102
|
-
break
|
|
103
|
-
end
|
|
104
|
-
end
|
|
105
78
|
return { 'ds' => dscontents, 'rfc822' => emailparts[1] }
|
|
106
79
|
end
|
|
107
80
|
def description; return 'DragonFly'; end
|
|
@@ -11,7 +11,6 @@ module Sisimai::Lhost
|
|
|
11
11
|
message: ['This message was created automatically by mail delivery software'],
|
|
12
12
|
error: ['For the following reason:'],
|
|
13
13
|
}.freeze
|
|
14
|
-
MessagesOf = {'emailtoolarge' => ['Mail size limit exceeded']}.freeze
|
|
15
14
|
|
|
16
15
|
# @abstract Decode the bounce message from 1&1
|
|
17
16
|
# @param [Hash] mhead Message headers of a bounce email
|
|
@@ -90,7 +89,7 @@ module Sisimai::Lhost
|
|
|
90
89
|
p1 = e['diagnosis'].index('host: ')
|
|
91
90
|
p2 = e['diagnosis'].index(' reason:')
|
|
92
91
|
|
|
93
|
-
e['rhost'] =
|
|
92
|
+
e['rhost'] = e['diagnosis'][p1 + 6, p2 - p1 - 6]
|
|
94
93
|
e['command'] = 'DATA' if e['diagnosis'].include?('for TEXT command')
|
|
95
94
|
e['spec'] = 'SMTP' if e['diagnosis'].include?('SMTP error')
|
|
96
95
|
e['status'] = Sisimai::SMTP::Status.find(e['diagnosis'])
|
|
@@ -98,14 +97,6 @@ module Sisimai::Lhost
|
|
|
98
97
|
# For the following reason:
|
|
99
98
|
e['diagnosis'][0, StartingOf[:error][0].size] = ''
|
|
100
99
|
end
|
|
101
|
-
e['diagnosis'] = Sisimai::String.sweep(e['diagnosis'])
|
|
102
|
-
|
|
103
|
-
MessagesOf.each_key do |r|
|
|
104
|
-
# Verify each regular expression of session errors
|
|
105
|
-
next if MessagesOf[r].none? { |a| e['diagnosis'].include?(a) }
|
|
106
|
-
e['reason'] = r
|
|
107
|
-
break
|
|
108
|
-
end
|
|
109
100
|
end
|
|
110
101
|
|
|
111
102
|
return {"ds" => dscontents, "rfc822" => emailparts[1]}
|
|
@@ -158,19 +158,20 @@ module Sisimai::Lhost
|
|
|
158
158
|
# Subject: ...
|
|
159
159
|
# Sent: Thu, 29 Apr 2010 18:14:35 +0000
|
|
160
160
|
#
|
|
161
|
-
|
|
161
|
+
case
|
|
162
|
+
when e.start_with?(' To: ', ' To: ')
|
|
162
163
|
# To: shironeko@example.jp
|
|
163
164
|
next if connheader['to'].empty? == false
|
|
164
165
|
connheader['to'] = e[e.rindex(' ') + 1, e.size]
|
|
165
166
|
connvalues += 1
|
|
166
167
|
|
|
167
|
-
|
|
168
|
+
when e.start_with?(' Subject: ', ' Subject: ')
|
|
168
169
|
# Subject: ...
|
|
169
170
|
next if connheader['subject'].empty? == false
|
|
170
171
|
connheader['subject'] = e[e.rindex(' ') + 1, e.size]
|
|
171
172
|
connvalues += 1
|
|
172
173
|
|
|
173
|
-
|
|
174
|
+
when e.start_with?(' Sent: ', ' Sent: ')
|
|
174
175
|
# Sent: Thu, 29 Apr 2010 18:14:35 +0000
|
|
175
176
|
# Sent: 4/29/99 9:19:59 AM
|
|
176
177
|
next if connheader['date'].empty? == false
|
|
@@ -207,7 +208,6 @@ module Sisimai::Lhost
|
|
|
207
208
|
|
|
208
209
|
# Copy alternative error message
|
|
209
210
|
e['diagnosis'] = "#{e['alterrors']} #{e['diagnosis']}"
|
|
210
|
-
e['diagnosis'] = Sisimai::String.sweep(e['diagnosis'])
|
|
211
211
|
e.delete('alterrors')
|
|
212
212
|
end
|
|
213
213
|
end
|
|
@@ -64,7 +64,9 @@ module Sisimai::Lhost
|
|
|
64
64
|
def inquire(mhead, mbody)
|
|
65
65
|
proceedsto = 0
|
|
66
66
|
proceedsto += 1 if EmailTitle.any? { |a| mhead["subject"].include?(a) }
|
|
67
|
-
proceedsto += 1 if MailSender.any? { |a| mhead["from"].include?(a)
|
|
67
|
+
proceedsto += 1 if MailSender.any? { |a| mhead["from"].include?(a) }
|
|
68
|
+
proceedsto += 1 if StartingOf[:error].any? { |a| mbody.include?(a) }
|
|
69
|
+
proceedsto += 1 if StartingOf[:message].any? { |a| mbody.include?(a) }
|
|
68
70
|
proceedsto += 1 if mhead["content-language"]
|
|
69
71
|
return nil if proceedsto < 2
|
|
70
72
|
|
|
@@ -150,9 +152,6 @@ module Sisimai::Lhost
|
|
|
150
152
|
return nil if recipients == 0
|
|
151
153
|
|
|
152
154
|
dscontents.each do |e|
|
|
153
|
-
# Tidy up the error message in $e->{'diagnosis'}, Try to detect the bounce reason.
|
|
154
|
-
e["diagnosis"] = Sisimai::String.sweep(e["diagnosis"])
|
|
155
|
-
|
|
156
155
|
p0 = -1; StartingOf[:error].each do |r|
|
|
157
156
|
# Try to find the NDR subject string such as "RESOLVER.ADR.RecipientNotFound" from the
|
|
158
157
|
# error message
|
data/lib/sisimai/lhost/exim.rb
CHANGED
|
@@ -58,32 +58,9 @@ module Sisimai::Lhost
|
|
|
58
58
|
],
|
|
59
59
|
}.freeze
|
|
60
60
|
MessagesOf = {
|
|
61
|
-
# find exim/ -type f -exec grep 'message = US' {} /dev/null \;
|
|
62
|
-
# route.c:1158| DEBUG(D_uid) debug_printf("getpwnam() returned NULL (user not found)\n");
|
|
63
61
|
# find exim/ -type f -exec grep 'message = US' {} /dev/null \;
|
|
64
62
|
# route.c:1158| DEBUG(D_uid) debug_printf("getpwnam() returned NULL (user not found)\n");
|
|
65
63
|
"userunknown" => ["user not found"],
|
|
66
|
-
# transports/smtp.c:3524| addr->message = US"all host address lookups failed permanently";
|
|
67
|
-
# routers/dnslookup.c:331| addr->message = US"all relevant MX records point to non-existent hosts";
|
|
68
|
-
# route.c:1826| uschar *message = US"Unrouteable address";
|
|
69
|
-
"hostunknown" => [
|
|
70
|
-
"all host address lookups failed permanently",
|
|
71
|
-
"all relevant MX records point to non-existent hosts",
|
|
72
|
-
"Unrouteable address",
|
|
73
|
-
],
|
|
74
|
-
# transports/appendfile.c:2567| addr->user_message = US"mailbox is full";
|
|
75
|
-
# transports/appendfile.c:3049| addr->message = string_sprintf("mailbox is full "
|
|
76
|
-
# transports/appendfile.c:3050| "(quota exceeded while writing to file %s)", filename);
|
|
77
|
-
"mailboxfull" => [
|
|
78
|
-
"mailbox is full",
|
|
79
|
-
"error: quota exceed",
|
|
80
|
-
],
|
|
81
|
-
# routers/dnslookup.c:328| addr->message = US"an MX or SRV record indicated no SMTP service";
|
|
82
|
-
# transports/smtp.c:3502| addr->message = US"no host found for existing SMTP connection";
|
|
83
|
-
"notaccept" => [
|
|
84
|
-
"an MX or SRV record indicated no SMTP service",
|
|
85
|
-
"no host found for existing SMTP connection",
|
|
86
|
-
],
|
|
87
64
|
# parser.c:666| *errorptr = string_sprintf("%s (expected word or \"<\")", *errorptr);
|
|
88
65
|
# parser.c:701| if(bracket_count++ > 5) FAILED(US"angle-brackets nested too deep");
|
|
89
66
|
# parser.c:738| FAILED(US"domain missing in source-routed address");
|
|
@@ -94,35 +71,14 @@ module Sisimai::Lhost
|
|
|
94
71
|
"domain missing in source-routed address",
|
|
95
72
|
"malformed address:",
|
|
96
73
|
],
|
|
97
|
-
# deliver.c:5614| addr->message = US"delivery to file forbidden";
|
|
98
|
-
# deliver.c:5624| addr->message = US"delivery to pipe forbidden";
|
|
99
|
-
# transports/pipe.c:1156| addr->user_message = US"local delivery failed";
|
|
100
|
-
"systemerror" => [
|
|
101
|
-
"delivery to file forbidden",
|
|
102
|
-
"delivery to pipe forbidden",
|
|
103
|
-
"local delivery failed",
|
|
104
|
-
"LMTP error after ",
|
|
105
|
-
],
|
|
106
|
-
# deliver.c:5425| new->message = US"Too many \"Received\" headers - suspected mail loop";
|
|
107
|
-
"contenterror" => ['Too many "Received" headers'],
|
|
108
74
|
}.freeze
|
|
109
75
|
DelayedFor = [
|
|
110
|
-
#
|
|
111
|
-
#
|
|
112
|
-
#
|
|
113
|
-
# smtp.c:3508| US"all hosts have been failing for a long time and were last tried "
|
|
114
|
-
# "after this message arrived";
|
|
115
|
-
# deliver.c:7459| print_address_error(addr, f, US"Delay reason: ");
|
|
116
|
-
# deliver.c:7586| "Message %s has been frozen%s.\nThe sender is <%s>.\n", message_id,
|
|
117
|
-
# receive.c:4021| moan_tell_someone(freeze_tell, NULL, US"Message frozen on arrival",
|
|
118
|
-
# receive.c:4022| "Message %s was frozen on arrival by %s.\nThe sender is <%s>.\n",
|
|
119
|
-
"retry timeout exceeded",
|
|
76
|
+
# deliver.c:7475| "No action is required on your part. Delivery attempts will continue for\n"
|
|
77
|
+
# smtp.c:3508| US"retry time not reached for any host after a long failure period" :
|
|
78
|
+
# deliver.c:7459| print_address_error(addr, f, US"Delay reason: ");
|
|
120
79
|
"No action is required on your part",
|
|
121
80
|
"retry time not reached for any host after a long failure period",
|
|
122
|
-
"all hosts have been failing for a long time and were last tried",
|
|
123
81
|
"Delay reason: ",
|
|
124
|
-
"has been frozen",
|
|
125
|
-
"was frozen on arrival by ",
|
|
126
82
|
].freeze
|
|
127
83
|
|
|
128
84
|
# @abstract Decodes the bounce message from Exim
|
|
@@ -133,6 +89,8 @@ module Sisimai::Lhost
|
|
|
133
89
|
def inquire(mhead, mbody)
|
|
134
90
|
# Message-Id: <E1P1YNN-0003AD-Ga@example.org>
|
|
135
91
|
# X-Failed-Recipients: kijitora@example.ed.jp
|
|
92
|
+
return nil if Sisimai::Lhost.BannerDTAG.any? { |a| mbody.include?(a) }
|
|
93
|
+
|
|
136
94
|
thirdparty = false
|
|
137
95
|
proceedsto = 0
|
|
138
96
|
messageidv = mhead["message-id"] || ""
|
|
@@ -255,7 +213,7 @@ module Sisimai::Lhost
|
|
|
255
213
|
# parser.c:749| goto PARSE_FAILED;
|
|
256
214
|
# parser.c:750| }
|
|
257
215
|
cv = Sisimai::Address.s3s4(e[p1, p2 - p1 - 1])
|
|
258
|
-
v["diagnosis"] =
|
|
216
|
+
v["diagnosis"] = e[p2 + 1, e.size]
|
|
259
217
|
else
|
|
260
218
|
# There is an email address only in the line
|
|
261
219
|
# kijitora@example.jp
|
|
@@ -288,7 +246,8 @@ module Sisimai::Lhost
|
|
|
288
246
|
# "e" matched with any field defined in RFC3464
|
|
289
247
|
next unless o = Sisimai::RFC1894.field(e)
|
|
290
248
|
|
|
291
|
-
|
|
249
|
+
case o[3]
|
|
250
|
+
when "addr"
|
|
292
251
|
# Final-Recipient: rfc822; kijitora@example.jp
|
|
293
252
|
# X-Actual-Recipient: rfc822; kijitora@example.co.jp
|
|
294
253
|
next if o[0] != "final-recipient"
|
|
@@ -296,7 +255,7 @@ module Sisimai::Lhost
|
|
|
296
255
|
v["spec"] = o[2].include?('@') ? "SMTP" : "X-UNIX"
|
|
297
256
|
end
|
|
298
257
|
|
|
299
|
-
|
|
258
|
+
when "code"
|
|
300
259
|
# Diagnostic-Code: SMTP; 550 5.1.1 <userunknown@example.jp>... User Unknown
|
|
301
260
|
v["spec"] = o[1].upcase
|
|
302
261
|
v["diagnosis"] = o[2]
|
|
@@ -341,24 +300,22 @@ module Sisimai::Lhost
|
|
|
341
300
|
q["recipient"] = q["alias"]
|
|
342
301
|
end
|
|
343
302
|
end
|
|
344
|
-
|
|
303
|
+
elsif mhead["x-failed-recipients"]
|
|
345
304
|
# Fallback for getting recipient addresses
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
recipients = rcptinhead.size
|
|
305
|
+
# X-Failed-Recipients: kijitora@example.jp
|
|
306
|
+
rcptinhead = mhead["x-failed-recipients"].split(",")
|
|
307
|
+
rcptinhead.each do |e|
|
|
308
|
+
# Remove space characters
|
|
309
|
+
e.lstrip!
|
|
310
|
+
e.rstrip!
|
|
311
|
+
end
|
|
312
|
+
recipients = rcptinhead.size
|
|
355
313
|
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
end
|
|
314
|
+
while e = rcptinhead.shift do
|
|
315
|
+
# Insert each recipient address into dscontents
|
|
316
|
+
dscontents[-1]["recipient"] = e
|
|
317
|
+
next if dscontents.size == recipients
|
|
318
|
+
dscontents << Sisimai::Lhost.DELIVERYSTATUS
|
|
362
319
|
end
|
|
363
320
|
end
|
|
364
321
|
return nil if recipients == 0
|
|
@@ -406,7 +363,7 @@ module Sisimai::Lhost
|
|
|
406
363
|
end
|
|
407
364
|
e.delete("alterrors")
|
|
408
365
|
end
|
|
409
|
-
|
|
366
|
+
p1 = e["diagnosis"].index("__") || -1
|
|
410
367
|
e["diagnosis"] = e["diagnosis"][0, p1] if p1 > 1
|
|
411
368
|
|
|
412
369
|
if e["rhost"].empty?
|
|
@@ -429,14 +386,11 @@ module Sisimai::Lhost
|
|
|
429
386
|
end
|
|
430
387
|
|
|
431
388
|
# Detect the reason of bounce
|
|
432
|
-
|
|
433
|
-
# HELO | Connected to 192.0.2.135 but my name was rejected.
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
# MAIL | Connected to 192.0.2.135 but sender was rejected.
|
|
438
|
-
e["reason"] = "onhold"
|
|
439
|
-
|
|
389
|
+
case e["command"]
|
|
390
|
+
# - HELO | Connected to 192.0.2.135 but my name was rejected.
|
|
391
|
+
# - MAIL | Connected to 192.0.2.135 but sender was rejected.
|
|
392
|
+
when "HELO", "EHLO" then e["reason"] = "blocked"
|
|
393
|
+
when "MAIL" then e["reason"] = "onhold"
|
|
440
394
|
else
|
|
441
395
|
# Try to match the error message with each message pattern
|
|
442
396
|
MessagesOf.each_key do |r|
|
|
@@ -447,9 +401,7 @@ module Sisimai::Lhost
|
|
|
447
401
|
end
|
|
448
402
|
|
|
449
403
|
if e["reason"].empty?
|
|
450
|
-
# The reason "expired", or "mailererror"
|
|
451
404
|
e["reason"] = "expired" if DelayedFor.any? { |a| e["diagnosis"].include?(a) }
|
|
452
|
-
e["reason"] = "mailererror" if e["reason"].empty? && e["diagnosis"].include?("pipe to |")
|
|
453
405
|
end
|
|
454
406
|
end
|
|
455
407
|
end
|
|
@@ -468,7 +420,7 @@ module Sisimai::Lhost
|
|
|
468
420
|
re = e["reason"]
|
|
469
421
|
cv = ""
|
|
470
422
|
|
|
471
|
-
if Sisimai::SMTP::Failure.is_temporary(cr) || re == "expired"
|
|
423
|
+
if Sisimai::SMTP::Failure.is_temporary(cr) || re == "expired"
|
|
472
424
|
# Set the pseudo status code as a temporary error
|
|
473
425
|
cv = Sisimai::SMTP::Status.code(re, true) if Sisimai::Reason.is_explicit(re)
|
|
474
426
|
end
|
data/lib/sisimai/lhost/ezweb.rb
CHANGED
|
@@ -8,22 +8,12 @@ module Sisimai::Lhost
|
|
|
8
8
|
Indicators = Sisimai::Lhost.INDICATORS
|
|
9
9
|
Boundaries = ["--------------------------------------------------", "Content-Type: message/rfc822"].freeze
|
|
10
10
|
StartingOf = {message: ['The user(s) ', 'Your message ', 'Each of the following', '<']}.freeze
|
|
11
|
-
|
|
12
|
-
#
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
],
|
|
18
|
-
'mailboxfull' => ['The user(s) account is temporarily over quota'],
|
|
19
|
-
'onhold' => ['Each of the following recipients was rejected by a remote mail server'],
|
|
20
|
-
'suspend' => [
|
|
21
|
-
# http://www.naruhodo-au.kddi.com/qa3429203.html
|
|
22
|
-
# The recipient may be unpaid user...?
|
|
23
|
-
'The user(s) account is disabled.',
|
|
24
|
-
'The user(s) account is temporarily limited.',
|
|
25
|
-
],
|
|
26
|
-
}.freeze
|
|
11
|
+
UnpaidUser = [
|
|
12
|
+
# http://www.naruhodo-au.kddi.com/qa3429203.html
|
|
13
|
+
# The recipient may be unpaid user...?
|
|
14
|
+
'The user(s) account is disabled.',
|
|
15
|
+
'The user(s) account is temporarily limited.',
|
|
16
|
+
].freeze
|
|
27
17
|
|
|
28
18
|
# @abstract Decodes the bounce message from au EZweb
|
|
29
19
|
# @param [Hash] mhead Message headers of a bounce email
|
|
@@ -48,7 +38,6 @@ module Sisimai::Lhost
|
|
|
48
38
|
bodyslices = emailparts[0].split("\n")
|
|
49
39
|
readcursor = 0 # (Integer) Points the current cursor position
|
|
50
40
|
recipients = 0 # (Integer) The number of 'Final-Recipient' header
|
|
51
|
-
substrings = []; Messagesof.each_value { |a| substrings << a }; substrings.flatten!
|
|
52
41
|
|
|
53
42
|
while e = bodyslices.shift do
|
|
54
43
|
# Read error messages and delivery status lines from the head of the email to the previous
|
|
@@ -94,53 +83,27 @@ module Sisimai::Lhost
|
|
|
94
83
|
|
|
95
84
|
else
|
|
96
85
|
# Other error messages
|
|
86
|
+
# >>> RCPT TO:<******@ezweb.ne.jp>
|
|
87
|
+
# <<< 550 ...
|
|
97
88
|
next if Sisimai::String.is_8bit(e)
|
|
98
|
-
if e.include?(" >>> ")
|
|
99
|
-
|
|
100
|
-
v["command"] = Sisimai::SMTP::Command.find(e)
|
|
101
|
-
v["diagnosis"] += " #{e}"
|
|
102
|
-
|
|
103
|
-
elsif e.include?(" <<< ")
|
|
104
|
-
# <<< 550 ...
|
|
105
|
-
v["diagnosis"] += " #{e}"
|
|
106
|
-
|
|
107
|
-
else
|
|
108
|
-
# Check error message
|
|
109
|
-
isincluded = false
|
|
110
|
-
if substrings.any? { |a| e.include?(a) }
|
|
111
|
-
# Check with regular expressions of each error
|
|
112
|
-
v["diagnosis"] += " #{e}"
|
|
113
|
-
isincluded = true
|
|
114
|
-
end
|
|
115
|
-
v["diagnosis"] += " #{e}" if isincluded
|
|
116
|
-
end
|
|
89
|
+
v["command"] = Sisimai::SMTP::Command.find(e) if e.include?(" >>> ")
|
|
90
|
+
v["diagnosis"] += " #{e}"
|
|
117
91
|
end
|
|
118
92
|
end
|
|
119
93
|
return nil if recipients == 0
|
|
120
94
|
|
|
121
95
|
dscontents.each do |e|
|
|
122
96
|
# Check each value of DeliveryMatter{}, try to detect the bounce reason.
|
|
123
|
-
e['diagnosis'] =
|
|
97
|
+
e['diagnosis'] = e['diagnosis'].split.join(" ")
|
|
124
98
|
e["command"] = Sisimai::SMTP::Command.find(e["diagnosis"]) if e["command"].empty?
|
|
125
99
|
|
|
126
100
|
if mhead['x-spasign'].to_s == 'NG'
|
|
127
101
|
# Content-Type: text/plain; ..., X-SPASIGN: NG (spamghetti, au by EZweb)
|
|
128
102
|
# Filtered recipient returns message that include 'X-SPASIGN' header
|
|
129
103
|
e['reason'] = 'filtered'
|
|
130
|
-
e['toxic'] = true
|
|
131
104
|
else
|
|
132
105
|
# There is no X-SPASIGN header or the value of the header is not "NG"
|
|
133
|
-
|
|
134
|
-
Messagesof.each_key do |r|
|
|
135
|
-
# Try to match with each session error message
|
|
136
|
-
Messagesof[r].each do |f|
|
|
137
|
-
# Check each error message pattern
|
|
138
|
-
next if e['diagnosis'].include?(f) == false
|
|
139
|
-
e['reason'] = r
|
|
140
|
-
throw :FINDREASON
|
|
141
|
-
end
|
|
142
|
-
end
|
|
143
|
-
end
|
|
106
|
+
e['reason'] = "suspend" if UnpaidUser.any? { |a| e['diagnosis'].include?(a) }
|
|
144
107
|
end
|
|
145
108
|
next if e['reason'] != ""
|
|
146
109
|
next if e['recipient'].end_with?('@ezweb.ne.jp', '@au.com')
|
data/lib/sisimai/lhost/fml.rb
CHANGED
|
@@ -22,21 +22,6 @@ module Sisimai::Lhost
|
|
|
22
22
|
],
|
|
23
23
|
'securityerror' => ['Security Alert'],
|
|
24
24
|
}.freeze
|
|
25
|
-
ErrorTable = {
|
|
26
|
-
'rejected' => [
|
|
27
|
-
' header may cause mail loop',
|
|
28
|
-
'NOT MEMBER article from ',
|
|
29
|
-
'reject mail from ',
|
|
30
|
-
'reject spammers:',
|
|
31
|
-
'You are not a member of this mailing list',
|
|
32
|
-
],
|
|
33
|
-
'notcompliantrfc' => ['Duplicated Message-ID'],
|
|
34
|
-
'securityerror' => ['Security alert:'],
|
|
35
|
-
'systemerror' => [
|
|
36
|
-
' has detected a loop condition so that',
|
|
37
|
-
'Loop Back Warning:',
|
|
38
|
-
],
|
|
39
|
-
}.freeze
|
|
40
25
|
|
|
41
26
|
# @abstract Decodes the bounce message from fml mailling list server/manager
|
|
42
27
|
# @param [Hash] mhead Message headers of a bounce email
|
|
@@ -84,23 +69,13 @@ module Sisimai::Lhost
|
|
|
84
69
|
return nil if recipients == 0
|
|
85
70
|
|
|
86
71
|
dscontents.each do |e|
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
# Try to match with
|
|
90
|
-
next if
|
|
72
|
+
# Error messages in the message body did not matched
|
|
73
|
+
ErrorTitle.each_key do |f|
|
|
74
|
+
# Try to match with the Subject string
|
|
75
|
+
next if ErrorTitle[f].none? { |a| mhead["subject"].include?(a) }
|
|
91
76
|
e['reason'] = f
|
|
92
77
|
break
|
|
93
78
|
end
|
|
94
|
-
|
|
95
|
-
if e['reason'].nil?
|
|
96
|
-
# Error messages in the message body did not matched
|
|
97
|
-
ErrorTitle.each_key do |f|
|
|
98
|
-
# Try to match with the Subject string
|
|
99
|
-
next if ErrorTitle[f].none? { |a| mhead["subject"].include?(a) }
|
|
100
|
-
e['reason'] = f
|
|
101
|
-
break
|
|
102
|
-
end
|
|
103
|
-
end
|
|
104
79
|
end
|
|
105
80
|
|
|
106
81
|
return { 'ds' => dscontents, 'rfc822' => emailparts[1] }
|