sisimai 5.7.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 (98) hide show
  1. checksums.yaml +4 -4
  2. data/ChangeLog.md +15 -0
  3. data/README-JA.md +8 -8
  4. data/README.md +8 -8
  5. data/lib/sisimai/arf.rb +2 -1
  6. data/lib/sisimai/eb.rb +116 -0
  7. data/lib/sisimai/fact.rb +24 -17
  8. data/lib/sisimai/lda.rb +20 -18
  9. data/lib/sisimai/lhost/amazonses.rb +11 -10
  10. data/lib/sisimai/lhost/courier.rb +4 -3
  11. data/lib/sisimai/lhost/domino.rb +2 -1
  12. data/lib/sisimai/lhost/einsundeins.rb +3 -2
  13. data/lib/sisimai/lhost/exchange2003.rb +8 -7
  14. data/lib/sisimai/lhost/exchange2007.rb +14 -13
  15. data/lib/sisimai/lhost/exim.rb +7 -6
  16. data/lib/sisimai/lhost/ezweb.rb +4 -3
  17. data/lib/sisimai/lhost/fml.rb +4 -3
  18. data/lib/sisimai/lhost/gmail.rb +10 -9
  19. data/lib/sisimai/lhost/googlegroups.rb +3 -2
  20. data/lib/sisimai/lhost/googleworkspace.rb +2 -1
  21. data/lib/sisimai/lhost/imailserver.rb +3 -2
  22. data/lib/sisimai/lhost/kddi.rb +4 -3
  23. data/lib/sisimai/lhost/notes.rb +2 -1
  24. data/lib/sisimai/lhost/postfix.rb +7 -6
  25. data/lib/sisimai/lhost/qmail.rb +10 -9
  26. data/lib/sisimai/lhost/sendmail.rb +4 -3
  27. data/lib/sisimai/lhost/trendmicro.rb +2 -1
  28. data/lib/sisimai/lhost/v5sendmail.rb +2 -1
  29. data/lib/sisimai/lhost/verizon.rb +3 -2
  30. data/lib/sisimai/reason/authfailure.rb +6 -5
  31. data/lib/sisimai/reason/badreputation.rb +5 -4
  32. data/lib/sisimai/reason/blocked.rb +6 -5
  33. data/lib/sisimai/reason/contenterror.rb +6 -5
  34. data/lib/sisimai/reason/delivered.rb +3 -2
  35. data/lib/sisimai/reason/emailtoolarge.rb +6 -5
  36. data/lib/sisimai/reason/expired.rb +3 -2
  37. data/lib/sisimai/reason/failedstarttls.rb +5 -3
  38. data/lib/sisimai/reason/feedback.rb +2 -1
  39. data/lib/sisimai/reason/filtered.rb +9 -8
  40. data/lib/sisimai/reason/hasmoved.rb +5 -4
  41. data/lib/sisimai/reason/hostunknown.rb +5 -4
  42. data/lib/sisimai/reason/mailboxfull.rb +7 -6
  43. data/lib/sisimai/reason/mailererror.rb +3 -2
  44. data/lib/sisimai/reason/networkerror.rb +3 -2
  45. data/lib/sisimai/reason/norelaying.rb +4 -3
  46. data/lib/sisimai/reason/notaccept.rb +5 -4
  47. data/lib/sisimai/reason/notcompliantrfc.rb +4 -3
  48. data/lib/sisimai/reason/onhold.rb +8 -7
  49. data/lib/sisimai/reason/policyviolation.rb +7 -6
  50. data/lib/sisimai/reason/ratelimited.rb +5 -4
  51. data/lib/sisimai/reason/rejected.rb +18 -14
  52. data/lib/sisimai/reason/requireptr.rb +5 -4
  53. data/lib/sisimai/reason/securityerror.rb +4 -3
  54. data/lib/sisimai/reason/spamdetected.rb +6 -5
  55. data/lib/sisimai/reason/suppressed.rb +4 -3
  56. data/lib/sisimai/reason/suspend.rb +4 -3
  57. data/lib/sisimai/reason/syntaxerror.rb +4 -3
  58. data/lib/sisimai/reason/systemerror.rb +3 -2
  59. data/lib/sisimai/reason/systemfull.rb +4 -3
  60. data/lib/sisimai/reason/undefined.rb +2 -1
  61. data/lib/sisimai/reason/userunknown.rb +13 -9
  62. data/lib/sisimai/reason/vacation.rb +2 -1
  63. data/lib/sisimai/reason/virusdetected.rb +6 -5
  64. data/lib/sisimai/reason.rb +45 -40
  65. data/lib/sisimai/rfc1894.rb +8 -2
  66. data/lib/sisimai/rfc2045.rb +6 -3
  67. data/lib/sisimai/rfc3464/thirdparty.rb +10 -9
  68. data/lib/sisimai/rfc3834.rb +4 -2
  69. data/lib/sisimai/rhost/aol.rb +3 -2
  70. data/lib/sisimai/rhost/apple.rb +12 -11
  71. data/lib/sisimai/rhost/cloudflare.rb +3 -2
  72. data/lib/sisimai/rhost/cox.rb +38 -37
  73. data/lib/sisimai/rhost/facebook.rb +17 -16
  74. data/lib/sisimai/rhost/franceptt.rb +39 -38
  75. data/lib/sisimai/rhost/godaddy.rb +35 -34
  76. data/lib/sisimai/rhost/google.rb +22 -21
  77. data/lib/sisimai/rhost/gsuite.rb +8 -7
  78. data/lib/sisimai/rhost/iua.rb +11 -10
  79. data/lib/sisimai/rhost/kddi.rb +3 -2
  80. data/lib/sisimai/rhost/messagelabs.rb +13 -12
  81. data/lib/sisimai/rhost/microsoft.rb +33 -32
  82. data/lib/sisimai/rhost/mimecast.rb +18 -17
  83. data/lib/sisimai/rhost/nttdocomo.rb +10 -9
  84. data/lib/sisimai/rhost/outlook.rb +3 -2
  85. data/lib/sisimai/rhost/spectrum.rb +19 -18
  86. data/lib/sisimai/rhost/tencent.rb +10 -9
  87. data/lib/sisimai/rhost/yahooinc.rb +11 -10
  88. data/lib/sisimai/rhost/zoho.rb +8 -7
  89. data/lib/sisimai/smtp/command.rb +13 -8
  90. data/lib/sisimai/smtp/failure.rb +9 -8
  91. data/lib/sisimai/smtp/reply.rb +23 -22
  92. data/lib/sisimai/smtp/status.rb +141 -140
  93. data/lib/sisimai/smtp/transcript.rb +3 -2
  94. data/lib/sisimai/version.rb +1 -1
  95. data/lib/sisimai.rb +2 -1
  96. data/set-of-emails/maildir/bsd/lhost-postfix-81.eml +130 -0
  97. data/set-of-emails/should-not-crash/go-423-deeply-nested-mime-parts.eml +20050 -0
  98. metadata +5 -2
