Module: Sisimai::Reason

Defined in:
lib/sisimai/reason.rb,
lib/sisimai/reason/onhold.rb,
lib/sisimai/reason/blocked.rb,
lib/sisimai/reason/expired.rb,
lib/sisimai/reason/suspend.rb,
lib/sisimai/reason/feedback.rb,
lib/sisimai/reason/filtered.rb,
lib/sisimai/reason/hasmoved.rb,
lib/sisimai/reason/rejected.rb,
lib/sisimai/reason/vacation.rb,
lib/sisimai/reason/delivered.rb,
lib/sisimai/reason/notaccept.rb,
lib/sisimai/reason/undefined.rb,
lib/sisimai/reason/norelaying.rb,
lib/sisimai/reason/requireptr.rb,
lib/sisimai/reason/suppressed.rb,
lib/sisimai/reason/systemfull.rb,
lib/sisimai/reason/authfailure.rb,
lib/sisimai/reason/hostunknown.rb,
lib/sisimai/reason/mailboxfull.rb,
lib/sisimai/reason/mailererror.rb,
lib/sisimai/reason/ratelimited.rb,
lib/sisimai/reason/syntaxerror.rb,
lib/sisimai/reason/systemerror.rb,
lib/sisimai/reason/userunknown.rb,
lib/sisimai/reason/contenterror.rb,
lib/sisimai/reason/networkerror.rb,
lib/sisimai/reason/spamdetected.rb,
lib/sisimai/reason/badreputation.rb,
lib/sisimai/reason/emailtoolarge.rb,
lib/sisimai/reason/securityerror.rb,
lib/sisimai/reason/virusdetected.rb,
lib/sisimai/reason/failedstarttls.rb,
lib/sisimai/reason/notcompliantrfc.rb,
lib/sisimai/reason/policyviolation.rb

Overview

Sisimai::Reason detects the bounce reason from the Hash table which is to be constructed to Sisimai::Fact object as an argument of find() method. This class is called only Sisimai::Fact.

Defined Under Namespace

Modules: AuthFailure, BadReputation, Blocked, ContentError, Delivered, EmailTooLarge, Expired, FailedSTARTTLS, Feedback, Filtered, HasMoved, HostUnknown, MailboxFull, MailerError, NetworkError, NoRelaying, NotAccept, NotCompliantRFC, OnHold, PolicyViolation, RateLimited, Rejected, RequirePTR, SecurityError, SpamDetected, Suppressed, Suspend, SyntaxError, SystemError, SystemFull, Undefined, UserUnknown, Vacation, VirusDetected

Constant Summary collapse

ModulePath =
Sisimai::Reason.path
GetRetried =
Sisimai::Reason.retry
ClassOrder =
[
  # 0. true() meethod in the following reasons are called from Reason->find()
  %w[MailboxFull EmailTooLarge Suspend HasMoved NoRelaying AuthFailure UserUnknown Filtered RequirePTR
     NotCompliantRFC BadReputation ContentError Rejected HostUnknown SpamDetected RateLimited Blocked
     FailedSTARTTLS NotAccept VirusDetected PolicyViolation
  ],
  # 1. match() method in the following reasons are called from Reason->find()
  %w[
    MailboxFull SpamDetected VirusDetected NoRelaying SystemError NetworkError Suspend SystemFull
    Suppressed MailerError SecurityError PolicyViolation SyntaxError Expired
  ],
  %w[
    MailboxFull EmailTooLarge Suspend UserUnknown Filtered Rejected HostUnknown SpamDetected
    RateLimited Blocked SpamDetected AuthFailure FailedSTARTTLS SecurityError SystemError
    NetworkError Suspend Expired ContentError HasMoved SystemFull NotAccept MailerError
    NoRelaying Suppressed SyntaxError OnHold
  ]
]

Class Method Summary collapse

Class Method Details

.anotherone(argvs) ⇒ String, Nil

Detect the other bounce reason, fall back method for find()

Parameters:

  • argvs (Hash)

    Decoded email object

