sisimai 5.2.1-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 (113) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/rake-test.yml +1 -1
  3. data/ChangeLog.md +24 -0
  4. data/Makefile +3 -2
  5. data/README-JA.md +4 -4
  6. data/README.md +8 -8
  7. data/lib/sisimai/address.rb +45 -56
  8. data/lib/sisimai/arf.rb +11 -16
  9. data/lib/sisimai/datetime.rb +16 -50
  10. data/lib/sisimai/fact/json.rb +5 -5
  11. data/lib/sisimai/fact/yaml.rb +3 -3
  12. data/lib/sisimai/fact.rb +21 -12
  13. data/lib/sisimai/lda.rb +3 -3
  14. data/lib/sisimai/lhost/activehunter.rb +4 -6
  15. data/lib/sisimai/lhost/amazonses.rb +5 -6
  16. data/lib/sisimai/lhost/apachejames.rb +7 -9
  17. data/lib/sisimai/lhost/biglobe.rb +3 -5
  18. data/lib/sisimai/lhost/courier.rb +4 -6
  19. data/lib/sisimai/lhost/domino.rb +4 -5
  20. data/lib/sisimai/lhost/dragonfly.rb +3 -5
  21. data/lib/sisimai/lhost/einsundeins.rb +6 -8
  22. data/lib/sisimai/lhost/exchange2003.rb +10 -12
  23. data/lib/sisimai/lhost/exchange2007.rb +4 -5
  24. data/lib/sisimai/lhost/exim.rb +6 -8
  25. data/lib/sisimai/lhost/ezweb.rb +10 -12
  26. data/lib/sisimai/lhost/fml.rb +2 -3
  27. data/lib/sisimai/lhost/gmail.rb +4 -6
  28. data/lib/sisimai/lhost/gmx.rb +6 -8
  29. data/lib/sisimai/lhost/googlegroups.rb +1 -2
  30. data/lib/sisimai/lhost/googleworkspace.rb +3 -4
  31. data/lib/sisimai/lhost/imailserver.rb +6 -7
  32. data/lib/sisimai/lhost/interscanmss.rb +1 -2
  33. data/lib/sisimai/lhost/kddi.rb +5 -8
  34. data/lib/sisimai/lhost/mailfoundry.rb +4 -7
  35. data/lib/sisimai/lhost/mailmarshalsmtp.rb +4 -6
  36. data/lib/sisimai/lhost/messagingserver.rb +5 -7
  37. data/lib/sisimai/lhost/mfilter.rb +4 -6
  38. data/lib/sisimai/lhost/notes.rb +7 -9
  39. data/lib/sisimai/lhost/opensmtpd.rb +2 -4
  40. data/lib/sisimai/lhost/postfix.rb +8 -11
  41. data/lib/sisimai/lhost/qmail.rb +5 -8
  42. data/lib/sisimai/lhost/sendmail.rb +7 -10
  43. data/lib/sisimai/lhost/v5sendmail.rb +15 -17
  44. data/lib/sisimai/lhost/verizon.rb +9 -14
  45. data/lib/sisimai/lhost/x1.rb +4 -6
  46. data/lib/sisimai/lhost/x2.rb +5 -7
  47. data/lib/sisimai/lhost/x3.rb +3 -4
  48. data/lib/sisimai/lhost/x6.rb +4 -6
  49. data/lib/sisimai/lhost/zoho.rb +6 -8
  50. data/lib/sisimai/lhost.rb +1 -1
  51. data/lib/sisimai/mail/mbox.rb +1 -1
  52. data/lib/sisimai/mail/memory.rb +1 -1
  53. data/lib/sisimai/mail.rb +8 -8
  54. data/lib/sisimai/message.rb +11 -13
  55. data/lib/sisimai/order.rb +12 -11
  56. data/lib/sisimai/reason/authfailure.rb +10 -10
  57. data/lib/sisimai/reason/badreputation.rb +4 -6
  58. data/lib/sisimai/reason/blocked.rb +6 -8
  59. data/lib/sisimai/reason/contenterror.rb +5 -6
  60. data/lib/sisimai/reason/delivered.rb +2 -2
  61. data/lib/sisimai/reason/exceedlimit.rb +7 -8
  62. data/lib/sisimai/reason/expired.rb +6 -7
  63. data/lib/sisimai/reason/failedstarttls.rb +5 -7
  64. data/lib/sisimai/reason/feedback.rb +2 -2
  65. data/lib/sisimai/reason/filtered.rb +7 -10
  66. data/lib/sisimai/reason/hasmoved.rb +4 -5
  67. data/lib/sisimai/reason/hostunknown.rb +6 -7
  68. data/lib/sisimai/reason/mailboxfull.rb +7 -8
  69. data/lib/sisimai/reason/mailererror.rb +5 -8
  70. data/lib/sisimai/reason/mesgtoobig.rb +5 -6
  71. data/lib/sisimai/reason/networkerror.rb +5 -8
  72. data/lib/sisimai/reason/norelaying.rb +4 -5
  73. data/lib/sisimai/reason/notaccept.rb +5 -8
  74. data/lib/sisimai/reason/notcompliantrfc.rb +5 -6
  75. data/lib/sisimai/reason/onhold.rb +6 -9
  76. data/lib/sisimai/reason/policyviolation.rb +6 -9
  77. data/lib/sisimai/reason/rejected.rb +5 -6
  78. data/lib/sisimai/reason/requireptr.rb +6 -7
  79. data/lib/sisimai/reason/securityerror.rb +6 -9
  80. data/lib/sisimai/reason/spamdetected.rb +8 -9
  81. data/lib/sisimai/reason/speeding.rb +6 -7
  82. data/lib/sisimai/reason/suppressed.rb +3 -7
  83. data/lib/sisimai/reason/suspend.rb +5 -7
  84. data/lib/sisimai/reason/syntaxerror.rb +3 -5
  85. data/lib/sisimai/reason/systemerror.rb +6 -9
  86. data/lib/sisimai/reason/systemfull.rb +5 -8
  87. data/lib/sisimai/reason/toomanyconn.rb +5 -6
  88. data/lib/sisimai/reason/undefined.rb +2 -2
  89. data/lib/sisimai/reason/userunknown.rb +8 -9
  90. data/lib/sisimai/reason/vacation.rb +4 -5
  91. data/lib/sisimai/reason/virusdetected.rb +4 -5
  92. data/lib/sisimai/reason.rb +13 -13
  93. data/lib/sisimai/rfc1123.rb +4 -8
  94. data/lib/sisimai/rfc1894.rb +5 -6
  95. data/lib/sisimai/rfc2045.rb +27 -31
  96. data/lib/sisimai/rfc3464/thirdparty.rb +1 -1
  97. data/lib/sisimai/rfc3464.rb +7 -9
  98. data/lib/sisimai/rfc3834.rb +5 -9
  99. data/lib/sisimai/rfc5322.rb +8 -26
  100. data/lib/sisimai/rfc791.rb +6 -4
  101. data/lib/sisimai/rhost/google.rb +8 -0
  102. data/lib/sisimai/rhost/microsoft.rb +17 -5
  103. data/lib/sisimai/rhost.rb +2 -2
  104. data/lib/sisimai/smtp/command.rb +1 -1
  105. data/lib/sisimai/smtp/failure.rb +5 -12
  106. data/lib/sisimai/smtp/reply.rb +33 -12
  107. data/lib/sisimai/smtp/status.rb +21 -22
  108. data/lib/sisimai/smtp/transcript.rb +1 -10
  109. data/lib/sisimai/string.rb +20 -30
  110. data/lib/sisimai/version.rb +1 -1
  111. data/lib/sisimai.rb +11 -11
  112. data/set-of-emails/maildir/bsd/rhost-microsoft-06.eml +45 -0
  113. metadata +8 -10