@@ -1,11 +1,12 @@
1
1
  module Sisimai
2
2
  module Reason
3
- # Sisimai::Reason::RequirePTR checks the bounce reason is "requireptr" or not. This class is
3
+ # Sisimai::Reason::RequirePTR checks the bounce reason is "RequirePTR" or not. This class is
4
4
  # called only from Sisimai::Reason class. This is the error that SMTP connection was rejected
5
5
  # due to missing PTR record or having invalid PTR record at the source IP address used for the
6
6
  # SMTP connection.
7
7
  module RequirePTR
8
8
  class << self
9
+ require 'sisimai/eb'
9
10
  Index = [
10
11
  "cannot find your hostname",
11
12
  "cannot resolve your address.",
@@ -25,7 +26,7 @@ module Sisimai
25
26
  ["service permits ", " unverifyable sending ips"],
26
27
  ].freeze
27
28
 
28
- def text; return 'requireptr'; end
29
+ def text; return Sisimai::Eb::ReQPTR; end
29
30
  def description; return 'Email rejected due to missing PTR record or having invalid PTR record'; end
30
31
 
31
32
  # Try to match that the given text and regular expressions
@@ -44,8 +45,8 @@ module Sisimai
44
45
  # false: is not blocked due to missing PTR record
45
46
  # @see http://www.ietf.org/rfc/rfc5322.txt
46
47
  def true(argvs)
47
- return true if argvs['reason'] == 'requireptr'
48
- return true if Sisimai::SMTP::Status.name(argvs['deliverystatus']) == 'requireptr'
48
+ return true if argvs['reason'] == Sisimai::Eb::ReQPTR
49
+ return true if Sisimai::SMTP::Status.name(argvs['deliverystatus']) == Sisimai::Eb::ReQPTR
49
50
  return match(argvs['diagnosticcode'].downcase)
50
51
  end
51
52
 
@@ -1,12 +1,12 @@
1
1
  module Sisimai
2
2
  module Reason
3
- # Sisimai::Reason::SecurityError checks the bounce reason is "securityerror" or not. This class
3
+ # Sisimai::Reason::SecurityError checks the bounce reason is "SecurityError" or not. This class
4
4
  # is called only Sisimai::Reason class.
5
5
  #
6
6
  # This is the error that a security violation was detected on a destination mail server. Depends
7
7
  # on the security policy on the server, a sender's email address is camouflaged address.
8
8
  #
9
- # Sisimai will set "securityerror" to the reason of email bounce if the value of Status: field
9
+ # Sisimai will set "SecurityError" to the reason of email bounce if the value of Status: field
10
10
  # in a bounce email is "5.7.*".
11
11
  #
12
12
  # Action: failed
@@ -17,6 +17,7 @@ module Sisimai
17
17
  #
18
18
  module SecurityError
19
19
  class << self
20
+ require 'sisimai/eb'
20
21
  Index = [
21
22
  "account not subscribed to ses",
22
23
  "authentication credentials invalid",
@@ -36,7 +37,7 @@ module Sisimai
36
37
  ["user ", " is not authorized to perform ses:sendrawemail on resource"],
37
38
  ].freeze
38
39
 
39
- def text; return 'securityerror'; end
40
+ def text; return Sisimai::Eb::ReSAFE; end
40
41
  def description; return 'Email rejected due to security violation was detected on a destination host'; end
41
42
 
42
43
  # Try to match that the given text and regular expressions
@@ -1,6 +1,6 @@
1
1
  module Sisimai
2
2
  module Reason
3
- # Sisimai::Reason::SpamDetected checks the bounce reason is "spamdetected" due to Spam content
3
+ # Sisimai::Reason::SpamDetected checks the bounce reason is "SpamDetected" due to Spam content
4
4
  # in the message or not. This class is called only Sisimai::Reason class. This is the error that