Returns:

  • (String, Nil)

    Bounce reason or nli if the argument is missing or not Hash

See Also:

  • find()


127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
# File 'lib/sisimai/reason.rb', line 127

def anotherone(argvs)
  return argvs['reason'] if argvs['reason'].empty? == false

  require 'sisimai/smtp/status'
  issuedcode = argvs['diagnosticcode'].downcase || ''
  codeformat = argvs['diagnostictype']          || ''
  actiontext = argvs['action']                  || ''
  statuscode = argvs['deliverystatus']          || ''
  reasontext = Sisimai::SMTP::Status.name(statuscode)
  trytomatch = reasontext.empty? ? true : false
  trytomatch ||= true if GetRetried[reasontext] || codeformat != 'SMTP'

  while trytomatch
    # Could not decide the reason by the value of Status:
    ClassOrder[1].each do |e|
      # Trying to match with other patterns in Sisimai::Reason::* classes
      p = "Sisimai::Reason::#{e}"
      r = nil
      begin
        require ModulePath[p]
        r = Module.const_get(p)
      rescue
        warn " ***warning: Failed to load #{p}"
        next
      end

      next if r.match(issuedcode) == false
      reasontext = e.downcase
      break
    end
    break if reasontext.empty? == false

    # Check the value of Status:
    code2digit = statuscode[0, 3] || ''
    if code2digit == '5.6' || code2digit == '4.6'
      #  X.6.0   Other or undefined media error
      reasontext = 'contenterror'

    elsif code2digit == '5.7' || code2digit == '4.7'
      #  X.7.0   Other or undefined security status
      reasontext = 'securityerror'

    elsif codeformat.start_with?('X-UNIX')
      # Diagnostic-Code: X-UNIX; ...
      reasontext = 'mailererror'

    else
      # 50X Syntax Error?
      require 'sisimai/reason/syntaxerror'
      reasontext = 'syntaxerror' if Sisimai::Reason::SyntaxError.true(argvs)
    end
    break if reasontext.empty? == false

    # Check the value of Action: field, first
    if actiontext.start_with?('delayed', 'expired')
      # Action: delayed, expired
      reasontext = 'expired'
    else
      # Rejected at connection or after EHLO|HELO
      thecommand = argvs['command'] || ''
      reasontext = 'blocked' if %w[HELO EHLO].index(thecommand)
    end
    break
  end
  return reasontext
end

.find(argvs) ⇒ String

Detect the bounce reason

Parameters:

  • argvs (Hash)

    Decoded email object

Returns:

  • (String)

    Bounce reason or an empty string if the argument is missing or not Hash

See Also:



70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
# File 'lib/sisimai/reason.rb', line 70

def find(argvs)
  return "" if argvs.nil?
  if GetRetried[argvs['reason']].nil?
    # Return reason text already decided except reason match with the regular expression of
    # retry() method.
    return argvs['reason'] if argvs['reason'].empty? == false
  end
  return 'delivered' if argvs['deliverystatus'].start_with?('2.')

  reasontext = ''
  issuedcode = argvs['diagnosticcode'] || ''
  codeformat = argvs['diagnostictype'] || ''

  if codeformat == 'SMTP' || codeformat == ''
    # Diagnostic-Code: SMTP; ... or empty value
    ClassOrder[0].each do |e|
      # Check the value of Diagnostic-Code: and the value of Status:, it is a deliverystats,
      # with true() method in each Sisimai::Reason::* class.
      p = "Sisimai::Reason::#{e}"
      r = nil
      begin
        require ModulePath[p]
        r = Module.const_get(p)
      rescue
        warn " ***warning: Failed to load #{p}"
        next
      end
      next if r.true(argvs) == false
      reasontext = r.text
      break
    end
  end

  if reasontext.empty? || reasontext == 'undefined'
    # Bounce reason is not detected yet.
    reasontext = self.anotherone(argvs)

    if reasontext == 'undefined' || reasontext.empty?
      # Action: delayed => "expired"
      reasontext   = nil
      reasontext ||= 'expired' if argvs['action'] == 'delayed'
      return reasontext if reasontext

      # Try to match with message patterns in Sisimai::Reason::Vacation
      require 'sisimai/reason/vacation'
      reasontext   = 'vacation' if Sisimai::Reason::Vacation.match(issuedcode.downcase)
      reasontext ||= 'onhold'   if issuedcode.empty? == false
      reasontext ||= 'undefined'
    end
  end
  return reasontext
