sisimai 5.6.0 → 5.7.1

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 (130) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/rake-test.yml +0 -4
  3. data/ChangeLog.md +47 -0
  4. data/README-JA.md +19 -16
  5. data/README.md +23 -21
  6. data/lib/sisimai/address.rb +8 -31
  7. data/lib/sisimai/arf.rb +5 -6
  8. data/lib/sisimai/eb.rb +116 -0
  9. data/lib/sisimai/fact.rb +33 -51
  10. data/lib/sisimai/lda.rb +20 -18
  11. data/lib/sisimai/lhost/activehunter.rb +0 -2
  12. data/lib/sisimai/lhost/amazonses.rb +17 -18
  13. data/lib/sisimai/lhost/apachejames.rb +0 -1
  14. data/lib/sisimai/lhost/biglobe.rb +0 -16
  15. data/lib/sisimai/lhost/courier.rb +9 -7
  16. data/lib/sisimai/lhost/deutschetelekom.rb +120 -0
  17. data/lib/sisimai/lhost/domino.rb +2 -4
  18. data/lib/sisimai/lhost/dragonfly.rb +0 -27
  19. data/lib/sisimai/lhost/einsundeins.rb +4 -12
  20. data/lib/sisimai/lhost/exchange2003.rb +12 -11
  21. data/lib/sisimai/lhost/exchange2007.rb +17 -17
  22. data/lib/sisimai/lhost/exim.rb +34 -81
  23. data/lib/sisimai/lhost/ezweb.rb +15 -51
  24. data/lib/sisimai/lhost/fml.rb +8 -32
  25. data/lib/sisimai/lhost/gmail.rb +10 -32
  26. data/lib/sisimai/lhost/gmx.rb +7 -24
  27. data/lib/sisimai/lhost/googlegroups.rb +6 -5
  28. data/lib/sisimai/lhost/googleworkspace.rb +2 -5
  29. data/lib/sisimai/lhost/imailserver.rb +4 -9
  30. data/lib/sisimai/lhost/kddi.rb +8 -21
  31. data/lib/sisimai/lhost/mailfoundry.rb +0 -2
  32. data/lib/sisimai/lhost/mailmarshal.rb +1 -3
  33. data/lib/sisimai/lhost/messagingserver.rb +4 -15
  34. data/lib/sisimai/lhost/mfilter.rb +0 -1
  35. data/lib/sisimai/lhost/mimecast.rb +0 -1
  36. data/lib/sisimai/lhost/notes.rb +3 -3
  37. data/lib/sisimai/lhost/opensmtpd.rb +0 -40
  38. data/lib/sisimai/lhost/postfix.rb +14 -14
  39. data/lib/sisimai/lhost/qmail.rb +23 -89
  40. data/lib/sisimai/lhost/sendmail.rb +8 -7
  41. data/lib/sisimai/lhost/trendmicro.rb +5 -4
  42. data/lib/sisimai/lhost/v5sendmail.rb +2 -2
  43. data/lib/sisimai/lhost/verizon.rb +4 -4
  44. data/lib/sisimai/lhost/x1.rb +1 -2
  45. data/lib/sisimai/lhost/x2.rb +0 -2
  46. data/lib/sisimai/lhost/x3.rb +4 -9
  47. data/lib/sisimai/lhost/x6.rb +0 -1
  48. data/lib/sisimai/lhost/zoho.rb +0 -12
  49. data/lib/sisimai/lhost.rb +38 -19
  50. data/lib/sisimai/message.rb +1 -1
  51. data/lib/sisimai/order.rb +4 -1
  52. data/lib/sisimai/reason/authfailure.rb +8 -7
  53. data/lib/sisimai/reason/badreputation.rb +5 -4
  54. data/lib/sisimai/reason/blocked.rb +6 -5
  55. data/lib/sisimai/reason/contenterror.rb +8 -5
  56. data/lib/sisimai/reason/delivered.rb +3 -2
  57. data/lib/sisimai/reason/emailtoolarge.rb +7 -6
  58. data/lib/sisimai/reason/expired.rb +16 -4
  59. data/lib/sisimai/reason/failedstarttls.rb +5 -3
  60. data/lib/sisimai/reason/feedback.rb +2 -1
  61. data/lib/sisimai/reason/filtered.rb +9 -8
  62. data/lib/sisimai/reason/hasmoved.rb +5 -4
  63. data/lib/sisimai/reason/hostunknown.rb +14 -4
  64. data/lib/sisimai/reason/mailboxfull.rb +10 -8
  65. data/lib/sisimai/reason/mailererror.rb +3 -2
  66. data/lib/sisimai/reason/networkerror.rb +16 -3
  67. data/lib/sisimai/reason/norelaying.rb +8 -6
  68. data/lib/sisimai/reason/notaccept.rb +15 -7
  69. data/lib/sisimai/reason/notcompliantrfc.rb +5 -3
  70. data/lib/sisimai/reason/onhold.rb +8 -7
  71. data/lib/sisimai/reason/policyviolation.rb +11 -5
  72. data/lib/sisimai/reason/ratelimited.rb +5 -4
  73. data/lib/sisimai/reason/rejected.rb +24 -14
  74. data/lib/sisimai/reason/requireptr.rb +5 -4
  75. data/lib/sisimai/reason/securityerror.rb +5 -3
  76. data/lib/sisimai/reason/spamdetected.rb +6 -5
  77. data/lib/sisimai/reason/suppressed.rb +4 -3
  78. data/lib/sisimai/reason/suspend.rb +8 -3
  79. data/lib/sisimai/reason/syntaxerror.rb +5 -11
  80. data/lib/sisimai/reason/systemerror.rb +21 -2
  81. data/lib/sisimai/reason/systemfull.rb +4 -3
  82. data/lib/sisimai/reason/undefined.rb +2 -1
  83. data/lib/sisimai/reason/userunknown.rb +15 -9
  84. data/lib/sisimai/reason/vacation.rb +2 -1
  85. data/lib/sisimai/reason/virusdetected.rb +6 -5
  86. data/lib/sisimai/reason.rb +47 -42
  87. data/lib/sisimai/rfc1123.rb +1 -1
  88. data/lib/sisimai/rfc1894.rb +15 -8
  89. data/lib/sisimai/rfc2045.rb +8 -5
  90. data/lib/sisimai/rfc3464/thirdparty.rb +11 -10
  91. data/lib/sisimai/rfc3464.rb +10 -14
  92. data/lib/sisimai/rfc3834.rb +6 -5
  93. data/lib/sisimai/rfc791.rb +3 -38
  94. data/lib/sisimai/rhost/aol.rb +3 -2
  95. data/lib/sisimai/rhost/apple.rb +12 -11
  96. data/lib/sisimai/rhost/cloudflare.rb +3 -2
  97. data/lib/sisimai/rhost/cox.rb +38 -37
  98. data/lib/sisimai/rhost/facebook.rb +17 -16
  99. data/lib/sisimai/rhost/franceptt.rb +39 -38
  100. data/lib/sisimai/rhost/godaddy.rb +35 -34
  101. data/lib/sisimai/rhost/google.rb +22 -21
  102. data/lib/sisimai/rhost/gsuite.rb +8 -7
  103. data/lib/sisimai/rhost/iua.rb +11 -10
  104. data/lib/sisimai/rhost/kddi.rb +3 -2
  105. data/lib/sisimai/rhost/messagelabs.rb +13 -12
  106. data/lib/sisimai/rhost/microsoft.rb +37 -32
  107. data/lib/sisimai/rhost/mimecast.rb +18 -17
  108. data/lib/sisimai/rhost/nttdocomo.rb +10 -9
  109. data/lib/sisimai/rhost/outlook.rb +3 -2
  110. data/lib/sisimai/rhost/spectrum.rb +19 -18
  111. data/lib/sisimai/rhost/tencent.rb +10 -9
  112. data/lib/sisimai/rhost/yahooinc.rb +11 -10
  113. data/lib/sisimai/rhost/zoho.rb +8 -7
  114. data/lib/sisimai/rhost.rb +1 -1
  115. data/lib/sisimai/smtp/command.rb +13 -8
  116. data/lib/sisimai/smtp/failure.rb +9 -8
  117. data/lib/sisimai/smtp/reply.rb +23 -22
  118. data/lib/sisimai/smtp/status.rb +164 -159
  119. data/lib/sisimai/smtp/transcript.rb +3 -2
  120. data/lib/sisimai/string.rb +0 -12
  121. data/lib/sisimai/version.rb +1 -1
  122. data/lib/sisimai.rb +2 -1
  123. data/set-of-emails/maildir/bsd/lhost-deutschetelekom-01.eml +66 -0
  124. data/set-of-emails/maildir/bsd/lhost-deutschetelekom-02.eml +68 -0
  125. data/set-of-emails/maildir/bsd/lhost-deutschetelekom-03.eml +50 -0
  126. data/set-of-emails/maildir/bsd/lhost-postfix-81.eml +130 -0
  127. data/set-of-emails/should-not-crash/go-423-deeply-nested-mime-parts.eml +20050 -0
  128. data/set-of-emails/should-not-crash/p5-664-iomart-mail-filter.eml +258 -0
  129. data/set-of-emails/to-be-debugged-because/sisimai-cannot-parse-yet/.gitkeep +0 -0
  130. metadata +11 -2
