sisimai 5.1.0-java → 5.2.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 (192) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/rake-test.yml +1 -1
  3. data/ChangeLog.md +102 -0
  4. data/Makefile +4 -2
  5. data/README-JA.md +23 -16
  6. data/README.md +22 -15
  7. data/lib/sisimai/arf.rb +121 -210
  8. data/lib/sisimai/fact.rb +208 -158
  9. data/lib/sisimai/lda.rb +98 -0
  10. data/lib/sisimai/lhost/activehunter.rb +1 -1
  11. data/lib/sisimai/lhost/amazonses.rb +185 -301
  12. data/lib/sisimai/lhost/apachejames.rb +48 -51
  13. data/lib/sisimai/lhost/biglobe.rb +1 -2
  14. data/lib/sisimai/lhost/courier.rb +10 -8
  15. data/lib/sisimai/lhost/domino.rb +25 -25
  16. data/lib/sisimai/lhost/dragonfly.rb +3 -4
  17. data/lib/sisimai/lhost/einsundeins.rb +3 -4
  18. data/lib/sisimai/lhost/exchange2003.rb +6 -8
  19. data/lib/sisimai/lhost/exchange2007.rb +111 -101
  20. data/lib/sisimai/lhost/exim.rb +232 -242
  21. data/lib/sisimai/lhost/ezweb.rb +43 -51
  22. data/lib/sisimai/lhost/fml.rb +2 -3
  23. data/lib/sisimai/lhost/gmail.rb +32 -28
  24. data/lib/sisimai/lhost/gmx.rb +4 -16
  25. data/lib/sisimai/lhost/googlegroups.rb +9 -8
  26. data/lib/sisimai/lhost/googleworkspace.rb +94 -0
  27. data/lib/sisimai/lhost/imailserver.rb +7 -16
  28. data/lib/sisimai/lhost/interscanmss.rb +1 -1
  29. data/lib/sisimai/lhost/kddi.rb +3 -4
  30. data/lib/sisimai/lhost/mailfoundry.rb +2 -5
  31. data/lib/sisimai/lhost/mailmarshalsmtp.rb +1 -2
  32. data/lib/sisimai/lhost/messagingserver.rb +14 -13
  33. data/lib/sisimai/lhost/mfilter.rb +4 -3
  34. data/lib/sisimai/lhost/notes.rb +2 -4
  35. data/lib/sisimai/lhost/opensmtpd.rb +2 -2
  36. data/lib/sisimai/lhost/postfix.rb +25 -27
  37. data/lib/sisimai/lhost/qmail.rb +130 -106
  38. data/lib/sisimai/lhost/sendmail.rb +19 -18
  39. data/lib/sisimai/lhost/v5sendmail.rb +88 -60
  40. data/lib/sisimai/lhost/verizon.rb +2 -2
  41. data/lib/sisimai/lhost/x1.rb +1 -1
  42. data/lib/sisimai/lhost/x2.rb +1 -2
  43. data/lib/sisimai/lhost/x3.rb +2 -2
  44. data/lib/sisimai/lhost/x6.rb +1 -1
  45. data/lib/sisimai/lhost/zoho.rb +2 -2
  46. data/lib/sisimai/lhost.rb +18 -21
  47. data/lib/sisimai/message.rb +93 -146
  48. data/lib/sisimai/order.rb +21 -77
  49. data/lib/sisimai/reason/authfailure.rb +1 -4
  50. data/lib/sisimai/reason/badreputation.rb +2 -2
  51. data/lib/sisimai/reason/blocked.rb +7 -10
  52. data/lib/sisimai/reason/contenterror.rb +7 -1
  53. data/lib/sisimai/reason/exceedlimit.rb +1 -4
  54. data/lib/sisimai/reason/failedstarttls.rb +42 -0
  55. data/lib/sisimai/reason/filtered.rb +5 -4
  56. data/lib/sisimai/reason/hasmoved.rb +1 -2
  57. data/lib/sisimai/reason/hostunknown.rb +3 -3
  58. data/lib/sisimai/reason/mailboxfull.rb +2 -4
  59. data/lib/sisimai/reason/mailererror.rb +1 -2
  60. data/lib/sisimai/reason/mesgtoobig.rb +2 -4
  61. data/lib/sisimai/reason/norelaying.rb +2 -3
  62. data/lib/sisimai/reason/notaccept.rb +2 -3
  63. data/lib/sisimai/reason/notcompliantrfc.rb +10 -4
  64. data/lib/sisimai/reason/rejected.rb +1 -1
  65. data/lib/sisimai/reason/requireptr.rb +2 -2
  66. data/lib/sisimai/reason/securityerror.rb +1 -3
  67. data/lib/sisimai/reason/spamdetected.rb +6 -8
  68. data/lib/sisimai/reason/speeding.rb +1 -2
  69. data/lib/sisimai/reason/suppressed.rb +36 -0
  70. data/lib/sisimai/reason/suspend.rb +1 -3
  71. data/lib/sisimai/reason/systemerror.rb +5 -0
  72. data/lib/sisimai/reason/toomanyconn.rb +1 -2
  73. data/lib/sisimai/reason/userunknown.rb +1 -1
  74. data/lib/sisimai/reason/virusdetected.rb +5 -6
  75. data/lib/sisimai/reason.rb +77 -73
  76. data/lib/sisimai/rfc1123.rb +152 -0
  77. data/lib/sisimai/rfc1894.rb +102 -62
  78. data/lib/sisimai/rfc2045.rb +2 -1
  79. data/lib/sisimai/rfc3464/thirdparty.rb +102 -0
  80. data/lib/sisimai/rfc3464.rb +222 -343
  81. data/lib/sisimai/rfc3834.rb +1 -1
  82. data/lib/sisimai/rfc5322.rb +7 -17
  83. data/lib/sisimai/rfc791.rb +69 -0
  84. data/lib/sisimai/rhost/aol.rb +36 -0
  85. data/lib/sisimai/rhost/apple.rb +5 -2
  86. data/lib/sisimai/rhost/cox.rb +3 -2
  87. data/lib/sisimai/rhost/facebook.rb +100 -0
  88. data/lib/sisimai/rhost/franceptt.rb +3 -2
  89. data/lib/sisimai/rhost/godaddy.rb +3 -2
  90. data/lib/sisimai/rhost/google.rb +19 -17
  91. data/lib/sisimai/rhost/gsuite.rb +42 -0
  92. data/lib/sisimai/rhost/iua.rb +3 -3
  93. data/lib/sisimai/rhost/kddi.rb +3 -2
  94. data/lib/sisimai/rhost/messagelabs.rb +37 -0
  95. data/lib/sisimai/rhost/microsoft.rb +56 -49
  96. data/lib/sisimai/rhost/mimecast.rb +29 -27
  97. data/lib/sisimai/rhost/nttdocomo.rb +4 -3
  98. data/lib/sisimai/rhost/outlook.rb +36 -0
  99. data/lib/sisimai/rhost/spectrum.rb +3 -2
  100. data/lib/sisimai/rhost/tencent.rb +3 -2
  101. data/lib/sisimai/rhost/yahooinc.rb +4 -3
  102. data/lib/sisimai/rhost.rb +69 -39
  103. data/lib/sisimai/smtp/command.rb +31 -21
  104. data/lib/sisimai/smtp/failure.rb +103 -0
  105. data/lib/sisimai/smtp/reply.rb +29 -25
  106. data/lib/sisimai/smtp/status.rb +36 -19
  107. data/lib/sisimai/smtp/transcript.rb +15 -15
  108. data/lib/sisimai/string.rb +0 -46
  109. data/lib/sisimai/version.rb +1 -1
  110. data/set-of-emails/maildir/bsd/lhost-postfix-30.eml +81 -81
  111. data/set-of-emails/maildir/bsd/{lhost-aol-03.eml → rhost-aol-03.eml} +1264 -1264
  112. data/set-of-emails/maildir/bsd/{lhost-aol-04.eml → rhost-aol-04.eml} +1260 -1260
  113. data/set-of-emails/maildir/bsd/{lhost-aol-05.eml → rhost-aol-05.eml} +105 -105
  114. data/set-of-emails/maildir/bsd/{lhost-aol-06.eml → rhost-aol-06.eml} +105 -105
  115. data/set-of-emails/maildir/bsd/rhost-gsuite-01.eml +189 -0
  116. data/set-of-emails/maildir/bsd/rhost-gsuite-02.eml +180 -0
  117. data/set-of-emails/maildir/bsd/rhost-gsuite-03.eml +251 -0
  118. data/set-of-emails/maildir/bsd/rhost-gsuite-04.eml +211 -0
  119. data/set-of-emails/maildir/bsd/rhost-gsuite-05.eml +226 -0
  120. data/set-of-emails/maildir/bsd/rhost-gsuite-06.eml +257 -0
  121. data/set-of-emails/maildir/bsd/rhost-gsuite-07.eml +289 -0
  122. data/set-of-emails/maildir/bsd/rhost-gsuite-08.eml +231 -0
  123. data/set-of-emails/maildir/bsd/rhost-gsuite-09.eml +231 -0
  124. data/set-of-emails/maildir/bsd/rhost-gsuite-10.eml +254 -0
  125. data/set-of-emails/maildir/bsd/rhost-gsuite-11.eml +228 -0
  126. data/set-of-emails/maildir/bsd/rhost-gsuite-12.eml +271 -0
  127. data/set-of-emails/maildir/bsd/rhost-gsuite-13.eml +261 -0
  128. data/set-of-emails/maildir/bsd/rhost-gsuite-14.eml +273 -0
  129. data/set-of-emails/maildir/bsd/rhost-gsuite-15.eml +229 -0
  130. data/set-of-emails/maildir/bsd/{lhost-messagelabs-01.eml → rhost-messagelabs-01.eml} +93 -93
  131. data/set-of-emails/maildir/bsd/rhost-outlook-01.eml +72 -0
  132. data/set-of-emails/maildir/bsd/rhost-outlook-02.eml +72 -0
  133. data/set-of-emails/maildir/bsd/rhost-outlook-03.eml +72 -0
  134. data/set-of-emails/maildir/bsd/rhost-outlook-04.eml +79 -0
  135. data/set-of-emails/maildir/bsd/rhost-outlook-06.eml +75 -0
  136. data/set-of-emails/maildir/bsd/rhost-outlook-07.eml +70 -0
  137. data/set-of-emails/maildir/bsd/rhost-outlook-08.eml +70 -0
  138. data/set-of-emails/maildir/bsd/rhost-outlook-09.eml +56 -0
  139. data/set-of-emails/maildir/tmp/arf-22.eml +49 -0
  140. data/set-of-emails/maildir/tmp/arf-23.eml +49 -0
  141. data/set-of-emails/maildir/tmp/arf-24.eml +50 -0
  142. data/set-of-emails/maildir/tmp/lhost-exim-07.eml +28 -0
  143. metadata +73 -56
  144. data/lib/sisimai/lhost/amavis.rb +0 -163
  145. data/lib/sisimai/lhost/amazonworkmail.rb +0 -127
  146. data/lib/sisimai/lhost/aol.rb +0 -125
  147. data/lib/sisimai/lhost/barracuda.rb +0 -92
  148. data/lib/sisimai/lhost/bigfoot.rb +0 -125
  149. data/lib/sisimai/lhost/facebook.rb +0 -188
  150. data/lib/sisimai/lhost/gsuite.rb +0 -194
  151. data/lib/sisimai/lhost/mailru.rb +0 -214
  152. data/lib/sisimai/lhost/mcafee.rb +0 -109
  153. data/lib/sisimai/lhost/messagelabs.rb +0 -120
  154. data/lib/sisimai/lhost/mxlogic.rb +0 -198
  155. data/lib/sisimai/lhost/office365.rb +0 -252
  156. data/lib/sisimai/lhost/outlook.rb +0 -129
  157. data/lib/sisimai/lhost/powermta.rb +0 -118
  158. data/lib/sisimai/lhost/receivingses.rb +0 -126
  159. data/lib/sisimai/lhost/sendgrid.rb +0 -150
  160. data/lib/sisimai/lhost/surfcontrol.rb +0 -105
  161. data/lib/sisimai/lhost/x4.rb +0 -269
  162. data/lib/sisimai/lhost/x5.rb +0 -112
  163. data/lib/sisimai/lhost/yahoo.rb +0 -102
  164. data/lib/sisimai/lhost/yandex.rb +0 -118
  165. data/lib/sisimai/mda.rb +0 -121
  166. data/lib/sisimai/smtp/error.rb +0 -119
  167. /data/set-of-emails/maildir/bsd/{lhost-googlegroups-15.eml → lhost-googleworkspace-01.eml} +0 -0
  168. /data/set-of-emails/maildir/bsd/{lhost-x4-08.eml → lhost-x2-06.eml} +0 -0
  169. /data/set-of-emails/maildir/bsd/{lhost-gsuite-01.eml → rfc3464-51.eml} +0 -0
  170. /data/set-of-emails/maildir/bsd/{lhost-gsuite-03.eml → rfc3464-52.eml} +0 -0
  171. /data/set-of-emails/maildir/bsd/{lhost-gsuite-04.eml → rfc3464-53.eml} +0 -0
  172. /data/set-of-emails/maildir/bsd/{lhost-gsuite-05.eml → rfc3464-54.eml} +0 -0
  173. /data/set-of-emails/maildir/bsd/{lhost-gsuite-06.eml → rfc3464-55.eml} +0 -0
  174. /data/set-of-emails/maildir/bsd/{lhost-gsuite-07.eml → rfc3464-56.eml} +0 -0
  175. /data/set-of-emails/maildir/bsd/{lhost-gsuite-08.eml → rfc3464-57.eml} +0 -0
  176. /data/set-of-emails/maildir/bsd/{lhost-gsuite-09.eml → rfc3464-58.eml} +0 -0
  177. /data/set-of-emails/maildir/bsd/{lhost-gsuite-10.eml → rfc3464-59.eml} +0 -0
  178. /data/set-of-emails/maildir/bsd/{lhost-gsuite-11.eml → rfc3464-60.eml} +0 -0
  179. /data/set-of-emails/maildir/bsd/{lhost-gsuite-12.eml → rfc3464-61.eml} +0 -0
  180. /data/set-of-emails/maildir/bsd/{lhost-gsuite-13.eml → rfc3464-62.eml} +0 -0
  181. /data/set-of-emails/maildir/bsd/{lhost-gsuite-14.eml → rfc3464-63.eml} +0 -0
  182. /data/set-of-emails/maildir/bsd/{lhost-gsuite-15.eml → rfc3464-64.eml} +0 -0
  183. /data/set-of-emails/maildir/bsd/{lhost-gsuite-02.eml → rfc3464-65.eml} +0 -0
  184. /data/set-of-emails/maildir/bsd/{lhost-aol-01.eml → rhost-aol-01.eml} +0 -0
  185. /data/set-of-emails/maildir/bsd/{lhost-aol-02.eml → rhost-aol-02.eml} +0 -0
  186. /data/set-of-emails/maildir/bsd/{lhost-facebook-03.eml → rhost-facebook-03.eml} +0 -0
  187. /data/set-of-emails/maildir/bsd/{lhost-facebook-04.eml → rhost-facebook-04.eml} +0 -0
  188. /data/set-of-emails/maildir/bsd/{lhost-messagelabs-02.eml → rhost-messagelabs-02.eml} +0 -0
  189. /data/set-of-emails/maildir/bsd/{lhost-messagelabs-03.eml → rhost-messagelabs-03.eml} +0 -0
  190. /data/set-of-emails/maildir/{bsd → tmp}/rfc3464-37.eml +0 -0
  191. /data/set-of-emails/maildir/{bsd → tmp}/rfc3464-38.eml +0 -0
  192. /data/set-of-emails/maildir/{bsd → tmp}/rfc3464-39.eml +0 -0