5
5
  # the message you sent was rejected by "spam filter" which is running on the remote host.
6
6
  #
@@ -10,6 +10,7 @@ module Sisimai
10
10
  # Last-Attempt-Date: Thu, 9 Apr 2008 23:34:45 +0900 (JST)
11
11
  module SpamDetected
12
12
  class << self
13
+ require 'sisimai/eb'
13
14
  Index = [
14
15
  "blacklisted url in message",
15
16
  "block for spam",
@@ -65,7 +66,7 @@ module Sisimai
65
66
  ["spam ", "score"],
66
67
  ].freeze
67
68
 
68
- def text; return 'spamdetected'; end
69
+ def text; return Sisimai::Eb::ReSPAM; end
69
70
  def description; return 'Email rejected by spam filter running on the remote host'; end
70
71
 
71
72
  # Try to match that the given text and regular expressions
@@ -85,10 +86,10 @@ module Sisimai
85
86
  # @see http://www.ietf.org/rfc/rfc2822.txt
86
87
  def true(argvs)
87
88
  return false if argvs['deliverystatus'].empty?
88
- return true if argvs['reason'] == 'spamdetected'
89
- return true if Sisimai::SMTP::Status.name(argvs['deliverystatus']) == 'spamdetected'
89
+ return true if argvs['reason'] == Sisimai::Eb::ReSPAM
90
+ return true if Sisimai::SMTP::Status.name(argvs['deliverystatus']) == Sisimai::Eb::ReSPAM
90
91
 
91
- # The value of "reason" isn't "spamdetected" when the value of "command" is an SMTP command
92
+ # The value of "reason" isn't "SpamDetected" when the value of "command" is an SMTP command
92
93
  # to be sent before the SMTP DATA command because all the MTAs read the headers and the
93
94
  # entire message body after the DATA command.
94
95
  return false if Sisimai::SMTP::Command::ExceptDATA.include?(argvs['command'])
@@ -1,13 +1,14 @@
1
1
  module Sisimai
2
2
  module Reason
3
- # Sisimai::Reason::Suppressed checks the bounce reason is "suppressed" or not. This class is called
3
+ # Sisimai::Reason::Suppressed checks the bounce reason is "Suppressed" or not. This class is called
4
4
  # only Sisimai::Reason class.
5
5
  #
6
6
  # This is the error that the recipient adddress is listed in the suppression list of the relay
7
7
  # server, and was not delivered.
8
8
  module Suppressed
9
9
  class << self
10
- def text; return 'suppressed'; end
10
+ require 'sisimai/eb'
11
+ def text; return Sisimai::Eb::ReSTOP; end
11
12
  def description; return "Email was not delivered due to being listed in the suppression list of MTA"; end
12
13
 
13
14
  # Try to match that the given text and regular expressions
@@ -21,7 +22,7 @@ module Sisimai
21
22
  # false: is not listed in the suppression list
22
23
  # @see http://www.ietf.org/rfc/rfc2822.txt
23
24
  def true(argvs)
24
- return true if argvs['reason'] == 'suppressed'
25
+ return true if argvs['reason'] == Sisimai::Eb::ReSTOP
25
26
  return match(argvs['diagnosticcode'].downcase)
26
27
  end
27
28
 
@@ -1,10 +1,11 @@
1
1
  module Sisimai
2
2
  module Reason
3
- # Sisimai::Reason::Suspend checks the bounce reason is "suspend" or not. This class is called
3
+ # Sisimai::Reason::Suspend checks the bounce reason is "Suspend" or not. This class is called
4
4
  # only Sisimai::Reason class. This is the error that a recipient account is being suspended
5
5
  # due to unpaid or other reasons.
6
6
  module Suspend
7
7
  class << self
8
+ require 'sisimai/eb'
8
9
  Index = [
9
10
  " currently suspended",
10
11
  " temporary locked",
@@ -37,7 +38,7 @@ module Sisimai
37
38
  ["not ", "active"],
38
39
  ].freeze
39
40
 
40
- def text; return 'suspend'; end
41
+ def text; return Sisimai::Eb::ReQUIT; end
41
42
  def description; return 'Email rejected due to a recipient account is being suspended'; end
42
43
 
43
44
  # Try to match that the given text and regular expressions
@@ -56,7 +57,7 @@ module Sisimai
56
57
  # false: is not suspended
57
58
  # @see http://www.ietf.org/rfc/rfc2822.txt
58
59
  def true(argvs)
59
- return true if argvs['reason'] == 'suspend' || argvs['replycode'].to_i == 525
60
+ return true if argvs['reason'] == Sisimai::Eb::ReQUIT || argvs['replycode'].to_i == 525
60
61
  return match(argvs['diagnosticcode'].downcase)
61
62
  end
62
63
 
@@ -1,10 +1,10 @@
1
1
  module Sisimai
2
2
  module Reason
3
- # Sisimai::Reason::SyntaxError checks the bounce reason is "syntaxerror" or not. This class is
3
+ # Sisimai::Reason::SyntaxError checks the bounce reason is "SyntaxError" or not. This class is
4
4
  # called only Sisimai::Reason class.
5
5
  #
6
6
  # This is the error that a destination mail server could not recognize SMTP command which is
7
- # sent from a sender's MTA. Sisimai will set "syntaxerror" to the reason if the value of
7
+ # sent from a sender's MTA. Sisimai will set "SyntaxError" to the reason if the value of
8
8
  # "replycode" begins with "50" such as 502, or 503.
