sisimai 5.3.0-java → 5.4.0-java

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 (111) hide show
  1. checksums.yaml +4 -4
  2. data/ChangeLog.md +12 -0
  3. data/Makefile +3 -2
  4. data/README-JA.md +2 -2
  5. data/README.md +6 -6
  6. data/lib/sisimai/address.rb +45 -56
  7. data/lib/sisimai/arf.rb +11 -16
  8. data/lib/sisimai/datetime.rb +16 -50
  9. data/lib/sisimai/fact/json.rb +5 -5
  10. data/lib/sisimai/fact/yaml.rb +3 -3
  11. data/lib/sisimai/fact.rb +11 -12
  12. data/lib/sisimai/lda.rb +3 -3
  13. data/lib/sisimai/lhost/activehunter.rb +4 -6
  14. data/lib/sisimai/lhost/amazonses.rb +5 -6
  15. data/lib/sisimai/lhost/apachejames.rb +7 -9
  16. data/lib/sisimai/lhost/biglobe.rb +3 -5
  17. data/lib/sisimai/lhost/courier.rb +4 -6
  18. data/lib/sisimai/lhost/domino.rb +4 -5
  19. data/lib/sisimai/lhost/dragonfly.rb +3 -5
  20. data/lib/sisimai/lhost/einsundeins.rb +6 -8
  21. data/lib/sisimai/lhost/exchange2003.rb +10 -12
  22. data/lib/sisimai/lhost/exchange2007.rb +4 -5
  23. data/lib/sisimai/lhost/exim.rb +6 -8
  24. data/lib/sisimai/lhost/ezweb.rb +10 -12
  25. data/lib/sisimai/lhost/fml.rb +2 -3
  26. data/lib/sisimai/lhost/gmail.rb +4 -6
  27. data/lib/sisimai/lhost/gmx.rb +6 -8
  28. data/lib/sisimai/lhost/googlegroups.rb +1 -2
  29. data/lib/sisimai/lhost/googleworkspace.rb +3 -4
  30. data/lib/sisimai/lhost/imailserver.rb +6 -7
  31. data/lib/sisimai/lhost/interscanmss.rb +1 -2
  32. data/lib/sisimai/lhost/kddi.rb +5 -8
  33. data/lib/sisimai/lhost/mailfoundry.rb +4 -7
  34. data/lib/sisimai/lhost/mailmarshalsmtp.rb +4 -6
  35. data/lib/sisimai/lhost/messagingserver.rb +5 -7
  36. data/lib/sisimai/lhost/mfilter.rb +4 -6
  37. data/lib/sisimai/lhost/notes.rb +7 -9
  38. data/lib/sisimai/lhost/opensmtpd.rb +2 -4
  39. data/lib/sisimai/lhost/postfix.rb +8 -11
  40. data/lib/sisimai/lhost/qmail.rb +5 -8
  41. data/lib/sisimai/lhost/sendmail.rb +7 -10
  42. data/lib/sisimai/lhost/v5sendmail.rb +15 -17
  43. data/lib/sisimai/lhost/verizon.rb +9 -14
  44. data/lib/sisimai/lhost/x1.rb +4 -6
  45. data/lib/sisimai/lhost/x2.rb +5 -7
  46. data/lib/sisimai/lhost/x3.rb +3 -4
  47. data/lib/sisimai/lhost/x6.rb +4 -6
  48. data/lib/sisimai/lhost/zoho.rb +6 -8
  49. data/lib/sisimai/lhost.rb +1 -1
  50. data/lib/sisimai/mail/mbox.rb +1 -1
  51. data/lib/sisimai/mail/memory.rb +1 -1
  52. data/lib/sisimai/mail.rb +8 -8
  53. data/lib/sisimai/message.rb +11 -13
  54. data/lib/sisimai/reason/authfailure.rb +10 -10
  55. data/lib/sisimai/reason/badreputation.rb +4 -6
  56. data/lib/sisimai/reason/blocked.rb +6 -8
  57. data/lib/sisimai/reason/contenterror.rb +5 -6
  58. data/lib/sisimai/reason/delivered.rb +2 -2
  59. data/lib/sisimai/reason/exceedlimit.rb +7 -8
  60. data/lib/sisimai/reason/expired.rb +6 -7
  61. data/lib/sisimai/reason/failedstarttls.rb +5 -7
  62. data/lib/sisimai/reason/feedback.rb +2 -2
  63. data/lib/sisimai/reason/filtered.rb +7 -10
  64. data/lib/sisimai/reason/hasmoved.rb +4 -5
  65. data/lib/sisimai/reason/hostunknown.rb +6 -7
  66. data/lib/sisimai/reason/mailboxfull.rb +7 -8
  67. data/lib/sisimai/reason/mailererror.rb +5 -8
  68. data/lib/sisimai/reason/mesgtoobig.rb +5 -6
  69. data/lib/sisimai/reason/networkerror.rb +5 -8
  70. data/lib/sisimai/reason/norelaying.rb +4 -5
  71. data/lib/sisimai/reason/notaccept.rb +5 -8
  72. data/lib/sisimai/reason/notcompliantrfc.rb +5 -6
  73. data/lib/sisimai/reason/onhold.rb +6 -9
  74. data/lib/sisimai/reason/policyviolation.rb +6 -9
  75. data/lib/sisimai/reason/rejected.rb +5 -6
  76. data/lib/sisimai/reason/requireptr.rb +6 -7
  77. data/lib/sisimai/reason/securityerror.rb +6 -9
  78. data/lib/sisimai/reason/spamdetected.rb +8 -9
  79. data/lib/sisimai/reason/speeding.rb +6 -7
  80. data/lib/sisimai/reason/suppressed.rb +3 -7
  81. data/lib/sisimai/reason/suspend.rb +5 -7
  82. data/lib/sisimai/reason/syntaxerror.rb +3 -5
  83. data/lib/sisimai/reason/systemerror.rb +6 -9
  84. data/lib/sisimai/reason/systemfull.rb +5 -8
  85. data/lib/sisimai/reason/toomanyconn.rb +5 -6
  86. data/lib/sisimai/reason/undefined.rb +2 -2
  87. data/lib/sisimai/reason/userunknown.rb +8 -9
  88. data/lib/sisimai/reason/vacation.rb +4 -5
  89. data/lib/sisimai/reason/virusdetected.rb +4 -5
  90. data/lib/sisimai/reason.rb +13 -13
  91. data/lib/sisimai/rfc1123.rb +4 -8
  92. data/lib/sisimai/rfc1894.rb +5 -6
  93. data/lib/sisimai/rfc2045.rb +27 -31
  94. data/lib/sisimai/rfc3464/thirdparty.rb +1 -1
  95. data/lib/sisimai/rfc3464.rb +7 -9
  96. data/lib/sisimai/rfc3834.rb +5 -9
  97. data/lib/sisimai/rfc5322.rb +8 -26
  98. data/lib/sisimai/rfc791.rb +6 -4
  99. data/lib/sisimai/rhost/google.rb +8 -0
  100. data/lib/sisimai/rhost/microsoft.rb +17 -5
  101. data/lib/sisimai/rhost.rb +2 -2
  102. data/lib/sisimai/smtp/command.rb +1 -1
  103. data/lib/sisimai/smtp/failure.rb +5 -12
  104. data/lib/sisimai/smtp/reply.rb +3 -5
  105. data/lib/sisimai/smtp/status.rb +14 -24
  106. data/lib/sisimai/smtp/transcript.rb +1 -10
  107. data/lib/sisimai/string.rb +20 -30
  108. data/lib/sisimai/version.rb +1 -1
  109. data/lib/sisimai.rb +11 -11
  110. data/set-of-emails/maildir/bsd/rhost-microsoft-06.eml +45 -0
  111. metadata +3 -2
