sisimai 5.4.0 → 5.5.0

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 (147) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/codecovio.yml +1 -1
  3. data/.github/workflows/rake-test.yml +1 -1
  4. data/ChangeLog.md +45 -0
  5. data/Makefile +2 -4
  6. data/README-JA.md +23 -20
  7. data/README.md +23 -20
  8. data/lib/sisimai/address.rb +92 -44
  9. data/lib/sisimai/arf.rb +7 -8
  10. data/lib/sisimai/datetime.rb +2 -2
  11. data/lib/sisimai/fact/json.rb +1 -2
  12. data/lib/sisimai/fact/yaml.rb +1 -2
  13. data/lib/sisimai/fact.rb +60 -35
  14. data/lib/sisimai/lda.rb +2 -2
  15. data/lib/sisimai/lhost/activehunter.rb +4 -5
  16. data/lib/sisimai/lhost/amazonses.rb +3 -4
  17. data/lib/sisimai/lhost/apachejames.rb +2 -2
  18. data/lib/sisimai/lhost/biglobe.rb +6 -6
  19. data/lib/sisimai/lhost/courier.rb +7 -7
  20. data/lib/sisimai/lhost/domino.rb +6 -6
  21. data/lib/sisimai/lhost/dragonfly.rb +5 -5
  22. data/lib/sisimai/lhost/einsundeins.rb +4 -4
  23. data/lib/sisimai/lhost/exchange2003.rb +7 -7
  24. data/lib/sisimai/lhost/exchange2007.rb +3 -3
  25. data/lib/sisimai/lhost/exim.rb +7 -7
  26. data/lib/sisimai/lhost/ezweb.rb +3 -2
  27. data/lib/sisimai/lhost/fml.rb +9 -9
  28. data/lib/sisimai/lhost/gmail.rb +9 -9
  29. data/lib/sisimai/lhost/gmx.rb +3 -3
  30. data/lib/sisimai/lhost/googlegroups.rb +6 -7
  31. data/lib/sisimai/lhost/googleworkspace.rb +5 -6
  32. data/lib/sisimai/lhost/imailserver.rb +4 -4
  33. data/lib/sisimai/lhost/kddi.rb +4 -4
  34. data/lib/sisimai/lhost/mailfoundry.rb +3 -3
  35. data/lib/sisimai/lhost/{mailmarshalsmtp.rb → mailmarshal.rb} +5 -5
  36. data/lib/sisimai/lhost/messagingserver.rb +8 -8
  37. data/lib/sisimai/lhost/mfilter.rb +8 -4
  38. data/lib/sisimai/lhost/mimecast.rb +105 -0
  39. data/lib/sisimai/lhost/notes.rb +5 -5
  40. data/lib/sisimai/lhost/opensmtpd.rb +5 -5
  41. data/lib/sisimai/lhost/postfix.rb +38 -32
  42. data/lib/sisimai/lhost/qmail.rb +6 -6
  43. data/lib/sisimai/lhost/sendmail.rb +13 -13
  44. data/lib/sisimai/lhost/{interscanmss.rb → trendmicro.rb} +8 -9
  45. data/lib/sisimai/lhost/v5sendmail.rb +7 -7
  46. data/lib/sisimai/lhost/verizon.rb +3 -3
  47. data/lib/sisimai/lhost/x1.rb +7 -4
  48. data/lib/sisimai/lhost/x2.rb +40 -12
  49. data/lib/sisimai/lhost/x3.rb +3 -3
  50. data/lib/sisimai/lhost/x6.rb +2 -2
  51. data/lib/sisimai/lhost/zoho.rb +5 -5
  52. data/lib/sisimai/lhost.rb +18 -17
  53. data/lib/sisimai/mail/maildir.rb +4 -4
  54. data/lib/sisimai/mail/mbox.rb +4 -4
  55. data/lib/sisimai/mail/memory.rb +1 -1
  56. data/lib/sisimai/mail/stdin.rb +2 -2
  57. data/lib/sisimai/message.rb +34 -34
  58. data/lib/sisimai/order.rb +5 -4
  59. data/lib/sisimai/reason/authfailure.rb +1 -1
  60. data/lib/sisimai/reason/badreputation.rb +1 -1
  61. data/lib/sisimai/reason/blocked.rb +2 -1
  62. data/lib/sisimai/reason/contenterror.rb +1 -2
  63. data/lib/sisimai/reason/exceedlimit.rb +1 -1
  64. data/lib/sisimai/reason/expired.rb +1 -1
  65. data/lib/sisimai/reason/failedstarttls.rb +1 -1
  66. data/lib/sisimai/reason/filtered.rb +1 -1
  67. data/lib/sisimai/reason/hasmoved.rb +1 -1
  68. data/lib/sisimai/reason/hostunknown.rb +2 -2
  69. data/lib/sisimai/reason/mailboxfull.rb +1 -1
  70. data/lib/sisimai/reason/mailererror.rb +1 -1
  71. data/lib/sisimai/reason/mesgtoobig.rb +1 -1
  72. data/lib/sisimai/reason/networkerror.rb +1 -1
  73. data/lib/sisimai/reason/norelaying.rb +2 -2
  74. data/lib/sisimai/reason/notaccept.rb +2 -3
  75. data/lib/sisimai/reason/notcompliantrfc.rb +1 -1
  76. data/lib/sisimai/reason/policyviolation.rb +2 -4
  77. data/lib/sisimai/reason/rejected.rb +5 -3
  78. data/lib/sisimai/reason/requireptr.rb +1 -1
  79. data/lib/sisimai/reason/securityerror.rb +1 -1
  80. data/lib/sisimai/reason/spamdetected.rb +1 -1
  81. data/lib/sisimai/reason/speeding.rb +1 -1
  82. data/lib/sisimai/reason/suspend.rb +2 -1
  83. data/lib/sisimai/reason/systemerror.rb +1 -1
  84. data/lib/sisimai/reason/systemfull.rb +1 -1
  85. data/lib/sisimai/reason/toomanyconn.rb +1 -1
  86. data/lib/sisimai/reason/userunknown.rb +4 -3
  87. data/lib/sisimai/reason/vacation.rb +1 -1
  88. data/lib/sisimai/reason/virusdetected.rb +1 -1
  89. data/lib/sisimai/reason.rb +12 -12
  90. data/lib/sisimai/rfc1123.rb +58 -18
  91. data/lib/sisimai/rfc1894.rb +6 -8
  92. data/lib/sisimai/rfc2045.rb +25 -13
  93. data/lib/sisimai/rfc3464/thirdparty.rb +2 -3
  94. data/lib/sisimai/rfc3464.rb +6 -6
  95. data/lib/sisimai/rfc3834.rb +18 -8
  96. data/lib/sisimai/rfc5322.rb +9 -9
  97. data/lib/sisimai/rfc791.rb +2 -2
  98. data/lib/sisimai/rhost/aol.rb +4 -1
  99. data/lib/sisimai/rhost/apple.rb +11 -7
  100. data/lib/sisimai/rhost/cox.rb +9 -5
  101. data/lib/sisimai/rhost/facebook.rb +9 -3
  102. data/lib/sisimai/rhost/franceptt.rb +86 -37
  103. data/lib/sisimai/rhost/godaddy.rb +10 -1
  104. data/lib/sisimai/rhost/google.rb +26 -22
  105. data/lib/sisimai/rhost/gsuite.rb +1 -1
  106. data/lib/sisimai/rhost/kddi.rb +1 -1
  107. data/lib/sisimai/rhost/messagelabs.rb +160 -2
  108. data/lib/sisimai/rhost/microsoft.rb +80 -29
  109. data/lib/sisimai/rhost/mimecast.rb +30 -21
  110. data/lib/sisimai/rhost/nttdocomo.rb +69 -89
  111. data/lib/sisimai/rhost/outlook.rb +1 -1
  112. data/lib/sisimai/rhost/spectrum.rb +1 -1
  113. data/lib/sisimai/rhost/tencent.rb +5 -4
  114. data/lib/sisimai/rhost/yahooinc.rb +2 -2
  115. data/lib/sisimai/rhost/zoho.rb +72 -0
  116. data/lib/sisimai/rhost.rb +5 -4
  117. data/lib/sisimai/smtp/command.rb +2 -2
  118. data/lib/sisimai/smtp/reply.rb +11 -4
  119. data/lib/sisimai/smtp/status.rb +17 -8
  120. data/lib/sisimai/smtp/transcript.rb +3 -3
  121. data/lib/sisimai/string.rb +6 -10
  122. data/lib/sisimai/version.rb +1 -1
  123. data/lib/sisimai.rb +1 -1
  124. data/set-of-emails/maildir/bsd/lhost-exim-56.eml +40 -40
  125. data/set-of-emails/maildir/bsd/lhost-mfilter-05.eml +41 -0
  126. data/set-of-emails/maildir/bsd/lhost-mimecast-01.eml +46 -0
  127. data/set-of-emails/maildir/bsd/lhost-mimecast-02.eml +59 -0
  128. data/set-of-emails/maildir/bsd/lhost-postfix-79.eml +81 -0
  129. data/set-of-emails/maildir/bsd/lhost-postfix-80.eml +84 -0
  130. data/set-of-emails/maildir/bsd/lhost-x1-03.eml +26 -0
  131. data/set-of-emails/maildir/bsd/lhost-x1-04.eml +45 -0
  132. data/set-of-emails/maildir/bsd/lhost-x2-07.eml +30 -0
  133. data/set-of-emails/maildir/bsd/rfc3464-66.eml +170 -0
  134. data/set-of-emails/maildir/bsd/rfc3834-06.eml +59 -0
  135. data/set-of-emails/maildir/bsd/rhost-apple-05.eml +96 -0
  136. data/set-of-emails/maildir/bsd/rhost-zoho-01.eml +88 -0
  137. data/set-of-emails/maildir/bsd/rhost-zoho-02.eml +86 -0
  138. data/set-of-emails/maildir/bsd/rhost-zoho-03.eml +87 -0
  139. data/set-of-emails/maildir/bsd/rhost-zoho-04.eml +86 -0
  140. data/set-of-emails/maildir/not/rb-issue-368-bug.eml +39 -0
  141. data/sisimai-java.gemspec +1 -1
  142. data/sisimai.gemspec +1 -1
  143. metadata +27 -9
  144. /data/set-of-emails/maildir/bsd/{lhost-mailmarshalsmtp-02.eml → lhost-mailmarshal-02.eml} +0 -0
  145. /data/set-of-emails/maildir/bsd/{lhost-interscanmss-01.eml → lhost-trendmicro-01.eml} +0 -0
  146. /data/set-of-emails/maildir/bsd/{lhost-interscanmss-02.eml → lhost-trendmicro-02.eml} +0 -0
  147. /data/set-of-emails/maildir/bsd/{lhost-interscanmss-03.eml → lhost-trendmicro-03.eml} +0 -0