@@ -145,24 +145,23 @@ module Sisimai
145
145
 
146
146
  # Try to match that the given text and regular expressions
147
147
  # @param [String] argv1 String to be matched with regular expressions
148
- # @return [True,False] false: Did not match
149
- # true: Matched
148
+ # @return [Boolean] false: Did not match, true: Matched
150
149
  def match(argv1)
151
- return nil unless argv1
152
- return true if Index.any? { |a| argv1.include?(a) }
153
- return true if Pairs.any? { |a| Sisimai::String.aligned(argv1, a) }
150
+ return false unless argv1
151
+ return true if Index.any? { |a| argv1.include?(a) }
152
+ return true if Pairs.any? { |a| Sisimai::String.aligned(argv1, a) }
154
153
  return false
155
154
  end
156
155
 
157
156
  # Whether the address is "userunknown" or not
158
157
  # @param [Sisimai::Fact] argvs Object to be detected the reason
159
- # @return [True,False] true: is unknown user
158
+ # @return [Boolean] true: is unknown user
160
159
  # false: is not unknown user.
161
160
  # @see http://www.ietf.org/rfc/rfc2822.txt
162
161
  def true(argvs)
163
162
  return true if argvs['reason'] == 'userunknown'
164
163
 
165
- tempreason = Sisimai::SMTP::Status.name(argvs['deliverystatus']) || ''
164
+ tempreason = Sisimai::SMTP::Status.name(argvs['deliverystatus'])
166
165
  return false if tempreason == 'suspend'