@@ -57,9 +57,9 @@ module Sisimai
57
57
  #"text" => ["X-Original-Message-ID", "Final-Log-ID", "Original-Envelope-ID"],
58
58
  }.freeze
59
59
 
60
- SubtypeSet = { "addr" => "RFC822", "cdoe" => "SMTP", "host" => "DNS" }.freeze
60
+ SubtypeSet = {"addr" => "RFC822", "cdoe" => "SMTP", "host" => "DNS"}.freeze
61
61
  ActionList = ["failed", "delayed", "delivered", "relayed", "expanded"].freeze
62
- Correction = { 'deliverable' => 'delivered', 'expired' => 'delayed', 'failure' => 'failed' }
62
+ Correction = {'deliverable' => 'delivered', 'expired' => 'delayed', 'failure' => 'failed'}
63
63
  FieldGroup = {
64
64
  'original-recipient' => 'addr',
65
65
  'final-recipient' => 'addr',
@@ -108,9 +108,8 @@ module Sisimai
108
108
  # 2: Matched with per-recipient field
109
109
  # @since v4.25.0
110
110
  def match(argv0 = '')
111
- return 0 unless argv0
112
- return 0 unless argv0.size > 0
113
- label = Sisimai::RFC1894.label(argv0); return 0 unless label
111
+ return 0 if argv0.to_s == ""
112
+ label = Sisimai::RFC1894.label(argv0); return 0 if label.empty?
114
113
  match = 0
115
114
 
116
115
  FieldNames[0].each_key do |e|
@@ -135,7 +134,7 @@ module Sisimai
135
134
  # @return [String] Field name as a label
136
135
  # @since v4.25.15
137
136
  def label(argv0 = '')
138
- return nil if argv0.empty?
137
+ return "" if argv0.empty?
139
138
  return argv0.split(':', 2).shift.downcase
140
139
  end
141
140
 
@@ -7,10 +7,9 @@ module Sisimai
7
7
 
8
8
  # Check that the argument is MIME-Encoded string or not
9
9
  # @param [String] argvs String to be checked
10
- # @return [True,False] false: Not MIME encoded string
11
- # true: MIME encoded string
10
+ # @return [Boolean] false: Not MIME encoded string, true: MIME encoded string
12
11
  def is_encoded(argv1)
13
- return nil unless argv1
12
+ return false unless argv1
14
13
 
15
14
  text1 = argv1.delete('"')
16
15
  mime1 = false
@@ -60,13 +59,12 @@ module Sisimai
60
59
  textblocks[-1].gsub!(/\r\n/, '')
61
60
  textblocks << cv[5]
62
61
  else
63
- textblocks << if textblocks.empty? then e else ' ' << e end
62
+ textblocks << if textblocks.empty? then e else " #{e}" end
64
63
  end
65
64
  end
66
-
67
65
  return '' if textblocks.empty?
68
- p = textblocks.join('')
69
66
 
67
+ p = textblocks.join('')
70
68
  if ctxcharset && qbencoding
71
69
  # utf8 => UTF-8
72
70
  ctxcharset = 'UTF-8' if ctxcharset.casecmp('UTF8') == 0
@@ -74,32 +72,32 @@ module Sisimai
74
72
  unless ctxcharset.casecmp('UTF-8') == 0
75
73
  # Characterset is not UTF-8
76
74
  begin
77
- p .encode!('UTF-8', ctxcharset)
75
+ p = p.encode!('UTF-8', ctxcharset)
78
76
  rescue
79
77
  p = 'FAILED TO CONVERT THE SUBJECT'
80
78
  end
81
79
  end
82
80
  end
83
-
84
- return p.force_encoding('UTF-8').scrub('?')
81
+ q = p.dup
82
+ return q.force_encoding('UTF-8').scrub('?')
85
83
  end
86
84
 
87
85
  # Decode MIME BASE64 Encoded string
88
86
  # @param [String] argv0 MIME Encoded text
89
87
  # @return [String] MIME-Decoded text
90
88
  def decodeB(argv0 = nil)
91
- return nil unless argv0
89
+ return "" if argv0.nil? || argv0.empty?
92
90
 
93
91
  p = nil
94
92
  if cv = argv0.match(%r|([+/\=0-9A-Za-z\r\n]+)|) then p = Base64.decode64(cv[1]) end
95
- return p ? p.scrub('?') : nil
93
+ return p ? p.scrub('?') : ""
96
94
  end
97
95
 
98
96
  # Decode MIME Quoted-Printable Encoded string
99
97
  # @param [String] argv0 MIME Encoded text
100
98
  # @return [String] MIME Decoded text
101
99
  def decodeQ(argv0 = nil)
102
- return nil unless argv0
100
+ return "" if argv0.nil? || argv0.empty?
103
101
  return argv0.unpack('M').first.scrub('?')
104
102
  end
105
103
 
@@ -109,7 +107,7 @@ module Sisimai
109
107
  # @return [String] The value of the parameter
110
108
  # @since v5.0.0
111
109
  def parameter(argv0 = '', argv1 = '')
112
- return nil if argv0.empty?
110
+ return "" if argv0.empty?
113
111
  parameterq = argv1.size > 0 ? argv1 + '=' : ''
114
112
  paramindex = argv1.size > 0 ? argv0.index(parameterq) : 0
115
113
  return '' unless paramindex
@@ -128,7 +126,7 @@ module Sisimai
128
126
  # 1: End of boundary
129
127
  # @return [String] Boundary string
130
128
  def boundary(argv0 = '', start = -1)
131
- return nil if argv0.empty?
129
+ return "" if argv0.empty?
132
130
  btext = parameter(argv0, 'boundary')
133
131
  return '' if btext.empty?
134
132
 
@@ -175,8 +173,8 @@ module Sisimai
175
173
  elsif e.index('boundary=') || e.index('charset=')
176
174
  # "Content-Type" field has boundary="..." or charset="utf-8"
177
175
  next if headerpart[0].empty?
178
- headerpart[0] << " " << e
179
- headerpart[0].gsub!(/\s\s+/, ' ')
176
+ headerpart[0] += " #{e}"
177
+ headerpart[0] = headerpart[0].gsub(/\s\s+/, ' ')
180
178
  end
181
179
  end
182
180
  return headerpart if heads
@@ -197,7 +195,7 @@ module Sisimai
197
195
  break if mediatypev.index('/feedback-report')
198
196
  break if ctencoding.empty?
199
197
 
200
- multipart1[2] << sprintf("Content-Transfer-Encoding: %s\n", ctencoding)
198
+ multipart1[2] += sprintf("Content-Transfer-Encoding: %s\n", ctencoding)
201
199
  break
202
200
  end
203
201
 
@@ -206,10 +204,10 @@ module Sisimai
206
204
  break if lowerchunk.empty?
207
205
  break if lowerchunk[0, 1] == "\n"
208
206
 
209
- multipart1[2] << "\n"
207
+ multipart1[2] += "\n"
210
208
  break
211
209
  end
212
- multipart1[2] << lowerchunk
210
+ multipart1[2] += lowerchunk
213
211
  return multipart1
214
212
  end
215
213
 
@@ -264,10 +262,8 @@ module Sisimai
264
262
  # @param [String] argv1 A pointer to multipart/* message blocks
265
263
  # @return [String] Message body
266
264
  def makeflat(argv0 = '', argv1 = '')
267
- return nil unless argv0
268
- return nil unless argv1
269
- return '' unless argv0.index('multipart/')
270
- return '' unless argv0.index('boundary=')
265
+ return "" if argv0.nil? || argv1.nil?
266
+ return "" if argv0.index('multipart/') == false || argv0.index('boundary=') == false
271
267
 
272
268
  # Some bounce messages include lower-cased "content-type:" field such as the followings:
273
269
  # - content-type: message/delivery-status => Content-Type: message/delivery-status
@@ -284,7 +280,7 @@ module Sisimai
284
280
  # - text/plain, text/rfc822-headers
285
281
  # - message/delivery-status, message/rfc822, message/partial, message/feedback-report
286
282
  istexthtml = false
287
- mediatypev = parameter(e[0]) || 'text/plain';
283
+ mediatypev = parameter(e[0]); mediatypev = "text/plain" if mediatypev.empty?
288
284
  next if mediatypev.start_with?('text/', 'message/') == false
289
285
 
290
286
  if mediatypev == 'text/html'
@@ -312,7 +308,7 @@ module Sisimai
312
308
  # Content-Transfer-Encoding: 7bit
313
309
  if cv = e[0].downcase.match(iso2022set)
314
310
  # Content-Type: text/plain; charset=ISO-2022-JP
315
- bodystring = Sisimai::String.to_utf8(bodyinside, cv[1]) || ''
311
+ bodystring = Sisimai::String.to_utf8(bodyinside, cv[1])
316
312
  else
317
313
  # No "charset" parameter in the value of Content-Type: header
318
314
  bodystring = bodyinside
@@ -324,7 +320,7 @@ module Sisimai
324
320
 
325
321
  if istexthtml
326
322
  # Try to delete HTML tags inside of text/html part whenever possible
327
- bodystring = Sisimai::String.to_plain(bodystring) || ''
323
+ bodystring = Sisimai::String.to_plain(bodystring)
328
324
  end
329
325
  next if bodystring.empty?
330
326
 
@@ -341,16 +337,16 @@ module Sisimai
341
337
  bodystring.scrub!('?')
342
338
  else
343
339
  # ISO-8859-1, GB2312, and so on
344
- bodystring = Sisimai::String.to_utf8(bodystring, ctxcharset) || ''
340
+ bodystring = Sisimai::String.to_utf8(bodystring, ctxcharset)
345
341
  end
346
- bodystring << "\n\n"
342
+ bodystring += "\n\n"
347
343
  end
348
344
 
349
345
  bodystring.gsub!(/\r\n/, "\n") if bodystring.include?("\r\n") # Convert CRLF to LF
350
346
 
351
347
  else
352
348
  # There is no Content-Transfer-Encoding header in the part
353
- bodystring << bodyinside
349
+ bodystring += bodyinside
354
350
  end
355
351
 
356
352
  if delimiters.any? { |a| mediatypev.include?(a) }
@@ -361,8 +357,8 @@ module Sisimai
361
357
  end
362
358
 
363
359
  # Append "\n" when the last character of $bodystring is not LF
364
- bodystring << "\n\n" unless bodystring[-2, 2] == "\n\n"
365
- flattenout << bodystring
360
+ bodystring += "\n\n" unless bodystring[-2, 2] == "\n\n"
361
+ flattenout += bodystring
366
362
  end
367
363
 
368
364
  return flattenout
@@ -39,7 +39,7 @@ module Sisimai
39
39
  def xfield(argv1 = "")
40
40
  return [] if argv1.nil? || argv1.empty?
41
41
  party = Sisimai::RFC3464::ThirdParty.returnedby(argv1); return [] if party.empty?
42
- return Module.const_get("Sisimai::RFC3464::ThirdParty::" << party).xfield(argv1)
42
+ return Module.const_get("Sisimai::RFC3464::ThirdParty::#{party}").xfield(argv1)
43
43
  end
44
44
  end
45
45
 
@@ -20,7 +20,7 @@ module Sisimai
20
20
  "Content-Type: message/partial",
21
21
  "Content-Disposition: inline", # See lhost-amavis-*.eml, lhost-facebook-*.eml
22
22
  ].freeze
