Module: Sisimai::SMTP::Reply

Defined in:
lib/sisimai/smtp/reply.rb

Overview

Sisimai::SMTP::Reply is utilities for getting SMTP Reply Code value from error message text.

Constant Summary collapse

ReplyCode2 =
[
  # http://www.ietf.org/rfc/rfc5321.txt
  # 211   System status, or system help reply
  # 214   Help message (Information on how to use the receiver or the meaning of a particular
  #       non-standard command; this reply is useful only to the human user)
  # 220   <domain> Service ready
  # 221   <domain> Service closing transmission channel
  # 235   Authentication successful (See RFC2554)
  # 250   Requested mail action okay, completed
  # 251   User not local; will forward to <forward-path> (See Section 3.4)
  # 252   Cannot VRFY user, but will accept message and attempt delivery (See Section 3.5.3)
  # 253   OK, <n> pending messages for node <domain> started (See RFC1985)
  # 334   A server challenge is sent as a 334 reply with the text part containing the [BASE64]
  #       encoded string supplied by the SASL mechanism.  This challenge MUST NOT contain any
  #       text other than the BASE64 encoded challenge. (RFC4954)
  # 354   Start mail input; end with <CRLF>.<CRLF>
  '211', '214', '220', '221', '235', '250', '251', '252', '253', '334', '354'
].freeze
ReplyCode4 =
[
  # 421   <domain> Service not available, closing transmission channel (This may be a reply
  #       to any command if the service knows it must shut down)
  # 422   (See RFC5248)
  # 430   (See RFC5248)
  # 432   A password transition is needed (See RFC4954)
  # 450   Requested mail action not taken: mailbox unavailable (e.g., mailbox busy or temporarily
  #       blocked for policy reasons)
  # 451   Requested action aborted: local error in processing
  # 452   Requested action not taken: insufficient system storage
  # 453   You have no mail (See RFC2645)
  # 454   Temporary authentication failure (See RFC4954)
  # 455   Server unable to accommodate parameters
  # 456   please retry immediately the message over IPv4 because it fails SPF and DKIM (See
  #       https://datatracker.ietf.org/doc/html/draft-martin-smtp-ipv6-to-ipv4-fallback-00
  # 458   Unable to queue messages for node <domain> (See RFC1985)
  # 459   Node <domain> not allowed: <reason> (See RFC51985)
  '421', '450', '451', '452', '422', '430', '432', '453', '454', '455', '456', '458', '459'
].freeze
ReplyCode5 =
[
  # 500   Syntax error, command unrecognized (This may include errors such as command line too long)
  # 501   Syntax error in parameters or arguments
  # 502   Command not implemented (see Section 4.2.4)
  # 503   Bad sequence of commands
  # 504   Command parameter not implemented
  # 521   Host does not accept mail (See RFC7504)
  # 523   Encryption Needed (See RFC5248)
  # 524   (See RFC5248)
  # 525   User Account Disabled (See RFC5248)
  # 530   Authentication required (See RFC4954)
  # 533   (See RFC5248)
  # 534   Authentication mechanism is too weak (See RFC4954)
  # 535   Authentication credentials invalid (See RFC4954)
  # 538   Encryption required for requested authentication mechanism (See RFC4954)
  # 550   Requested action not taken: mailbox unavailable (e.g., mailbox not found, no access, or
  #       command rejected for policy reasons)
  # 551   User not local; please try <forward-path> (See Section 3.4)
  # 552   Requested mail action aborted: exceeded storage allocation
  # 553   Requested action not taken: mailbox name not allowed (e.g., mailbox syntax incorrect)
  # 554   Transaction failed (Or, in the case of a connection-opening response, "No SMTP service here")
  # 555   MAIL FROM/RCPT TO parameters not recognized or not implemented
  # 556   Domain does not accept mail (See RFC7504)
  "550", "552", "553", "551", "521", "525", "523", "524", "530", "533", "534", "535", "538",
  "555", "556", "554", "500", "501", "502", "503", "504",
].freeze
CodeOfSMTP =
{'2' => ReplyCode2, '4' => ReplyCode4, '5' => ReplyCode5}.freeze
Associated =
{
  "422" => ["AUTH",     "4.7.12",  "securityerror"], # RFC5238
  "432" => ["AUTH",     "4.7.12",  "securityerror"], # RFC4954, RFC5321
  "451" => ["",         "",        "systemerror"],   # RFC2465, RFC5321
  "452" => ["",         "",        "systemfull"],    # RFC5321
  "454" => ["AUTH",     "4.7.0",   "securityerror"], # RFC3027, RFC4954
  "455" => ["",         "",        "syntaxerror"],   # RFC5321
  "500" => ["",         "",        "syntaxerror"],   # RFC5321
  "501" => ["",         "",        "syntaxerror"],   # RFC5321
  "502" => ["",         "",        "syntaxerror"],   # RFC5321
  "503" => ["",         "",        "syntaxerror"],   # RFC5321
  "504" => ["",         "",        "syntaxerror"],   # RFC5321
  "521" => ["CONN",     "",        "notaccept"],     # RFC7504
  "523" => ["AUTH",     "5.7.10",  "securityerror"], # RFC5248
  "524" => ["AUTH",     "5.7.11",  "securityerror"], # RFC5248
  "525" => ["AUTH",     "5.7.13",  "securityerror"], # RFC5248
  "534" => ["AUTH",     "5.7.9",   "securityerror"], # RFC4954, RFC5248
  "535" => ["AUTH",     "5.7.8",   "securityerror"], # RFC4954, RFC5248
  "538" => ["AUTH",     "5.7.11",  "securityerror"], # RFC4954, RFC5248
  "551" => ["",         "",        "hasmoved"],      # RFC5321, RFC5336, RFC6531
  "552" => ["",         "",        "mailboxfull"],   # RFC5321
  "555" => ["",         "",        "syntaxerror"],   # RFC5321
  "556" => ["RCPT",     "",        "notaccept"],     # RFC7504
}.freeze