end

.indexArray

All the error reason list Sisimai support

Returns:

  • (Array)

    Reason list



8
9
10
11
12
13
14
15
# File 'lib/sisimai/reason.rb', line 8

def index
  return %w[
    AuthFailure BadReputation Blocked ContentError EmailTooLarge Expired FailedSTARTTLS Filtered
    HasMoved HostUnknown MailboxFull MailerError NetworkError NotAccept NotCompliantRFC RateLimited
    OnHold Rejected NoRelaying SpamDetected VirusDetected PolicyViolation SecurityError Suspend
    RequirePTR SystemError SystemFull Suppressed UserUnknown SyntaxError
  ]
end

.is_explicit(argv1 = '') ⇒ Object

This method is abstract.

is_explicit() returns 0 when the argument is empty or is “undefined” or is “onhold”

Returns bool false: The reaosn is not explicit.

Parameters:

  • string

    argv1 Reason name

Returns:

  • bool false: The reaosn is not explicit



20
21
22
23
24
25
# File 'lib/sisimai/reason.rb', line 20

def is_explicit(argv1 = '')
  return false if argv1.nil?
  return false if argv1.empty?
  return false if argv1 == "undefined" || argv1 == "onhold" || argv1.empty?
  return true
end

.match(argv1) ⇒ String

Detect the bounce reason from given text

Parameters:

  • argv1 (String)

    Error message

Returns:



197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
# File 'lib/sisimai/reason.rb', line 197

def match(argv1)
  return "" if argv1.nil?

  reasontext = ''
  issuedcode = argv1.downcase

  # Diagnostic-Code: SMTP; ... or empty value
  ClassOrder[2].each do |e|
    # Check the value of Diagnostic-Code: and the value of Status:, it is a deliverystats, with
    # true() method in each Sisimai::Reason::* class.
    p = "Sisimai::Reason::#{e}"
    r = nil
    begin
      require ModulePath[p]
      r = Module.const_get(p)
    rescue
      warn " ***warning: Failed to load #{p}"
      next
    end

    next if r.match(issuedcode) == false
    reasontext = r.text
    break
  end
  return reasontext if reasontext.empty? == false

  if issuedcode.upcase.include?('X-UNIX; ')
    # X-Unix; ...
    reasontext = 'mailererror'
  else
    # Detect the bounce reason from "Status:" code
    require 'sisimai/smtp/status'
    reasontext = Sisimai::SMTP::Status.name(Sisimai::SMTP::Status.find(argv1))
    reasontext = "undefined" if reasontext.empty?
  end
  return reasontext
end

.pathHash

This method is abstract.

Returns Sisimai::Reason::* module path table

Returns Module path table.

Returns:

  • (Hash)

    Module path table

Since:

  • v4.25.6



30
31
32
33
34
35
# File 'lib/sisimai/reason.rb', line 30

def path
  index = Sisimai::Reason.index
  table = {}
  index.each { |e| table["Sisimai::Reason::#{e}"] = "sisimai/reason/#{e.downcase}" }
  return table
end

.retryHash

Reason list better to retry detecting an error reason

Returns:

  • (Hash)

    Reason list



39
40
41
42
43
44
# File 'lib/sisimai/reason.rb', line 39

def retry
  return {
    'undefined' => true, 'onhold' => true, 'systemerror' => true, 'securityerror' => true,
    'expired' => true, 'networkerror' => true, 'hostunknown' => true, 'userunknown' => true
  }.freeze
end