23
- StartingOf = { message: ["Content-Type: message/delivery-status"] }.freeze
23
+ StartingOf = {message: ["Content-Type: message/delivery-status"]}.freeze
24
24
  FieldTable = Sisimai::RFC1894.FIELDTABLE
25
25
 
26
26
  # Decode a bounce mail which have fields defined in RFC3464
@@ -37,7 +37,7 @@ module Sisimai
37
37
  end
38
38
 
39
39
  permessage = {}
40
- dscontents = [Sisimai::Lhost.DELIVERYSTATUS]
40
+ dscontents = [Sisimai::Lhost.DELIVERYSTATUS]; v = nil
41
41
  alternates = Sisimai::Lhost.DELIVERYSTATUS
42
42
  emailparts = Sisimai::RFC5322.part(mbody, Boundaries)
43
43
  readslices = [""]
@@ -46,7 +46,6 @@ module Sisimai
46
46
  beforemesg = "" # (String) String before StartingOf[:message]
47
47
  goestonext = false # (Bool) Flag: do not append the line into beforemesg
48
48
  isboundary = [Sisimai::RFC2045.boundary(mhead["content-type"], 0)]; isboundary[0] ||= ""
49
- v = nil
50
49
 
51
50
  while emailparts[0].index('@').nil? do