@@ -17,7 +17,7 @@ module Sisimai
17
17
  # @param [String] argv1 String to be matched with regular expressions
18
18
  # @return [Boolean] false: Did not match, true: Matched
19
19
  def match(argv1)
20
- return false unless argv1
20
+ return false if argv1.nil? || argv1.empty?
21
21
  return true if Index.any? { |a| argv1.include?(a) }
22
22
  return false
23
23
  end
@@ -35,7 +35,7 @@ module Sisimai
35
35
  # @return [Boolean] false: Did not match, true: Matched
36
36
  # @since v4.0.0
37
37
  def match(argv1)
38
- return false unless argv1
38
+ return false if argv1.nil? || argv1.empty?
39
39
  return true if Index.any? { |a| argv1.include?(a) }
40
40
  return true if Pairs.any? { |a| Sisimai::String.aligned(argv1, a) }
41
41
  return false
@@ -55,7 +55,7 @@ module Sisimai
55
55
  if Sisimai::SMTP::Status.name(statuscode) == 'hostunknown'
56
56
  # To prevent classifying DNS errors as "HostUnknown"
57
57
  require 'sisimai/reason/networkerror'
58
- return true unless Sisimai::Reason::NetworkError.match(issuedcode)
58
+ return true if Sisimai::Reason::NetworkError.match(issuedcode) == false
59
59
  else