9
9
  # Action: failed
10
10
  # Status: 5.5.0
@@ -12,7 +12,8 @@ module Sisimai
12
12
  #
13
13
  module SyntaxError
14
14
  class << self
15
- def text; return 'syntaxerror'; end
15
+ require 'sisimai/eb'
16
+ def text; return Sisimai::Eb::ReCOMM; end
16
17
  def description; return 'Email rejected due to syntax error at sent commands in SMTP session'; end
17
18
  def match(*); return false; end
18
19
 
@@ -1,6 +1,6 @@
1
1
  module Sisimai
2
2
  module Reason
3
- # Sisimai::Reason::SystemError checks the bounce reason is "systemerror" or not. This class is
3
+ # Sisimai::Reason::SystemError checks the bounce reason is "SystemError" or not. This class is
4
4
  # called only Sisimai::Reason class. This is the error that an email has bounced due to system
5
5
  # error on the remote host such as LDAP connection failure or other internal system error.
6
6
  #
@@ -9,6 +9,7 @@ module Sisimai
9
9
  # message has been in the queue too long.
10
10
  module SystemError
11
11
  class << self
12
+ require 'sisimai/eb'
12
13
  Index = [
13
14
  "aliasing/forwarding loop broken",
14
15
  "automatic homedir creator crashed", # qmail-ldap-1.03-20040101.patch:19817 - 19866
@@ -52,7 +53,7 @@ module Sisimai
52
53
  ["unable to connect ", "daemon"],
53
54
  ].freeze
54
55
 
55
- def text; return 'systemerror'; end
56
+ def text; return Sisimai::Eb::RePROC; end
56
57
  def description; return 'Email returned due to system error on the remote host'; end
57
58
 
58
59
  # Try to match that the given text and regular expressions
@@ -1,19 +1,20 @@
1
1
  module Sisimai
2
2
  module Reason
3
- # Sisimai::Reason::SystemFull checks the bounce reason is "systemfull" or not. This class is called
3
+ # Sisimai::Reason::SystemFull checks the bounce reason is "SystemFull" or not. This class is called
4
4
  # only Sisimai::Reason class.
5
5
  #
6
6
  # This is the error that a destination mail server's disk (or spool) is full. Sisimai will set
7
- # systemfull to the reason of email bounce if the value of Status: field in a bounce email is
7
+ # "SystemFull" to the reason of email bounce if the value of Status: field in a bounce email is
8
8
  # "4.3.1" or "5.3.1".
9
9
  module SystemFull
10
10
  class << self
11
+ require 'sisimai/eb'
11
12
  Index = [
12
13
  'exceeded storage allocation', # MS Exchange
13
14
  'mail system full',
14
15
  ].freeze
15
16
 
16
- def text; return 'systemfull'; end
17
+ def text; return Sisimai::Eb::ReDISK; end
17
18
  def description; return "Email rejected due to a destination mail server's disk is full"; end
18
19
 
19
20
  # Try to match that the given text and regular expressions
@@ -3,8 +3,9 @@ module Sisimai
3
3
  # Sisimai::Reason::Undefined is for only returning text and description. This class is called only
4
4
  # from Sisimai.reason method.
5
5
  module Undefined
6
+ require 'sisimai/eb'
6
7
  class << self
7
- def text; return 'undefined'; end
8
+ def text; return Sisimai::Eb::Re___0; end
8
9
  def description; return 'Sisimai could not detect an error reason'; end
9
10
  def match; return false; end
10
11
  def true(*); return false; end
@@ -1,11 +1,11 @@
1
1
  module Sisimai
2
2
  module Reason
3
- # Sisimai::Reason::UserUnknown checks the bounce reason is "userunknown" or not. This class is
3
+ # Sisimai::Reason::UserUnknown checks the bounce reason is "UserUnknown" or not. This class is
4
4
  # called only Sisimai::Reason class.
5
5
  #
6
6
  # This is the error that a local part (Left hand side of @ sign) of a recipient's email address
7
7
  # does not exist. In many case, a user has changed internet service provider, or has quit company,
8
- # or the local part is misspelled. Sisimai will set "userunknown" to the reason of email bounce
8
+ # or the local part is misspelled. Sisimai will set "UserUnknown" to the reason of email bounce
9
9
  # if the value of Status: field in a bounce email is "5.1.1", or connection was refused at SMTP
10
10
  # RCPT command, or the contents of Diagnostic-Code: field represents that it is unknown user.
11
11
  #
@@ -14,7 +14,11 @@ module Sisimai
14
14
  # RCPT TO command)
15
15
  module UserUnknown
16
16
  class << self
17
- PreMatches = %w[NoRelaying Blocked MailboxFull HasMoved Rejected NotAccept]
17
+ require 'sisimai/eb'
18
+ PreMatches = [
19
+ Sisimai::Eb::RePASS, Sisimai::Eb::ReBLOC, Sisimai::Eb::ReFULL,
20
+ Sisimai::Eb::ReMOVE, Sisimai::Eb::ReFROM, Sisimai::Eb::Re00MX,
21
+ ]
18
22
  ModulePath = {
19
23
  'Sisimai::Reason::NoRelaying' => 'sisimai/reason/norelaying',
20
24
  'Sisimai::Reason::Blocked' => 'sisimai/reason/blocked',
@@ -109,7 +113,7 @@ module Sisimai
109
113
  ["user <", "> unknown"],
110
114
  ].freeze