52
51
  # There is no email address in the first element of emailparts
@@ -134,12 +133,11 @@ module Sisimai
134
133
  break if e.start_with?("--") # Boundary string
135
134
  break if e.include?("--- The follow") # ----- The following addresses had delivery problems -----
136
135
  break if e.include?("--- Transcript") # ----- Transcript of session follows -----
137
- beforemesg << e + " "; break
136
+ beforemesg += "#{e} "; break
138
137
  end
139
138
  next
140
139
  end
141
- next if (readcursor & Indicators[:deliverystatus]) == 0
142
- next if e.empty?
140
+ next if (readcursor & Indicators[:deliverystatus]) == 0 || e.empty?
143
141
 
144
142
  f = Sisimai::RFC1894.match(e)
145
143
  if f > 0
@@ -177,7 +175,7 @@ module Sisimai
177
175
  # There are other error messages as a comment such as the following:
178
176
  # Status: 5.0.0 (permanent failure)
179
177
  # Status: 4.0.0 (cat.example.net: host name lookup failure)
180
- v["diagnosis"] << " " + o[4] + " "
178
+ v["diagnosis"] += " #{o[4]} "
181
179
  end
182
180
  next unless FieldTable[o[0]]
183
181
  next if o[3] == "host" && Sisimai::RFC1123.is_internethost(o[2]) == false