@@ -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
@@ -3,6 +3,7 @@ module Sisimai::Lhost
3
3
  # Methods in the module are called from only Sisimai::Message.
4
4
  module EinsUndEins
5
5
  class << self
6
+ require 'sisimai/eb'
6
7
  require 'sisimai/lhost'
7
8
 
8
9
  Indicators = Sisimai::Lhost.INDICATORS
@@ -11,7 +12,6 @@ module Sisimai::Lhost
11
12
  message: ['This message was created automatically by mail delivery software'],
12
13
  error: ['For the following reason:'],
13
14
  }.freeze
14
- MessagesOf = {'emailtoolarge' => ['Mail size limit exceeded']}.freeze
15
15
 
16
16
  # @abstract Decode the bounce message from 1&1
17
17
  # @param [Hash] mhead Message headers of a bounce email
@@ -90,22 +90,14 @@ module Sisimai::Lhost
90
90
  p1 = e['diagnosis'].index('host: ')
91
91
  p2 = e['diagnosis'].index(' reason:')
92
92
 
93
- e['rhost'] = Sisimai::String.sweep(e['diagnosis'][p1 + 6, p2 - p1 - 6])
94
- e['command'] = 'DATA' if e['diagnosis'].include?('for TEXT command')
95
- e['spec'] = 'SMTP' if e['diagnosis'].include?('SMTP error')
93
+ e['rhost'] = e['diagnosis'][p1 + 6, p2 - p1 - 6]
94
+ e['command'] = Sisimai::Eb::CeDATA if e['diagnosis'].include?('for TEXT command')
95
+ e['spec'] = 'SMTP' if e['diagnosis'].include?('SMTP error')
96
96
  e['status'] = Sisimai::SMTP::Status.find(e['diagnosis'])