60
60
  # Status: 5.1.2
61
61
  # Diagnostic-Code: SMTP; 550 Host unknown
@@ -66,7 +66,7 @@ module Sisimai
66
66
  # @param [String] argv1 String to be matched with regular expressions
67
67
  # @return [Boolean] false: Did not match, true: Matched
68
68
  def match(argv1)
69
- return false unless argv1
69
+ return false if argv1.nil? || argv1.empty?
70
70
  return true if Index.any? { |a| argv1.include?(a) }
71
71
  return false
72
72
  end
@@ -30,7 +30,7 @@ module Sisimai
30
30
  # @param [String] argv1 String to be matched with regular expressions
31
31
  # @return [Boolean] false: Did not match, true: Matched
32
32
  def match(argv1)
33
- return false unless argv1
33
+ return false if argv1.nil? || argv1.empty?
34
34
  return true if Index.any? { |a| argv1.include?(a) }
35
35
  return false
36
36
  end
@@ -29,7 +29,7 @@ module Sisimai
29
29
  # @param [String] argv1 String to be matched with regular expressions
30
30
  # @return [Boolean] false: Did not match, true: Matched
31
31
  def match(argv1)
32
- return false unless argv1
32
+ return false if argv1.nil? || argv1.empty?
33
33
  return true if Index.any? { |a| argv1.include?(a) }
34
34
  return false
35
35
  end
@@ -36,7 +36,7 @@ module Sisimai
36
36
  # @param [String] argv1 String to be matched with regular expressions
37
37
  # @return [Boolean] false: Did not match, true: Matched
38
38
  def match(argv1)
39
- return false unless argv1
39
+ return false if argv1.nil? || argv1.empty?
40
40
  return true if Index.any? { |a| argv1.include?(a) }
41
41
  return false
42
42
  end
@@ -28,7 +28,7 @@ module Sisimai
28
28
  'specified domain is not allowed',