@@ -208,13 +206,13 @@ module Sisimai
208
206
  # In the case of multiple "message/delivery-status" line
209
207
  next if e.start_with?("Content-") # Content-Disposition:, ...
210
208
  next if e.start_with?("--") # Boundary string
211
- beforemesg << e + " "; next
209
+ beforemesg += "#{e} "; next
212
210
  end
213
211
 
214
212
  # Diagnostic-Code: SMTP; 550-5.7.26 The MAIL FROM domain [email.example.jp]
215
213
  # has an SPF record with a hard fail
216
214
  next unless e.start_with?(" ")
217
- v["diagnosis"] << " " + Sisimai::String.sweep(e)
215
+ v["diagnosis"] += " #{Sisimai::String.sweep(e)}"
218
216
  end
219
217
  end
220
218
  end
@@ -3,7 +3,7 @@ module Sisimai
3
3
  module RFC3834
4
4
  class << self
5
5
  # http://tools.ietf.org/html/rfc3834
6
- MarkingsOf = { :boundary => '__SISIMAI_PSEUDO_BOUNDARY__' }
6
+ MarkingsOf = {:boundary => '__SISIMAI_PSEUDO_BOUNDARY__'}
7
7
  LowerLabel = %w[from to subject auto-submitted precedence x-apple-action].freeze
