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
@@ -4,34 +4,7 @@ module Sisimai
4
4
  require 'date'
5
5
 
6
6
  class << self
7
- BASE_D = 86400 # 1 day = 86400 sec
8
- BASE_Y = 365.2425 # 1 year = 365.2425 days
9
- BASE_L = 29.53059 # 1 lunar month = 29.53059 days
10
-
11
- CONST_P = 4 * Math.atan2(1, 1) # PI, 3.1415926535
12
- CONST_E = Math.exp(1) # e, Napier's constant
13
- TZ_OFFSET = 54000 # Max time zone offset, 54000 seconds
14
-
15
- TimeUnit = {
16
- 'o' => (BASE_D * BASE_Y * 4), # Olympiad, 4 years
17
- 'y' => (BASE_D * BASE_Y), # Year, Gregorian Calendar
18
- 'q' => (BASE_D * BASE_Y / 4), # Quarter, year/4
19
- 'l' => (BASE_D * BASE_L), # Lunar month
20
- 'f' => (BASE_D * 14), # Fortnight, 2 weeks
21
- 'w' => (BASE_D * 7), # Week, 604800 seconds
22
- 'd' => BASE_D, # Day
23
- 'h' => 3600, # Hour
24
- 'b' => 86.4, # Beat, Swatch internet time: 1000b = 1d
25
- 'm' => 60, # Minute,
26
- 's' => 1, # Second
27
- }.freeze
28
-
29
- MathematicalConstant = {
30
- 'e' => CONST_E,
31
- 'p' => CONST_P,
32
- 'g' => CONST_E**CONST_P,
33
- }.freeze
34
-
7
+ TZ_OFFSET = 54000 # Max time zone offset, 54000 seconds
35
8
  MonthName = {
36
9
  full: %w[January February March April May June July August September October November December],
37
10
  abbr: %w[Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec],
@@ -191,12 +164,9 @@ module Sisimai
191
164
  # parse("2015-11-03T23:34:45 Tue") #=> Tue, 3 Nov 2015 23:34:45 +0900
192
165
  # parse("Tue, Nov 3 2015 2:2:2") #=> Tue, 3 Nov 2015 02:02:02 +0900
193
166
  def parse(argv1)
194
- return nil unless argv1.is_a?(::String)
195
- return nil if argv1.empty?
167
+ return "" if argv1.is_a?(::String) == false || argv1.empty?
196
168
 
197
- datestring = argv1
198
- datestring.sub!(/[,](\d+)/, ', \1') # Thu,13 -> Thu, 13
199
- datestring.sub!(/(\d{1,2}),/, '\1') # Apr,29 -> Apr 29
169
+ datestring = argv1.sub(/[,](\d+)/, ', \1').sub(/(\d{1,2}),/, '\1')
200
170
  timetokens = datestring.split(' ')
201
171
  afternoon1 = 0 # (Integer) After noon flag
202
172
  altervalue = {} # (Hash) To store alternative values
@@ -267,7 +237,7 @@ module Sisimai
267
237
 
268
238
  elsif p =~ /\A[(]?[A-Z]{2,5}[)]?\z/
269
239
  # Timezone abbreviation; JST, GMT, UTC, ...
270
- v[:z] ||= abbr2tz(p) || '+0000'
240
+ v[:z] ||= abbr2tz(p); v[:z] = "+0000" if v[:z].empty?
271
241
  else
272
242
  # Other date format
273
243
  if cr = p.match(%r|\A(\d{4})[-/](\d{1,2})[-/](\d{1,2})\z|)
@@ -333,13 +303,9 @@ module Sisimai
333
303
  if v.value?(nil)
334
304
  # Strange date format
335
305
  warn sprintf(' ***warning: Strange date format [%s]', datestring)
336
- return nil
337
- end
338
-
339
- if v[:Y].to_i < 1902 || v[:Y].to_i > 2037
340
- # -(2^31) ~ (2^31)
341
- return nil
306
+ return ""
342
307
  end
308
+ return "" if v[:Y].to_i < 1902 || v[:Y].to_i > 2037 # -(2^31) ~ (2^31)
343
309
 
344
310
  # Build date string
345
311
  # Thu, 29 Apr 2004 10:01:11 +0900
@@ -348,23 +314,23 @@ module Sisimai
348
314
 
349
315
  # Abbreviation -> Tiemzone
350
316
  # @param [String] argv1 Abbr. e.g.) JST, GMT, PDT
351
- # @return [String, Nil] +0900, +0000, -0600 or nil if the argument is invalid format or
352
- # not supported abbreviation
317
+ # @return [String] +0900, +0000, -0600 or an empty string if the argument is invalid
318
+ # format or not supported abbreviation
353
319
  # @example Get the timezone string of "JST"
354
320
  # abbr2tz('JST') #=> '+0900'
355
321
  def abbr2tz(argv1)
356
- return nil unless argv1.is_a?(::String)
357
- return TimeZones[argv1]
322
+ return "" if argv1.is_a?(::String) == false
323
+ return TimeZones[argv1] || ""
358
324
  end
359
325
 
360
326
  # Convert to second
361
327
  # @param [String] argv1 Timezone string e.g) +0900
362
- # @return [Integer, Nil] n: seconds or nil it the argument is invalid format string
328
+ # @return [Integer] n: seconds or 0 when the argument is invalid format
363
329
  # @see second2tz
364
330
  # @example Convert '+0900' to seconds
365
331
  # tz2second('+0900') #=> 32400
366
332
  def tz2second(argv1)
367
- return nil unless argv1.is_a?(::String)
333
+ return 0 if argv1.is_a?(::String) == false
368
334
  ztime = 0
369
335
 
370
336
  if cr = argv1.match(/\A([-+])(\d)(\d)(\d{2})\z/)
@@ -378,13 +344,13 @@ module Sisimai
378
344
  ztime += (digit[:'minutes'] * 60)
379
345
  ztime *= -1 if digit[:'operator'] == '-'
380
346
 
381
- return nil if ztime.abs > TZ_OFFSET
347
+ return 0 if ztime.abs > TZ_OFFSET
382
348
  return ztime
383
349
 
384
350
  elsif argv1 =~ /\A[A-Za-z]+\z/
385
351
  return tz2second(TimeZones[argv1])
386
352
  else
387
- return nil
353
+ return 0
388
354
  end
389
355
  end
390
356
 
@@ -396,9 +362,9 @@ module Sisimai
396
362
  # second2tz(12345) #=> '+0325'
397
363
  def second2tz(argv1)
398
364
  return '+0000' unless argv1.is_a?(::Integer)
399
- return nil if argv1.abs > TZ_OFFSET # UTC+14 + 1(DST?)
365
+ return "" if argv1.abs > TZ_OFFSET # UTC+14 + 1(DST?)
400
366
 
401
- digit = { :operator => '+' }
367
+ digit = {:operator => '+'}
402
368
  digit[:operator] = '-' if argv1 < 0
403
369
  digit[:hours] = (argv1.abs / 3600).to_i
404
370
  digit[:minutes] = ((argv1.abs % 3600) / 60).to_i
@@ -6,10 +6,10 @@ module Sisimai
6
6
  class << self
7
7
  # Serializer (JSON)
8
8
  # @param [Sisimai::Fact] argvs Object
9
- # @return [String, nil] Dumped data or nil if the argument is missing
9
+ # @return [String] Dumped data or an empty string if the argument is missing
10
10
  def dump(argvs)
11
- return nil unless argvs
12
- return nil unless argvs.is_a? Sisimai::Fact
11
+ return "" unless argvs
12
+ return "" unless argvs.is_a? Sisimai::Fact
13
13
 
14
14
  if RUBY_PLATFORM.start_with?('java')
15
15
  # java-based ruby environment like JRuby.
@@ -17,7 +17,7 @@ module Sisimai
17
17
  require 'jrjackson'
18
18
  jsonstring = JrJackson::Json.dump(argvs.damn)
19
19
  rescue StandardError => ce
20
- warn '***warning: Failed to JrJackson::Json.dump: ' << ce.to_s
20
+ warn '***warning: Failed to JrJackson::Json.dump: ' + ce.to_s
21
21
  end
22
22
  else
23
23
  # MRI
@@ -25,7 +25,7 @@ module Sisimai
25
25
  require 'oj'
26
26
  jsonstring = Oj.dump(argvs.damn, :mode => :compat)
27
27
  rescue StandardError => ce
28
- warn '***warning: Failed to Oj.dump: ' << ce.to_s
28
+ warn '***warning: Failed to Oj.dump: ' + ce.to_s
29
29
  end
30
30
  end
31
31
 
@@ -10,8 +10,8 @@ module Sisimai
10
10
  # @param [Sisimai::Fact] argvs Object
11
11
  # @return [String, nil] Dumped data or nil if the argument is missing
12
12
  def dump(argvs)
13
- return nil unless argvs
14
- return nil unless argvs.is_a? Sisimai::Fact
13
+ return "" unless argvs
14
+ return "" unless argvs.is_a? Sisimai::Fact
15
15
 
16
16
  damneddata = argvs.damn
17
17
  yamlstring = nil
@@ -19,7 +19,7 @@ module Sisimai
19
19
  begin
20
20
  yamlstring = ::YAML.dump(damneddata)
21
21
  rescue StandardError => ce
22
- warn '***warning: Failed to YAML.dump: ' << ce.to_s
22
+ warn '***warning: Failed to YAML.dump: ' + ce.to_s
23
23
  end
24
24
 
25
25
  return yamlstring
data/lib/sisimai/fact.rb CHANGED
@@ -48,7 +48,7 @@ module Sisimai
48
48
 
49
49
  RetryIndex = Sisimai::Reason.retry
50
50
  RFC822Head = Sisimai::RFC5322.HEADERTABLE
51
- ActionList = { delayed: 1, delivered: 1, expanded: 1, failed: 1, relayed: 1 };
51
+ ActionList = {delayed: 1, delivered: 1, expanded: 1, failed: 1, relayed: 1};
52
52
 
53
53
  if RUBY_PLATFORM.start_with?('java')
54
54
  # [WORKAROUND] #159 #267 JRuby seems to fail and throws exception at strptime(), but this
@@ -108,7 +108,7 @@ module Sisimai
108
108
  return nil unless argvs.is_a? Hash
109
109
 
110
110
  email = argvs[:data]; return nil unless email
111
- args1 = { data: email, hook: argvs[:hook] }
111
+ args1 = {data: email, hook: argvs[:hook]}
112
112
  mesg1 = Sisimai::Message.rise(**args1)
113
113
  return nil unless mesg1
114
114
  return nil unless mesg1['ds']
@@ -179,7 +179,7 @@ module Sisimai
179
179
  datevalues << mesg1['header']['date'] if datevalues.size < 2
180
180
  while v = datevalues.shift do
181
181
  # Parse each date value in the array
182
- datestring = Sisimai::DateTime.parse(v) || next
182
+ datestring = Sisimai::DateTime.parse(v); next if datestring.empty?
183
183
 
184
184
  if cv = datestring.match(/\A(.+)[ ]+([-+]\d{4})\z/)
185
185
  # Get the value of timezone offset from datestring: Wed, 26 Feb 2014 06:05:48 -0500
@@ -195,7 +195,7 @@ module Sisimai
195
195
  t = TimeModule.strptime(datestring, '%a, %d %b %Y %T')
196
196
  piece['timestamp'] = (t.to_time.to_i - zoneoffset) || nil
197
197
  rescue
198
- warn ' ***warning: Failed to strptime ' << datestring.to_s
198
+ warn " ***warning: Failed to strptime #{datestring.to_s}"
199
199
  end
200
200
  next unless piece['timestamp']
201
201
 
@@ -402,8 +402,7 @@ module Sisimai
402
402
  next unless er.include?(' for ')
403
403
 
404
404
  af = Sisimai::RFC5322.received(er)
405
- next if af.empty?
406
- next if af[5].empty?
405
+ next if af.empty? || af[5].empty?
407
406
  next unless Sisimai::Address.is_emailaddress(af[5])
408
407
  next if thing['recipient'].address == af[5]
409
408
 
@@ -434,14 +433,14 @@ module Sisimai
434
433
  thing['replycode'] = '' unless thing['reason'] == 'delivered'
435
434
  else
436
435
  # The reason is not "delivered", or "feedback", or "vacation"
437
- smtperrors = piece['deliverystatus'] + ' ' << piece['diagnosticcode']
436
+ smtperrors = "#{piece['deliverystatus']} #{piece['diagnosticcode']}"
438
437
  smtperrors = '' if smtperrors.size < 4
439
438
  thing['hardbounce'] = Sisimai::SMTP::Failure.is_hardbounce(thing['reason'], smtperrors)
440
439
  end
441
440
 
442
441
  # DELIVERYSTATUS: Set a pseudo status code if the value of "deliverystatus" is empty
443
442
  if thing['deliverystatus'].empty?
444
- smtperrors = piece['replycode'] + ' ' << piece['diagnosticcode']
443
+ smtperrors = "#{piece['replycode']} #{piece['diagnosticcode']}"
445
444
  smtperrors = '' if smtperrors.size < 4
446
445
  permanent0 = Sisimai::SMTP::Failure.is_permanent(smtperrors)
447
446
  temporary0 = Sisimai::SMTP::Failure.is_temporary(smtperrors)
@@ -459,7 +458,7 @@ module Sisimai
459
458
 
460
459
  unless ActionList.has_key?(thing['action'])
461
460
  # There is an action value which is not described at RFC1894
462
- if ox = Sisimai::RFC1894.field('Action: ' << thing['action'])
461
+ if ox = Sisimai::RFC1894.field("Action: #{thing['action']}")
463
462
  # Rewrite the value of "Action:" field to the valid value
464
463
  #
465
464
  # The syntax for the action-field is:
@@ -473,6 +472,16 @@ module Sisimai
473
472
  thing["action"] = "delayed" if thing["action"].empty? && thing["reason"] == "expired"
474
473
  thing["action"] = "failed" if thing["action"].empty? && cx[0] == "4" || cx[0] == "5"
475
474
 
475
+ if thing["replycode"].size > 0
476
+ # Fill empty values: ["SMTP Command", "DSN", "Reason"]
477
+ cv = Sisimai::SMTP::Reply.associatedwith(thing["replycode"])
478
+ if cv.size > 0
479
+ thing["command"] = cv[0] if cv[0] != "" && thing["command"].empty?
480
+ thing["deliverystatus"] = cv[1] if cv[1] != "" && Sisimai::SMTP::Status.is_explicit(thing["deliverystatus"]) == false
481
+ thing["reason"] = cv[2] if cv[2] != "" && Sisimai::Reason.is_explicit(thing["reason"]) == false
482
+ end
483
+ end
484
+
476
485
  # Feedback-ID: 1.us-west-2.QHuyeCQrGtIIMGKQfVdUhP9hCQR2LglVOrRamBc+Prk=:AmazonSES
477
486
  thing["feedbackid"] = rfc822data["feedback-id"] || ""
478
487
 
@@ -499,7 +508,7 @@ module Sisimai
499
508
  v['recipient'] = self.recipient.address
500
509
  v['timestamp'] = self.timestamp.to_time.to_i
501
510
 
502
- # Backward compatibility until v5.2.0
511
+ # Backward compatibility until v5.5.0
503
512
  v["smtpagent"] = self.decodedby
504
513
  v["smtpcommand"] = self.command
505
514
  data = v
@@ -516,12 +525,12 @@ module Sisimai
516
525
  # [Nil] The value of the first argument is neither "json" nor "yaml"
517
526
  def dump(type = 'json')
518
527
  return nil unless %w[json yaml].include?(type)
519
- referclass = 'Sisimai::Fact::' << type.upcase
528
+ referclass = "Sisimai::Fact::#{type.upcase}"
520
529
 
521
530
  begin
522
531
  require referclass.downcase.gsub('::', '/')
523
532
  rescue
524
- warn '***warning: Failed to load' << referclass
533
+ warn "***warning: Failed to load #{referclass}"
525
534
  end
526
535
 
527
536
  dumpeddata = Module.const_get(referclass).dump(self)
data/lib/sisimai/lda.rb CHANGED
@@ -68,9 +68,9 @@ module Sisimai
68
68
  # @param [Sisimai::Fact] argvs Decoded email object
69
69
  # @return [String] Bounce reason
70
70
  def find(argvs)
71
- return nil if argvs.nil?
72
- return "" if argvs["diagnosticcode"].empty?
73
- return "" if argvs["command"] != "" && argvs["command"] != "DATA"
71
+ return "" if argvs.nil?
72
+ return "" if argvs["diagnosticcode"].empty?
73
+ return "" if argvs["command"] != "" && argvs["command"] != "DATA"
74
74
 
75
75
  deliversby = "" # [String] Local Delivery Agent name
76
76
  reasontext = "" # [String] Error reason
@@ -8,7 +8,7 @@ module Sisimai::Lhost
8
8
 
9
9
  Indicators = Sisimai::Lhost.INDICATORS
10
10
  Boundaries = ['Content-Type: message/rfc822'].freeze
11
- StartingOf = { message: [' ----- The following addresses had permanent fatal errors -----'] }.freeze
11
+ StartingOf = {message: [' ----- The following addresses had permanent fatal errors -----']}.freeze
12
12
 
13
13
  # @abstract decodes the bounce message from QUALITIA Active!hunter
14
14
  # @param [Hash] mhead Message headers of a bounce email
@@ -20,12 +20,11 @@ module Sisimai::Lhost
20
20
  # :subject => %r/FAILURE NOTICE :/,
21
21
  return nil unless mhead['x-ahmailid']
22
22
 
23
- dscontents = [Sisimai::Lhost.DELIVERYSTATUS]
23
+ dscontents = [Sisimai::Lhost.DELIVERYSTATUS]; v = nil
24
24
  emailparts = Sisimai::RFC5322.part(mbody, Boundaries)
25
25
  bodyslices = emailparts[0].split("\n")
26
26
  readcursor = 0 # (Integer) Points the current cursor position
27
27
  recipients = 0 # (Integer) The number of 'Final-Recipient' header
28
- v = nil
29
28
 
30
29
  while e = bodyslices.shift do
31
30
  # Read error messages and delivery status lines from the head of the email to the previous
@@ -35,8 +34,7 @@ module Sisimai::Lhost
35
34
  readcursor |= Indicators[:deliverystatus] if e == StartingOf[:message][0]
36
35
  next
37
36
  end
38
- next if (readcursor & Indicators[:deliverystatus]) == 0
39
- next if e.empty?
37
+ next if (readcursor & Indicators[:deliverystatus]) == 0 || e.empty?
40
38
 
41
39
  # ----- The following addresses had permanent fatal errors -----
42
40
  #
@@ -68,7 +66,7 @@ module Sisimai::Lhost
68
66
  return nil unless recipients > 0
69
67
 
70
68
  dscontents.each { |e| e['diagnosis'] = Sisimai::String.sweep(e['diagnosis']) }
71
- return { 'ds' => dscontents, 'rfc822' => emailparts[1] }
69
+ return {"ds" => dscontents, "rfc822" => emailparts[1]}
72
70
  end
73
71
  def description; return 'TransWARE Active!hunter'; end
74
72
  end
@@ -111,7 +111,7 @@ module Sisimai::Lhost
111
111
  end
112
112
  rescue StandardError => ce
113
113
  # Something wrong in decoding JSON
114
- warn ' ***warning: Failed to decode JSON: ' << ce.to_s
114
+ warn " ***warning: Failed to decode JSON: #{ce.to_s}"
115
115
  return nil
116
116
  end
117
117
  return nil if jsonobject.has_key?("notificationType") == false
@@ -121,10 +121,9 @@ module Sisimai::Lhost
121
121
  require "sisimai/smtp/reply"
122
122
  require "sisimai/smtp/status"
123
123
  require "sisimai/smtp/command"
124
- dscontents = [Sisimai::Lhost.DELIVERYSTATUS]
124
+ dscontents = [Sisimai::Lhost.DELIVERYSTATUS]; v = dscontents[-1]
125
125
  recipients = 0 # (Integer) The number of 'Final-Recipient' header
126
126
  whatnotify = jsonobject["notificationType"][0, 1] || ""
127
- v = dscontents[-1]
128
127
 
129
128
  if whatnotify == "B"
130
129
  # "notificationType":"Bounce"
@@ -207,13 +206,13 @@ module Sisimai::Lhost
207
206
 
208
207
  cv = ""
209
208
  jsonobject["mail"]["headers"].each do |e|
210
- cv << sprintf("%s: %s\n", e["name"], e["value"])
209
+ cv += sprintf("%s: %s\n", e["name"], e["value"])
211
210
  end
212
211
  %w[date subject].each do |e|
213
212
  next if jsonobject["mail"]["commonHeaders"].has_key?(e) == false
214
- cv << sprintf("%s: %s\n", e.capitalize, jsonobject["mail"]["commonHeaders"][e])
213
+ cv += sprintf("%s: %s\n", e.capitalize, jsonobject["mail"]["commonHeaders"][e])
215
214
  end
216
- return { "ds" => dscontents, "rfc822" => cv }
215
+ return {"ds" => dscontents, "rfc822" => cv}
217
216
  end
218
217
  def description; return 'Amazon SES(Sending): https://aws.amazon.com/ses/'; end
219
218
  end
@@ -26,13 +26,12 @@ module Sisimai::Lhost
26
26
  match += 1 if mhead["received"].any? { |a| a.include?("JAMES SMTP Server") }
27
27
  return nil unless match > 0
28
28
 
29
- dscontents = [Sisimai::Lhost.DELIVERYSTATUS]
29
+ dscontents = [Sisimai::Lhost.DELIVERYSTATUS]; v = dscontents[-1]
30
30
  emailparts = Sisimai::RFC5322.part(mbody, Boundaries)
31
31
  bodyslices = emailparts[0].split("\n")
32
32
  readcursor = 0 # Points the current cursor position
33
33
  recipients = 0 # The number of 'Final-Recipient' header
34
34
  alternates = ["", "", "", ""] # [Envelope-From, Header-From, Date, Subject]
35
- v = dscontents[-1]
36
35
 
37
36
  while e = bodyslices.shift do
38
37
  # Read error messages and delivery status lines from the head of the email to the previous
@@ -45,11 +44,10 @@ module Sisimai::Lhost
45
44
  readcursor |= Indicators[:deliverystatus]
46
45
  next
47
46
  end
48
- v["diagnosis"] << e << " " if e != ""
47
+ v["diagnosis"] += "#{e }" if e != ""
49
48
  next
50
49
  end
51
- next if (readcursor & Indicators[:deliverystatus]) == 0
52
- next if e.empty?
50
+ next if (readcursor & Indicators[:deliverystatus]) == 0 || e.empty?
53
51
 
54
52
  # Message details:
55
53
  # Subject: Nyaaan
@@ -93,16 +91,16 @@ module Sisimai::Lhost
93
91
 
94
92
  if emailparts[1].empty?
95
93
  # The original message is empty
96
- emailparts[1] << sprintf("From: %s\n", alternates[1]) if alternates[1] != ""
97
- emailparts[1] << sprintf("Date: %s\n", alternates[2]) if alternates[2] != ""
94
+ emailparts[1] += sprintf("From: %s\n", alternates[1]) if alternates[1] != ""
95
+ emailparts[1] += sprintf("Date: %s\n", alternates[2]) if alternates[2] != ""
98
96
  end
99
97
  if emailparts[1].include?("Return-Path: ") == false
100
98
  # Set the envelope from address as a Return-Path: header
101
- emailparts[1] << sprintf("Return-Path: <%s>\n", alternates[0]) if alternates[0] != ""
99
+ emailparts[1] += sprintf("Return-Path: <%s>\n", alternates[0]) if alternates[0] != ""
102
100
  end
103
101
  if emailparts[1].include?("\nSubject: ") == false
104
102
  # Set the envelope from address as a Return-Path: header
105
- emailparts[1] << sprintf("Subject: %s\n", alternates[3]) if alternates[3] != ""
103
+ emailparts[1] += sprintf("Subject: %s\n", alternates[3]) if alternates[3] != ""
106
104
  end
107
105
 
108
106
  dscontents.each { |e| e["diagnosis"] = Sisimai::String.sweep(e["diagnosis"]) }
@@ -26,12 +26,11 @@ module Sisimai::Lhost
26
26
  return nil unless %w[biglobe inacatv tmtv ttv].any? { |a| mhead['from'].include?('@' + a + '.ne.jp') }
27
27
  return nil unless mhead['subject'].start_with?('Returned mail:')
28
28
 
29
- dscontents = [Sisimai::Lhost.DELIVERYSTATUS]
29
+ dscontents = [Sisimai::Lhost.DELIVERYSTATUS]; v = nil
30
30
  emailparts = Sisimai::RFC5322.part(mbody, Boundaries)
31
31
  bodyslices = emailparts[0].split("\n")
32
32
  readcursor = 0 # (Integer) Points the current cursor position
33
33
  recipients = 0 # (Integer) The number of 'Final-Recipient' header
34
- v = nil
35
34
 
36
35
  while e = bodyslices.shift do
37
36
  # Read error messages and delivery status lines from the head of the email to the previous
@@ -41,8 +40,7 @@ module Sisimai::Lhost
41
40
  readcursor |= Indicators[:deliverystatus] if e == StartingOf[:message][0]
42
41
  next
43
42
  end
44
- next if (readcursor & Indicators[:deliverystatus]) == 0
45
- next if e.empty?
43
+ next if (readcursor & Indicators[:deliverystatus]) == 0 || e.empty?
46
44
 
47
45
  # This is a MIME-encapsulated message.
48
46
  #
@@ -74,7 +72,7 @@ module Sisimai::Lhost
74
72
  recipients += 1
75
73
  else
76
74
  next if e.include?('--')
77
- v['diagnosis'] << e + ' '
75
+ v['diagnosis'] += "#{e }"
78
76
  end
79
77
  end
80
78
  return nil unless recipients > 0
@@ -44,14 +44,13 @@ module Sisimai::Lhost
44
44
  fieldtable = Sisimai::RFC1894.FIELDTABLE
45
45
  permessage = {} # (Hash) Store values of each Per-Message field
46
46
 
47
- dscontents = [Sisimai::Lhost.DELIVERYSTATUS]
47
+ dscontents = [Sisimai::Lhost.DELIVERYSTATUS]; v = nil
48
48
  emailparts = Sisimai::RFC5322.part(mbody, Boundaries)
49
49
  bodyslices = emailparts[0].split("\n")
50
50
  readslices = ['']
51
51
  readcursor = 0 # (Integer) Points the current cursor position
52
52
  recipients = 0 # (Integer) The number of 'Final-Recipient' header
53
53
  thecommand = '' # (String) SMTP Command name begin with the string '>>>'
54
- v = nil
55
54
 
56
55
  while e = bodyslices.shift do
57
56
  # Read error messages and delivery status lines from the head of the email to the previous
@@ -65,8 +64,7 @@ module Sisimai::Lhost
65
64
  next
66
65
  end
67
66
  end
68
- next if (readcursor & Indicators[:deliverystatus]) == 0
69
- next if e.empty?
67
+ next if (readcursor & Indicators[:deliverystatus]) == 0 || e.empty?
70
68
 
71
69
  f = Sisimai::RFC1894.match(e)
72
70
  if f > 0
@@ -118,8 +116,8 @@ module Sisimai::Lhost
118
116
  # Continued line of the value of Diagnostic-Code field
119
117
  next unless readslices[-2].start_with?('Diagnostic-Code:')
120
118
  next unless e.start_with?(' ')
121
- v['diagnosis'] << ' ' << Sisimai::String.sweep(e)
122
- readslices[-1] = 'Diagnostic-Code: ' << e
119
+ v['diagnosis'] += " #{Sisimai::String.sweep(e)}"
120
+ readslices[-1] = "Diagnostic-Code: #{e}"
123
121
  end
124
122
  end
125
123
  end
@@ -7,7 +7,7 @@ module Sisimai::Lhost
7
7
 
8
8
  Indicators = Sisimai::Lhost.INDICATORS
9
9
  Boundaries = ['Content-Type: message/rfc822'].freeze
10
- StartingOf = { message: ['Your message'] }.freeze
10
+ StartingOf = {message: ['Your message']}.freeze
11
11
  MessagesOf = {
12
12
  "filtered" => ["Cannot route mail to user"],
13
13
  "systemerror" => ["Several matches found in Domino Directory"],
@@ -33,13 +33,12 @@ module Sisimai::Lhost
33
33
  fieldtable = Sisimai::RFC1894.FIELDTABLE
34
34
  permessage = {} # (Hash) Store values of each Per-Message field
35
35
 
36
- dscontents = [Sisimai::Lhost.DELIVERYSTATUS]
36
+ dscontents = [Sisimai::Lhost.DELIVERYSTATUS]; v = nil
37
37
  emailparts = Sisimai::RFC5322.part(mbody, Boundaries)
38
38
  bodyslices = emailparts[0].split("\n")
39
39
  readcursor = 0 # (Integer) Points the current cursor position
40
40
  recipients = 0 # (Integer) The number of 'Final-Recipient' header
41
41
  subjecttxt = '' # (String) The value of Subject:
42
- v = nil
43
42
 
44
43
  while e = bodyslices.shift do
45
44
  # Read error messages and delivery status lines from the head of the email to the previous
@@ -135,9 +134,9 @@ module Sisimai::Lhost
135
134
  end
136
135
 
137
136
  # Set the value of subjecttxt as a Subject if there is no original message in the bounce mail.
138
- emailparts[1] << ('Subject: ' << subjecttxt << "\n") unless emailparts[1].include?("\nSubject:")
137
+ emailparts[1] += "Subject: #{subjecttxt}\n" unless emailparts[1].include?("\nSubject:")
139
138
 
140
- return { 'ds' => dscontents, 'rfc822' => emailparts[1] }
139
+ return {"ds" => dscontents, "rfc822" => emailparts[1]}
141
140
  end
142
141
  def description; return 'IBM Domino Server'; end
143
142
  end
@@ -41,12 +41,11 @@ module Sisimai::Lhost
41
41
  return nil unless mhead['subject'].start_with?('Mail delivery failed')
42
42
  return nil unless mhead['received'].any? { |a| a.include?(' (DragonFly Mail Agent') }
43
43
 
44
- dscontents = [Sisimai::Lhost.DELIVERYSTATUS]
44
+ dscontents = [Sisimai::Lhost.DELIVERYSTATUS]; v = nil
45
45
  emailparts = Sisimai::RFC5322.part(mbody, Boundaries)
46
46
  bodyslices = emailparts[0].split("\n")
47
47
  readcursor = 0 # (Integer) Points the current cursor position
48
48
  recipients = 0 # (Integer) The number of 'Final-Recipient' header
49
- v = nil
50
49
 
51
50
  while e = bodyslices.shift do
52
51
  # Read error messages and delivery status lines from the head of the email to the previous
@@ -56,8 +55,7 @@ module Sisimai::Lhost
56
55
  readcursor |= Indicators[:deliverystatus] if e.start_with?(StartingOf[:message][0])
57
56
  next
58
57
  end
59
- next if (readcursor & Indicators[:deliverystatus]) == 0
60
- next if e.empty?
58
+ next if (readcursor & Indicators[:deliverystatus]) == 0 || e.empty?
61
59
 
62
60
  # This is the DragonFly Mail Agent v0.13 at df.example.jp.
63
61
  #
@@ -81,7 +79,7 @@ module Sisimai::Lhost
81
79
  recipients += 1
82
80
  else
83
81
  # Pick the error message
84
- v['diagnosis'] << ' ' << e
82
+ v['diagnosis'] += " #{e}"
85
83
 
86
84
  # Pick the remote hostname, and the SMTP command
87
85
  # net.c:500| snprintf(errmsg, sizeof(errmsg), "%s [%s] did not like our %s:\n%s",
@@ -11,7 +11,7 @@ module Sisimai::Lhost
11
11
  message: ['This message was created automatically by mail delivery software'],
12
12
  error: ['For the following reason:'],
13
13
  }.freeze
14
- MessagesOf = { 'mesgtoobig' => ['Mail size limit exceeded'] }.freeze
14
+ MessagesOf = {'mesgtoobig' => ['Mail size limit exceeded']}.freeze
15
15
 
16
16
  # @abstract Decode the bounce message from 1&1
17
17
  # @param [Hash] mhead Message headers of a bounce email
@@ -22,12 +22,11 @@ module Sisimai::Lhost
22
22
  return nil unless mhead['from'].start_with?('"Mail Delivery System"')
23
23
  return nil unless mhead['subject'] == 'Mail delivery failed: returning message to sender'
24
24
 
25
- dscontents = [Sisimai::Lhost.DELIVERYSTATUS]
25
+ dscontents = [Sisimai::Lhost.DELIVERYSTATUS]; v = nil
26
26
  emailparts = Sisimai::RFC5322.part(mbody, Boundaries)
27
27
  bodyslices = emailparts[0].split("\n")
28
28
  readcursor = 0 # (Integer) Points the current cursor position
29
29
  recipients = 0 # (Integer) The number of 'Final-Recipient' header
30
- v = nil
31
30
 
32
31
  while e = bodyslices.shift do
33
32
  # Read error messages and delivery status lines from the head of the email to the previous
@@ -38,8 +37,7 @@ module Sisimai::Lhost
38
37
  readcursor |= Indicators[:deliverystatus] if e.start_with?(StartingOf[:message][0])
39
38
  next
40
39
  end
41
- next if (readcursor & Indicators[:deliverystatus]) == 0
42
- next if e.empty?
40
+ next if (readcursor & Indicators[:deliverystatus]) == 0 || e.empty?
43
41
 
44
42
  # The following address failed:
45
43
  #
@@ -68,13 +66,13 @@ module Sisimai::Lhost
68
66
  else
69
67
  if v['diagnosis']
70
68
  # Get error message and append error message strings
71
- v['diagnosis'] << ' ' << e
69
+ v['diagnosis'] += " #{e}"
72
70
  else
73
71
  # OR the following format:
74
72
  # neko@example.fr:
75
73
  # SMTP error from remote server for TEXT command, host: ...
76
74
  v['alterrors'] ||= ''
77
- v['alterrors'] << ' ' << e
75
+ v['alterrors'] += " #{e}"
78
76
  end
79
77
  end
80
78
  end
@@ -110,7 +108,7 @@ module Sisimai::Lhost
110
108
  end
111
109
  end
112
110
 
113
- return { 'ds' => dscontents, 'rfc822' => emailparts[1] }
111
+ return {"ds" => dscontents, "rfc822" => emailparts[1]}
114
112
  end
115
113
  def description; return '1&1: https://www.1und1.de'; end
116
114
  end