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
@@ -1,194 +0,0 @@
1
- module Sisimai::Lhost
2
- # Sisimai::Lhost::GSuite decodes a bounce email which created by Google Workspace https://workspace.google.com/.
3
- # Methods in the module are called from only Sisimai::Message.
4
- module GSuite
5
- class << self
6
- require 'sisimai/lhost'
7
-
8
- Indicators = Sisimai::Lhost.INDICATORS
9
- Boundaries = ['Content-Type: message/rfc822', 'Content-Type: text/rfc822-headers'].freeze
10
- MarkingsOf = {
11
- message: ['** '],
12
- error: ['The response was:', 'The response from the remote server was:'],
13
- }.freeze
14
- MessagesOf = {
15
- 'userunknown' => ["because the address couldn't be found. Check for typos or unnecessary spaces and try again."],
16
- 'notaccept' => ['Null MX'],
17
- 'networkerror' => [' had no relevant answers.', ' responded with code NXDOMAIN'],
18
- }.freeze
19
-
20
- # @abstract Decodes the bounce message from G Suite (Transfer from G Suite to a destinaion host)
21
- # @param [Hash] mhead Message headers of a bounce email
22
- # @param [String] mbody Message body of a bounce email
23
- # @return [Hash] Bounce data list and message/rfc822 part
24
- # @return [Nil] it failed to decode or the arguments are missing
25
- def inquire(mhead, mbody)
26
- return nil unless mhead['from'].end_with?('<mailer-daemon@googlemail.com>')
27
- return nil unless mhead['subject'].start_with?('Delivery Status Notification')
28
- return nil unless mhead['x-gm-message-state']
29
-
30
- fieldtable = Sisimai::RFC1894.FIELDTABLE
31
- permessage = {} # (Hash) Store values of each Per-Message field
32
-
33
- dscontents = [Sisimai::Lhost.DELIVERYSTATUS]
34
- emailparts = Sisimai::RFC5322.part(mbody, Boundaries)
35
- bodyslices = emailparts[0].split("\n")
36
- readcursor = 0 # (Integer) Points the current cursor position
37
- recipients = 0 # (Integer) The number of 'Final-Recipient' header
38
- endoferror = false # (Integer) Flag for a blank line after error messages
39
- anotherset = {} # (Hash) Another error information
40
- emptylines = 0 # (Integer) The number of empty lines
41
- v = nil
42
-
43
- while e = bodyslices.shift do
44
- # Read error messages and delivery status lines from the head of the email to the previous
45
- # line of the beginning of the original message.
46
- if readcursor == 0
47
- # Beginning of the bounce message or message/delivery-status part
48
- readcursor |= Indicators[:deliverystatus] if MarkingsOf[:message].any? { |a| e.start_with?(a) }
49
- end
50
- next if (readcursor & Indicators[:deliverystatus]) == 0
51
-
52
- if f = Sisimai::RFC1894.match(e)
53
- # "e" matched with any field defined in RFC3464
54
- next unless o = Sisimai::RFC1894.field(e)
55
- v = dscontents[-1]
56
-
57
- if o[-1] == 'addr'
58
- # Final-Recipient: rfc822; kijitora@example.jp
59
- # X-Actual-Recipient: rfc822; kijitora@example.co.jp
60
- if o[0] == 'final-recipient'
61
- # Final-Recipient: rfc822; kijitora@example.jp
62
- if v['recipient']
63
- # There are multiple recipient addresses in the message body.
64
- dscontents << Sisimai::Lhost.DELIVERYSTATUS
65
- v = dscontents[-1]
66
- end
67
- v['recipient'] = o[2]
68
- recipients += 1
69
- else
70
- # X-Actual-Recipient: rfc822; kijitora@example.co.jp
71
- v['alias'] = o[2]
72
- end
73
- elsif o[-1] == 'code'
74
- # Diagnostic-Code: SMTP; 550 5.1.1 <userunknown@example.jp>... User Unknown
75
- v['spec'] = o[1]
76
- v['diagnosis'] = o[2]
77
- else
78
- # Other DSN fields defined in RFC3464
79
- next unless fieldtable[o[0]]
80
- v[fieldtable[o[0]]] = o[2]
81
-
82
- if fieldtable[o[0]] == 'lhost'
83
- # Do not set an email address as a hostname in "lhost" value
84
- v['lhost'] = '' if v['lhost'].include?('@')
85
- end
86
-
87
- next unless f
88
- permessage[fieldtable[o[0]]] = o[2]
89
- end
90
- else
91
- # The line does not begin with a DSN field defined in RFC3464 Append error messages continued
92
- # from the previous line
93
- if endoferror == false && v && ! v['diagnosis'].to_s.empty?
94
- endoferror ||= true if e.empty?
95
-
96
- next if endoferror
97
- next unless e.start_with?(' ')
98
- v['diagnosis'] << e
99
-
100
- elsif MarkingsOf[:error].any? { |a| e.start_with?(a) }
101
- # Detect SMTP session error or connection error
102
- # The response from the remote server was:
103
- anotherset['diagnosis'] << ' ' << e
104
- else
105
- # ** Address not found **
106
- #
107
- # Your message wasn't delivered to * because the address couldn't be found.
108
- # Check for typos or unnecessary spaces and try again.
109
- #
110
- # The response from the remote server was:
111
- # 550 #5.1.0 Address rejected.
112
- next if e.start_with?('Content-Type:')
113
- if anotherset['diagnosis']
114
- # Continued error messages from the previous line like "550 #5.1.0 Address rejected."
115
- next if emptylines > 5
116
- if e.empty?
117
- # Count and next()
118
- emptylines += 1
119
- next
120
- end
121
- anotherset['diagnosis'] << ' ' << e
122
- else
123
- # ** Address not found **
124
- #
125
- # Your message wasn't delivered to * because the address couldn't be found.
126
- # Check for typos or unnecessary spaces and try again.
127
- next if e.empty?
128
- next unless MarkingsOf[:message].any? { |a| e.start_with?(a) }
129
- anotherset['diagnosis'] = e
130
- end
131
- end
132
- end
133
- end
134
- return nil unless recipients > 0
135
-
136
- dscontents.each do |e|
137
- # Set default values if each value is empty.
138
- permessage.each_key { |a| e[a] ||= permessage[a] || '' }
139
-
140
- if anotherset['diagnosis']
141
- # Copy alternative error message
142
- e['diagnosis'] = anotherset['diagnosis'] unless e['diagnosis']
143
-
144
- if e['diagnosis'].include?(' ') == false && e['diagnosis'].to_i > 0
145
- e['diagnosis'] = anotherset['diagnosis']
146
- else
147
- # More detailed error message is in "anotherset"
148
- as = nil # status
149
- ar = nil # replycode
150
-
151
- e['status'] ||= ''
152
- e['replycode'] ||= ''
153
-
154
- if e['status'] == '' || e['status'].start_with?('4.0.0', '5.0.0')
155
- # Check the value of D.S.N. in anotherset
156
- as = Sisimai::SMTP::Status.find(anotherset['diagnosis'])
157
- if as && as[-3, 3] != '0.0'
158
- # The D.S.N. is neither an empty nor *.0.0
159
- e['status'] = as
160
- end
161
- end
162
-
163
- if e['replycode'].empty? || e['replycode'].start_with?('400', '500')
164
- # Check the value of SMTP reply code in anotherset
165
- ar = Sisimai::SMTP::Reply.find(anotherset['diagnosis'])
166
- if ar && ar[-2, 2].to_i != 0
167
- # The SMTP reply code is neither an empty nor *00
168
- e['replycode'] = ar
169
- end
170
- end
171
-
172
- if (as || ar) && (anotherset['diagnosis'].size > e['diagnosis'].size)
173
- # Update the error message in e['diagnosis']
174
- e['diagnosis'] = anotherset['diagnosis']
175
- end
176
- end
177
- end
178
-
179
- e['diagnosis'] = Sisimai::String.sweep(e['diagnosis'])
180
- MessagesOf.each_key do |r|
181
- # Guess an reason of the bounce
182
- next unless MessagesOf[r].any? { |a| e['diagnosis'].include?(a) }
183
- e['reason'] = r
184
- break
185
- end
186
- end
187
-
188
- return { 'ds' => dscontents, 'rfc822' => emailparts[1] }
189
- end
190
- def description; return 'G Suite: https://gsuite.google.com'; end
191
- end
192
- end
193
- end
194
-
@@ -1,214 +0,0 @@
1
- module Sisimai::Lhost
2
- # Sisimai::Lhost::MailRu decodes a bounce email which created by @mail.ru https://mail.ru/.
3
- # Methods in the module are called from only Sisimai::Message.
4
- module MailRu
5
- class << self
6
- # Based on Sisimai::Lhost::Exim
7
- require 'sisimai/lhost'
8
-
9
- Indicators = Sisimai::Lhost.INDICATORS
10
- Boundaries = ['------ This is a copy of the message, including all the headers. ------'].freeze
11
- StartingOf = { message: ['This message was created automatically by mail delivery software.'] }.freeze
12
- ReCommands = [
13
- %r/SMTP error from remote (?:mail server|mailer) after ([A-Za-z]{4})/,
14
- %r/SMTP error from remote (?:mail server|mailer) after end of ([A-Za-z]{4})/,
15
- ].freeze
16
- MessagesOf = {
17
- 'expired' => [
18
- 'retry timeout exceeded',
19
- 'No action is required on your part',
20
- ],
21
- 'userunknown' => ['user not found'],
22
- 'hostunknown' => [
23
- 'all host address lookups failed permanently',
24
- 'all relevant MX records point to non-existent hosts',
25
- 'Unrouteable address',
26
- ],
27
- 'mailboxfull' => ['mailbox is full', 'error: quota exceed'],
28
- 'notaccept' => [
29
- 'an MX or SRV record indicated no SMTP service',
30
- 'no host found for existing SMTP connection',
31
- ],
32
- 'syntaxerror' => [
33
- 'angle-brackets nested too deep',
34
- 'expected word or "<"',
35
- 'domain missing in source-routed address',
36
- 'malformed address:',
37
- ],
38
- 'systemerror' => [
39
- 'delivery to file forbidden',
40
- 'delivery to pipe forbidden',
41
- 'local delivery failed',
42
- 'LMTP error after ',
43
- ],
44
- 'contenterror' => ['Too many "Received" headers'],
45
- }.freeze
46
-
47
- # @abstract Decodes the bounce message from @mail.ru
48
- # @param [Hash] mhead Message headers of a bounce email
49
- # @param [String] mbody Message body of a bounce email
50
- # @return [Hash] Bounce data list and message/rfc822 part
51
- # @return [Nil] it failed to decode or the arguments are missing
52
- def inquire(mhead, mbody)
53
- mfrom = mhead['from'].downcase
54
- msgid = mhead['message-id'] ? mhead['message-id'].downcase : ''
55
- match = 0
56
- match += 1 if mfrom.include?('mailer-daemon@') && mfrom.include?('mail.ru')
57
- match += 1 if msgid.end_with?('.mail.ru>', 'smailru.net>')
58
- match += 1 if [
59
- 'Delivery Status Notification',
60
- 'Mail delivery failed',
61
- 'Mail failure',
62
- 'Message frozen',
63
- 'Warning: message ',
64
- 'error(s) in forwarding or filtering'].any? { |a| mhead['subject'].include?(a) }
65
- return nil unless match > 2
66
-
67
- dscontents = [Sisimai::Lhost.DELIVERYSTATUS]
68
- emailparts = Sisimai::RFC5322.part(mbody, Boundaries)
69
- bodyslices = emailparts[0].split("\n")
70
- readcursor = 0 # (Integer) Points the current cursor position
71
- recipients = 0 # (Integer) The number of 'Final-Recipient' header
72
- v = nil
73
-
74
- while e = bodyslices.shift do
75
- # Read error messages and delivery status lines from the head of the email to the previous
76
- # line of the beginning of the original message.
77
- if readcursor == 0
78
- # Beginning of the bounce message or delivery status part
79
- readcursor |= Indicators[:deliverystatus] if e.start_with?(StartingOf[:message][0])
80
- next
81
- end
82
- next if (readcursor & Indicators[:deliverystatus]) == 0
83
- next if e.empty?
84
-
85
- # Это письмо создано автоматически
86
- # сервером Mail.Ru, # отвечать на него не
87
- # нужно.
88
- #
89
- # К сожалению, Ваше письмо не может
90
- # быть# доставлено одному или нескольким
91
- # получателям:
92
- #
93
- # **********************
94
- #
95
- # This message was created automatically by mail delivery software.
96
- #
97
- # A message that you sent could not be delivered to one or more of its
98
- # recipients. This is a permanent error. The following address(es) failed:
99
- #
100
- # kijitora@example.jp
101
- # SMTP error from remote mail server after RCPT TO:<kijitora@example.jp>:
102
- # host neko.example.jp [192.0.2.222]: 550 5.1.1 <kijitora@example.jp>... User Unknown
103
- v = dscontents[-1]
104
-
105
- if e.start_with?(' ') && e.include?(' ') == false && e.index('@') > 1
106
- # kijitora@example.jp
107
- if v['recipient']
108
- # There are multiple recipient addresses in the message body.
109
- dscontents << Sisimai::Lhost.DELIVERYSTATUS
110
- v = dscontents[-1]
111
- end
112
- v['recipient'] = e[2, e.size]
113
- recipients += 1
114
-
115
- elsif dscontents.size == recipients
116
- # Error message
117
- next if e.empty?
118
- v['diagnosis'] ||= ''
119
- v['diagnosis'] << e + ' '
120
- else
121
- # Error message when email address above does not include '@'
122
- # and domain part.
123
- next unless e.start_with?(' ')
124
- v['alterrors'] ||= ''
125
- v['alterrors'] << e + ' '
126
- end
127
- end
128
-
129
- unless recipients > 0
130
- # Fallback for getting recipient addresses
131
- if mhead['x-failed-recipients']
132
- # X-Failed-Recipients: kijitora@example.jp
133
- rcptinhead = mhead['x-failed-recipients'].split(',')
134
- rcptinhead.each { |a| a.delete(' ') }
135
- recipients = rcptinhead.size
136
-
137
- while e = rcptinhead.shift do
138
- # Insert each recipient address into dscontents
139
- dscontents[-1]['recipient'] = e
140
- next if dscontents.size == recipients
141
- dscontents << Sisimai::Lhost.DELIVERYSTATUS
142
- end
143
- end
144
- end
145
- return nil unless recipients > 0
146
-
147
- # Get the name of the local MTA
148
- # Received: from marutamachi.example.org (c192128.example.net [192.0.2.128])
149
- receivedby = mhead['received'] || []
150
- recvdtoken = Sisimai::RFC5322.received(receivedby[-1])
151
-
152
- dscontents.each do |e|
153
- # Check the error message, the rhost, the lhost, and the smtp command.
154
- unless e['alterrors'].to_s.empty?
155
- # Copy alternative error message
156
- e['diagnosis'] ||= e['alterrors']
157
- if e['diagnosis'].start_with?('-') || e['diagnosis'].end_with?('__')
158
- # Override the value of diagnostic code message
159
- e['diagnosis'] = e['alterrors'] unless e['alterrors'].empty?
160
- end
161
- e.delete('alterrors')
162
- end
163
- e['diagnosis'] = Sisimai::String.sweep(e['diagnosis']) || ''
164
- p1 = e['diagnosis'].rindex('__') || -1
165
- e['diagnosis'] = e['diagnosis'][0, p1 - 1] if p1 > 2
166
-
167
- unless e['rhost']
168
- # Get the remote host name
169
- # host neko.example.jp [192.0.2.222]: 550 5.1.1 <kijitora@example.jp>... User Unknown
170
- p1 = e['diagnosis'].index('host ') || -1
171
- p2 = e['diagnosis'].index(' ', p1 + 5) || -1
172
- e['rhost'] = e['diagnosis'][p1 + 5, p2 - p1 - 5] if p1 > -1
173
- e['rhost'] ||= recvdtoken[1]
174
- end
175
- e['lhost'] ||= recvdtoken[0]
176
-
177
- unless e['command']
178
- # Get the SMTP command name for the session
179
- ReCommands.each do |r|
180
- # Verify each regular expression of SMTP commands
181
- if cv = e['diagnosis'].match(r)
182
- e['command'] = cv[1].upcase
183
- break
184
- end
185
- end
186
-
187
- # Detect the reason of bounce
188
- if %w[HELO EHLO].index(e['command'])
189
- # HELO | Connected to 192.0.2.135 but my name was rejected.
190
- e['reason'] = 'blocked'
191
-
192
- elsif e['command'] == 'MAIL'
193
- # MAIL | Connected to 192.0.2.135 but sender was rejected.
194
- e['reason'] = 'rejected'
195
- else
196
- # Verify each regular expression of session errors
197
- MessagesOf.each_key do |r|
198
- # Check each regular expression
199
- next unless MessagesOf[r].any? { |a| e['diagnosis'].include?(a) }
200
- e['reason'] = r
201
- break
202
- end
203
- end
204
- end
205
- e['command'] ||= ''
206
- end
207
-
208
- return { 'ds' => dscontents, 'rfc822' => emailparts[1] }
209
- end
210
- def description; return '@mail.ru: https://mail.ru'; end
211
- end
212
- end
213
- end
214
-
@@ -1,109 +0,0 @@
1
- module Sisimai::Lhost
2
- # Sisimai::Lhost::McAfee decodes a bounce email which created by McAfee Email Appliance.
3
- # Methods in the module are called from only Sisimai::Message.
4
- module McAfee
5
- class << self
6
- require 'sisimai/lhost'
7
-
8
- Indicators = Sisimai::Lhost.INDICATORS
9
- Boundaries = ['Content-Type: message/rfc822'].freeze
10
- StartingOf = { message: ['--- The following addresses had delivery problems ---'] }.freeze
11
- MessagesOf = { 'userunknown' => [' User not exist', ' unknown.', '550 Unknown user ', 'No such user'] }.freeze
12
-
13
- # @abstract Decodes the bounce message from McAfee Email Appliance
14
- # @param [Hash] mhead Message headers of a bounce email
15
- # @param [String] mbody Message body of a bounce email
16
- # @return [Hash] Bounce data list and message/rfc822 part
17
- # @return [Nil] it failed to decode or the arguments are missing
18
- def inquire(mhead, mbody)
19
- # X-NAI-Header: Modified by McAfee Email and Web Security Virtual Appliance
20
- return nil unless mhead['x-nai-header']
21
- return nil unless mhead['x-nai-header'].start_with?('Modified by McAfee ')
22
- return nil unless mhead['subject'] == 'Delivery Status'
23
-
24
- fieldtable = Sisimai::RFC1894.FIELDTABLE
25
- dscontents = [Sisimai::Lhost.DELIVERYSTATUS]
26
- emailparts = Sisimai::RFC5322.part(mbody, Boundaries)
27
- bodyslices = emailparts[0].split("\n")
28
- readslices = ['']
29
- readcursor = 0 # (Integer) Points the current cursor position
30
- recipients = 0 # (Integer) The number of 'Final-Recipient' header
31
- issuedcode = '' # (String) Alternative diagnostic message
32
- v = nil
33
-
34
- while e = bodyslices.shift do
35
- # Read error messages and delivery status lines from the head of the email to the previous
36
- # line of the beginning of the original message.
37
- readslices << e # Save the current line for the next loop
38
-
39
- if readcursor == 0
40
- # Beginning of the bounce message or delivery status part
41
- readcursor |= Indicators[:deliverystatus] if e.include?(StartingOf[:message][0])
42
- next
43
- end
44
- next if (readcursor & Indicators[:deliverystatus]) == 0
45
- next if e.empty?
46
-
47
- # Content-Type: text/plain; name="deliveryproblems.txt"
48
- #
49
- # --- The following addresses had delivery problems ---
50
- #
51
- # <user@example.com> (User unknown user@example.com)
52
- #
53
- # --------------Boundary-00=_00000000000000000000
54
- # Content-Type: message/delivery-status; name="deliverystatus.txt"
55
- #
56
- v = dscontents[-1]
57
-
58
- if Sisimai::String.aligned(e, ['<', '@', '>', '(', ')'])
59
- # <kijitora@example.co.jp> (Unknown user kijitora@example.co.jp)
60
- if v['recipient']
61
- # There are multiple recipient addresses in the message body.
62
- dscontents << Sisimai::Lhost.DELIVERYSTATUS
63
- v = dscontents[-1]
64
- end
65
- v['recipient'] = Sisimai::Address.s3s4(e[e.index('<'), e.index('>')])
66
- issuedcode = e[e.index('(') + 1, e.size]
67
- recipients += 1
68
-
69
- elsif f = Sisimai::RFC1894.match(e)
70
- # "e" matched with any field defined in RFC3464
71
- o = Sisimai::RFC1894.field(e)
72
- unless o
73
- # Fallback code for empty value or invalid formatted value
74
- # - Original-Recipient: <kijitora@example.co.jp>
75
- if e.start_with?('Original-Recipient: ')
76
- v['alias'] = Sisimai::Address.s3s4(e[e.index(':') + 1, e.size])
77
- end
78
- next
79
- end
80
- next unless fieldtable[o[0]]
81
- v[fieldtable[o[0]]] = o[2]
82
-
83
- else
84
- # Continued line of the value of Diagnostic-Code field
85
- next unless readslices[-2].start_with?('Diagnostic-Code:')
86
- next unless e.start_with?(' ')
87
- v['diagnosis'] << ' ' << Sisimai::String.sweep(e)
88
- readslices[-1] = 'Diagnostic-Code: ' << e
89
- end
90
- end
91
- return nil unless recipients > 0
92
-
93
- dscontents.each do |e|
94
- e['diagnosis'] = Sisimai::String.sweep(e['diagnosis'] || issuedcode)
95
- MessagesOf.each_key do |r|
96
- # Verify each regular expression of session errors
97
- next unless MessagesOf[r].any? { |a| e['diagnosis'].include?(a) }
98
- e['reason'] = r
99
- break
100
- end
101
- end
102
-
103
- return { 'ds' => dscontents, 'rfc822' => emailparts[1] }
104
- end
105
- def description; return 'McAfee Email Appliance'; end
106
- end
107
- end
108
- end
109
-
@@ -1,120 +0,0 @@
1
- module Sisimai::Lhost
2
- # Sisimai::Lhost::MessageLabs decodes a bounce email which created by Email Security (formerly MessageLab)
3
- # https://www.broadcom.com/products/cybersecurity/emails.
4
- # Methods in the module are called from only Sisimai::Message.
5
- module MessageLabs
6
- class << self
7
- require 'sisimai/lhost'
8
-
9
- Indicators = Sisimai::Lhost.INDICATORS
10
- Boundaries = ['Content-Type: text/rfc822-headers'].freeze
11
- StartingOf = { message: ['Content-Type: message/delivery-status'] }.freeze
12
- MessagesOf = {
13
- 'userunknown' => ['542 ', ' Rejected', 'No such user'],
14
- 'securityerror' => ['Please turn on SMTP Authentication in your mail client'],
15
- }.freeze
16
-
17
- # @abstract Decodes the bounce message from Email Security (formerly Symantec.cloud (formerly MessageLabs))
18
- # @param [Hash] mhead Message headers of a bounce email
19
- # @param [String] mbody Message body of a bounce email
20
- # @return [Hash] Bounce data list and message/rfc822 part
21
- # @return [Nil] it failed to decode or the arguments are missing
22
- def inquire(mhead, mbody)
23
- # X-Msg-Ref: server-11.tower-143.messagelabs.com!1419367175!36473369!1
24
- # X-Originating-IP: [10.245.230.38]
25
- # X-StarScan-Received:
26
- # X-StarScan-Version: 6.12.5; banners=-,-,-
27
- # X-VirusChecked: Checked
28
- return nil unless mhead['x-msg-ref']
29
- return nil unless mhead['from'].include?('MAILER-DAEMON@messagelabs.com')
30
- return nil unless mhead['subject'].start_with?('Mail Delivery Failure')
31
-
32
- fieldtable = Sisimai::RFC1894.FIELDTABLE
33
- permessage = {} # (Hash) Store values of each Per-Message field
34
-
35
- dscontents = [Sisimai::Lhost.DELIVERYSTATUS]
36
- emailparts = Sisimai::RFC5322.part(mbody, Boundaries)
37
- bodyslices = emailparts[0].split("\n")
38
- readslices = ['']
39
- readcursor = 0 # (Integer) Points the current cursor position
40
- recipients = 0 # (Integer) The number of 'Final-Recipient' header
41
- commandset = [] # (Array) ``in reply to * command'' list
42
- v = nil
43
-
44
- while e = bodyslices.shift do
45
- # Read error messages and delivery status lines from the head of the email to the previous
46
- # line of the beginning of the original message.
47
- readslices << e # Save the current line for the next loop
48
-
49
- if readcursor == 0
50
- # Beginning of the bounce message or message/delivery-status part
51
- readcursor |= Indicators[:deliverystatus] if e.start_with?(StartingOf[:message][0])
52
- next
53
- end
54
- next if (readcursor & Indicators[:deliverystatus]) == 0
55
- next if e.empty?
56
-
57
- if f = Sisimai::RFC1894.match(e)
58
- # "e" matched with any field defined in RFC3464
59
- next unless o = Sisimai::RFC1894.field(e)
60
- v = dscontents[-1]
61
-
62
- if o[-1] == 'addr'
63
- # Final-Recipient: rfc822; kijitora@example.jp
64
- # X-Actual-Recipient: rfc822; kijitora@example.co.jp
65
- if o[0] == 'final-recipient'
66
- # Final-Recipient: rfc822; kijitora@example.jp
67
- if v['recipient']
68
- # There are multiple recipient addresses in the message body.
69
- dscontents << Sisimai::Lhost.DELIVERYSTATUS
70
- v = dscontents[-1]
71
- end
72
- v['recipient'] = o[2]
73
- recipients += 1
74
- else
75
- # X-Actual-Recipient: rfc822; kijitora@example.co.jp
76
- v['alias'] = o[2]
77
- end
78
- elsif o[-1] == 'code'
79
- # Diagnostic-Code: SMTP; 550 5.1.1 <userunknown@example.jp>... User Unknown
80
- v['spec'] = o[1]
81
- v['diagnosis'] = o[2]
82
- else
83
- # Other DSN fields defined in RFC3464
84
- next unless fieldtable[o[0]]
85
- v[fieldtable[o[0]]] = o[2]
86
-
87
- next unless f
88
- permessage[fieldtable[o[0]]] = o[2]
89
- end
90
- else
91
- # Continued line of the value of Diagnostic-Code field
92
- next unless readslices[-2].start_with?('Diagnostic-Code:')
93
- next unless e.start_with?(' ')
94
- v['diagnosis'] << ' ' << Sisimai::String.sweep(e)
95
- readslices[-1] = 'Diagnostic-Code: ' << e
96
- end
97
- end
98
- return nil unless recipients > 0
99
-
100
- dscontents.each do |e|
101
- # Set default values if each value is empty.
102
- permessage.each_key { |a| e[a] ||= permessage[a] || '' }
103
- e['command'] = commandset.shift || ''
104
- e['diagnosis'] = Sisimai::String.sweep(e['diagnosis'])
105
-
106
- MessagesOf.each_key do |r|
107
- # Verify each regular expression of session errors
108
- next unless MessagesOf[r].any? { |a| e['diagnosis'].include?(a) }
109
- e['reason'] = r
110
- break
111
- end
112
- end
113
-
114
- return { 'ds' => dscontents, 'rfc822' => emailparts[1] }
115
- end
116
- def description; return 'Symantec.cloud http://www.messagelabs.com'; end
117
- end
118
- end
119
- end
120
-