29
29
  "that domain isn't in my list of allowed rcpthost",
30
30
  'this system is not configured to relay mail',
31
- 'unable to relay for',
31
+ 'unable to relay ',
32
32
  "we don't handle mail for",
33
33
  ].freeze
34
34
 
@@ -39,7 +39,7 @@ module Sisimai
39
39
  # @param [String] argv1 String to be matched with regular expressions
40
40
  # @return [Boolean] false: Did not match, true: Matched
41
41
  def match(argv1)
42
- return false unless argv1
42
+ return false if argv1.nil? || argv1.empty?
43
43
  return true if Index.any? { |a| argv1.include?(a) }
44
44
  return false
45
45
  end
@@ -11,9 +11,8 @@ module Sisimai
11
11
  class << self
12
12
  # Destination mail server does not accept any message
13
13
  Index = [
14
- 'does not accept mail (nullmx)',
14
+ 'does not accept mail', # Sendmail
15
15
  'host/domain does not accept mail', # iCloud
16
- 'host does not accept mail', # Sendmail
17
16
  'mail receiving disabled',
18
17
  'name server: .: host not found', # Sendmail
19
18
  'no mx record found for domain=', # Oath(Yahoo!)
@@ -28,7 +27,7 @@ module Sisimai
28
27
  # @param [String] argv1 String to be matched with regular expressions
29
28
  # @return [Boolean] false: Did not match, true: Matched
30
29
  def match(argv1)
31
- return false unless argv1
30
+ return false if argv1.nil? || argv1.empty?
32
31
  return true if Index.any? { |a| argv1.include?(a) }
33
32
  return false
34
33
  end
@@ -25,7 +25,7 @@ module Sisimai
25
25
  # @param [String] argv1 String to be matched with regular expressions
26
26
  # @return [Boolean] false: Did not match, true: Matched
27
27
  def match(argv1)
28
- return false unless argv1
28
+ return false if argv1.nil? || argv1.empty?
29
29
  return true if Index.any? { |a| argv1.include?(a) }
30
30
  return true if Pairs.any? { |a| Sisimai::String.aligned(argv1, a) }
31
31
  return false
@@ -19,7 +19,6 @@ module Sisimai
19
19
  Index = [
20
20
  'an illegal attachment on your message',
21
21
  'because the recipient is not accepting mail with ', # AOL Phoenix
22
- 'by non-member to a members-only list',
23
22
  'closed mailing list',
24
23
  'denied by policy',
25
24
  'email not accepted for policy reasons',
@@ -35,9 +34,8 @@ module Sisimai
35
34
  'messages with multiple addresses',
36
35
  'rejected for policy reasons',
37
36
  'protocol violation',
38
- 'the email address used to send your message is not subscribed to this group',
39
37
  'the message was rejected by organization policy',
40
- 'this message was blocked because its content presents a potential',
38
+ 'this message was blocked because its content presents a potential', # https://support.google.com/mail/answer/6590
41
39
  'we do not accept messages containing images or other attachments',
42
40
  "you're using a mass mailer",
43
41
  ].freeze
@@ -53,7 +51,7 @@ module Sisimai
53
51
  # @return [Boolean] false: Did not match, true: Matched
54
52
  # @since 4.22.0
55
53
  def match(argv1)
56
- return false unless argv1
54
+ return false if argv1.nil? || argv1.empty?
57
55
  return true if Index.any? { |a| argv1.include?(a) }
58
56
  return true if Pairs.any? { |a| Sisimai::String.aligned(argv1, a) }
59
57
  return false
@@ -23,11 +23,12 @@ module Sisimai
23
23
  'access denied (sender blacklisted)',
24
24
  'address rejected',
25
25
  'administrative prohibition',
26
- 'batv failed to verify', # SoniWall
27
- 'batv validation failure', # SoniWall
26
+ 'batv failed to verify', # SonicWall
27
+ 'batv validation failure', # SonicWall
28
28
  'backscatter protection detected an invalid or expired email address', # MDaemon
29
29
  "because the sender isn't on the recipient's list of senders to accept mail from",
30
30
  'bogus mail from', # IMail - block empty sender
31
+ 'by non-member to a members-only list',
31
32
  "can't determine purported responsible address",
32
33
  'connections not accepted from servers without a valid sender domain',
33
34
  'denied [bouncedeny]', # McAfee
@@ -66,6 +67,7 @@ module Sisimai
66
67
  'sender was rejected', # qmail
67
68
  'spam reporting address', # SendGrid|a message to an address has previously been marked as Spam by the recipient.
68
69
  'syntax error: empty email address',
70
+ 'the email address used to send your message is not subscribed to this group',
69
71
  'the message has been rejected by batv defense',
70
72
  'this server does not accept mail from',
71
73
  'transaction failed unsigned dsn for',
@@ -83,7 +85,7 @@ module Sisimai
83
85
  # @param [String] argv1 String to be matched with regular expressions
84
86
  # @return [Boolean] false: Did not match, true: Matched
85
87
  def match(argv1)
86
- return false unless argv1
88
+ return false if argv1.nil? || argv1.empty?
87
89
  return false if IsNot.any? { |a| argv1.include?(a) }
88
90
  return true if Index.any? { |a| argv1.include?(a) }
89
91
  return false
@@ -44,7 +44,7 @@ module Sisimai
44
44
  # @param [String] argv1 String to be matched with regular expressions
45
45
  # @return [Boolean] false: Did not match, true: Matched
46
46
  def match(argv1)
47
- return false unless argv1
47
+ return false if argv1.nil? || argv1.empty?
48
48
  return true if Index.any? { |a| argv1.include?(a) }
49
49
  return true if Pairs.any? { |a| Sisimai::String.aligned(argv1, a) }
50
50
  return false
@@ -48,7 +48,7 @@ module Sisimai
48
48
  # @param [String] argv1 String to be matched with regular expressions
49
49
  # @return [Boolean] false: Did not match, true: Matched
50
50
  def match(argv1)
51
- return false unless argv1
51
+ return false if argv1.nil? || argv1.empty?
52
52
  return true if Index.any? { |a| argv1.include?(a) }
53
53
  return true if Pairs.any? { |a| Sisimai::String.aligned(argv1, a) }
54
54
  return false
@@ -122,7 +122,7 @@ module Sisimai
122
122
  # @param [String] argv1 String to be matched with regular expressions
123
123
  # @return [Boolean] false: Did not match, true: Matched
124
124
  def match(argv1)
125
- return false unless argv1
125
+ return false if argv1.nil? || argv1.empty?
126
126
  return true if Index.any? { |a| argv1.include?(a) }
127
127
  return true if Pairs.any? { |a| Sisimai::String.aligned(argv1, a) }
128
128
  return false
@@ -18,7 +18,7 @@ module Sisimai
18
18
  # @param [String] argv1 String to be matched with regular expressions
19
19
  # @return [Boolean] false: Did not match, true: Matched
20
20
  def match(argv1)
21
- return false unless argv1
21
+ return false if argv1.nil? || argv1.empty?
22
22
  return true if Index.any? { |a| argv1.include?(a) }
23
23
  return false
24
24
  end
@@ -22,6 +22,7 @@ module Sisimai
22
22
  'mailbox unavailable or access denied',
23
23
  'recipient rejected: temporarily inactive',
24
24
  'recipient suspend the service',
25
+ "the email account that you tried to reach is inactive",
25
26
  'this account has been disabled or discontinued',
26
27
  'this account has been temporarily suspended',
27
28
  'this address no longer accepts mail',
@@ -38,7 +39,7 @@ module Sisimai
38
39
  # @param [String] argv1 String to be matched with regular expressions
39
40
  # @return [Boolean] false: Did not match, true: Matched
40
41
  def match(argv1)
41
- return false unless argv1
42
+ return false if argv1.nil? || argv1.empty?
42
43
  return true if Index.any? { |a| argv1.include?(a) }
43
44
  return false
44
45
  end
@@ -45,7 +45,7 @@ module Sisimai
45
45
  # @param [String] argv1 String to be matched with regular expressions
46
46
  # @return [Boolean] false: Did not match, true: Matched
47
47
  def match(argv1)
48
- return false unless argv1
48
+ return false if argv1.nil? || argv1.empty?
49
49
  return true if Index.any? { |a| argv1.include?(a) }
50
50
  return true if Pairs.any? { |a| Sisimai::String.aligned(argv1, a) }
51
51
  return false
@@ -20,7 +20,7 @@ module Sisimai
20
20
  # @param [String] argv1 String to be matched with regular expressions
21
21
  # @return [Boolean] false: Did not match, true: Matched
22
22
  def match(argv1)
23
- return false unless argv1
23
+ return false if argv1.nil? || argv1.empty?
24
24
  return true if Index.any? { |a| argv1.include?(a) }
25
25
  return false
26
26
  end
@@ -34,7 +34,7 @@ module Sisimai
34
34
  # @param [String] argv1 String to be matched with regular expressions
35
35
  # @return [Boolean] false: Did not match, true: Matched
36
36
  def match(argv1)
37
- return false unless argv1
37
+ return false if argv1.nil? || argv1.empty?
38
38
  return true if Index.any? { |a| argv1.include?(a) }
39
39
  return false
40
40
  end
@@ -112,6 +112,7 @@ module Sisimai
112
112
  'user unknown',
113
113
  'utilisateur inconnu !',
114
114
  'vdeliver: invalid or unknown virtual user',
115
+ 'weil die adresse nicht gefunden wurde oder keine e-mails empfangen kann',
115
116
  'your envelope recipient is in my badrcptto list',
116
117
  ].freeze