167
166
 
168
167
  issuedcode = argvs['diagnosticcode'].downcase
@@ -174,13 +173,13 @@ module Sisimai
174
173
  matchother = false
175
174
  PreMatches.each do |e|
176
175
  # Check the value of "Diagnostic-Code" with other error patterns.
177
- p = 'Sisimai::Reason::' << e
176
+ p = "Sisimai::Reason::#{e}"
178
177
  r = nil
179
178
  begin
180
179
  require ModulePath[p]
181
180
  r = Module.const_get(p)
182
181
  rescue
183
- warn '***warning: Failed to load ' << p
182
+ warn "***warning: Failed to load #{p}"
184
183
  next
185
184
  end
186
185
 
@@ -16,15 +16,14 @@ module Sisimai
16
16
 
17
17
  # Try to match that the given text and regular expressions
18
18
  # @param [String] argv1 String to be matched with regular expressions
19
- # @return [True,False] false: Did not match
20
- # true: Matched
19
+ # @return [Boolean] false: Did not match, true: Matched
21
20
  def match(argv1)
22
- return nil unless argv1
23
- return true if Index.any? { |a| argv1.include?(a) }
21
+ return false unless argv1
22
+ return true if Index.any? { |a| argv1.include?(a) }
24
23
  return false
25
24
  end
26
25
 
27
- def true(*); return nil; end
26
+ def true(*); return false; end
28
27
  end
29
28
  end
30
29
  end
@@ -28,18 +28,17 @@ module Sisimai
28
28
 
29
29
  # Try to match that the given text and regular expressions
30
30
  # @param [String] argv1 String to be matched with regular expressions
31
- # @return [True,False] false: Did not match
32
- # true: Matched
31
+ # @return [Boolean] false: Did not match, true: Matched
33
32
  # @since 4.22.0
34
33
  def match(argv1)
35
- return nil unless argv1
36
- return true if Index.any? { |a| argv1.include?(a) }
34
+ return false unless argv1
35
+ return true if Index.any? { |a| argv1.include?(a) }
37
36
  return false
38
37
  end
39
38
 
40
39
  # The bounce reason is "virusdetected" or not
41
40
  # @param [Sisimai::Fact] argvs Object to be detected the reason
42
- # @return [True,False] true: virus detected
41
+ # @return [Boolean] true: virus detected
43
42
  # false: virus was not detected
44
43
  # @since 4.22.0
45
44
  # @see http://www.ietf.org/rfc/rfc2822.txt
@@ -30,7 +30,7 @@ module Sisimai
30
30
  def path
31
31
  index = Sisimai::Reason.index
32
32
  table = {}
33
- index.each { |e| table['Sisimai::Reason::' << e] = 'sisimai/reason/' << e.downcase }
33
+ index.each { |e| table["Sisimai::Reason::#{e}"] = "sisimai/reason/#{e.downcase}" }
34
34
  return table
35
35
  end
36
36
 
@@ -65,10 +65,10 @@ module Sisimai
65
65
 
66
66
  # Detect the bounce reason
67
67
  # @param [Hash] argvs Decoded email object
68
- # @return [String, nil] Bounce reason or nil if the argument is missing or not Hash
68
+ # @return [String] Bounce reason or an empty string if the argument is missing or not Hash
69
69
  # @see anotherone
70
70
  def find(argvs)
71
- return nil unless argvs
71
+ return "" unless argvs
72
72
  unless GetRetried[argvs['reason']]
73
73
  # Return reason text already decided except reason match with the regular expression of
74
74
  # retry() method.
@@ -85,13 +85,13 @@ module Sisimai
85
85
  ClassOrder[0].each do |e|
86
86
  # Check the value of Diagnostic-Code: and the value of Status:, it is a deliverystats,
87
87
  # with true() method in each Sisimai::Reason::* class.
88
- p = 'Sisimai::Reason::' << e
88
+ p = "Sisimai::Reason::#{e}"
89
89
  r = nil
90
90
  begin
91
91
  require ModulePath[p]
92
92
  r = Module.const_get(p)
93
93
  rescue
