sisimai 4.25.16-java → 5.0.2-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 (180) hide show
  1. checksums.yaml +5 -5
  2. data/.github/workflows/rake-test.yml +55 -0
  3. data/.travis.yml +3 -3
  4. data/ANALYTICAL-PRECISION +2 -2
  5. data/Benchmarks.mk +3 -3
  6. data/CONTRIBUTING +1 -1
  7. data/ChangeLog.md +451 -393
  8. data/Developers.mk +5 -6
  9. data/Gemfile +1 -1
  10. data/Makefile +15 -15
  11. data/README-JA.md +323 -149
  12. data/README.md +319 -149
  13. data/Rakefile +9 -3
  14. data/Repository.mk +2 -3
  15. data/lib/sisimai/address.rb +118 -74
  16. data/lib/sisimai/arf.rb +84 -82
  17. data/lib/sisimai/datetime.rb +5 -52
  18. data/lib/sisimai/{data → fact}/json.rb +7 -9
  19. data/lib/sisimai/fact/yaml.rb +31 -0
  20. data/lib/sisimai/fact.rb +506 -0
  21. data/lib/sisimai/lhost/activehunter.rb +12 -14
  22. data/lib/sisimai/lhost/amavis.rb +11 -14
  23. data/lib/sisimai/lhost/amazonses.rb +37 -42
  24. data/lib/sisimai/lhost/amazonworkmail.rb +15 -19
  25. data/lib/sisimai/lhost/aol.rb +12 -15
  26. data/lib/sisimai/lhost/apachejames.rb +19 -21
  27. data/lib/sisimai/lhost/barracuda.rb +10 -12
  28. data/lib/sisimai/lhost/bigfoot.rb +21 -22
  29. data/lib/sisimai/lhost/biglobe.rb +15 -16
  30. data/lib/sisimai/lhost/courier.rb +20 -20
  31. data/lib/sisimai/lhost/domino.rb +23 -20
  32. data/lib/sisimai/lhost/einsundeins.rb +23 -18
  33. data/lib/sisimai/lhost/exchange2003.rb +30 -29
  34. data/lib/sisimai/lhost/exchange2007.rb +70 -58
  35. data/lib/sisimai/lhost/exim.rb +179 -174
  36. data/lib/sisimai/lhost/ezweb.rb +31 -56
  37. data/lib/sisimai/lhost/facebook.rb +21 -34
  38. data/lib/sisimai/lhost/fml.rb +43 -48
  39. data/lib/sisimai/lhost/gmail.rb +29 -29
  40. data/lib/sisimai/lhost/gmx.rb +18 -17
  41. data/lib/sisimai/lhost/googlegroups.rb +11 -11
  42. data/lib/sisimai/lhost/gsuite.rb +21 -28
  43. data/lib/sisimai/lhost/imailserver.rb +25 -39
  44. data/lib/sisimai/lhost/interscanmss.rb +28 -31
  45. data/lib/sisimai/lhost/kddi.rb +22 -28
  46. data/lib/sisimai/lhost/mailfoundry.rb +11 -12
  47. data/lib/sisimai/lhost/mailmarshalsmtp.rb +25 -29
  48. data/lib/sisimai/lhost/mailru.rb +37 -40
  49. data/lib/sisimai/lhost/mcafee.rb +21 -31
  50. data/lib/sisimai/lhost/messagelabs.rb +17 -21
  51. data/lib/sisimai/lhost/messagingserver.rb +40 -37
  52. data/lib/sisimai/lhost/mfilter.rb +16 -17
  53. data/lib/sisimai/lhost/mxlogic.rb +24 -33
  54. data/lib/sisimai/lhost/notes.rb +17 -17
  55. data/lib/sisimai/lhost/office365.rb +64 -28
  56. data/lib/sisimai/lhost/opensmtpd.rb +12 -13
  57. data/lib/sisimai/lhost/outlook.rb +12 -16
  58. data/lib/sisimai/lhost/postfix.rb +179 -130
  59. data/lib/sisimai/lhost/powermta.rb +12 -14
  60. data/lib/sisimai/lhost/qmail.rb +44 -47
  61. data/lib/sisimai/lhost/receivingses.rb +15 -21
  62. data/lib/sisimai/lhost/sendgrid.rb +34 -34
  63. data/lib/sisimai/lhost/sendmail.rb +65 -53
  64. data/lib/sisimai/lhost/surfcontrol.rb +19 -19
  65. data/lib/sisimai/lhost/v5sendmail.rb +45 -39
  66. data/lib/sisimai/lhost/verizon.rb +35 -39
  67. data/lib/sisimai/lhost/x1.rb +18 -17
  68. data/lib/sisimai/lhost/x2.rb +17 -14
  69. data/lib/sisimai/lhost/x3.rb +19 -19
  70. data/lib/sisimai/lhost/x4.rb +72 -57
  71. data/lib/sisimai/lhost/x5.rb +17 -19
  72. data/lib/sisimai/lhost/x6.rb +41 -17
  73. data/lib/sisimai/lhost/yahoo.rb +17 -16
  74. data/lib/sisimai/lhost/yandex.rb +16 -21
  75. data/lib/sisimai/lhost/zoho.rb +16 -15
  76. data/lib/sisimai/lhost.rb +8 -10
  77. data/lib/sisimai/mail/maildir.rb +1 -3
  78. data/lib/sisimai/mail/mbox.rb +3 -4
  79. data/lib/sisimai/mail/memory.rb +0 -1
  80. data/lib/sisimai/mail/stdin.rb +1 -3
  81. data/lib/sisimai/mail.rb +3 -7
  82. data/lib/sisimai/mda.rb +28 -42
  83. data/lib/sisimai/message.rb +444 -326
  84. data/lib/sisimai/order.rb +5 -5
  85. data/lib/sisimai/reason/authfailure.rb +65 -0
  86. data/lib/sisimai/reason/badreputation.rb +53 -0
  87. data/lib/sisimai/reason/blocked.rb +96 -160
  88. data/lib/sisimai/reason/contenterror.rb +8 -9
  89. data/lib/sisimai/reason/delivered.rb +4 -6
  90. data/lib/sisimai/reason/exceedlimit.rb +10 -12
  91. data/lib/sisimai/reason/expired.rb +7 -8
  92. data/lib/sisimai/reason/feedback.rb +2 -3
  93. data/lib/sisimai/reason/filtered.rb +17 -19
  94. data/lib/sisimai/reason/hasmoved.rb +9 -10
  95. data/lib/sisimai/reason/hostunknown.rb +15 -15
  96. data/lib/sisimai/reason/mailboxfull.rb +11 -12
  97. data/lib/sisimai/reason/mailererror.rb +18 -20
  98. data/lib/sisimai/reason/mesgtoobig.rb +9 -11
  99. data/lib/sisimai/reason/networkerror.rb +5 -8
  100. data/lib/sisimai/reason/norelaying.rb +8 -11
  101. data/lib/sisimai/reason/notaccept.rb +13 -14
  102. data/lib/sisimai/reason/notcompliantrfc.rb +43 -0
  103. data/lib/sisimai/reason/onhold.rb +6 -9
  104. data/lib/sisimai/reason/policyviolation.rb +14 -12
  105. data/lib/sisimai/reason/rejected.rb +26 -24
  106. data/lib/sisimai/reason/requireptr.rb +69 -0
  107. data/lib/sisimai/reason/securityerror.rb +34 -36
  108. data/lib/sisimai/reason/spamdetected.rb +115 -147
  109. data/lib/sisimai/reason/speeding.rb +49 -0
  110. data/lib/sisimai/reason/suspend.rb +12 -11
  111. data/lib/sisimai/reason/syntaxerror.rb +11 -10
  112. data/lib/sisimai/reason/systemerror.rb +7 -9
  113. data/lib/sisimai/reason/systemfull.rb +7 -8
  114. data/lib/sisimai/reason/toomanyconn.rb +9 -11
  115. data/lib/sisimai/reason/undefined.rb +2 -3
  116. data/lib/sisimai/reason/userunknown.rb +129 -146
  117. data/lib/sisimai/reason/vacation.rb +3 -4
  118. data/lib/sisimai/reason/virusdetected.rb +10 -11
  119. data/lib/sisimai/reason.rb +59 -64
  120. data/lib/sisimai/rfc1894.rb +55 -28
  121. data/lib/sisimai/rfc2045.rb +373 -0
  122. data/lib/sisimai/rfc3464.rb +250 -308
  123. data/lib/sisimai/rfc3834.rb +42 -45
  124. data/lib/sisimai/rfc5322.rb +177 -146
  125. data/lib/sisimai/rfc5965.rb +31 -0
  126. data/lib/sisimai/rhost/cox.rb +5 -6
  127. data/lib/sisimai/rhost/franceptt.rb +6 -8
  128. data/lib/sisimai/rhost/godaddy.rb +12 -12
  129. data/lib/sisimai/rhost/google.rb +530 -0
  130. data/lib/sisimai/rhost/iua.rb +9 -10
  131. data/lib/sisimai/rhost/kddi.rb +6 -8
  132. data/lib/sisimai/rhost/{exchangeonline.rb → microsoft.rb} +115 -114
  133. data/lib/sisimai/rhost/mimecast.rb +51 -42
  134. data/lib/sisimai/rhost/nttdocomo.rb +12 -12
  135. data/lib/sisimai/rhost/spectrum.rb +10 -12
  136. data/lib/sisimai/rhost/{tencentqq.rb → tencent.rb} +7 -8
  137. data/lib/sisimai/rhost.rb +23 -31
  138. data/lib/sisimai/smtp/command.rb +59 -0
  139. data/lib/sisimai/smtp/error.rb +4 -7
  140. data/lib/sisimai/smtp/reply.rb +161 -74
  141. data/lib/sisimai/smtp/status.rb +507 -393
  142. data/lib/sisimai/smtp/transcript.rb +124 -0
  143. data/lib/sisimai/smtp.rb +0 -1
  144. data/lib/sisimai/string.rb +74 -5
  145. data/lib/sisimai/time.rb +1 -2
  146. data/lib/sisimai/version.rb +1 -1
  147. data/lib/sisimai.rb +46 -31
  148. data/set-of-emails/maildir/bsd/lhost-domino-02.eml +6 -3
  149. data/set-of-emails/maildir/bsd/lhost-googlegroups-15.eml +174 -0
  150. data/set-of-emails/maildir/bsd/lhost-gsuite-15.eml +229 -0
  151. data/set-of-emails/maildir/bsd/lhost-postfix-75.eml +51 -0
  152. data/set-of-emails/maildir/bsd/lhost-postfix-76.eml +101 -0
  153. data/set-of-emails/maildir/bsd/lhost-postfix-77.eml +74 -0
  154. data/set-of-emails/maildir/bsd/lhost-postfix-78.eml +91 -0
  155. data/set-of-emails/maildir/bsd/lhost-receivingses-08.eml +88 -0
  156. data/set-of-emails/maildir/bsd/lhost-sendmail-60.eml +85 -0
  157. data/set-of-emails/maildir/bsd/rfc3464-43.eml +88 -0
  158. data/set-of-emails/maildir/bsd/rhost-google-03.eml +101 -0
  159. data/set-of-emails/maildir/bsd/rhost-google-04.eml +102 -0
  160. data/set-of-emails/maildir/bsd/rhost-google-05.eml +82 -0
  161. data/set-of-emails/maildir/bsd/rhost-google-06.eml +102 -0
  162. data/set-of-emails/maildir/bsd/rhost-google-07.eml +69 -0
  163. data/set-of-emails/maildir/bsd/rhost-google-08.eml +99 -0
  164. data/sisimai-java.gemspec +1 -1
  165. data/sisimai.gemspec +1 -1
  166. metadata +48 -26
  167. data/.rspec +0 -2
  168. data/lib/sisimai/data/yaml.rb +0 -33
  169. data/lib/sisimai/data.rb +0 -411
  170. data/lib/sisimai/mime.rb +0 -456
  171. data/lib/sisimai/rhost/googleapps.rb +0 -261
  172. /data/set-of-emails/maildir/bsd/{rfc3464-41.eml → rfc3834-05.eml} +0 -0
  173. /data/set-of-emails/maildir/bsd/{rhost-googleapps-01.eml → rhost-google-01.eml} +0 -0
  174. /data/set-of-emails/maildir/bsd/{rhost-googleapps-02.eml → rhost-google-02.eml} +0 -0
  175. /data/set-of-emails/maildir/bsd/{rhost-exchangeonline-01.eml → rhost-microsoft-01.eml} +0 -0
  176. /data/set-of-emails/maildir/bsd/{rhost-exchangeonline-02.eml → rhost-microsoft-02.eml} +0 -0
  177. /data/set-of-emails/maildir/bsd/{rhost-exchangeonline-03.eml → rhost-microsoft-03.eml} +0 -0
  178. /data/set-of-emails/maildir/bsd/{rhost-tencentqq-01.eml → rhost-tencent-01.eml} +0 -0
  179. /data/set-of-emails/maildir/bsd/{rhost-tencentqq-02.eml → rhost-tencent-02.eml} +0 -0
  180. /data/set-of-emails/maildir/bsd/{rhost-tencentqq-03.eml → rhost-tencent-03.eml} +0 -0