97
97
  else
98
98
  # For the following reason:
99
99
  e['diagnosis'][0, StartingOf[:error][0].size] = ''
100
100
  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
101
  end
110
102
 
111
103
  return {"ds" => dscontents, "rfc822" => emailparts[1]}
@@ -4,6 +4,7 @@ module Sisimai::Lhost
4
4
  # Methods in the module are called from only Sisimai::Message.
5
5
  module Exchange2003
6
6
  class << self
7
+ require 'sisimai/eb'
7
8
  require 'sisimai/lhost'
8
9
 
9
10
  Indicators = Sisimai::Lhost.INDICATORS
@@ -13,29 +14,29 @@ module Sisimai::Lhost
13
14
  error: ['did not reach the following recipient(s):'],
14
15
  }.freeze
15
16
  ErrorCodes = {
16
- 'onhold' => [
17
+ Sisimai::Eb::Re___1 => [
17
18
  '000B099C', # Host Unknown, Message exceeds size limit, ...
18
19
  '000B09AA', # Unable to relay for, Message exceeds size limit,...
19
20
  '000B09B6', # Error messages by remote MTA
20
21
  ],
21
- 'userunknown' => [
22
+ Sisimai::Eb::ReUSER => [
22
23
  '000C05A6', # Unknown Recipient,
23
24
  ],
24
- 'systemerror' => [
25
+ Sisimai::Eb::RePROC => [
25
26
  '00010256', # Too many recipients.
26
27
  '000D06B5', # No proxy for recipient (non-smtp mail?)
27
28
  ],
28
- 'networkerror' => [
29
+ Sisimai::Eb::ReINET => [
29
30
  '00120270', # Too Many Hops
30
31
  ],
31
- 'contenterror' => [
32
+ Sisimai::Eb::ReBODY => [
32
33
  '00050311', # Conversion to Internet format failed
33
34
  '000502CC', # Conversion to Internet format failed
34
35
  ],
35
- 'securityerror' => [
36
+ Sisimai::Eb::ReSAFE => [
36
37
  '000B0981', # 502 Server does not support AUTH
37
38
  ],
38
- 'filtered' => [
39
+ Sisimai::Eb::ReFILT => [
39
40
  '000C0595', # Ambiguous Recipient
40
41
  ],
41
42
  }.freeze
@@ -158,19 +159,20 @@ module Sisimai::Lhost
158
159
  # Subject: ...
159
160
  # Sent: Thu, 29 Apr 2010 18:14:35 +0000
160
161
  #
161
- if e.start_with?(' To: ') || e.start_with?(' To: ')
162
+ case
163
+ when e.start_with?(' To: ', ' To: ')
162
164
  # To: shironeko@example.jp
163
165
  next if connheader['to'].empty? == false
164
166
  connheader['to'] = e[e.rindex(' ') + 1, e.size]
165
167
  connvalues += 1
166
168
 
167
- elsif e.start_with?(' Subject: ') || e.start_with?(' Subject: ')
169
+ when e.start_with?(' Subject: ', ' Subject: ')
168
170
  # Subject: ...
169
171
  next if connheader['subject'].empty? == false
170
172
  connheader['subject'] = e[e.rindex(' ') + 1, e.size]
171
173
  connvalues += 1
172
174
 
173
- elsif e.start_with?(' Sent: ') || e.start_with?(' Sent: ')
175
+ when e.start_with?(' Sent: ', ' Sent: ')
174
176
  # Sent: Thu, 29 Apr 2010 18:14:35 +0000
175
177
  # Sent: 4/29/99 9:19:59 AM
176
178
  next if connheader['date'].empty? == false
@@ -207,7 +209,6 @@ module Sisimai::Lhost
207
209
 
208
210
  # Copy alternative error message
209
211
  e['diagnosis'] = "#{e['alterrors']} #{e['diagnosis']}"
210
- e['diagnosis'] = Sisimai::String.sweep(e['diagnosis'])
211
212
  e.delete('alterrors')
212
213
  end
213
214
  end
@@ -4,6 +4,7 @@ module Sisimai::Lhost
4
4
  # Methods in the module are called from only Sisimai::Message.
5
5
  module Exchange2007
6
6
  class << self
7
+ require 'sisimai/eb'
7
8
  require 'sisimai/lhost'
8
9
 
9
10
  Indicators = Sisimai::Lhost.INDICATORS
@@ -32,19 +33,19 @@ module Sisimai::Lhost
32
33
  ]
33
34
  }.freeze
34
35
  NDRSubject = {
35
- "SMTPSEND.DNS.NonExistentDomain" => "hostunknown", # 554 5.4.4 SMTPSEND.DNS.NonExistentDomain
36
- "SMTPSEND.DNS.MxLoopback" => "networkerror", # 554 5.4.4 SMTPSEND.DNS.MxLoopback
37
- "RESOLVER.ADR.BadPrimary" => "systemerror", # 550 5.2.0 RESOLVER.ADR.BadPrimary
38
- "RESOLVER.ADR.RecipNotFound" => "userunknown", # 550 5.1.1 RESOLVER.ADR.RecipNotFound
39
- "RESOLVER.ADR.RecipientNotFound" => "userunknown", # 550 5.1.1 RESOLVER.ADR.RecipientNotFound
40
- "RESOLVER.ADR.ExRecipNotFound" => "userunknown", # 550 5.1.1 RESOLVER.ADR.ExRecipNotFound
41
- "RESOLVER.ADR.RecipLimit" => "ratelimited", # 550 5.5.3 RESOLVER.ADR.RecipLimit
42
- "RESOLVER.ADR.InvalidInSmtp" => "systemerror", # 550 5.1.0 RESOLVER.ADR.InvalidInSmtp
43
- "RESOLVER.ADR.Ambiguous" => "systemerror", # 550 5.1.4 RESOLVER.ADR.Ambiguous, 420 4.2.0 RESOLVER.ADR.Ambiguous
44
- "RESOLVER.RST.AuthRequired" => "securityerror", # 550 5.7.1 RESOLVER.RST.AuthRequired
45
- "RESOLVER.RST.NotAuthorized" => "rejected", # 550 5.7.1 RESOLVER.RST.NotAuthorized
46
- "RESOLVER.RST.RecipSizeLimit" => "emailtoolarge", # 550 5.2.3 RESOLVER.RST.RecipSizeLimit
47
- "QUEUE.Expired" => "expired", # 550 4.4.7 QUEUE.Expired
36
+ "SMTPSEND.DNS.NonExistentDomain" => Sisimai::Eb::ReHOST, # 554 5.4.4 SMTPSEND.DNS.NonExistentDomain
37
+ "SMTPSEND.DNS.MxLoopback" => Sisimai::Eb::ReINET, # 554 5.4.4 SMTPSEND.DNS.MxLoopback
38
+ "RESOLVER.ADR.BadPrimary" => Sisimai::Eb::RePROC, # 550 5.2.0 RESOLVER.ADR.BadPrimary
39
+ "RESOLVER.ADR.RecipNotFound" => Sisimai::Eb::ReUSER, # 550 5.1.1 RESOLVER.ADR.RecipNotFound
40
+ "RESOLVER.ADR.RecipientNotFound" => Sisimai::Eb::ReUSER, # 550 5.1.1 RESOLVER.ADR.RecipientNotFound
41
+ "RESOLVER.ADR.ExRecipNotFound" => Sisimai::Eb::ReUSER, # 550 5.1.1 RESOLVER.ADR.ExRecipNotFound
42
+ "RESOLVER.ADR.RecipLimit" => Sisimai::Eb::ReRATE, # 550 5.5.3 RESOLVER.ADR.RecipLimit
43
+ "RESOLVER.ADR.InvalidInSmtp" => Sisimai::Eb::RePROC, # 550 5.1.0 RESOLVER.ADR.InvalidInSmtp
44
+ "RESOLVER.ADR.Ambiguous" => Sisimai::Eb::RePROC, # 550 5.1.4 RESOLVER.ADR.Ambiguous, 420 4.2.0 RESOLVER.ADR.Ambiguous
45
+ "RESOLVER.RST.AuthRequired" => Sisimai::Eb::ReSAFE, # 550 5.7.1 RESOLVER.RST.AuthRequired
46
+ "RESOLVER.RST.NotAuthorized" => Sisimai::Eb::ReFROM, # 550 5.7.1 RESOLVER.RST.NotAuthorized
47
+ "RESOLVER.RST.RecipSizeLimit" => Sisimai::Eb::ReSIZE, # 550 5.2.3 RESOLVER.RST.RecipSizeLimit
48
+ "QUEUE.Expired" => Sisimai::Eb::ReTIME, # 550 4.4.7 QUEUE.Expired
48
49
  }.freeze
49
50
  MailSender = ["postmaster@outlook.com", ".onmicrosoft.com"].freeze
50
51
  EmailTitle = [
@@ -64,7 +65,9 @@ module Sisimai::Lhost
64
65
  def inquire(mhead, mbody)
65
66
  proceedsto = 0
66
67
  proceedsto += 1 if EmailTitle.any? { |a| mhead["subject"].include?(a) }
67
- proceedsto += 1 if MailSender.any? { |a| mhead["from"].include?(a) }
68
+ proceedsto += 1 if MailSender.any? { |a| mhead["from"].include?(a) }
69
+ proceedsto += 1 if StartingOf[:error].any? { |a| mbody.include?(a) }
70
+ proceedsto += 1 if StartingOf[:message].any? { |a| mbody.include?(a) }
68
71
  proceedsto += 1 if mhead["content-language"]
69
72
  return nil if proceedsto < 2
70
73
 
@@ -150,9 +153,6 @@ module Sisimai::Lhost
150
153
  return nil if recipients == 0
151
154
 
152
155
  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
156
  p0 = -1; StartingOf[:error].each do |r|
157
157
  # Try to find the NDR subject string such as "RESOLVER.ADR.RecipientNotFound" from the
158
158
  # error message
@@ -3,6 +3,7 @@ module Sisimai::Lhost
3
3
  # Methods in the module are called from only Sisimai::Message.
4
4
  module Exim
5
5
  class << self
6
+ require 'sisimai/eb'
6
7
  require 'sisimai/lhost'
7
8
  require 'sisimai/rfc1894'
8
9
 
@@ -60,69 +61,25 @@ module Sisimai::Lhost
60
61
  MessagesOf = {
61
62
  # find exim/ -type f -exec grep 'message = US' {} /dev/null \;
62
63
  # route.c:1158| DEBUG(D_uid) debug_printf("getpwnam() returned NULL (user not found)\n");
63
- # find exim/ -type f -exec grep 'message = US' {} /dev/null \;
64
- # route.c:1158| DEBUG(D_uid) debug_printf("getpwnam() returned NULL (user not found)\n");
65
- "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
- ],
64
+ Sisimai::Eb::ReUSER => ["user not found"],
87
65
  # parser.c:666| *errorptr = string_sprintf("%s (expected word or \"<\")", *errorptr);
88
66
  # parser.c:701| if(bracket_count++ > 5) FAILED(US"angle-brackets nested too deep");
89
67
  # parser.c:738| FAILED(US"domain missing in source-routed address");
90
68
  # parser.c:747| : string_sprintf("malformed address: %.32s may not follow %.*s",
91
- "syntaxerror" => [
69
+ Sisimai::Eb::ReCOMM => [
92
70
  "angle-brackets nested too deep",
93
71
  'expected word or "<"',
94
72
  "domain missing in source-routed address",
95
73
  "malformed address:",
96
74
  ],
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
75
  }.freeze
109
76
  DelayedFor = [
110
- # retry.c:902| addr->message = (addr->message == NULL)? US"retry timeout exceeded" :
111
- # deliver.c:7475| "No action is required on your part. Delivery attempts will continue for\n"
112
- # smtp.c:3508| US"retry time not reached for any host after a long failure period" :
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",
77
+ # deliver.c:7475| "No action is required on your part. Delivery attempts will continue for\n"
78
+ # smtp.c:3508| US"retry time not reached for any host after a long failure period" :
79
+ # deliver.c:7459| print_address_error(addr, f, US"Delay reason: ");
120
80
  "No action is required on your part",
121
81
  "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
82
  "Delay reason: ",
124
- "has been frozen",
125
- "was frozen on arrival by ",
126
83
  ].freeze
127
84
 
128
85
  # @abstract Decodes the bounce message from Exim
@@ -133,6 +90,8 @@ module Sisimai::Lhost
133
90
  def inquire(mhead, mbody)
134
91
  # Message-Id: <E1P1YNN-0003AD-Ga@example.org>
135
92
  # X-Failed-Recipients: kijitora@example.ed.jp
93
+ return nil if Sisimai::Lhost.BannerDTAG.any? { |a| mbody.include?(a) }
94
+
136
95
  thirdparty = false
137
96
  proceedsto = 0
138
97
  messageidv = mhead["message-id"] || ""
@@ -255,7 +214,7 @@ module Sisimai::Lhost
255
214
  # parser.c:749| goto PARSE_FAILED;
256
215
  # parser.c:750| }
257
216
  cv = Sisimai::Address.s3s4(e[p1, p2 - p1 - 1])
258
- v["diagnosis"] = Sisimai::String.sweep(e[p2 + 1, e.size])
217
+ v["diagnosis"] = e[p2 + 1, e.size]
259
218
  else
260
219
  # There is an email address only in the line
261
220
  # kijitora@example.jp
@@ -288,7 +247,8 @@ module Sisimai::Lhost
288
247
  # "e" matched with any field defined in RFC3464
289
248
  next unless o = Sisimai::RFC1894.field(e)
290
249
 
291
- if o[3] == "addr"
250
+ case o[3]
251
+ when "addr"
292
252
  # Final-Recipient: rfc822; kijitora@example.jp
293
253
  # X-Actual-Recipient: rfc822; kijitora@example.co.jp
294
254
  next if o[0] != "final-recipient"
@@ -296,7 +256,7 @@ module Sisimai::Lhost
296
256
  v["spec"] = o[2].include?('@') ? "SMTP" : "X-UNIX"
297
257
  end
298
258
 
299
- elsif o[3] == "code"
259
+ when "code"
300
260
  # Diagnostic-Code: SMTP; 550 5.1.1 <userunknown@example.jp>... User Unknown
301
261
  v["spec"] = o[1].upcase
302
262
  v["diagnosis"] = o[2]
@@ -341,24 +301,22 @@ module Sisimai::Lhost
341
301
  q["recipient"] = q["alias"]
342
302
  end
343
303
  end
344
- else
304
+ elsif mhead["x-failed-recipients"]
345
305
  # Fallback for getting recipient addresses
346
- if mhead["x-failed-recipients"]
347
- # X-Failed-Recipients: kijitora@example.jp
348
- rcptinhead = mhead["x-failed-recipients"].split(",")
349
- rcptinhead.each do |e|
350
- # Remove space characters
351
- e.lstrip!
352
- e.rstrip!
353
- end
354
- recipients = rcptinhead.size
306
+ # X-Failed-Recipients: kijitora@example.jp
307
+ rcptinhead = mhead["x-failed-recipients"].split(",")
308
+ rcptinhead.each do |e|
309
+ # Remove space characters
310
+ e.lstrip!
311
+ e.rstrip!
312
+ end
313
+ recipients = rcptinhead.size
355
314
 
356
- while e = rcptinhead.shift do
357
- # Insert each recipient address into dscontents
358
- dscontents[-1]["recipient"] = e
359
- next if dscontents.size == recipients
360
- dscontents << Sisimai::Lhost.DELIVERYSTATUS
361
- end
315
+ while e = rcptinhead.shift do
316
+ # Insert each recipient address into dscontents
317
+ dscontents[-1]["recipient"] = e
318
+ next if dscontents.size == recipients
319
+ dscontents << Sisimai::Lhost.DELIVERYSTATUS
362
320
  end
363
321
  end
364
322
  return nil if recipients == 0
@@ -406,7 +364,7 @@ module Sisimai::Lhost
406
364
  end
407
365
  e.delete("alterrors")
408
366
  end
409
- e["diagnosis"] = Sisimai::String.sweep(e["diagnosis"]) || ""; p1 = e["diagnosis"].index("__") || -1
367
+ p1 = e["diagnosis"].index("__") || -1
410
368
  e["diagnosis"] = e["diagnosis"][0, p1] if p1 > 1
411
369
 
412
370
  if e["rhost"].empty?
@@ -429,14 +387,11 @@ module Sisimai::Lhost
429
387
  end
430
388
 
431
389
  # Detect the reason of bounce
432
- if %w[HELO EHLO].index(e["command"])
433
- # HELO | Connected to 192.0.2.135 but my name was rejected.
434
- e["reason"] = "blocked"
435
-
436
- elsif e["command"] == "MAIL"
437
- # MAIL | Connected to 192.0.2.135 but sender was rejected.
438
- e["reason"] = "onhold"
439
-
390
+ case e["command"]
391
+ # - HELO | Connected to 192.0.2.135 but my name was rejected.
392
+ # - MAIL | Connected to 192.0.2.135 but sender was rejected.
393
+ when Sisimai::Eb::CeHELO, Sisimai::Eb::CeEHLO then e["reason"] = Sisimai::Eb::ReBLOC
394
+ when Sisimai::Eb::CeMAIL then e["reason"] = Sisimai::Eb::Re___1
440
395
  else
441
396
  # Try to match the error message with each message pattern
442
397
  MessagesOf.each_key do |r|
@@ -447,9 +402,7 @@ module Sisimai::Lhost
447
402
  end
448
403
 
449
404
  if e["reason"].empty?
450
- # The reason "expired", or "mailererror"
451
- e["reason"] = "expired" if DelayedFor.any? { |a| e["diagnosis"].include?(a) }
452
- e["reason"] = "mailererror" if e["reason"].empty? && e["diagnosis"].include?("pipe to |")
405
+ e["reason"] = Sisimai::Eb::ReTIME if DelayedFor.any? { |a| e["diagnosis"].include?(a) }
453
406
  end
454
407
  end
455
408
  end
@@ -468,7 +421,7 @@ module Sisimai::Lhost
468
421
  re = e["reason"]
469
422
  cv = ""
470
423
 
471
- if Sisimai::SMTP::Failure.is_temporary(cr) || re == "expired" || re == "mailboxfull"
424
+ if Sisimai::SMTP::Failure.is_temporary(cr) || re == Sisimai::Eb::ReTIME
472
425
  # Set the pseudo status code as a temporary error
473
426
  cv = Sisimai::SMTP::Status.code(re, true) if Sisimai::Reason.is_explicit(re)
474
427
  end
@@ -3,27 +3,18 @@ module Sisimai::Lhost
3
3
  # Methods in the module are called from only Sisimai::Message.
4
4
  module EZweb
5
5
  class << self
6
+ require 'sisimai/eb'
6
7
  require 'sisimai/lhost'
7
8
 
8
9
  Indicators = Sisimai::Lhost.INDICATORS
9
10
  Boundaries = ["--------------------------------------------------", "Content-Type: message/rfc822"].freeze
10
11
  StartingOf = {message: ['The user(s) ', 'Your message ', 'Each of the following', '<']}.freeze
11
- Messagesof = {
12
- # notaccept: ['The following recipients did not receive this message:'],
13
- 'expired' => [
14
- # Your message was not delivered within 0 days and 1 hours.
15
- # Remote host is not responding.
16
- 'Your message was not delivered within ',
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
12
+ UnpaidUser = [
13
+ # http://www.naruhodo-au.kddi.com/qa3429203.html
14
+ # The recipient may be unpaid user...?
15
+ 'The user(s) account is disabled.',
16
+ 'The user(s) account is temporarily limited.',
17
+ ].freeze
27
18
 
28
19
  # @abstract Decodes the bounce message from au EZweb
29
20
  # @param [Hash] mhead Message headers of a bounce email
@@ -48,7 +39,6 @@ module Sisimai::Lhost
48
39
  bodyslices = emailparts[0].split("\n")
49
40
  readcursor = 0 # (Integer) Points the current cursor position
50
41
  recipients = 0 # (Integer) The number of 'Final-Recipient' header
51
- substrings = []; Messagesof.each_value { |a| substrings << a }; substrings.flatten!
52
42
 
53
43
  while e = bodyslices.shift do
54
44
  # Read error messages and delivery status lines from the head of the email to the previous
@@ -94,57 +84,31 @@ module Sisimai::Lhost
94
84
 
95
85
  else
96
86
  # Other error messages
87
+ # >>> RCPT TO:<******@ezweb.ne.jp>
88
+ # <<< 550 ...
97
89
  next if Sisimai::String.is_8bit(e)
98
- if e.include?(" >>> ")
99
- # >>> RCPT TO:<******@ezweb.ne.jp>
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
90
+ v["command"] = Sisimai::SMTP::Command.find(e) if e.include?(" >>> ")
91
+ v["diagnosis"] += " #{e}"
117
92
  end
118
93
  end
119
94
  return nil if recipients == 0
120
95
 
121
96
  dscontents.each do |e|
122
97
  # Check each value of DeliveryMatter{}, try to detect the bounce reason.
123
- e['diagnosis'] = Sisimai::String.sweep(e['diagnosis'])
98
+ e['diagnosis'] = e['diagnosis'].split.join(" ")
124
99
  e["command"] = Sisimai::SMTP::Command.find(e["diagnosis"]) if e["command"].empty?
125
100
 
126
101
  if mhead['x-spasign'].to_s == 'NG'
127
102
  # Content-Type: text/plain; ..., X-SPASIGN: NG (spamghetti, au by EZweb)
128
103
  # Filtered recipient returns message that include 'X-SPASIGN' header
129
- e['reason'] = 'filtered'
130
- e['toxic'] = true
104
+ e['reason'] = Sisimai::Eb::ReFILT
131
105
  else
132
106
  # There is no X-SPASIGN header or the value of the header is not "NG"
133
- catch :FINDREASON do
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
107
+ e['reason'] = Sisimai::Eb::ReQUIT if UnpaidUser.any? { |a| e['diagnosis'].include?(a) }
144
108
  end
145
109
  next if e['reason'] != ""
146
110
  next if e['recipient'].end_with?('@ezweb.ne.jp', '@au.com')
147
- e["reason"] = "userunknown" if e["diagnosis"].start_with?("<")
111
+ e["reason"] = Sisimai::Eb::ReUSER if e["diagnosis"].start_with?("<")
148
112
  end
149
113
 
150
114
  return {"ds" => dscontents, "rfc822" => emailparts[1]}
@@ -3,39 +3,25 @@ module Sisimai::Lhost
3
3
  # https://www.fml.org/. Methods in the module are called from only Sisimai::Message.
4
4
  module FML
5
5
  class << self
6
+ require 'sisimai/eb'
6
7
  require 'sisimai/lhost'
7
8
 
8
9
  Indicators = Sisimai::Lhost.INDICATORS
9
10
  Boundaries = ['Original mail as follows:'].freeze
10
11
  ErrorTitle = {
11
- 'rejected' => [
12
+ Sisimai::Eb::ReFROM => [
12
13
  ' are not member',
13
14
  'NOT MEMBER article from ',
14
15
  'reject mail ',
15
16
  'Spam mail from a spammer is rejected',
16
17
  ],
17
- 'systemerror' => [
18
+ Sisimai::Eb::RePROC => [
18
19
  'fml system error message',
19
20
  'Loop Alert: ',
20
21
  'Loop Back Warning: ',
21
22
  'WARNING: UNIX FROM Loop',
22
23
  ],
23
- 'securityerror' => ['Security Alert'],
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
- ],
24
+ Sisimai::Eb::ReSAFE => ['Security Alert'],
39
25
  }.freeze
40
26
 
41
27
  # @abstract Decodes the bounce message from fml mailling list server/manager
@@ -84,23 +70,13 @@ module Sisimai::Lhost
84
70
  return nil if recipients == 0
85
71
 
86
72
  dscontents.each do |e|
87
- e['diagnosis'] = Sisimai::String.sweep(e['diagnosis'])
88
- ErrorTable.each_key do |f|
89
- # Try to match with error messages defined in ErrorTable
90
- next if ErrorTable[f].none? { |a| e['diagnosis'].include?(a) }
73
+ # Error messages in the message body did not matched
74
+ ErrorTitle.each_key do |f|
75
+ # Try to match with the Subject string
76
+ next if ErrorTitle[f].none? { |a| mhead["subject"].include?(a) }
91
77
  e['reason'] = f
92
78
  break
93
79
  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
80
  end
105
81
 
106
82
  return { 'ds' => dscontents, 'rfc822' => emailparts[1] }