8
8
  DoNotParse = {
9
9
  'from' => ['root@', 'postmaster@', 'mailer-daemon@'],
@@ -66,7 +66,7 @@ module Sisimai
66
66
  return nil if match < 1
67
67
 
68
68
  require 'sisimai/lhost'
69
- dscontents = [Sisimai::Lhost.DELIVERYSTATUS]
69
+ dscontents = [Sisimai::Lhost.DELIVERYSTATUS]; v = dscontents[-1]
70
70
  bodyslices = mbody.scrub('?').split("\n")
71
71
  rfc822part = '' # (String) message/rfc822-headers part
72
72
  recipients = 0 # (Integer) The number of 'Final-Recipient' header
@@ -74,13 +74,11 @@ module Sisimai
74
74
  haveloaded = 0 # (Integer) The number of lines loaded from message body
75
75
  blanklines = 0 # (Integer) Counter for countinuous blank lines
76
76
  countuntil = 1 # (Integer) Maximum value of blank lines in the body part
77
- v = dscontents[-1]
78
77
 
79
78
  # RECIPIENT_ADDRESS
80
79
  %w[from return-path].each do |e|
81
80
  # Try to get the address of the recipient
82
81
  next unless mhead[e]
83
- next unless mhead[e]
84
82
  v['recipient'] = mhead[e]
85
83
  break
86
84
  end
@@ -109,12 +107,10 @@ module Sisimai
109
107
  break if blanklines > countuntil
110
108
  next
111
109
  end
112
- next unless e.include?(' ')
113
- next if e.start_with?('Content-Type')
114
- next if e.start_with?('Content-Transfer')
110
+ next if !e.include?(' ') || e.start_with?('Content-Type', 'Content-Transfer')
115
111
 
116
112
  v['diagnosis'] ||= ''
117
- v['diagnosis'] << e + ' '
113
+ v['diagnosis'] += "#{e }"
118
114
  haveloaded += 1
119
115
  break if haveloaded >= maxmsgline
120
116
  end
@@ -126,7 +122,7 @@ module Sisimai
126
122
 
127
123
  if cv = lower['subject'].match(SubjectSet)
128
124
  # Get the Subject header from the original message
129
- rfc822part = 'Subject: ' << cv[1] + "\n"
125
+ rfc822part = "Subject: #{cv[1]}\n"
130
126
  end
131
127
  return { 'ds' => dscontents, 'rfc822' => rfc822part }
132
128
  end
@@ -32,19 +32,12 @@ module Sisimai
32
32
  return []
33
33
  end
34
34
 
35
- # Fields that might be long
36
- # @return [Hash] Long filed(email header) list
37
- def LONGFIELDS
38
- return { 'to' => true, 'from' => true, 'subject' => true, 'message-id' => true }
39
- end
40
-
41
35
  # Convert Received headers to a structured data
42
36
  # @param [String] argv1 Received header
43
37
  # @return [Array] Received header as a structured data
44
38
  def received(argv1)
45
39
  return [] unless argv1.is_a?(::String)
46
- return [] if argv1.include?(' invoked by uid')
47
- return [] if argv1.include?(' invoked from network')
40
+ return [] if argv1.include?(' invoked by uid') || argv1.include?(' invoked from network')
48
41
 
49
42
  # - https://datatracker.ietf.org/doc/html/rfc5322
50
43
  # received = "Received:" *received-token ";" date-time CRLF
@@ -105,41 +98,30 @@ module Sisimai
105
98
  # Check alternatives in "other", and then delete uninformative values.
106
99
  next if e.nil?
107
100
  next if e.size < 4
108
- next if e == 'unknown'
109
- next if e == 'localhost'
110
- next if e == '[127.0.0.1]'
111
- next if e == '[IPv6:::1]'
112
- next unless e.include?('.')
113
- next if e.include?('=')
101
+ next if e == 'unknown' || e == 'localhost' || e == '[127.0.0.1]' || e == '[IPv6:::1]'
102
+ next if e.include?('.') == false || e.include?('=') == true
114
103
  alter << e
115
104
  end
116
105
 
117
106
  %w[from by].each do |e|
118
107
  # Remove square brackets from the IP address such as "[192.0.2.25]"
119
- next if token[e].nil?
120
- next if token[e].empty?
121
- next unless token[e].start_with?('[')
108
+ next if token[e].to_s.empty? || token[e].start_with?('[') == false
122
109
  token[e] = Sisimai::RFC791.find(token[e]).shift || ''
123
110
  end
124
111
  token['from'] ||= ''
125
112
 
126
113
  while true do
127
114
  # Prefer hostnames over IP addresses, except for localhost.localdomain and similar.
128
- break if token['from'] == 'localhost'
129
- break if token['from'] == 'localhost.localdomain'
130
- break unless token['from'].include?('.') # A hostname without a domain name
131
- break unless Sisimai::RFC791.find(token['from']).empty?
115
+ break if token['from'] == 'localhost' || token['from'] == 'localhost.localdomain'
116
+ break if token['from'].include?('.') == false || Sisimai::RFC791.find(token['from']).empty? == false
132
117
 
133
- # No need to rewrite token['from']
134
- right = true
118
+ right = true # No need to rewrite token['from']
135
119
  break
136
120
  end
137
121
 
138
122
  while true do
139
123
  # Try to rewrite uninformative hostnames and IP addresses in token['from']
140
- break if right # There is no need to rewrite
141
- break if alter.empty? # There is no alternative to rewriting
142
- break if alter[0].include?(token['from'])
124
+ break if right || alter.empty? || alter[0].include?(token['from'])
143
125
 
144
126
  if token['from'].start_with?('localhost')
145
127
  # localhost or localhost.localdomain
@@ -18,6 +18,7 @@ module Sisimai
18
18
  end
19
19
  return true
20
20
  end
21
+
21
22
  # Find an IPv4 address from the given string
22
23
  # @param [String] argv1 String including an IPv4 address
23
24
  # @return [Array] List of IPv4 addresses
@@ -26,14 +27,15 @@ module Sisimai
26
27
  return nil if argv0.to_s.empty?
27
28
  return [] if argv0.size < 7
28
29
 
30
+ given = argv0.dup
29
31
  ipv4a = []
30
32
  %w|( ) [ ] ,|.each do |e|
31
33
  # Rewrite: "mx.example.jp[192.0.2.1]" => "mx.example.jp 192.0.2.1"
32
- p0 = argv0.index(e); next unless p0
33
- argv0[p0, 1] = ' '
34
+ p0 = given.index(e); next unless p0
35
+ given[p0, 1] = ' '
34
36
  end
35
37
 
36
- argv0.split(' ').each do |e|
38
+ given.split(' ').each do |e|
37
39
  # Find string including an IPv4 address
38
40
  next unless e.index('.') # IPv4 address must include "." character
39
41
 
@@ -55,7 +57,7 @@ module Sisimai
55
57
  eo = ''
56
58
  next
57
59
  end
58
- eo << as.chr
60
+ eo += as.chr
59
61
  break if eo.to_i > 255
60
62
  end
61
63
  ipv4a << e if eo.size > 0 && eo.to_i < 256
@@ -82,7 +82,15 @@ module Sisimai
82
82
  # - 421 4.7.32 Your email has been rate limited because the From: header (RFC5322) in
83
83
  # this message isn't aligned with either the authenticated SPF or DKIM organizational
84
84
  # domain.
85
+ # - 421 5.7.32 Your email was blocked because the From: header (RFC5322) in this message
86
+ # isn't aligned with either the authenticated SPF or DKIM organizational domain.
85
87
  ['421', '4.7.32', 'aligned with either the authenticated spf or dkim'],
88
+ ["421", "5.7.32", "aligned with either the authenticated spf or dkim"],
89
+
90
+ # - 421 4.7.40 Your email has been rate limited because the sending domain doesn't
91
+ # have a DMARC record, or the DMARC record doesn’t specify a DMARC policy. Gmail
92
+ # requires all bulk email senders to add a DMARC record to their sending domain.
93
+ ["421", "4.7.40", "to add a dmarc record to "],
86
94
  ],