Class Method Summary collapse

Class Method Details

.associatedwith(argv1 = '') ⇒ Array

associatedwith returns a slice associated with the SMTP reply code of the argument

Parameters:

  • argv1 (String) (defaults to: '')

    SMTP reply code like 550

Returns:

  • (Array)
    “SMTP Command”, “DSN”, “Reason”

Since:

  • v5.2.2



212
# File 'lib/sisimai/smtp/reply.rb', line 212

def associatedwith(argv1 = ''); return Associated[argv1] || []; end

.find(argv1 = '', argv2 = '0') ⇒ String

Get SMTP Reply Code from the given string

Parameters:

  • argv1 (String) (defaults to: '')

    String including SMTP Reply Code like 550

  • argv2 (String) (defaults to: '0')

    Status code like 5.1.1 or 2 or 4 or 5

Returns:

  • (String)

    SMTP Reply Code or an empty string



174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
# File 'lib/sisimai/smtp/reply.rb', line 174

def find(argv1 = '', argv2 = '0')
  return "" if argv1.to_s.size < 3 || argv1.upcase.include?('X-UNIX')

  esmtperror = ' ' + argv1 + ' '
  esmtpreply = ''
  statuscode = argv2[0, 1]
  replycodes = if statuscode == '5' || statuscode == '4' || statuscode == '2'
                 CodeOfSMTP[statuscode]
               else
                 [*CodeOfSMTP['5'], *CodeOfSMTP['4'], *CodeOfSMTP['2']]
               end

  replycodes.each do |e|
    # Try to find an SMTP Reply Code from the given string
    appearance = esmtperror.index(e); next if appearance.nil?
    startingat = 1
    mesglength = esmtperror.size

    while startingat + 3 < mesglength
      # Find all the reply code in the error message
      replyindex = esmtperror.index(e, startingat); break if replyindex.nil?
      formerchar = esmtperror[replyindex - 1, 1].ord || 0
      latterchar = esmtperror[replyindex + 3, 1].ord || 0

      if formerchar > 45 && formerchar < 58 then startingat += 3; next; end
      if latterchar > 45 && latterchar < 58 then startingat += 3; next; end
      esmtpreply = e
      break
    end
    break if esmtpreply != ""
  end
  return esmtpreply
end

.test(argv1 = '') ⇒ Boolean

Check whether a reply code is a valid code or not

Parameters:

  • argv1 (String) (defaults to: '')

    Reply Code(DSN)

Returns:

  • (Boolean)

    false = Invalid reply code, true = Valid reply code

See Also:

  • code

Since:

  • v5.0.0



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
# File 'lib/sisimai/smtp/reply.rb', line 143

def test(argv1 = '')
  return false if argv1.empty?

  reply = argv1.to_i
  first = (reply / 100).to_i

  return false if reply < 211
  return false if reply > 556
  return false if reply % 100 > 59

  if first == 2
    # 2yz
    return true  if reply == 235                # 235 is a valid code for AUTH (RFC4954)
    return false if reply >  253                # The maximum code of 2xy is 253 (RFC5248)
    return false if reply >  221 && reply < 250 # There is no reply code between 221 and 250 
    return true
  end

  if first == 3
    # 3yz
    return false if reply != 334 && reply != 354
    return true
  end

  return true
end