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()
  [Sisimai::Eb::ReFULL, Sisimai::Eb::ReSIZE, Sisimai::Eb::ReQUIT, Sisimai::Eb::ReMOVE, Sisimai::Eb::RePASS,
   Sisimai::Eb::ReAUTH, Sisimai::Eb::ReUSER, Sisimai::Eb::ReFILT, Sisimai::Eb::ReQPTR, Sisimai::Eb::ReNRFC,
   Sisimai::Eb::ReFAMA, Sisimai::Eb::ReBODY, Sisimai::Eb::ReFROM, Sisimai::Eb::ReHOST, Sisimai::Eb::ReSPAM, 
   Sisimai::Eb::ReRATE, Sisimai::Eb::ReBLOC, Sisimai::Eb::ReTTLS, Sisimai::Eb::Re00MX, Sisimai::Eb::ReEXEC,
   Sisimai::Eb::ReWONT],

  # 1. match() method in the following reasons are called from Reason->find()
  [Sisimai::Eb::ReFULL, Sisimai::Eb::ReSPAM, Sisimai::Eb::ReEXEC, Sisimai::Eb::RePASS, Sisimai::Eb::RePROC,
   Sisimai::Eb::ReINET, Sisimai::Eb::ReQUIT, Sisimai::Eb::ReDISK, Sisimai::Eb::ReSTOP, Sisimai::Eb::ReUNIX,
   Sisimai::Eb::ReSAFE, Sisimai::Eb::ReWONT, Sisimai::Eb::ReCOMM, Sisimai::Eb::ReTIME],
  [Sisimai::Eb::ReFULL, Sisimai::Eb::ReSIZE, Sisimai::Eb::ReQUIT, Sisimai::Eb::ReUSER, Sisimai::Eb::ReFILT,
   Sisimai::Eb::ReFROM, Sisimai::Eb::ReHOST, Sisimai::Eb::ReSPAM, Sisimai::Eb::ReRATE, Sisimai::Eb::ReBLOC,
   Sisimai::Eb::ReAUTH, Sisimai::Eb::ReTTLS, Sisimai::Eb::ReSAFE, Sisimai::Eb::RePROC, Sisimai::Eb::ReINET,
   Sisimai::Eb::ReTIME, Sisimai::Eb::ReBODY, Sisimai::Eb::ReMOVE, Sisimai::Eb::ReDISK, Sisimai::Eb::Re00MX,
   Sisimai::Eb::ReUNIX, Sisimai::Eb::RePASS, Sisimai::Eb::ReSTOP, Sisimai::Eb::ReCOMM, Sisimai::Eb::Re___1],
]

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()


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
193
194
195
196
197
# File 'lib/sisimai/reason.rb', line 132

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
      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 = Sisimai::Eb::ReBODY

    elsif code2digit == '5.7' || code2digit == '4.7'
      #  X.7.0   Other or undefined security status
      reasontext = Sisimai::Eb::ReSAFE

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

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

    # Check the value of Action: field, first
    if actiontext.start_with?(Sisimai::Eb::AeSTAY, 'expired')
      # Action: delayed, expired
      reasontext = Sisimai::Eb::ReTIME
    else
      # Rejected at connection or after EHLO|HELO
      thecommand = argvs['command'] || ''
      reasontext = Sisimai::Eb::ReBLOC 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:



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
122
123
124
125
126
# File 'lib/sisimai/reason.rb', line 75

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 Sisimai::Eb::ReSENT 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 == Sisimai::Eb::Re___0
    # Bounce reason is not detected yet.
    reasontext = self.anotherone(argvs)

    if reasontext == Sisimai::Eb::Re___0 || reasontext.empty?
      # Action: delayed => "expired"
      reasontext   = nil
      reasontext ||= Sisimai::Eb::ReTIME if argvs['action'] == Sisimai::Eb::AeSTAY
      return reasontext if reasontext

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

.indexArray

All the error reason list Sisimai support

Returns:

  • (Array)

    Reason list



10
11
12
13
14
15
16
17
18
19
# File 'lib/sisimai/reason.rb', line 10

def index
  return [
    Sisimai::Eb::ReAUTH, Sisimai::Eb::ReFAMA, Sisimai::Eb::ReBLOC, Sisimai::Eb::ReBODY, Sisimai::Eb::ReSIZE,
    Sisimai::Eb::ReTIME, Sisimai::Eb::ReTTLS, Sisimai::Eb::ReFILT, Sisimai::Eb::ReMOVE, Sisimai::Eb::ReHOST,
    Sisimai::Eb::ReFULL, Sisimai::Eb::ReUNIX, Sisimai::Eb::ReINET, Sisimai::Eb::Re00MX, Sisimai::Eb::ReNRFC,
    Sisimai::Eb::ReRATE, Sisimai::Eb::Re___1, Sisimai::Eb::ReFROM, Sisimai::Eb::RePASS, Sisimai::Eb::ReSPAM,
    Sisimai::Eb::ReEXEC, Sisimai::Eb::ReWONT, Sisimai::Eb::ReSAFE, Sisimai::Eb::ReQUIT, Sisimai::Eb::ReQPTR,
    Sisimai::Eb::RePROC, Sisimai::Eb::ReDISK, Sisimai::Eb::ReSTOP, Sisimai::Eb::ReUSER, Sisimai::Eb::ReCOMM,
  ]
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



24
25
26
27
28
29
# File 'lib/sisimai/reason.rb', line 24

def is_explicit(argv1 = '')
  return false if argv1.nil?
  return false if argv1.empty?
  return false if argv1 == Sisimai::Eb::Re___0 || argv1 == Sisimai::Eb::Re___1 || argv1.empty?
  return true
end

.match(argv1) ⇒ String

Detect the bounce reason from given text

Parameters:

  • argv1 (String)

    Error message

Returns:



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
234
235
236
237
238
# File 'lib/sisimai/reason.rb', line 202

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 = Sisimai::Eb::ReUNIX
  else
    # Detect the bounce reason from "Status:" code
    require 'sisimai/smtp/status'
    reasontext = Sisimai::SMTP::Status.name(Sisimai::SMTP::Status.find(argv1))
    reasontext = Sisimai::Eb::Re___0 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



34
35
36
37
38
39
# File 'lib/sisimai/reason.rb', line 34

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



43
44
45
46
47
48
49
# File 'lib/sisimai/reason.rb', line 43

def retry
  return {
    Sisimai::Eb::Re___0 => true, Sisimai::Eb::Re___1 => true, Sisimai::Eb::RePROC => true,
    Sisimai::Eb::ReSAFE => true, Sisimai::Eb::ReTIME => true, Sisimai::Eb::ReINET => true,
    Sisimai::Eb::ReHOST => true, Sisimai::Eb::ReUSER => true, 
  }.freeze
end