87
95
  'badreputation' => [
88
96
  # - 421 4.7.0 This message is suspicious due to the very low reputation of the sending
@@ -23,6 +23,13 @@ module Sisimai
23
23
  # - Access denied, sending domain [$SenderDomain] does not pass DMARC verification
24
24
  # - The sender's domain in the 5322.From address doesn't pass DMARC.
25
25
  ['5.7.509', 0, 0, 'does not pass dmarc verification'],
26
+
27
+ # - 550 5.7.515 Access denied, sending domain EXAMPLE.JP doesn't meet the required
28
+ # authentication level. The sender's domain in the 5322.From address doesn't meet
29
+ # the authentication requirements defined for the sender. To learn how to fix this
30
+ # see: https://go.microsoft.com/fwlink/p/?linkid=2319303
31
+ # Spf= Fail , Dkim= Pass , DMARC= Pass ...
32
+ ["5.7.515", 0, 0, "doesn't meet the required authentication level"],
26
33
  ],
27
34
  'badreputation' => [
28
35
  # Undocumented error messages ---------------------------------------------------------
@@ -597,11 +604,16 @@ module Sisimai
597
604
  ['5.2.14', 0, 0, 'misconfigured forwarding address'],
598
605
 
599
606
  # Undocumented error messages ---------------------------------------------------------
600
- ['4.4.3', 0, 0, 'temporary server error. please try again later attr18'],
601
- ['4.7.0', 0, 0, 'temporary server error. please try again later. prx4 nexthop:'],
602
- ['4.4.24', 0, 0, 'message failed to be replicated: insufficient system resource:'],
603
- ['4.4.25', 0, 0, 'message failed to be replicated: no healthy secondary server available to accept replica at this time.'],
604
- ['4.4.28', 0, 0, 'message failed to be replicated: the operation was canceled'],
607
+ # - 451 4.4.22 Message failed to be replicated: no healthy peers found ... (in reply to end of DATA command)
608
+ # - 451 4.4.23 Message failed to be replicated: No healthy secondary server available
609
+ # to accept replica at this time. ... (in reply to end of DATA command)
610
+ # - 451 4.4.28 Message failed to be replicated:
611
+ # Microsoft.Exchange.Transport.Net.Http.TransportHttpException(session Id: -1) ...(in reply to end of DATA command)
612
+ # - 451 4.4.28 Message failed to be replicated:
613
+ # System.Net.Http.HttpRequestException(session Id: ****) ... (in reply to end of DATA command)
614
+ ["4.4.", "22", "28", "message failed to be replicated:"],
615
+ ["4.4.3", "", "", "temporary server error. please try again later attr18"],
616
+ ["4.7.0", "", "", "temporary server error. please try again later. prx4 nexthop:"],
605
617
 
606
618
  # 550 5.4.318 Message expired, connection reset (SuspiciousRemoteServerError)
607
619
  # 450 4.4.318 Connection was closed abruptly (SuspiciousRemoteServerError)
data/lib/sisimai/rhost.rb CHANGED
@@ -74,8 +74,8 @@ module Sisimai
74
74
  return "" if argvs.nil?
75
75
 
76
76
  rhostclass = name(argvs); return "" if rhostclass.empty?
77
- modulepath = "sisimai/rhost/" << rhostclass.downcase; require modulepath
78
- modulename = "Sisimai::Rhost::" << rhostclass
77
+ modulepath = "sisimai/rhost/#{rhostclass.downcase}"; require modulepath
78
+ modulename = "Sisimai::Rhost::#{rhostclass}"
79
79
 
80
80
  #rhostclass = "sisimai/rhost/" << modulename.downcase.split("::")[2]; require rhostclass
81
81
  reasontext = Module.const_get(modulename).find(argvs)
@@ -31,7 +31,7 @@ module Sisimai
31
31
  return "" unless Sisimai::SMTP::Command.test(argv0)
32
32
 
33
33
  issuedcode = " " + argv0.downcase + " "
34
- commandmap = { "STAR" => "STARTTLS", "XFOR" => "XFORWARD" }
34
+ commandmap = {"STAR" => "STARTTLS", "XFOR" => "XFORWARD"}
35
35
  commandset = []
36
36
 
37
37
  Detectable.each do |e|
@@ -12,8 +12,7 @@ module Sisimai
12
12
  # false: Is not a permanent error
13
13
  # @since v4.17.3
14
14
  def is_permanent(argv1 = '')
15
- return false unless argv1
16
- return false unless argv1.size > 0
15
+ return false if argv1.to_s == ""
17
16
 
18
17
  statuscode = Sisimai::SMTP::Status.find(argv1)
19
18
  statuscode = Sisimai::SMTP::Reply.find(argv1) if statuscode.empty?
@@ -28,16 +27,14 @@ module Sisimai
28
27
  # false: is not a temporary error
29
28
  # @since v5.2.0
30
29
  def is_temporary(argv1 = '')
31
- return false unless argv1
32
- return false unless argv1.size > 0
30
+ return false if argv1.to_s == ""
33
31
 
34
32
  statuscode = Sisimai::SMTP::Status.find(argv1);
35
33
  statuscode = Sisimai::SMTP::Reply.find(argv1) if statuscode.empty?
36
34
  issuedcode = argv1.downcase
37
35
 
38
36
  return true if statuscode[0, 1] == "4"
39
- return true if issuedcode.include?(' temporar')
40
- return true if issuedcode.include?(' persistent')
37
+ return true if issuedcode.include?(' temporar') || issuedcode.include?(' persistent')
41
38
  return false
42
39
  end
43
40
 
@@ -46,9 +43,7 @@ module Sisimai
46
43
  # @param [String] argv2 String including SMTP Status code
47
44
  # @return [Boolean] true: is a hard bounce
48
45
  def is_hardbounce(argv1 = '', argv2 = '')
49
- return false unless argv1
50
- return false unless argv1.size > 0
51
-
46
+ return false if argv1.to_s == ""
52
47
  return false if argv1 == "undefined" || argv1 == "onhold"
53
48
  return false if argv1 == "delivered" || argv1 == "feedback" || argv1 == "vacation"
54
49
  return true if argv1 == "hasmoved" || argv1 == "userunknown" || argv1 == "hostunknown"
@@ -76,9 +71,7 @@ module Sisimai
76
71
  # @param [String] argv2 String including SMTP Status code
77
72
  # @return [Boolean] true: is a soft bounce
78
73
  def is_softbounce(argv1 = '', argv2 = '')
79
- return false unless argv1
80
- return false unless argv1.size > 0
81
-
74
+ return false if argv1.to_s == ""
82
75
  return false if argv1 == "delivered" || argv1 == "feedback" || argv1 == "vacation"
83
76
  return false if argv1 == "hasmoved" || argv1 == "userunknown" || argv1 == "hostunknown"
84
77
  return true if argv1 == "undefined" || argv1 == "onhold"
@@ -109,7 +109,7 @@ module Sisimai
109
109
  "550", "552", "553", "551", "521", "525", "523", "524", "530", "533", "534", "535", "538",
110
110
  "555", "556", "554", "500", "501", "502", "503", "504",
111
111
  ].freeze