94
- warn ' ***warning: Failed to load ' << p
94
+ warn " ***warning: Failed to load #{p}"
95
95
  next
96
96
  end
97
97
  next unless r.true(argvs)
@@ -132,7 +132,7 @@ module Sisimai
132
132
  codeformat = argvs['diagnostictype'] || ''
133
133
  actiontext = argvs['action'] || ''
134
134
  statuscode = argvs['deliverystatus'] || ''
135
- reasontext = Sisimai::SMTP::Status.name(statuscode) || ''
135
+ reasontext = Sisimai::SMTP::Status.name(statuscode)
136
136
  trytomatch = reasontext.empty? ? true : false
137
137
  trytomatch ||= true if GetRetried[reasontext] || codeformat != 'SMTP'
138
138
 
@@ -140,13 +140,13 @@ module Sisimai
140
140
  # Could not decide the reason by the value of Status:
141
141
  ClassOrder[1].each do |e|
142
142
  # Trying to match with other patterns in Sisimai::Reason::* classes
143
- p = 'Sisimai::Reason::' << e
143
+ p = "Sisimai::Reason::#{e}"
144
144
  r = nil
145
145
  begin
146
146
  require ModulePath[p]
147
147
  r = Module.const_get(p)
148
148
  rescue
149
- warn ' ***warning: Failed to load ' << p
149
+ warn " ***warning: Failed to load #{p}"
150
150
  next
151
151
  end
152
152
 
@@ -195,7 +195,7 @@ module Sisimai
195
195
  # @param [String] argv1 Error message
196
196
  # @return [String] Bounce reason
197
197
  def match(argv1)
198
- return nil unless argv1
198
+ return "" unless argv1
199
199
 
200
200
  reasontext = ''
201
201
  issuedcode = argv1.downcase
@@ -204,13 +204,13 @@ module Sisimai
204
204
  ClassOrder[2].each do |e|
205
205
  # Check the value of Diagnostic-Code: and the value of Status:, it is a deliverystats, with
206
206
  # true() method in each Sisimai::Reason::* class.
207
- p = 'Sisimai::Reason::' << e
207
+ p = "Sisimai::Reason::#{e}"
208
208
  r = nil
209
209
  begin
210
210
  require ModulePath[p]
211
211
  r = Module.const_get(p)
212
212
  rescue
213
- warn ' ***warning: Failed to load ' << p
213
+ warn " ***warning: Failed to load #{p}"
214
214
  next
215
215
  end
216
216
 
@@ -226,8 +226,8 @@ module Sisimai
226
226
  else
227
227
  # Detect the bounce reason from "Status:" code
228
228
  require 'sisimai/smtp/status'
229
- cv = Sisimai::SMTP::Status.find(argv1)
230
- reasontext = Sisimai::SMTP::Status.name(cv) || 'undefined'
229
+ reasontext = Sisimai::SMTP::Status.name(Sisimai::SMTP::Status.find(argv1))
230
+ reasontext = "undefined" if reasontext.empty?
231
231
  end
232
232
  return reasontext
233
233
  end
@@ -24,7 +24,7 @@ module Sisimai
24
24
  ].freeze
25
25
  StartAfter = [
26
26
  "generating server: ", # (Exchange2007) en-US/Generating server: mta4.example.org
27
- "serveur de g", # (Exchange2007) fr-FR/Serveur de g辿n辿ration
27
+ "serveur de g", # (Exchange2007) fr-FR/Serveur de g辿?辿?ation
28
28
  "server di generazione", # (Exchange2007) it-CH
29
29
  "genererande server", # (Exchange2007) sv-SE
30
30
  ].freeze
@@ -38,14 +38,10 @@ module Sisimai
38
38
  # @since v5.2.0
39
39
  def is_internethost(argv0 = '')
40
40
  return false unless argv0
41
- return false if argv0.size < 4
42
- return false if argv0.size > 255
41
+ return false if argv0.size < 4 || argv0.size > 255
43
42
  return false if argv0.include?(".") == false
44
- return false if argv0.include?("..")
45
- return false if argv0.include?("--")
46
- return false if argv0.start_with?(".")
47
- return false if argv0.start_with?("-")
48
- return false if argv0.end_with?("-")
43
+ return false if argv0.include?("..") || argv0.include?("--")
44
+ return false if argv0.start_with?(".", "-") || argv0.end_with?("-")
49
45
 
50
46
  hostnameok = true
51
47
  characters = argv0.upcase.split("")
@@ -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