117
118
  Pairs = [
@@ -147,7 +148,7 @@ module Sisimai
147
148
  # @param [String] argv1 String to be matched with regular expressions
148
149
  # @return [Boolean] false: Did not match, true: Matched
149
150
  def match(argv1)
150
- return false unless argv1
151
+ return false if argv1.nil? || argv1.empty?
151
152
  return true if Index.any? { |a| argv1.include?(a) }
152
153
  return true if Pairs.any? { |a| Sisimai::String.aligned(argv1, a) }
153
154
  return false
@@ -183,12 +184,12 @@ module Sisimai
183
184
  next
184
185
  end
185
186
 
186
- next unless r.match(issuedcode)
187
+ next if r.match(issuedcode) == false
187
188
  # Match with reason defined in Sisimai::Reason::* except UserUnknown.
188
189
  matchother = true
189
190
  break
190
191
  end
191
- return true unless matchother # Did not match with other message patterns
192
+ return true if matchother == false # Did not match with other message patterns
192
193
 
193
194
  elsif argvs['command'] == 'RCPT'
194
195
  # When the SMTP command is not "RCPT", the session rejected by other
@@ -18,7 +18,7 @@ module Sisimai
18
18
  # @param [String] argv1 String to be matched with regular expressions
19
19
  # @return [Boolean] false: Did not match, true: Matched
20
20
  def match(argv1)
21
- return false unless argv1
21
+ return false if argv1.nil? || argv1.empty?
22
22
  return true if Index.any? { |a| argv1.include?(a) }
23
23
  return false
24
24
  end
@@ -31,7 +31,7 @@ module Sisimai
31
31
  # @return [Boolean] false: Did not match, true: Matched
32
32
  # @since 4.22.0
33
33
  def match(argv1)
34
- return false unless argv1
34
+ return false if argv1.nil? || argv1.empty?
35
35
  return true if Index.any? { |a| argv1.include?(a) }
36
36
  return false
37
37
  end
@@ -68,11 +68,11 @@ module Sisimai
68
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 "" unless argvs
72
- unless GetRetried[argvs['reason']]
71
+ return "" if argvs.nil?
72
+ if GetRetried[argvs['reason']].nil?
73
73
  # Return reason text already decided except reason match with the regular expression of
74
74
  # retry() method.
75
- return argvs['reason'] unless argvs['reason'].empty?
75
+ return argvs['reason'] if argvs['reason'].empty? == false
76
76
  end
77
77
  return 'delivered' if argvs['deliverystatus'].start_with?('2.')
78
78
 
@@ -94,7 +94,7 @@ module Sisimai
94
94
  warn " ***warning: Failed to load #{p}"
95
95
  next
96
96
  end
97
- next unless r.true(argvs)
97
+ next if r.true(argvs) == false
98
98
  reasontext = r.text
99
99
  break
100
100
  end
@@ -113,7 +113,7 @@ module Sisimai
113
113
  # Try to match with message patterns in Sisimai::Reason::Vacation
114
114
  require 'sisimai/reason/vacation'
115
115
  reasontext = 'vacation' if Sisimai::Reason::Vacation.match(issuedcode.downcase)
116
- reasontext ||= 'onhold' unless issuedcode.empty?
116
+ reasontext ||= 'onhold' if issuedcode.empty? == false
117
117
  reasontext ||= 'undefined'
118
118
  end
119
119
  end
@@ -125,7 +125,7 @@ module Sisimai
125
125
  # @return [String, Nil] Bounce reason or nli if the argument is missing or not Hash
126
126
  # @see find()
127
127
  def anotherone(argvs)
128
- return argvs['reason'] unless argvs['reason'].empty?
128
+ return argvs['reason'] if argvs['reason'].empty? == false
129
129
 
130
130
  require 'sisimai/smtp/status'
131
131
  issuedcode = argvs['diagnosticcode'].downcase || ''
@@ -150,11 +150,11 @@ module Sisimai
150
150
  next
151
151
  end
152
152
 
153
- next unless r.match(issuedcode)
153
+ next if r.match(issuedcode) == false
154
154
  reasontext = e.downcase
155
155
  break
156
156
  end
157
- break unless reasontext.empty?
157
+ break if reasontext.empty? == false
158
158
 
159
159
  # Check the value of Status:
160
160
  code2digit = statuscode[0, 3] || ''
@@ -175,7 +175,7 @@ module Sisimai
175
175
  require 'sisimai/reason/syntaxerror'
176
176
  reasontext = 'syntaxerror' if Sisimai::Reason::SyntaxError.true(argvs)
177
177
  end
178
- break unless reasontext.empty?
178
+ break if reasontext.empty? == false
179
179
 
180
180
  # Check the value of Action: field, first
181
181
  if actiontext.start_with?('delayed', 'expired')
@@ -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 "" unless argv1
198
+ return "" if argv1.nil?
199
199
 
200
200
  reasontext = ''
201
201
  issuedcode = argv1.downcase
@@ -214,11 +214,11 @@ module Sisimai
214
214
  next
215
215
  end
216
216
 
217
- next unless r.match(issuedcode)
217
+ next if r.match(issuedcode) == false
218
218
  reasontext = r.text
219
219
  break
220
220
  end
221
- return reasontext unless reasontext.empty?
221
+ return reasontext if reasontext.empty? == false
222
222
 
223
223
  if issuedcode.upcase.include?('X-UNIX; ')
224
224
  # X-Unix; ...
@@ -3,6 +3,7 @@ module Sisimai
3
3
  module RFC1123
4
4
  class << self
5
5
  require 'sisimai/string'
6
+ require 'sisimai/rfc791'
6
7
  Sandwiched = [
7
8
  # (Postfix) postfix/src/smtp/smtp_proto.c: "host %s said: %s (in reply to %s)",
8
9
  # - <kijitora@example.com>: host re2.example.com[198.51.100.2] said: 550 ...
@@ -37,32 +38,72 @@ module Sisimai
37
38
  # @return [Boolean] false: is not a valid internet host, true: is a valid interneet host
38
39
  # @since v5.2.0
39
40
  def is_internethost(argv0 = '')
40
- return false unless argv0
41
- return false if argv0.size < 4 || argv0.size > 255
41
+ return false if argv0.nil? || argv0.size < 4 || argv0.size > 255
42
+ return true if argv0 == 'localhost' || argv0 == 'localhost6'
42
43
  return false if argv0.include?(".") == false
43
44
  return false if argv0.include?("..") || argv0.include?("--")
44
45
  return false if argv0.start_with?(".", "-") || argv0.end_with?("-")
45
46
 
46
- hostnameok = true
47
47
  characters = argv0.upcase.split("")
48
48
  characters.each do |e|
49
49
  # Check each characater is a number or an alphabet
50
50
  f = e.ord
51
- if f < 45 then hostnameok = false; break; end # 45 = '-'
52
- if f == 47 then hostnameok = false; break; end # 47 = '/'
53
- if f > 57 && f < 65 then hostnameok = false; break; end # 57 = '9', 65 = 'A'
54
- if f > 90 then hostnameok = false; break; end # 90 = 'Z'
51
+ return false if f < 45 # 45 = '-'
52
+ return false if f == 47 # 47 = '/'
53
+ return false if f > 57 && f < 65 # 57 = '9', 65 = 'A'
54
+ return false if f > 90 # 90 = 'Z'
55
55
  end
56
- return false if hostnameok == false
57
56
 
58
57
  p1 = argv0.rindex(".")
59
58
  cv = argv0[p1 + 1, argv0.size - p1]; return false if cv.size > 63
60
59
  cv.split("").each do |e|
61
60
  # The top level domain should not include a number
62
- f = e.ord
63
- if f > 47 && f < 58 then hostnameok = false; break; end
61
+ f = e.ord; return false if f > 47 && f < 58
64
62
  end
65
- return hostnameok
63
+ return true
64
+ end
65
+
66
+ # returns true if the domain part is [IPv4:...] or [IPv6:...].
67
+ # @param [String] email Email address.
68
+ # @return [Boolean] false: the domain part is not a domain literal.
69
+ # true: the domain part is a domain literal.
70
+ def is_domainliteral(email)
71
+ return false if email.is_a?(::String) == false
72
+
73
+ email = email.delete_prefix('<').delete_suffix('>')
74
+ return false if email.size < 16 # e@[IPv4:0.0.0.0] is 16 characters
75
+ return false if email[-1, 1] != ']'
76
+
77
+ lastb = email.rindex('@[IPv'); return false if lastb == false
78
+ dpart = email.split('@')[-1]
79
+
80
+ if email.include?('@[IPv4:')
81
+ # neko@[IPv4:192.0.2.25]
82
+ ipv4a = email[lastb + 7, 16].delete_suffix(']')
83
+ return Sisimai::RFC791.is_ipv4address(ipv4a)
84
+
85
+ elsif email.include?('@[IPv6:')
86
+ # neko@[IPv6:2001:0DB8:0000:0000:0000:0000:0000:0001]
87
+ # neko@[IPv6:2001:0DB8:0000:0000:0000:0000:0000:0001]
88
+ # IPv6-address-literal = "IPv6:" IPv6-addr
89
+ # IPv6-addr = IPv6-full / IPv6-comp / IPv6v4-full / IPv6v4-comp
90
+ # IPv6-hex = 1*4HEXDIG
91
+ # IPv6-full = IPv6-hex 7(":" IPv6-hex)
92
+ # IPv6-comp = [IPv6-hex *5(":" IPv6-hex)] "::"
93
+ # [IPv6-hex *5(":" IPv6-hex)]
94
+ # ; The "::" represents at least 2 16-bit groups of
95
+ # ; zeros. No more than 6 groups in addition to the
96
+ # ; "::" may be present.
97
+ # IPv6v4-full = IPv6-hex 5(":" IPv6-hex) ":" IPv4-address-literal
98
+ # IPv6v4-comp = [IPv6-hex *3(":" IPv6-hex)] "::"
99
+ # [IPv6-hex *3(":" IPv6-hex) ":"]
100
+ # IPv4-address-literal
101
+ # ; The "::" represents at least 2 16-bit groups of
102
+ # ; zeros. No more than 4 groups in addition to the
103
+ # ; "::" and IPv4-address-literal may be present.
104
+ return true if dpart.size > 2 && dpart.rindex(':') > 7
105
+ end
106
+ return false
66
107
  end
67
108
 
68
109
  # find() returns a valid internet hostname found from the argument
@@ -70,8 +111,7 @@ module Sisimai
70
111
  # @return string A valid internet hostname found in the argument
71
112
  # @since v5.2.0
72
113
  def find(argv1 = "")
73
- return "" unless argv1
74
- return "" unless argv1.size > 4
114
+ return "" if argv1.nil? || argv1.size < 5
75
115
 
76
116
  sourcetext = argv1.downcase
77
117
  sourcelist = []
@@ -91,7 +131,7 @@ module Sisimai
91
131
  Sandwiched.each do |e|
92
132
  # Check a hostname exists between the $e->[0] and $e->[1] at array "Sandwiched"
93
133
  # Each array in Sandwiched have 2 elements
94
- next unless Sisimai::String.aligned(sourcetext, e)
134
+ next if Sisimai::String.aligned(sourcetext, e) == false
95
135
 
96
136
  p1 = sourcetext.index(e[0])
97
137
  p2 = sourcetext.index(e[1])
@@ -112,7 +152,7 @@ module Sisimai
112
152
  end
113
153
  ExistUntil.each do |e|
114
154
  # ExistUntil have some strings, not an array
115
- p1 = sourcetext.index(e); next unless p1
155
+ p1 = sourcetext.index(e); next if p1.nil?
116
156
  sourcelist = sourcetext[0, p1].split(" ")
117
157
  throw :MAKELIST
118
158
  end
@@ -126,9 +166,9 @@ module Sisimai
126
166
  e.chop! if e[-1, 1] == "." # Remove "." at the end of the string
127
167
  e.delete!('[]()<>:;')
128
168
 
129
- next unless e.size > 3
130
- next unless e.include?(".")
131
- next unless Sisimai::RFC1123.is_internethost(e)
169
+ next if e.size < 4
170
+ next if e.include?(".") == false
171
+ next if Sisimai::RFC1123.is_internethost(e) == false
132
172
  foundtoken << e
133
173
  end
134
174
  return "" if foundtoken.size == 0
@@ -59,7 +59,7 @@ module Sisimai
59
59
 
60
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' => 'failed', 'failure' => 'failed'}
63
63
  FieldGroup = {
64
64
  'original-recipient' => 'addr',
65
65
  'final-recipient' => 'addr',
@@ -114,16 +114,16 @@ module Sisimai
114
114
 
115
115
  FieldNames[0].each_key do |e|
116
116
  # Per-Message fields
117
- next unless label == e
118
- next unless argv0.include?(FieldNames[0][label])
117
+ next if label != e
118
+ next if argv0.include?(FieldNames[0][label]) == false
119
119
  match = 1; break
120
120
  end
121
121
  return match if match > 0
122
122
 
123
123
  FieldNames[1].each_key do |e|
124
124
  # Per-Recipient field
125
- next unless label == e
126
- next unless argv0.include?(FieldNames[1][label])
125
+ next if label != e
126
+ next if argv0.include?(FieldNames[1][label]) == false
127
127
  match = 2; break
128
128
  end
129
129
  return match
@@ -147,9 +147,7 @@ module Sisimai
147
147
  label = Sisimai::RFC1894.label(argv0)
148
148
  group = FieldGroup[label] || ''
149
149
  parts = argv0.split(":", 2); parts[1] = parts[1].nil? ? "" : Sisimai::String.sweep(parts[1])
150
-
151
- return nil if group.empty?
152
- return nil unless CapturesOn[group]
150
+ return nil if group.empty? || CapturesOn[group].nil?
153
151
 
154
152
  # Try to match with each pattern of Per-Message field, Per-Recipient field
155
153
  # - 0: Field-Name