111
115
 
112
- def text; return 'userunknown'; end
116
+ def text; return Sisimai::Eb::ReUSER; end
113
117
  def description; return "Email rejected due to a local part of a recipient's email address does not exist"; end
114
118
 
115
119
  # Try to match that the given text and regular expressions
@@ -122,20 +126,20 @@ module Sisimai
122
126
  return false
123
127
  end
124
128
 
125
- # Whether the address is "userunknown" or not
129
+ # Whether the address is "UserUnknown" or not
126
130
  # @param [Sisimai::Fact] argvs Object to be detected the reason
127
131
  # @return [Boolean] true: is unknown user
128
132
  # false: is not unknown user.
129
133
  # @see http://www.ietf.org/rfc/rfc2822.txt
130
134
  def true(argvs)
131
- return true if argvs['reason'] == 'userunknown'
135
+ return true if argvs['reason'] == Sisimai::Eb::ReUSER
132
136
  return false if Sisimai::SMTP::Command::BeforeRCPT.include?(argvs['command'])
133
137
 
134
138
  tempreason = Sisimai::SMTP::Status.name(argvs['deliverystatus'])
135
- return false if tempreason == 'suspend'
139
+ return false if tempreason == Sisimai::Eb::ReQUIT
136
140
 
137
141
  issuedcode = argvs['diagnosticcode'].downcase
138
- if tempreason == 'userunknown'
142
+ if tempreason == Sisimai::Eb::ReUSER
139
143
  # *.1.1 = 'Bad destination mailbox address'
140
144
  # Status: 5.1.1
141
145
  # Diagnostic-Code: SMTP; 550 5.1.1 <***@example.jp>:
@@ -160,7 +164,7 @@ module Sisimai
160
164
  end
161
165
  return true if matchother == false # Did not match with other message patterns
162
166
 
163
- elsif argvs['command'] == 'RCPT'
167
+ elsif argvs['command'] == Sisimai::Eb::CeRCPT
164
168
  # When the SMTP command is not "RCPT", the session rejected by other
165
169
  # reason, maybe.
166
170
  return true if match(issuedcode)
@@ -4,6 +4,7 @@ module Sisimai
4
4
  # from Sisimai.reason method.
5
5
  module Vacation
6
6
  class << self
7
+ require 'sisimai/eb'
7
8
  Index = [
8
9
  'i am away on vacation',
9
10
  'i am away until',
@@ -11,7 +12,7 @@ module Sisimai
11
12
  'i will be traveling for work on',
12
13
  ].freeze
13
14
 
14
- def text; return 'vacation'; end
15
+ def text; return Sisimai::Eb::ReAWAY; end
15
16
  def description; return 'Email replied automatically due to a recipient is out of office'; end
16
17
 
17
18
  # Try to match that the given text and regular expressions
@@ -1,10 +1,10 @@
1
1
  module Sisimai
2
2
  module Reason
3
- # Sisimai::Reason::VirusDetected checks the bounce reason is "virusdetected" or not. This class
3
+ # Sisimai::Reason::VirusDetected checks the bounce reason is "VirusDetected" or not. This class
4
4
  # is called only Sisimai::Reason class.
5
5
  #
6
6
  # This is an error that any virus or trojan horse detected in the message by a virus scanner program
7
- # at a destination mail server. This reason has been divided from "securityerror" at Sisimai 4.22.0.
7
+ # at a destination mail server. This reason has been divided from "SecurityError" at Sisimai 4.22.0.
8
8
  #
9
9
  # Your message was infected with a virus. You should download a virus
10
10
  # scanner and check your computer for viruses.
@@ -14,13 +14,14 @@ module Sisimai
14
14
  #
15
15
  module VirusDetected
16
16
  class << self
17
+ require 'sisimai/eb'
17
18
  Index = ["it has a potentially executable attachment"].freeze
18
19
  Pairs = [
19
20
  ["message was ", "ected", " virus"],
20
21
  ["virus", " detected"],
21
22
  ].freeze
22
23
 
23
- def text; return 'virusdetected'; end
24
+ def text; return Sisimai::Eb::ReEXEC; end
24
25
  def description; return 'Email rejected due to a virus scanner on a destination host'; end
25
26
 
26
27
  # Try to match that the given text and regular expressions
@@ -34,7 +35,7 @@ module Sisimai
34
35
  return false
35
36
  end
36
37
 
37
- # The bounce reason is "virusdetected" or not
38
+ # The bounce reason is "VirusDetected" or not
38
39
  # @param [Sisimai::Fact] argvs Object to be detected the reason
39
40
  # @return [Boolean] true: virus detected
40
41
  # false: virus was not detected
@@ -44,7 +45,7 @@ module Sisimai
44
45
  # The value of "reason" isn't "visusdetected" when the value of "command" is an SMTP command
45
46
  # to be sent before the SMTP DATA command because all the MTAs read the headers and the
46
47
  # entire message body after the DATA command.
47
- return true if argvs['reason'] == 'virusdetected'
48
+ return true if argvs['reason'] == Sisimai::Eb::ReEXEC
48
49
  return false if Sisimai::SMTP::Command::ExceptDATA.include?(argvs['command'])
49
50
  return match(argvs['diagnosticcode'].downcase)
50
51
  end
@@ -3,24 +3,28 @@ module Sisimai
3
3
  # Sisimai::Fact object as an argument of find() method. This class is called only Sisimai::Fact.