112
- CodeOfSMTP = { '2' => ReplyCode2, '4' => ReplyCode4, '5' => ReplyCode5 }.freeze
112
+ CodeOfSMTP = {'2' => ReplyCode2, '4' => ReplyCode4, '5' => ReplyCode5}.freeze
113
113
  Associated = {
114
114
  "422" => ["AUTH", "4.7.12", "securityerror"], # RFC5238
115
115
  "432" => ["AUTH", "4.7.12", "securityerror"], # RFC4954, RFC5321
@@ -163,11 +163,9 @@ module Sisimai
163
163
  # Get SMTP Reply Code from the given string
164
164
  # @param [String] argv1 String including SMTP Reply Code like 550
165
165
  # @param [String] argv2 Status code like 5.1.1 or 2 or 4 or 5
166
- # @return [String] SMTP Reply Code
167
- # [Nil] The first argument did not include SMTP Reply Code value
166
+ # @return [String] SMTP Reply Code or an empty string
168
167
  def find(argv1 = '', argv2 = '0')
169
- return "" if argv1.to_s.size < 3
170
- return "" if argv1.upcase.include?('X-UNIX')
168
+ return "" if argv1.to_s.size < 3 || argv1.upcase.include?('X-UNIX')
171
169
 
172
170
  esmtperror = ' ' + argv1 + ' '
173
171
  esmtpreply = ''