@@ -6,23 +6,23 @@ module Sisimai::Lhost
6
6
  require 'sisimai/lhost'
7
7
 
8
8
  Indicators = Sisimai::Lhost.INDICATORS
9
- Boundaries = ['--------------------------------------------------', 'Content-Type: message/rfc822'].freeze
10
- MarkingsOf = { message: ['The user(s) ', 'Your message ', 'Each of the following', '<'] }.freeze
11
- ReFailures = {
9
+ Boundaries = ["--------------------------------------------------", "Content-Type: message/rfc822"].freeze
10
+ StartingOf = { message: ['The user(s) ', 'Your message ', 'Each of the following', '<'] }.freeze
11
+ Messagesof = {
12
12
  # notaccept: ['The following recipients did not receive this message:'],
13
+ 'expired' => [
14
+ # Your message was not delivered within 0 days and 1 hours.
15
+ # Remote host is not responding.
16
+ 'Your message was not delivered within ',
17
+ ],
13
18
  'mailboxfull' => ['The user(s) account is temporarily over quota'],
19
+ 'onhold' => ['Each of the following recipients was rejected by a remote mail server'],
14
20
  'suspend' => [
15
21
  # http://www.naruhodo-au.kddi.com/qa3429203.html
16
22
  # The recipient may be unpaid user...?
17
23
  'The user(s) account is disabled.',
18
24
  'The user(s) account is temporarily limited.',
19
25
  ],
20
- 'expired' => [
21
- # Your message was not delivered within 0 days and 1 hours.
22
- # Remote host is not responding.
23
- 'Your message was not delivered within ',
24
- ],
25
- 'onhold' => ['Each of the following recipients was rejected by a remote mail server'],
26
26
  }.freeze
27
27
 
28
28
  # @abstract Decodes the bounce message from au EZweb
@@ -48,7 +48,7 @@ module Sisimai::Lhost
48
48
  bodyslices = emailparts[0].split("\n")
49
49
  readcursor = 0 # (Integer) Points the current cursor position
50
50
  recipients = 0 # (Integer) The number of 'Final-Recipient' header
51
- rxmessages = []; ReFailures.each_value { |a| rxmessages << a }
51
+ substrings = []; Messagesof.each_value { |a| substrings << a }; substrings.flatten!
52
52
  v = nil
53
53
 
54
54
  while e = bodyslices.shift do
@@ -56,7 +56,7 @@ module Sisimai::Lhost
56
56
  # line of the beginning of the original message.
57
57
  if readcursor == 0
58
58
  # Beginning of the bounce message or delivery status part
59
- readcursor |= Indicators[:deliverystatus] if MarkingsOf[:message].any? { |a| e.include?(a) }
59
+ readcursor |= Indicators[:deliverystatus] if StartingOf[:message].any? { |a| e.include?(a) }
60
60
  end
61
61
  next if (readcursor & Indicators[:deliverystatus]) == 0
62
62
  next if e.empty?
@@ -79,81 +79,73 @@ module Sisimai::Lhost
79
79
  p1 = e.index('<') || -1
80
80
  p2 = e.index('>') || -1
81
81
 
82
- if v['recipient']
82
+ if v["recipient"] != ""
83
83
  # There are multiple recipient addresses in the message body.
84
84
  dscontents << Sisimai::Lhost.DELIVERYSTATUS
85
85
  v = dscontents[-1]
86
86
  end
87
- v['recipient'] = Sisimai::Address.s3s4(e[p1, p2 - p1])
87
+ v["recipient"] = Sisimai::Address.s3s4(e[p1, p2 - p1])
88
+ v["diagnosis"] << " " << e
88
89
  recipients += 1
89
90
 
90
- elsif f = Sisimai::RFC1894.match(e)
91
+ elsif Sisimai::RFC1894.match(e) > 0
91
92
  # "e" matched with any field defined in RFC3464
92
93
  next unless o = Sisimai::RFC1894.field(e)
93
94
  next unless fieldtable[o[0]]
94
95
  v[fieldtable[o[0]]] = o[2]
95
96
 
96
97
  else
97
- # The line does not begin with a DSN field defined in RFC3464
98
+ # Other error messages
98
99
  next if Sisimai::String.is_8bit(e)
99
- if e.include?('>>> ')
100
+ if e.include?(" >>> ")
100
101
  # >>> RCPT TO:<******@ezweb.ne.jp>
101
- v['command'] = Sisimai::SMTP::Command.find(e) || ''
102
+ v["command"] = Sisimai::SMTP::Command.find(e)
103
+ v["diagnosis"] << " " << e
104
+
105
+ elsif e.include?(" <<< ")
106
+ # <<< 550 ...
107
+ v["diagnosis"] << " " << e
108
+
102
109
  else
103
110
  # Check error message
104
- if rxmessages.any? { |messages| messages.any? { |message| e.include?(message) } }
111
+ isincluded = false
112
+ if substrings.any? { |a| e.include?(a) }
105
113
  # Check with regular expressions of each error
106
- v['diagnosis'] ||= ''
107
- v['diagnosis'] << ' ' << e
108
- else
109
- # >>> 550
110
- v['alterrors'] ||= ''
111
- v['alterrors'] << ' ' << e
114
+ v["diagnosis"] << " " << e
115
+ isincluded = true
112
116
  end
117
+ v["diagnosis"] << " " << e if isincluded
113
118
  end
114
119
  end
115
120
  end
116
121
  return nil unless recipients > 0
117
122
 
118
123
  dscontents.each do |e|
119
- unless e['alterrors'].to_s.empty?
120
- # Copy alternative error message
121
- e['diagnosis'] ||= e['alterrors']
122
- if e['diagnosis'].start_with?('-') || e['diagnosis'].end_with?('__')
123
- # Override the value of diagnostic code message
124
- e['diagnosis'] = e['alterrors'] unless e['alterrors'].empty?
125
- end
126
- e.delete('alterrors')
127
- end
128
- e['diagnosis'] = Sisimai::String.sweep(e['diagnosis']) || ''
124
+ # Check each value of DeliveryMatter{}, try to detect the bounce reason.
125
+ e['diagnosis'] = Sisimai::String.sweep(e['diagnosis'])
126
+ e["command"] = Sisimai::SMTP::Command.find(e["diagnosis"]) if e["command"].empty?
129
127
 
130
128
  if mhead['x-spasign'].to_s == 'NG'
131
129
  # Content-Type: text/plain; ..., X-SPASIGN: NG (spamghetti, au by EZweb)
132
130
  # Filtered recipient returns message that include 'X-SPASIGN' header
133
131
  e['reason'] = 'filtered'
134
132
  else
135
- if e['command'] == 'RCPT'
136
- # set "userunknown" when the remote server rejected after RCPT command.
137
- e['reason'] = 'userunknown'
138
- else
139
- # SMTP command is not RCPT
140
- catch :SESSION do
141
- ReFailures.each_key do |r|
142
- # Try to match with each session error message
143
- ReFailures[r].each do |rr|
144
- # Check each error message pattern
145
- next unless e['diagnosis'].include?(rr)
146
- e['reason'] = r
147
- throw :SESSION
148
- end
133
+ # There is no X-SPASIGN header or the value of the header is not "NG"
134
+ catch :FINDREASON do
135
+ Messagesof.each_key do |r|
136
+ # Try to match with each session error message
137
+ Messagesof[r].each do |f|
138
+ # Check each error message pattern
139
+ next unless e['diagnosis'].include?(f)
140
+ e['reason'] = r
141
+ throw :FINDREASON
149
142
  end
150
143
  end
151
-
152
144
  end
153
145
  end
154
- next if e['reason']
146
+ next if e['reason'] != ""
155
147
  next if e['recipient'].end_with?('@ezweb.ne.jp', '@au.com')
156
- e['reason'] = 'userunknown'
148
+ e["reason"] = "userunknown" if e["diagnosis"].start_with?("<")
157
149
  end
158
150
 
159
151
  return { 'ds' => dscontents, 'rfc822' => emailparts[1] }
@@ -68,7 +68,7 @@ module Sisimai::Lhost
68
68
  p2 = e.rindex('>') || -1
69
69
  if p1 > 0 && p2 > 0
70
70
  # You are not a member of this mailing list <neko-nyaan@example.org>.
71
- if v['recipient']
71
+ if v["recipient"] != ""
72
72
  # There are multiple recipient addresses in the message body.
73
73
  dscontents << Sisimai::Lhost.DELIVERYSTATUS
74
74
  v = dscontents[-1]
@@ -79,7 +79,6 @@ module Sisimai::Lhost
79
79
  else
80
80
  # If you know the general guide of this list, please send mail with
81
81
  # the mail body
82
- v['diagnosis'] ||= ''
83
82
  v['diagnosis'] << e
84
83
  end
85
84
  end
@@ -98,7 +97,7 @@ module Sisimai::Lhost
98
97
  # Error messages in the message body did not matched
99
98
  ErrorTitle.each_key do |f|
100
99
  # Try to match with the Subject string
101
- next unless mhead['subject'] =~ ErrorTitle[f]
100
+ next unless ErrorTitle[f].any? { |a| mhead["subject"].include?(a) }
102
101
  e['reason'] = f
103
102
  break
104
103
  end
@@ -28,7 +28,7 @@ module Sisimai::Lhost
28
28
  # We recommend contacting the other email provider for further information about the
29
29
  # cause of this error. The error that the other server returned was:
30
30
  # 500 Remote server does not support TLS (state 6).
31
- '6' => { 'command' => 'MAIL', 'reason' => 'systemerror' },
31
+ '6' => { 'command' => 'MAIL', 'reason' => 'failedstarttls' },
32
32
 
33
33
  # https://www.google.td/support/forum/p/gmail/thread?tid=08a60ebf5db24f7b&hl=en
34
34
  # Technical details of permanent failure:
@@ -44,7 +44,7 @@ module Sisimai::Lhost
44
44
  # We recommend contacting the other email provider for further information about the
45
45
  # cause of this error. The error that the other server returned was:
46
46
  # 454 454 TLS missing certificate: error:0200100D:system library:fopen:Permission denied (#4.3.0) (state 9).
47
- '9' => { 'command' => 'AUTH', 'reason' => 'systemerror' },
47
+ '9' => { 'command' => 'AUTH', 'reason' => 'failedstarttls' },
48
48
 
49
49
  # https://www.google.com/support/forum/p/gmail/thread?tid=5cfab8c76ec88638&hl=en
50
50
  # Technical details of permanent failure:
@@ -151,6 +151,7 @@ module Sisimai::Lhost
151
151
  return nil unless mhead['from'].end_with?('<mailer-daemon@googlemail.com>')
152
152
  return nil unless mhead['subject'].start_with?('Delivery Status Notification')
153
153
 
154
+ require 'sisimai/address'
154
155
  dscontents = [Sisimai::Lhost.DELIVERYSTATUS]
155
156
  emailparts = Sisimai::RFC5322.part(mbody, Boundaries)
156
157
  bodyslices = emailparts[0].split("\n")
@@ -187,7 +188,7 @@ module Sisimai::Lhost
187
188
 
188
189
  if e.start_with?(' ') && e.include?('@')
189
190
  # kijitora@example.jp: 550 5.2.2 <kijitora@example>... Mailbox Full
190
- if v['recipient']
191
+ if v["recipient"] != ""
191
192
  # There are multiple recipient addresses in the message body.
192
193
  dscontents << Sisimai::Lhost.DELIVERYSTATUS
193
194
  v = dscontents[-1]
@@ -198,41 +199,44 @@ module Sisimai::Lhost
198
199
  v['recipient'] = r
199
200
  recipients += 1
200
201
  else
201
- v['diagnosis'] ||= ''
202
- v['diagnosis'] << e + ' '
202
+ v["diagnosis"] << e << " "
203
203
  end
204
204
  end
205
205
  return nil unless recipients > 0
206
206
 
207
+ require 'sisimai/string'
208
+ require 'sisimai/rfc1123'
207
209
  dscontents.each do |e|
208
210
  e['diagnosis'] = Sisimai::String.sweep(e['diagnosis'])
209
211
 
210
- unless e['rhost']
212
+ if Sisimai::String.aligned(e['diagnosis'], [' by ', '. [', ']. '])
211
213
  # Get the value of remote host
212
- if Sisimai::String.aligned(e['diagnosis'], [' by ', '. [', ']. '])
213
- # Google tried to deliver your message, but it was rejected by the server for the recipient
214
- # domain example.jp by mx.example.jp. [192.0.2.153].
215
- p1 = e['diagnosis'].rindex(' by ') || -1
216
- p2 = e['diagnosis'].rindex('. [' ) || -1
217
- hostname = e['diagnosis'][p1 + 4, p2 - p1 - 4]
218
- ipv4addr = e['diagnosis'][p2 + 3, e['diagnosis'].rindex(']. ') - p2 - 3]
219
- lastchar = hostname[-1, 1].upcase.ord
214
+ # Google tried to deliver your message, but it was rejected by the server for the recipient
215
+ # domain example.jp by mx.example.jp. [192.0.2.153].
216
+ p1 = e['diagnosis'].rindex(' by ') || -1
217
+ p2 = e['diagnosis'].rindex('. [' ) || -1
218
+ hostname = e['diagnosis'][p1 + 4, p2 - p1 - 4]
219
+ ipv4addr = e['diagnosis'][p2 + 3, e['diagnosis'].rindex(']. ') - p2 - 3]
220
220
 
221
- e['rhost'] = hostname if lastchar > 64 && lastchar < 91
222
- e['rhost'] ||= ipv4addr
223
- end
221
+ e['rhost'] = hostname if Sisimai::RFC1123.is_internethost(hostname)
222
+ e['rhost'] = ipv4addr if e["rhost"].empty?
224
223
  end
225
224
 
226
- p1 = e['diagnosis'].rindex(' ') || -1
227
- p2 = e['diagnosis'].rindex(')') || -1
228
- statecode0 = e['diagnosis'][p1 + 1, p2 - p1 - 1] || 0
225
+ while true do
226
+ # Find "(state 18)" and pick "18" as a key of statetable
227
+ p1 = e['diagnosis'].rindex(' (state '); break unless p1
228
+ p2 = e['diagnosis'].rindex(')'); break unless p2
229
+ break if p1 > p2
230
+ cu = e['diagnosis'][p1 + 8, p2 - p1 - 8]
231
+ break if cu.empty?
232
+ break unless StateTable[cu]
233
+ e['reason'] = StateTable[cu]['reason']
234
+ e['command'] = StateTable[cu]['command']
235
+ break
236
+ end
229
237
 
230
- if StateTable[statecode0]
231
- # (state *)
232
- e['reason'] = StateTable[statecode0]['reason']
233
- e['command'] = StateTable[statecode0]['command']
234
- else
235
- # No state code
238
+ if e['reason'].empty?
239
+ # There is no no state code in the error message
236
240
  MessagesOf.each_key do |r|
237
241
  # Verify each regular expression of session errors
238
242
  next unless MessagesOf[r].any? { |a| e['diagnosis'].include?(a) }
@@ -240,10 +244,10 @@ module Sisimai::Lhost
240
244
  break
241
245
  end
242
246
  end
243
- next unless e['reason']
247
+ next if e['reason'].empty?
244
248
 
245
249
  # Set pseudo status code
246
- e['status'] = Sisimai::SMTP::Status.find(e['diagnosis']) || ''
250
+ e['status'] = Sisimai::SMTP::Status.find(e['diagnosis'])
247
251
  next if e['status'].size == 0 || e['status'].include?('.0')
248
252
  e['reason'] = Sisimai::SMTP::Status.name(e['status']).to_s || ''
249
253
  end
@@ -60,7 +60,7 @@ module Sisimai::Lhost
60
60
  #
61
61
  # Reason:
62
62
  # delivery retry timeout exceeded
63
- if v['recipient']
63
+ if v["recipient"] != ""
64
64
  # There are multiple recipient addresses in the message body.
65
65
  dscontents << Sisimai::Lhost.DELIVERYSTATUS
66
66
  v = dscontents[-1]
@@ -76,21 +76,9 @@ module Sisimai::Lhost
76
76
  # host: mx.example.jp
77
77
  v['rhost'] = e[6, e.size]
78
78
  else
79
- # Get error message
80
- if Sisimai::SMTP::Status.find(e) || Sisimai::String.aligned(e, ['<', '@', '>'])
81
- # 5.1.1 <shironeko@example.jp>... User Unknown
82
- v['diagnosis'] ||= e
83
- else
84
- next if e.empty?
85
- if e.start_with?('Reason:')
86
- # Reason:
87
- # delivery retry timeout exceeded
88
- v['diagnosis'] = e
89
-
90
- elsif v['diagnosis'] == 'Reason:'
91
- v['diagnosis'] = e
92
- end
93
- end
79
+ # Get error messages
80
+ next if e.empty?
81
+ v['diagnosis'] += e + ' '
94
82
  end
95
83
  end
96
84
  return nil unless recipients > 0
@@ -4,9 +4,7 @@ module Sisimai::Lhost
4
4
  module GoogleGroups
5
5
  class << self
6
6
  require 'sisimai/lhost'
7
-
8
- Indicators = Sisimai::Lhost.INDICATORS
9
- Boundaries = ['----- Original message -----'].freeze
7
+ Boundaries = ['----- Original message -----', 'Content-Type: message/rfc822'].freeze
10
8
 
11
9
  # @abstract Decodes the bounce message from Google Groups
12
10
  # @param [Hash] mhead Message headers of a bounce email
@@ -15,6 +13,7 @@ module Sisimai::Lhost
15
13
  # @return [Nil] it failed to decode or the arguments are missing
16
14
  # @since v4.25.6
17
15
  def inquire(mhead, mbody)
16
+ return nil unless mbody.include?("Google Groups")
18
17
  return nil unless mhead['from'].end_with?('<mailer-daemon@googlemail.com>')
19
18
  return nil unless mhead['subject'].start_with?('Delivery Status Notification')
20
19
  return nil unless mhead['x-failed-recipients']
@@ -39,7 +38,6 @@ module Sisimai::Lhost
39
38
  # Google Groups
40
39
  dscontents = [Sisimai::Lhost.DELIVERYSTATUS]
41
40
  emailparts = Sisimai::RFC5322.part(mbody, Boundaries)
42
- recordwide = { 'rhost' => '', 'reason' => '', 'diagnosis' => '' }
43
41
  recipients = 0
44
42
  v = dscontents[-1]
45
43
 
@@ -49,15 +47,18 @@ module Sisimai::Lhost
49
47
  # * This group may not be open to posting.
50
48
  entiremesg = emailparts[0].split(/\n\n/, 5).slice(0, 4).join(' ').tr("\n", ' ');
51
49
  receivedby = mhead['received'] || []
52
- recordwide['diagnosis'] = Sisimai::String.sweep(entiremesg)
53
- recordwide['reason'] = emailparts[0].scan(/^[ ]?[*][ ]?/).size == 4 ? 'rejected' : 'onhold'
54
- recordwide['rhost'] = Sisimai::RFC5322.received(receivedby[0])[1]
50
+ recordwide = {
51
+ 'diagnosis' => Sisimai::String.sweep(entiremesg),
52
+ 'reason' => 'onhold',
53
+ 'rhost' => Sisimai::RFC5322.received(receivedby[0])[1],
54
+ }
55
+ recordwide['reason'] = 'rejected' if emailparts[0].scan(/^[ ]?[*][ ]?/).size == 4
55
56
 
56
57
  mhead['x-failed-recipients'].split(',').each do |e|
57
58
  # X-Failed-Recipients: neko@example.jp, nyaan@example.org, ...
58
59
  next unless Sisimai::Address.is_emailaddress(e)
59
60
 
60
- if v['recipient']
61
+ if v["recipient"] != ""
61
62
  # There are multiple recipient addresses in the message body.
62
63
  dscontents << Sisimai::Lhost.DELIVERYSTATUS
63
64
  v = dscontents[-1]
@@ -0,0 +1,94 @@
1
+ module Sisimai::Lhost
2
+ # Sisimai::Lhost::GoogleWorkspace decodes a bounce email which created by Google Workspace.
3
+ # Methods in the module are called only from Sisimai::Message.
4
+ module GoogleWorkspace
5
+ class << self
6
+ require 'sisimai/lhost'
7
+ require 'sisimai/string'
8
+ require 'sisimai/address'
9
+
10
+ Indicators = Sisimai::Lhost.INDICATORS
11
+ Boundaries = ["Content-Type: message/rfc822", "Content-Type: text/rfc822-headers"].freeze
12
+ StartingOf = {
13
+ message: ["** "],
14
+ error: ["The response was:", "The response from the remote server was:"],
15
+ }.freeze
16
+ MessagesOf = {
17
+ "userunknown" => ["because the address couldn't be found. Check for typos or unnecessary spaces and try again."],
18
+ "notaccept" => ["Null MX"],
19
+ "networkerror" => [" had no relevant answers.", " responded with code NXDOMAIN"],
20
+ }.freeze
21
+
22
+ # @abstract Decodes the bounce message from Google Workspace
23
+ # @param [Hash] mhead Message headers of a bounce email
24
+ # @param [String] mbody Message body of a bounce email
25
+ # @return [Hash] Bounce data list and message/rfc822 part
26
+ # @return [Nil] it failed to decode or the arguments are missing
27
+ # @see https://workspace.google.com/.
28
+ def inquire(mhead, mbody)
29
+ return nil if mbody.include?("\nDiagnostic-Code:")
30
+ return nil if mbody.include?("\nFinal-Recipient:")
31
+ return nil unless mhead["from"].include?('<mailer-daemon@googlemail.com>')
32
+ return nil unless mhead["subject"].include?("Delivery Status Notification")
33
+
34
+ dscontents = [Sisimai::Lhost.DELIVERYSTATUS]
35
+ emailparts = Sisimai::RFC5322.part(mbody, Boundaries)
36
+ bodyslices = emailparts[0].split("\n")
37
+ entiremesg = ""
38
+ readcursor = 0 # (Integer) Points the current cursor position
39
+ recipients = 0 # (Integer) The number of 'Final-Recipient' header
40
+
41
+ while e = bodyslices.shift do
42
+ # Read error messages and delivery status lines from the head of the email to the previous
43
+ # line of the beginning of the original message.
44
+ if readcursor == 0
45
+ # Beginning of the bounce message or message/delivery-status part
46
+ if e.start_with?(StartingOf[:message][0])
47
+ # ** Message not delivered **
48
+ readcursor |= Indicators[:deliverystatus]
49
+ entiremesg << e + " "
50
+ end
51
+ end
52
+ next if (readcursor & Indicators[:deliverystatus]) == 0
53
+ next if e.empty?
54
+
55
+ # ** Message not delivered **
56
+ # You're sending this from a different address or alias using the 'Send mail as' feature.
57
+ # The settings for your 'Send mail as' account are misconfigured or out of date. Check those settings and try resending.
58
+ # Learn more here: https://support.google.com/mail/?p=CustomFromDenied
59
+ # The response was:
60
+ # Unspecified Error (SENT_SECOND_EHLO): Smtp server does not advertise AUTH capability
61
+ next if e.start_with?("Content-Type: ")
62
+ entiremesg << e + " "
63
+ end
64
+
65
+ while recipients == 0 do
66
+ # Pick the recipient address from the value of To: header of the original message after
67
+ # Content-Type: message/rfc822 field
68
+ p0 = emailparts[1].index("\nTo:"); break unless p0
69
+ p1 = emailparts[1].index("\n", p0 + 2)
70
+ cv = Sisimai::Address.s3s4(emailparts[1][p0 + 4, p1 - p0])
71
+ dscontents[0]["recipient"] = cv
72
+ recipients += 1
73
+ end
74
+ return nil if recipients == 0
75
+
76
+ dscontents[0]["diagnosis"] = entiremesg
77
+ dscontents.each do |e|
78
+ # Tidy up the error message in e["diagnosis"], Try to detect the bounce reason.
79
+ e["diagnosis"] = Sisimai::String.sweep(e["diagnosis"])
80
+ MessagesOf.each_key do |r|
81
+ # Guess an reason of the bounce
82
+ next unless MessagesOf[r].any? { |a| e["diagnosis"].include?(a) }
83
+ e["reason"] = r
84
+ break
85
+ end
86
+ end
87
+
88
+ return { "ds" => dscontents, "rfc822" => emailparts[1] }
89
+ end
90
+ def description; return "Google Workspace: https://workspace.google.com/"; end
91
+ end
92
+ end
93
+ end
94
+
@@ -8,12 +8,12 @@ module Sisimai::Lhost
8
8
 
9
9
  Boundaries = ['Original message follows.'].freeze
10
10
  StartingOf = { error: ['Body of message generated response:'] }.freeze
11
- ReFailures = {
11
+ MessagesOf = {
12
12
  'hostunknown' => ['Unknown host'],
13
13
  'userunknown' => ['Unknown user', 'Invalid final delivery userid'],
14
14
  'mailboxfull' => ['User mailbox exceeds allowed size'],
15
15
  'virusdetected' => ['Requested action not taken: virus detected'],
16
- 'undefined' => ['undeliverable to'],
16
+ 'spamdetected' => ['Blacklisted URL in message'],
17
17
  'expired' => ['Delivery failed '],
18
18
  }.freeze
19
19
 
@@ -45,24 +45,15 @@ module Sisimai::Lhost
45
45
  v = dscontents[-1]
46
46
 
47
47
  p0 = e.index(': ') || -1
48
- if p0 > 8 && Sisimai::String.aligned(e, [': ', '@'])
48
+ if (p0 > 8 && Sisimai::String.aligned(e, [': ', '@'])) || e.start_with?('undeliverable ')
49
49
  # Unknown user: kijitora@example.com
50
- if v['recipient']
51
- # There are multiple recipient addresses in the message body.
52
- dscontents << Sisimai::Lhost.DELIVERYSTATUS
53
- v = dscontents[-1]
54
- end
55
- v['diagnosis'] = e
56
- v['recipient'] = Sisimai::Address.s3s4(e[p0 + 2, e.size])
57
- recipients += 1
58
-
59
- elsif e.start_with?('undeliverable ')
60
50
  # undeliverable to kijitora@example.com
61
- if v['recipient']
51
+ if v["recipient"] != ""
62
52
  # There are multiple recipient addresses in the message body.
63
53
  dscontents << Sisimai::Lhost.DELIVERYSTATUS
64
54
  v = dscontents[-1]
65
55
  end
56
+ v['diagnosis'] = e
66
57
  v['recipient'] = Sisimai::Address.s3s4(e)
67
58
  recipients += 1
68
59
  else
@@ -88,9 +79,9 @@ module Sisimai::Lhost
88
79
  e['diagnosis'] = Sisimai::String.sweep(e['diagnosis']) || ''
89
80
  e['command'] = Sisimai::SMTP::Command.find(e['diagnosis'])
90
81
 
91
- ReFailures.each_key do |r|
82
+ MessagesOf.each_key do |r|
92
83
  # Verify each regular expression of session errors
93
- next unless ReFailures[r].any? { |a| e['diagnosis'].include?(a) }
84
+ next unless MessagesOf[r].any? { |a| e['diagnosis'].include?(a) }
94
85
  e['reason'] = r
95
86
  break
96
87
  end
@@ -46,7 +46,7 @@ module Sisimai::Lhost
46
46
  # Unable to deliver message to <kijitora@neko.example.jp>
47
47
  cr = e[e.rindex('<') + 1, e.rindex('>') - e.rindex('<') - 1]
48
48
 
49
- if v['recipient'] && cr != v['recipient']
49
+ if v["recipient"] != "" && cr != v['recipient']
50
50
  # There are multiple recipient addresses in the message body.
51
51
  dscontents << Sisimai::Lhost.DELIVERYSTATUS
52
52
  v = dscontents[-1]
@@ -7,7 +7,7 @@ module Sisimai::Lhost
7
7
 
8
8
  Indicators = Sisimai::Lhost.INDICATORS
9
9
  Boundaries = ['Content-Type: message/rfc822'].freeze
10
- MarkingsOf = { message: ['Your mail sent on:', 'Your mail attempted to be delivered on:'] }.freeze
10
+ StartingOf = { message: ['Your mail sent on:', 'Your mail attempted to be delivered on:'] }.freeze
11
11
  MessagesOf = {
12
12
  'mailboxfull' => ['As their mailbox is full'],
13
13
  'norelaying' => ['Due to the following SMTP relay error'],
@@ -40,7 +40,7 @@ module Sisimai::Lhost
40
40
  # line of the beginning of the original message.
41
41
  if readcursor == 0
42
42
  # Beginning of the bounce message or delivery status part
43
- readcursor |= Indicators[:deliverystatus] if MarkingsOf[:message].any? { |a| e.start_with?(a) }
43
+ readcursor |= Indicators[:deliverystatus] if StartingOf[:message].any? { |a| e.start_with?(a) }
44
44
  next
45
45
  end
46
46
  next if (readcursor & Indicators[:deliverystatus]) == 0
@@ -51,7 +51,7 @@ module Sisimai::Lhost
51
51
  # Your mail sent on: Thu, 29 Apr 2010 11:04:47 +0900
52
52
  # Could not be delivered to: <******@**.***.**>
53
53
  # As their mailbox is full.
54
- if v['recipient']
54
+ if v["recipient"] != ""
55
55
  # There are multiple recipient addresses in the message body.
56
56
  dscontents << Sisimai::Lhost.DELIVERYSTATUS
57
57
  v = dscontents[-1]
@@ -66,7 +66,6 @@ module Sisimai::Lhost
66
66
  v['date'] = e[19, e.size]
67
67
  else
68
68
  # As their mailbox is full.
69
- v['diagnosis'] ||= ''
70
69
  v['diagnosis'] << e + ' ' if e.start_with?(' ')
71
70
  end
72
71
  end
@@ -47,7 +47,7 @@ module Sisimai::Lhost
47
47
 
48
48
  if e.start_with?('Unable to deliver message to: <') && e.index('@') > 1
49
49
  # Unable to deliver message to: <kijitora@example.org>
50
- if v['recipient']
50
+ if v["recipient"] != ""
51
51
  # There are multiple recipient addresses in the message body.
52
52
  dscontents << Sisimai::Lhost.DELIVERYSTATUS
53
53
  v = dscontents[-1]
@@ -55,17 +55,14 @@ module Sisimai::Lhost
55
55
  v['recipient'] = e[e.index('<'), e.size]
56
56
  recipients += 1
57
57
  else
58
- # Error message
58
+ # Error messages
59
59
  if e == StartingOf[:error][0]
60
60
  # Delivery failed for the following reason:
61
61
  v['diagnosis'] = e
62
62
  else
63
63
  # Detect error message
64
- next if e.empty?
65
64
  next if v['diagnosis'].nil? || v['diagnosis'].empty?
66
65
  next if e.start_with?('-')
67
-
68
- # Server mx22.example.org[192.0.2.222] failed with: 550 <kijitora@example.org> No such user here
69
66
  v['diagnosis'] << ' ' << e
70
67
  end
71
68
  end
@@ -28,7 +28,6 @@ module Sisimai::Lhost
28
28
  readcursor = 0 # (Integer) Points the current cursor position
29
29
  recipients = 0 # (Integer) The number of 'Final-Recipient' header
30
30
  endoferror = false # (Boolean) Flag for the end of error message
31
- regularexp = nil
32
31
  q = Sisimai::RFC2045.boundary(mhead['content-type'], 1); Boundaries << q if q
33
32
  v = nil
34
33
 
@@ -56,7 +55,7 @@ module Sisimai::Lhost
56
55
  if e.start_with?(' ') && e.index('@') > 1
57
56
  # The following recipients were affected:
58
57
  # dummyuser@blabla.xxxxxxxxxxxx.com
59
- if v['recipient']
58
+ if v["recipient"] != ""
60
59
  # There are multiple recipient addresses in the message body.
61
60
  dscontents << Sisimai::Lhost.DELIVERYSTATUS
62
61
  v = dscontents[-1]