4
4
  module Reason
5
5
  class << self
6
+ require 'sisimai/eb'
7
+
6
8
  # All the error reason list Sisimai support
7
9
  # @return [Array] Reason list
8
10
  def index
9
- return %w[
10
- AuthFailure BadReputation Blocked ContentError EmailTooLarge Expired FailedSTARTTLS Filtered
11
- HasMoved HostUnknown MailboxFull MailerError NetworkError NotAccept NotCompliantRFC RateLimited
12
- OnHold Rejected NoRelaying SpamDetected VirusDetected PolicyViolation SecurityError Suspend
13
- RequirePTR SystemError SystemFull Suppressed UserUnknown SyntaxError
11
+ return [
12
+ Sisimai::Eb::ReAUTH, Sisimai::Eb::ReFAMA, Sisimai::Eb::ReBLOC, Sisimai::Eb::ReBODY, Sisimai::Eb::ReSIZE,
13
+ Sisimai::Eb::ReTIME, Sisimai::Eb::ReTTLS, Sisimai::Eb::ReFILT, Sisimai::Eb::ReMOVE, Sisimai::Eb::ReHOST,
14
+ Sisimai::Eb::ReFULL, Sisimai::Eb::ReUNIX, Sisimai::Eb::ReINET, Sisimai::Eb::Re00MX, Sisimai::Eb::ReNRFC,
15
+ Sisimai::Eb::ReRATE, Sisimai::Eb::Re___1, Sisimai::Eb::ReFROM, Sisimai::Eb::RePASS, Sisimai::Eb::ReSPAM,
16
+ Sisimai::Eb::ReEXEC, Sisimai::Eb::ReWONT, Sisimai::Eb::ReSAFE, Sisimai::Eb::ReQUIT, Sisimai::Eb::ReQPTR,
17
+ Sisimai::Eb::RePROC, Sisimai::Eb::ReDISK, Sisimai::Eb::ReSTOP, Sisimai::Eb::ReUSER, Sisimai::Eb::ReCOMM,
14
18
  ]
15
19
  end
16
20
 
17
- # @abstract is_explicit() returns 0 when the argument is empty or is "undefined" or is "onhold"
21
+ # @abstract is_explicit() returns 0 when the argument is empty or is "Undefined" or is "OnHold"
18
22
  # @param string argv1 Reason name
19
23
  # @return bool false: The reaosn is not explicit
20
24
  def is_explicit(argv1 = '')
21
25
  return false if argv1.nil?
22
26
  return false if argv1.empty?
23
- return false if argv1 == "undefined" || argv1 == "onhold" || argv1.empty?
27
+ return false if argv1 == Sisimai::Eb::Re___0 || argv1 == Sisimai::Eb::Re___1 || argv1.empty?
24
28
  return true
25
29
  end
26
30
 
@@ -38,29 +42,30 @@ module Sisimai
38
42
  # @return [Hash] Reason list
39
43
  def retry
40
44
  return {
41
- 'undefined' => true, 'onhold' => true, 'systemerror' => true, 'securityerror' => true,
42
- 'expired' => true, 'networkerror' => true, 'hostunknown' => true, 'userunknown' => true
45
+ Sisimai::Eb::Re___0 => true, Sisimai::Eb::Re___1 => true, Sisimai::Eb::RePROC => true,
46
+ Sisimai::Eb::ReSAFE => true, Sisimai::Eb::ReTIME => true, Sisimai::Eb::ReINET => true,
47
+ Sisimai::Eb::ReHOST => true, Sisimai::Eb::ReUSER => true,
43
48
  }.freeze
44
49
  end
45
50
  ModulePath = Sisimai::Reason.path
46
51
  GetRetried = Sisimai::Reason.retry
47
52
  ClassOrder = [
48
53
  # 0. true() meethod in the following reasons are called from Reason->find()
49
- %w[MailboxFull EmailTooLarge Suspend HasMoved NoRelaying AuthFailure UserUnknown Filtered RequirePTR
50
- NotCompliantRFC BadReputation ContentError Rejected HostUnknown SpamDetected RateLimited Blocked
51
- FailedSTARTTLS NotAccept VirusDetected PolicyViolation
52
- ],
54
+ [Sisimai::Eb::ReFULL, Sisimai::Eb::ReSIZE, Sisimai::Eb::ReQUIT, Sisimai::Eb::ReMOVE, Sisimai::Eb::RePASS,
55
+ Sisimai::Eb::ReAUTH, Sisimai::Eb::ReUSER, Sisimai::Eb::ReFILT, Sisimai::Eb::ReQPTR, Sisimai::Eb::ReNRFC,
56
+ Sisimai::Eb::ReFAMA, Sisimai::Eb::ReBODY, Sisimai::Eb::ReFROM, Sisimai::Eb::ReHOST, Sisimai::Eb::ReSPAM,
57
+ Sisimai::Eb::ReRATE, Sisimai::Eb::ReBLOC, Sisimai::Eb::ReTTLS, Sisimai::Eb::Re00MX, Sisimai::Eb::ReEXEC,
58
+ Sisimai::Eb::ReWONT],
59
+
53
60
  # 1. match() method in the following reasons are called from Reason->find()
54
- %w[
55
- MailboxFull SpamDetected VirusDetected NoRelaying SystemError NetworkError Suspend SystemFull
56
- Suppressed MailerError SecurityError PolicyViolation SyntaxError Expired
57
- ],
58
- %w[
59
- MailboxFull EmailTooLarge Suspend UserUnknown Filtered Rejected HostUnknown SpamDetected
60
- RateLimited Blocked SpamDetected AuthFailure FailedSTARTTLS SecurityError SystemError
61
- NetworkError Suspend Expired ContentError HasMoved SystemFull NotAccept MailerError
62
- NoRelaying Suppressed SyntaxError OnHold
63
- ]
61
+ [Sisimai::Eb::ReFULL, Sisimai::Eb::ReSPAM, Sisimai::Eb::ReEXEC, Sisimai::Eb::RePASS, Sisimai::Eb::RePROC,
62
+ Sisimai::Eb::ReINET, Sisimai::Eb::ReQUIT, Sisimai::Eb::ReDISK, Sisimai::Eb::ReSTOP, Sisimai::Eb::ReUNIX,
63
+ Sisimai::Eb::ReSAFE, Sisimai::Eb::ReWONT, Sisimai::Eb::ReCOMM, Sisimai::Eb::ReTIME],
64
+ [Sisimai::Eb::ReFULL, Sisimai::Eb::ReSIZE, Sisimai::Eb::ReQUIT, Sisimai::Eb::ReUSER, Sisimai::Eb::ReFILT,
65
+ Sisimai::Eb::ReFROM, Sisimai::Eb::ReHOST, Sisimai::Eb::ReSPAM, Sisimai::Eb::ReRATE, Sisimai::Eb::ReBLOC,
66
+ Sisimai::Eb::ReAUTH, Sisimai::Eb::ReTTLS, Sisimai::Eb::ReSAFE, Sisimai::Eb::RePROC, Sisimai::Eb::ReINET,
67
+ Sisimai::Eb::ReTIME, Sisimai::Eb::ReBODY, Sisimai::Eb::ReMOVE, Sisimai::Eb::ReDISK, Sisimai::Eb::Re00MX,
68
+ Sisimai::Eb::ReUNIX, Sisimai::Eb::RePASS, Sisimai::Eb::ReSTOP, Sisimai::Eb::ReCOMM, Sisimai::Eb::Re___1],
64
69
  ]
65
70
 
66
71
  # Detect the bounce reason
@@ -74,7 +79,7 @@ module Sisimai
74
79
  # retry() method.
75
80
  return argvs['reason'] if argvs['reason'].empty? == false
76
81
  end
77
- return 'delivered' if argvs['deliverystatus'].start_with?('2.')
82
+ return Sisimai::Eb::ReSENT if argvs['deliverystatus'].start_with?('2.')
78
83
 
79
84
  reasontext = ''
80
85
  issuedcode = argvs['diagnosticcode'] || ''
@@ -100,21 +105,21 @@ module Sisimai
100
105
  end
101
106
  end
102
107
 
103
- if reasontext.empty? || reasontext == 'undefined'
108
+ if reasontext.empty? || reasontext == Sisimai::Eb::Re___0
104
109
  # Bounce reason is not detected yet.
105
110
  reasontext = self.anotherone(argvs)
106
111
 
107
- if reasontext == 'undefined' || reasontext.empty?
112
+ if reasontext == Sisimai::Eb::Re___0 || reasontext.empty?
108
113
  # Action: delayed => "expired"
109
114
  reasontext = nil
110
- reasontext ||= 'expired' if argvs['action'] == 'delayed'
115
+ reasontext ||= Sisimai::Eb::ReTIME if argvs['action'] == Sisimai::Eb::AeSTAY
111
116
  return reasontext if reasontext
112
117
 
113
118
  # Try to match with message patterns in Sisimai::Reason::Vacation
114
119
  require 'sisimai/reason/vacation'
115
- reasontext = 'vacation' if Sisimai::Reason::Vacation.match(issuedcode.downcase)
116
- reasontext ||= 'onhold' if issuedcode.empty? == false
117
- reasontext ||= 'undefined'
120
+ reasontext = Sisimai::Eb::ReAWAY if Sisimai::Reason::Vacation.match(issuedcode.downcase)
121
+ reasontext ||= Sisimai::Eb::Re___1 if issuedcode.empty? == false
122
+ reasontext ||= Sisimai::Eb::Re___0
118
123
  end
119
124
  end
120
125
  return reasontext
@@ -151,7 +156,7 @@ module Sisimai
151
156
  end
152
157
 
153
158
  next if r.match(issuedcode) == false
154
- reasontext = e.downcase
159
+ reasontext = e
155
160
  break
156
161
  end
157
162
  break if reasontext.empty? == false
@@ -160,31 +165,31 @@ module Sisimai
160
165
  code2digit = statuscode[0, 3] || ''
161
166
  if code2digit == '5.6' || code2digit == '4.6'
162
167
  # X.6.0 Other or undefined media error
163
- reasontext = 'contenterror'
168
+ reasontext = Sisimai::Eb::ReBODY
164
169
 
165
170
  elsif code2digit == '5.7' || code2digit == '4.7'
166
171
  # X.7.0 Other or undefined security status
167
- reasontext = 'securityerror'
172
+ reasontext = Sisimai::Eb::ReSAFE
168
173
 
169
174
  elsif codeformat.start_with?('X-UNIX')
170
175
  # Diagnostic-Code: X-UNIX; ...
171
- reasontext = 'mailererror'
176
+ reasontext = Sisimai::Eb::ReUNIX
172
177
 
173
178
  else
174
179
  # 50X Syntax Error?
175
180
  require 'sisimai/reason/syntaxerror'
176
- reasontext = 'syntaxerror' if Sisimai::Reason::SyntaxError.true(argvs)
181
+ reasontext = Sisimai::Eb::ReCOMM if Sisimai::Reason::SyntaxError.true(argvs)
177
182
  end
178
183
  break if reasontext.empty? == false
179
184
 
180
185
  # Check the value of Action: field, first
181
- if actiontext.start_with?('delayed', 'expired')
186
+ if actiontext.start_with?(Sisimai::Eb::AeSTAY, 'expired')
182
187
  # Action: delayed, expired
183
- reasontext = 'expired'
188
+ reasontext = Sisimai::Eb::ReTIME
184
189
  else
185
190
  # Rejected at connection or after EHLO|HELO
186
191
  thecommand = argvs['command'] || ''
187
- reasontext = 'blocked' if %w[HELO EHLO].index(thecommand)
192
+ reasontext = Sisimai::Eb::ReBLOC if %w[HELO EHLO].index(thecommand)
188
193
  end
189
194
  break
190
195
  end
@@ -222,12 +227,12 @@ module Sisimai
222
227
 
223
228
  if issuedcode.upcase.include?('X-UNIX; ')
224
229
  # X-Unix; ...
225
- reasontext = 'mailererror'
230
+ reasontext = Sisimai::Eb::ReUNIX
226
231
  else
227
232
  # Detect the bounce reason from "Status:" code
228
233
  require 'sisimai/smtp/status'
229
234
  reasontext = Sisimai::SMTP::Status.name(Sisimai::SMTP::Status.find(argv1))
230
- reasontext = "undefined" if reasontext.empty?
235
+ reasontext = Sisimai::Eb::Re___0 if reasontext.empty?
231
236
  end
232
237
  return reasontext
233
238
  end
@@ -2,6 +2,7 @@ module Sisimai
2
2
  # Sisimai::RFC1894 DSN field defined in RFC3464 (obsoletes RFC1894)
3
3
  module RFC1894
4
4
  class << self
5
+ require 'sisimai/eb'
5
6
  require 'sisimai/string'
6
7
  FieldNames = [
7
8
  # https://tools.ietf.org/html/rfc3464#section-2.2
@@ -58,8 +59,13 @@ module Sisimai
58
59
  }.freeze
59
60
 
60
61
  SubtypeSet = {"addr" => "RFC822", "cdoe" => "SMTP", "host" => "DNS"}.freeze
61
- ActionList = ["failed", "delayed", "delivered", "relayed", "expanded"].freeze
62
- Correction = {'deliverable' => 'delivered', 'expired' => 'failed', 'failure' => 'failed'}
62
+ ActionList = [Sisimai::Eb::AeFAIL, Sisimai::Eb::AeSTAY, Sisimai::Eb::AeSENT,
63
+ Sisimai::Eb::AePASS, Sisimai::Eb::AeEXPN].freeze
64
+ Correction = {
65
+ 'deliverable' => Sisimai::Eb::AeSENT,
66
+ 'expired' => Sisimai::Eb::AeFAIL,
67
+ 'failure' => Sisimai::Eb::AeFAIL,
68
+ }
63
69
  FieldGroup = {
64
70
  'original-recipient' => 'addr',
65
71
  'final-recipient' => 'addr',
@@ -213,14 +213,17 @@ module Sisimai
213
213
  # Split argv1: multipart/* blocks by a boundary string in argv0
214
214
  # @param [String] argv0 The value of Content-Type header
215
215
  # @param [String] argv1 A pointer to multipart/* message blocks
216
+ # @param [String] depth Depth of MIME parts
216
217
  # @return [Array] List of each part of multipart/*
217
218
  # @since v5.0.0
218
- def levelout(argv0 = '', argv1 = '')
219
+ def levelout(argv0 = '', argv1 = '', depth = 0)
219
220
  return [] if argv0.empty?
220
221
  return [] if argv1.empty?
222
+ return [] if depth > 100
221
223
 
222
224
  boundary01 = boundary(argv0, 0); return [] if boundary01.empty?
223
225
  multiparts = argv1.split(Regexp.new(Regexp.escape(boundary01) + "\n"))
226
+ partsdepth = depth + 1
224
227
  partstable = []
225
228
 
226
229
  # Remove empty or useless preamble and epilogue of multipart/* block
@@ -237,7 +240,7 @@ module Sisimai
237
240
  bodyinside = f[-1].split("\n\n", 2)[-1]
238
241
  next if bodyinside.size < 9 || bodyinside.index(boundary02).nil?
239
242
 
240
- v = levelout(f[0], bodyinside)
243
+ v = levelout(f[0], bodyinside, partsdepth)
241
244
  partstable += v if v.size > 0
242
245
  else
243
246
  # The part is not a multipart/* block
@@ -271,7 +274,7 @@ module Sisimai
271
274
  # - CHARSET=, BOUNDARY= => charset-, boundary=
272
275
  # - message/xdelivery-status => message/delivery-status
273
276
  iso2022set = %r/charset=["']?(iso-2022-[-a-z0-9]+)['"]?\b/
274
- multiparts = levelout(argv0, argv1)
277
+ multiparts = levelout(argv0, argv1, 0)
275
278
  flattenout = ''
276
279
  delimiters = ["/delivery-status", "/rfc822", "/feedback-report", "/partial"]
277
280