data/lib/sisimai/data.rb DELETED
@@ -1,411 +0,0 @@
1
- module Sisimai
2
- # Sisimai::Data generate parsed data from Sisimai::Message object.
3
- class Data
4
- # Imported from p5-Sisimail/lib/Sisimai/Data.pm
5
- require 'sisimai/address'
6
- require 'sisimai/reason'
7
- require 'sisimai/string'
8
- require 'sisimai/rhost'
9
- require 'sisimai/time'
10
- require 'sisimai/datetime'
11
- require 'sisimai/smtp/error'
12
-
13
- @@rwaccessors = [
14
- :catch, # [?] Results generated by hook method
15
- :token, # [String] Message token/MD5 Hex digest value
16
- :lhost, # [String] local host name/Local MTA
17
- :rhost, # [String] Remote host name/Remote MTA
18
- :alias, # [String] Alias of the recipient address
19
- :listid, # [String] List-Id header of each ML
20
- :reason, # [String] Bounce reason
21
- :action, # [String] The value of Action: header
22
- :origin, # [String] Email path as a data source
23
- :subject, # [String] UTF-8 Subject text
24
- :timestamp, # [Sisimai::Time] Date: header in the original message
25
- :addresser, # [Sisimai::Address] From address
26
- :recipient, # [Sisimai::Address] Recipient address which bounced
27
- :messageid, # [String] Message-Id: header
28
- :replycode, # [String] SMTP Reply Code
29
- :smtpagent, # [String] Module(Engine) name
30
- :softbounce, # [Integer] 1 = Soft bounce, 0 = Hard bounce, -1 = ?
31
- :smtpcommand, # [String] The last SMTP command
32
- :destination, # [String] The domain part of the "recipinet"
33
- :senderdomain, # [String] The domain part of the "addresser"
34
- :feedbacktype, # [String] Feedback Type
35
- :diagnosticcode, # [String] Diagnostic-Code: Header
36
- :diagnostictype, # [String] The 1st part of Diagnostic-Code: Header
37
- :deliverystatus, # [String] Delivery Status(DSN)
38
- :timezoneoffset, # [Integer] Time zone offset(seconds)
39
- ]
40
- attr_accessor(*@@rwaccessors)
41
-
42
- RetryIndex = Sisimai::Reason.retry
43
- RFC822Head = Sisimai::RFC5322.HEADERFIELDS(:all)
44
-
45
- # Constructor of Sisimai::Data
46
- # @param [Hash] argvs Data
47
- # @return [Sisimai::Data] Structured email data
48
- def initialize(argvs)
49
- # Create email address object
50
- as = Sisimai::Address.make(argvs['addresser'])
51
- ar = Sisimai::Address.make(address: argvs['recipient'])
52
-
53
- return nil unless as.is_a? Sisimai::Address
54
- return nil unless ar.is_a? Sisimai::Address
55
- return nil if as.void
56
- return nil if ar.void
57
-
58
- @addresser = as
59
- @recipient = ar
60
- @senderdomain = as.host
61
- @destination = ar.host
62
- @alias = argvs['alias'] || ''
63
- @token = Sisimai::String.token(as.address, ar.address, argvs['timestamp'])
64
- @timestamp = Sisimai::Time.parse(::Time.at(argvs['timestamp']).to_s)
65
- @timezoneoffset = argvs['timezoneoffset'] || '+0000'
66
- @lhost = argvs['lhost'] || ''
67
- @rhost = argvs['rhost'] || ''
68
- @catch = argvs['catch'] || nil
69
- @reason = argvs['reason'] || ''
70
- @listid = argvs['listid'] || ''
71
- @subject = argvs['subject'] || ''
72
- @messageid = argvs['messageid'] || ''
73
- @smtpagent = argvs['smtpagent'] || ''
74
- @diagnosticcode = argvs['diagnosticcode'] || ''
75
- @diagnostictype = argvs['diagnostictype'] || ''
76
- @deliverystatus = argvs['deliverystatus'] || ''
77
- @smtpcommand = argvs['smtpcommand'] || ''
78
- @feedbacktype = argvs['feedbacktype'] || ''
79
- @action = argvs['action'] || ''
80
- @origin = argvs['origin'] || ''
81
- @replycode = argvs['replycode'] || ''
82
- @replycode = Sisimai::SMTP::Reply.find(argvs['diagnosticcode']).to_s if @replycode.empty?
83
- @softbounce = argvs['softbounce'] || ''
84
- end
85
-
86
- # Another constructor of Sisimai::Data
87
- # @param [Sisimai::Message] data Data Object
88
- # @param [Hash] argvs Parser options
89
- # @options argvs [Boolean] delivered true: Including "delivered" reason
90
- # @return [Array, Nil] List of Sisimai::Data or Nil if the
91
- # argument is not Sisimai::Message object
92
- def self.make(data: nil, **argvs)
93
- return nil unless data
94
- return nil unless data.is_a? Sisimai::Message
95
-
96
- messageobj = data
97
- rfc822data = messageobj.rfc822
98
- objectlist = []
99
- delivered1 = argvs[:delivered] || false
100
-
101
- return nil unless messageobj.ds
102
- return nil unless messageobj.rfc822
103
-
104
- eachobject = messageobj.ds.dup
105
- while e = eachobject.shift do
106
- # Create parameters for new() constructor.
107
- p = {
108
- 'catch' => messageobj.catch || nil,
109
- 'lhost' => e['lhost'] || '',
110
- 'rhost' => e['rhost'] || '',
111
- 'alias' => e['alias'] || '',
112
- 'action' => e['action'] || '',
113
- 'reason' => e['reason'] || '',
114
- 'replycode' => e['replycode'] || '',
115
- 'smtpagent' => e['agent'] || '',
116
- 'recipient' => e['recipient'] || '',
117
- 'softbounce' => e['softbounce'] || '',
118
- 'smtpcommand' => e['command'] || '',
119
- 'feedbacktype' => e['feedbacktype'] || '',
120
- 'diagnosticcode' => e['diagnosis'] || '',
121
- 'diagnostictype' => e['spec'] || '',
122
- 'deliverystatus' => e['status'] || '',
123
- }
124
- unless delivered1
125
- # Skip if the value of "deliverystatus" begins with "2." such as 2.1.5
126
- next if p['deliverystatus'].start_with?('2.')
127
- end
128
-
129
- # EMAIL_ADDRESS:
130
- # Detect email address from message/rfc822 part
131
- RFC822Head[:addresser].each do |f|
132
- # Check each header in message/rfc822 part
133
- next unless rfc822data[f]
134
- next if rfc822data[f].empty?
135
-
136
- j = Sisimai::Address.find(rfc822data[f]) || []
137
- next if j.empty?
138
- p['addresser'] = j[0]
139
- break
140
- end
141
-
142
- unless p['addresser']
143
- # Fallback: Get the sender address from the header of the bounced
144
- # email if the address is not set at loop above.
145
- j = Sisimai::Address.find(messageobj.header['to']) || []
146
- p['addresser'] = j[0] unless j.empty?
147
- end
148
- next unless p['addresser']
149
- next unless p['recipient']
150
-
151
- # TIMESTAMP:
152
- # Convert from a time stamp or a date string to a machine time.
153
- datestring = nil
154
- zoneoffset = 0
155
- datevalues = []
156
- datevalues << e['date'] unless e['date'].to_s.empty?
157
-
158
- # Date information did not exist in message/delivery-status part,...
159
- RFC822Head[:date].each do |f|
160
- # Get the value of Date header or other date related header.
161
- next unless rfc822data[f]
162
- datevalues << rfc822data[f]
163
- end
164
-
165
- # Set "date" getting from the value of "Date" in the bounce message
166
- datevalues << messageobj.header['date'] if datevalues.size < 2
167
-
168
- while v = datevalues.shift do
169
- # Parse each date value in the array
170
- datestring = Sisimai::DateTime.parse(v)
171
- break if datestring
172
- end
173
-
174
- if datestring && cv = datestring.match(/\A(.+)[ ]+([-+]\d{4})\z/)
175
- # Get the value of timezone offset from datestring
176
- # Wed, 26 Feb 2014 06:05:48 -0500
177
- datestring = cv[1]
178
- zoneoffset = Sisimai::DateTime.tz2second(cv[2])
179
- p['timezoneoffset'] = cv[2]
180
- end
181
-
182
- begin
183
- # Convert from the date string to an object then calculate time
184
- # zone offset.
185
- t = Sisimai::Time.strptime(datestring, '%a, %d %b %Y %T')
186
- p['timestamp'] = (t.to_time.to_i - zoneoffset) || nil
187
- rescue
188
- warn ' ***warning: Failed to strptime ' << datestring.to_s
189
- end
190
- next unless p['timestamp']
191
-
192
- # OTHER_TEXT_HEADERS:
193
- recvheader = data.header['received'] || []
194
- unless recvheader.empty?
195
- # Get localhost and remote host name from Received header.
196
- %w[lhost rhost].each { |a| e[a] ||= '' }
197
- e['lhost'] = Sisimai::RFC5322.received(recvheader[0]).shift if e['lhost'].empty?
198
- e['rhost'] = Sisimai::RFC5322.received(recvheader[-1]).pop if e['rhost'].empty?
199
- end
200
-
201
- # Remove square brackets and curly brackets from the host variable
202
- %w[rhost lhost].each do |v|
203
- p[v].delete!('[]()') # Remove square brackets and curly brackets from the host variable
204
- p[v].sub!(/\A.+=/, '') # Remove string before "="
205
- p[v].chomp!("\r") if p[v].end_with?("\r") # Remove CR at the end of the value
206
-
207
- # Check space character in each value and get the first element
208
- p[v] = p[v].split(' ', 2).shift if p[v].include?(' ')
209
- p[v].chomp!('.') if p[v].end_with?('.') # Remove "." at the end of the value
210
- end
211
-
212
- # Subject: header of the original message
213
- p['subject'] = rfc822data['subject'] || ''
214
- p['subject'].scrub!('?')
215
- p['subject'].chomp!("\r") if p['subject'].end_with?("\r")
216
-
217
- # The value of "List-Id" header
218
- p['listid'] = rfc822data['list-id'] || ''
219
- unless p['listid'].empty?
220
- # Get the value of List-Id header like "List name <list-id@example.org>"
221
- if cv = p['listid'].match(/\A.*([<].+[>]).*\z/) then p['listid'] = cv[1] end
222
- p['listid'].delete!('<>')
223
- p['listid'].chomp!("\r") if p['listid'].end_with?("\r")
224
- p['listid'] = '' if p['listid'].include?(' ')
225
- end
226
-
227
- # The value of "Message-Id" header
228
- p['messageid'] = rfc822data['message-id'] || ''
229
- unless p['messageid'].empty?
230
- # Leave only string inside of angle brackets(<>)
231
- if cv = p['messageid'].match(/\A([^ ]+)[ ].*/) then p['messageid'] = cv[1] end
232
- if cv = p['messageid'].match(/[<]([^ ]+?)[>]/) then p['messageid'] = cv[1] end
233
- end
234
-
235
- # CHECK_DELIVERY_STATUS_VALUE:
236
- # Cleanup the value of "Diagnostic-Code:" header
237
- unless p['diagnosticcode'].empty?
238
- # Count the number of D.S.N. and SMTP Reply Code
239
- vs = Sisimai::SMTP::Status.find(p['diagnosticcode'])
240
- vr = Sisimai::SMTP::Reply.find(p['diagnosticcode'])
241
- vm = 0
242
- re = nil
243
-
244
- if vs
245
- # How many times does the D.S.N. appeared
246
- vm += p['diagnosticcode'].scan(/\b#{vs}\b/).size
247
- p['deliverystatus'] = vs if vs =~ /\A[45][.][1-9][.][1-9]\z/
248
- end
249
-
250
- if vr
251
- # How many times does the SMTP reply code appeared
252
- vm += p['diagnosticcode'].scan(/\b#{vr}\b/).size
253
- p['replycode'] ||= vr
254
- end
255
-
256
- if vm > 2
257
- # Build regular expression for removing string like '550-5.1.1'
258
- # from the value of "diagnosticcode"
259
- re0 = %r/;?[ ]#{vr}[- ](?:#{vs})?/
260
- re1 = %r/;?[ ][45]\d\d[- ](?:#{vs})?/
261
- re2 = %r/;?[ ]#{vr}[- ](?:[45][.]\d[.]\d+)?/
262
-
263
- # 550-5.7.1 [192.0.2.222] Our system has detected that this message is
264
- # 550-5.7.1 likely unsolicited mail. To reduce the amount of spam sent to Gmail,
265
- # 550-5.7.1 this message has been blocked. Please visit
266
- # 550 5.7.1 https://support.google.com/mail/answer/188131 for more information.
267
- p['diagnosticcode'] = p['diagnosticcode'].gsub(re0, ' ')
268
- p['diagnosticcode'] = p['diagnosticcode'].gsub(re1, ' ')
269
- p['diagnosticcode'] = p['diagnosticcode'].gsub(re2, ' ')
270
- p['diagnosticcode'] = Sisimai::String.sweep(p['diagnosticcode'].sub(%r|<html>.+</html>|i, ''))
271
- end
272
- end
273
-
274
- if Sisimai::String.is_8bit(p['diagnosticcode'])
275
- # To avoid incompatible character encodings: ASCII-8BIT and UTF-8 (Encoding::CompatibilityError
276
- p['diagnosticcode'] = p['diagnosticcode'].force_encoding('UTF-8').scrub('?')
277
- end
278
-
279
- p['diagnostictype'] ||= 'X-UNIX' if p['reason'] == 'mailererror'
280
- p['diagnostictype'] ||= 'SMTP' unless %w[feedback vacation].include?(p['reason'])
281
-
282
- # Check the value of SMTP command
283
- p['smtpcommand'] = '' unless %w[EHLO HELO MAIL RCPT DATA QUIT].include?(p['smtpcommand'])
284
- p['origin'] = argvs[:origin] # Set the path to the original email
285
-
286
- if p['action'].empty?
287
- # Check the value of "action"
288
- if p['reason'] == 'expired'
289
- # Action: delayed
290
- p['action'] = 'delayed'
291
- elsif p['deliverystatus'].start_with?('5', '4')
292
- # Action: failed
293
- p['action'] = 'failed'
294
- end
295
- end
296
-
297
- o = Sisimai::Data.new(p)
298
- next unless o.recipient
299
-
300
- if o.reason.empty? || RetryIndex[o.reason]
301
- # Decide the reason of email bounce
302
- r = ''; r = Sisimai::Rhost.get(o) if Sisimai::Rhost.match(o.rhost)
303
- if r.empty?
304
- # Failed to detect a bounce reason by the value of "rhost"
305
- r = Sisimai::Rhost.get(o, o.destination) if Sisimai::Rhost.match(o.destination)
306
- r = Sisimai::Reason.get(o) if r.nil? || r.empty?
307
- r = 'undefined' if r.empty?
308
- end
309
- o.reason = r
310
- end
311
-
312
- if %w[delivered feedback vacation].include?(o.reason)
313
- # The value of reason is "vacation" or "feedback"
314
- o.softbounce = -1
315
- o.replycode = '' unless o.reason == 'delivered'
316
- else
317
- # Bounce message which reason is "feedback" or "vacation" does
318
- # not have the value of "deliverystatus".
319
- softorhard = nil
320
-
321
- if o.softbounce.to_s.empty?
322
- # The value is not set yet
323
- textasargv = (p['deliverystatus'] + ' ' + p['diagnosticcode']).lstrip
324
- softorhard = Sisimai::SMTP::Error.soft_or_hard(o.reason, textasargv) || ''
325
- o.softbounce = if softorhard.size > 0
326
- # Returned value is "soft" or "hard"
327
- (softorhard == 'soft') ? 1 : 0
328
- else
329
- # Returned value is an empty string
330
- -1
331
- end
332
- end
333
-
334
- if o.deliverystatus.empty?
335
- # Set pseudo status code
336
- textasargv = (o.replycode + ' ' + p['diagnosticcode']).lstrip
337
- getchecked = Sisimai::SMTP::Error.is_permanent(textasargv)
338
- tmpfailure = getchecked.nil? ? false : (getchecked ? false : true)
339
-
340
- if pseudocode = Sisimai::SMTP::Status.code(o.reason, tmpfailure)
341
- # Set the value of "deliverystatus" and "softbounce"
342
- o.deliverystatus = pseudocode
343
-
344
- if o.softbounce < 0
345
- # set the value of "softbounce" again when the value is -1
346
- softorhard = Sisimai::SMTP::Error.soft_or_hard(o.reason, pseudocode)
347
-
348
- o.softbounce = if softorhard.size > 0
349
- # Returned value is "soft" or "hard"
350
- softorhard == 'soft' ? 1 : 0
351
- else
352
- # Returned value is an empty string
353
- -1
354
- end
355
- end
356
- end
357
- end
358
-
359
- unless o.replycode.empty?
360
- # Check both of the first digit of "deliverystatus" and "replycode"
361
- o.replycode = '' unless o.replycode[0, 1] == o.deliverystatus[0, 1]
362
- end
363
-
364
- end
365
- objectlist << o
366
-
367
- end
368
- return objectlist
369
- end
370
-
371
- # Convert from object to hash reference
372
- # @return [Hash] Data in Hash reference
373
- def damn
374
- data = {}
375
- @@rwaccessors.each do |e|
376
- next if %w[addresser recipient timestamp].include?(e.to_s)
377
- data[e.to_s] = self.send(e) || ''
378
- end
379
- data['addresser'] = self.addresser.address
380
- data['recipient'] = self.recipient.address
381
- data['timestamp'] = self.timestamp.to_time.to_i
382
- return data
383
- end
384
- alias :to_hash :damn
385
-
386
- # Data dumper
387
- # @param [String] type Data format: json, yaml
388
- # @return [String, Nil] Dumped data or nil if the value of the first
389
- # argument is neither "json" nor "yaml"
390
- def dump(type = 'json')
391
- return nil unless %w[json yaml].include?(type)
392
- referclass = 'Sisimai::Data::' << type.upcase
393
-
394
- begin
395
- require referclass.downcase.gsub('::', '/')
396
- rescue
397
- warn '***warning: Failed to load' << referclass
398
- end
399
-
400
- dumpeddata = Module.const_get(referclass).dump(self)
401
- return dumpeddata
402
- end
403
-
404
- # JSON handler
405
- # @return [String] JSON string converted from Sisimai::Data
406
- def to_json(*)
407
- return self.dump('json')
408
- end
409
-
410
- end
411
- end