sisimai 4.25.3-java → 4.25.4-java
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of sisimai might be problematic. Click here for more details.
- checksums.yaml +5 -5
- data/.gitignore +1 -0
- data/.travis.yml +1 -0
- data/ANALYTICAL-PRECISION +2 -4
- data/CONTRIBUTING +4 -0
- data/ChangeLog.md +39 -0
- data/Developers.mk +1 -72
- data/Makefile +4 -0
- data/README-JA.md +3 -0
- data/README.md +5 -2
- data/lib/sisimai.rb +4 -3
- data/lib/sisimai/arf.rb +6 -6
- data/lib/sisimai/bite.rb +8 -3
- data/lib/sisimai/bite/email.rb +7 -50
- data/lib/sisimai/bite/json.rb +5 -25
- data/lib/sisimai/data.rb +1 -1
- data/lib/sisimai/lhost.rb +101 -0
- data/lib/sisimai/{bite/email → lhost}/activehunter.rb +10 -10
- data/lib/sisimai/{bite/email → lhost}/amavis.rb +9 -9
- data/lib/sisimai/lhost/amazonses.rb +403 -0
- data/lib/sisimai/{bite/email → lhost}/amazonworkmail.rb +9 -9
- data/lib/sisimai/{bite/email → lhost}/aol.rb +10 -10
- data/lib/sisimai/{bite/email → lhost}/apachejames.rb +10 -10
- data/lib/sisimai/{bite/email → lhost}/bigfoot.rb +9 -9
- data/lib/sisimai/{bite/email → lhost}/biglobe.rb +10 -10
- data/lib/sisimai/{bite/email → lhost}/courier.rb +10 -10
- data/lib/sisimai/{bite/email → lhost}/domino.rb +9 -9
- data/lib/sisimai/{bite/email → lhost}/einsundeins.rb +10 -10
- data/lib/sisimai/{bite/email → lhost}/exchange2003.rb +9 -9
- data/lib/sisimai/{bite/email → lhost}/exchange2007.rb +9 -9
- data/lib/sisimai/{bite/email → lhost}/exim.rb +10 -10
- data/lib/sisimai/{bite/email → lhost}/ezweb.rb +9 -9
- data/lib/sisimai/{bite/email → lhost}/facebook.rb +9 -9
- data/lib/sisimai/{bite/email → lhost}/fml.rb +10 -10
- data/lib/sisimai/{bite/email → lhost}/gmx.rb +10 -10
- data/lib/sisimai/{bite/email → lhost}/google.rb +14 -14
- data/lib/sisimai/{bite/email → lhost}/gsuite.rb +10 -10
- data/lib/sisimai/{bite/email → lhost}/imailserver.rb +10 -10
- data/lib/sisimai/{bite/email → lhost}/interscanmss.rb +9 -9
- data/lib/sisimai/{bite/email → lhost}/kddi.rb +10 -10
- data/lib/sisimai/{bite/email → lhost}/mailfoundry.rb +9 -9
- data/lib/sisimai/{bite/email → lhost}/mailmarshalsmtp.rb +9 -9
- data/lib/sisimai/{bite/email → lhost}/mailru.rb +11 -11
- data/lib/sisimai/{bite/email → lhost}/mcafee.rb +9 -9
- data/lib/sisimai/{bite/email → lhost}/messagelabs.rb +9 -9
- data/lib/sisimai/{bite/email → lhost}/messagingserver.rb +9 -9
- data/lib/sisimai/{bite/email → lhost}/mfilter.rb +9 -9
- data/lib/sisimai/{bite/email → lhost}/mxlogic.rb +10 -10
- data/lib/sisimai/{bite/email → lhost}/notes.rb +9 -9
- data/lib/sisimai/{bite/email → lhost}/office365.rb +10 -10
- data/lib/sisimai/{bite/email → lhost}/opensmtpd.rb +9 -9
- data/lib/sisimai/{bite/email → lhost}/outlook.rb +9 -9
- data/lib/sisimai/{bite/email → lhost}/postfix.rb +9 -9
- data/lib/sisimai/{bite/email → lhost}/qmail.rb +9 -9
- data/lib/sisimai/{bite/email → lhost}/receivingses.rb +11 -11
- data/lib/sisimai/{bite/email → lhost}/sendgrid.rb +120 -11
- data/lib/sisimai/{bite/email → lhost}/sendmail.rb +9 -9
- data/lib/sisimai/{bite/email → lhost}/surfcontrol.rb +9 -9
- data/lib/sisimai/{bite/email → lhost}/userdefined.rb +9 -9
- data/lib/sisimai/{bite/email → lhost}/v5sendmail.rb +10 -10
- data/lib/sisimai/{bite/email → lhost}/verizon.rb +11 -11
- data/lib/sisimai/{bite/email → lhost}/x1.rb +9 -9
- data/lib/sisimai/{bite/email → lhost}/x2.rb +9 -9
- data/lib/sisimai/{bite/email → lhost}/x3.rb +9 -9
- data/lib/sisimai/{bite/email → lhost}/x4.rb +10 -10
- data/lib/sisimai/{bite/email → lhost}/x5.rb +9 -9
- data/lib/sisimai/{bite/email → lhost}/yahoo.rb +9 -9
- data/lib/sisimai/{bite/email → lhost}/yandex.rb +10 -10
- data/lib/sisimai/{bite/email → lhost}/zoho.rb +10 -10
- data/lib/sisimai/mda.rb +1 -1
- data/lib/sisimai/message.rb +535 -28
- data/lib/sisimai/message/email.rb +3 -454
- data/lib/sisimai/message/json.rb +3 -177
- data/lib/sisimai/order.rb +238 -4
- data/lib/sisimai/order/email.rb +5 -190
- data/lib/sisimai/order/json.rb +3 -39
- data/lib/sisimai/reason.rb +1 -1
- data/lib/sisimai/reason/norelaying.rb +1 -1
- data/lib/sisimai/reason/userunknown.rb +1 -1
- data/lib/sisimai/reason/virusdetected.rb +1 -0
- data/lib/sisimai/rfc1894.rb +2 -2
- data/lib/sisimai/rfc3464.rb +13 -13
- data/lib/sisimai/rfc3834.rb +3 -3
- data/lib/sisimai/rfc5322.rb +1 -1
- data/lib/sisimai/rhost.rb +5 -4
- data/lib/sisimai/rhost/exchangeonline.rb +1 -1
- data/lib/sisimai/rhost/franceptt.rb +1 -1
- data/lib/sisimai/rhost/godaddy.rb +1 -1
- data/lib/sisimai/rhost/iua.rb +39 -0
- data/lib/sisimai/version.rb +1 -1
- data/set-of-emails/README.md +0 -1
- data/set-of-emails/maildir/bsd/email-gsuite-13.eml +261 -0
- data/set-of-emails/maildir/bsd/email-postfix-62.eml +105 -0
- data/set-of-emails/maildir/bsd/email-postfix-63.eml +85 -0
- data/set-of-emails/maildir/bsd/rhost-iua-01.eml +70 -0
- metadata +75 -94
- data/fig/sisimai-architecture.png +0 -0
- data/lib/sisimai/bite/email/amazonses.rb +0 -186
- data/lib/sisimai/bite/json/amazonses.rb +0 -242
- data/lib/sisimai/bite/json/sendgrid.rb +0 -123
- data/set-of-emails/jsonobj/json-amazonses-01.json +0 -1
- data/set-of-emails/jsonobj/json-amazonses-02.json +0 -11
- data/set-of-emails/jsonobj/json-amazonses-03.json +0 -1
- data/set-of-emails/jsonobj/json-amazonses-04.json +0 -1
- data/set-of-emails/jsonobj/json-amazonses-05.json +0 -1
- data/set-of-emails/jsonobj/json-amazonses-06.json +0 -1
- data/set-of-emails/jsonobj/json-sendgrid-01.json +0 -1
- data/set-of-emails/jsonobj/json-sendgrid-02.json +0 -1
- data/set-of-emails/jsonobj/json-sendgrid-03.json +0 -1
- data/set-of-emails/jsonobj/json-sendgrid-04.json +0 -1
- data/set-of-emails/jsonobj/json-sendgrid-05.json +0 -1
- data/set-of-emails/jsonobj/json-sendgrid-06.json +0 -1
- data/set-of-emails/jsonobj/json-sendgrid-07.json +0 -1
- data/set-of-emails/jsonobj/json-sendgrid-08.json +0 -1
- data/set-of-emails/jsonobj/json-sendgrid-09.json +0 -1
- data/set-of-emails/jsonobj/json-sendgrid-10.json +0 -1
- data/set-of-emails/jsonobj/json-sendgrid-11.json +0 -1
- data/set-of-emails/jsonobj/json-sendgrid-12.json +0 -1
- data/set-of-emails/jsonobj/json-sendgrid-13.json +0 -1
- data/set-of-emails/jsonobj/json-sendgrid-14.json +0 -1
- data/set-of-emails/jsonobj/json-sendgrid-15.json +0 -1
- data/set-of-emails/jsonobj/json-sendgrid-16.json +0 -1
- data/set-of-emails/jsonobj/json-sendgrid-17.json +0 -1
@@ -6,24 +6,7 @@ module Sisimai
|
|
6
6
|
# of "new" method is not a bounce email, the method returns nil.
|
7
7
|
class Email
|
8
8
|
# Imported from p5-Sisimail/lib/Sisimai/Message/Email.pm
|
9
|
-
require 'sisimai/
|
10
|
-
require 'sisimai/mime'
|
11
|
-
require 'sisimai/rfc3834'
|
12
|
-
require 'sisimai/order/email'
|
13
|
-
|
14
|
-
@@ToBeLoaded = []
|
15
|
-
@@TryOnFirst = []
|
16
|
-
|
17
|
-
BorderLine = '__MIME_ENCODED_BOUNDARY__'
|
18
|
-
EndOfEmail = Sisimai::String.EOM
|
19
|
-
RFC822Head = Sisimai::RFC5322.HEADERFIELDS
|
20
|
-
RFC3834Set = Sisimai::RFC3834.headerlist
|
21
|
-
HeaderList = %w[from to date subject content-type reply-to message-id
|
22
|
-
received content-transfer-encoding return-path x-mailer]
|
23
|
-
IsMultiple = { 'received' => true }
|
24
|
-
DefaultSet = Sisimai::Order::Email.another
|
25
|
-
SubjectTab = Sisimai::Order::Email.by('subject')
|
26
|
-
ExtHeaders = Sisimai::Order::Email.headers
|
9
|
+
require 'sisimai/message'
|
27
10
|
|
28
11
|
# Make data structure from the email message(a body part and headers)
|
29
12
|
# @param [Hash] argvs Email data
|
@@ -34,443 +17,9 @@ module Sisimai
|
|
34
17
|
# @options argvs [Code] hook Reference to callback method
|
35
18
|
# @return [Hash] Resolved data structure
|
36
19
|
def self.make(argvs)
|
37
|
-
|
38
|
-
|
39
|
-
hookmethod = argvs['hook'] || nil
|
40
|
-
processing = {
|
41
|
-
'from' => '', # From_ line
|
42
|
-
'header' => {}, # Email header
|
43
|
-
'rfc822' => '', # Original message part
|
44
|
-
'ds' => [], # Parsed data, Delivery Status
|
45
|
-
'catch' => nil, # Data parsed by callback method
|
46
|
-
}
|
47
|
-
methodargv = {
|
48
|
-
'load' => argvs['load'] || [],
|
49
|
-
'order' => argvs['order'] || []
|
50
|
-
}
|
51
|
-
tobeloaded = Sisimai::Message::Email.load(methodargv)
|
52
|
-
|
53
|
-
# 1. Split email data to headers and a body part.
|
54
|
-
return nil unless aftersplit = Sisimai::Message::Email.divideup(email)
|
55
|
-
|
56
|
-
# 2. Convert email headers from text to hash reference
|
57
|
-
headerargv = {
|
58
|
-
'extheaders' => ExtHeaders,
|
59
|
-
'tryonfirst' => [],
|
60
|
-
'extrafield' => argvs['field'] || [],
|
61
|
-
}
|
62
|
-
processing['from'] = aftersplit['from']
|
63
|
-
processing['header'] = Sisimai::Message::Email.headers(aftersplit['header'], headerargv)
|
64
|
-
|
65
|
-
# 3. Check headers for detecting MTA modules
|
66
|
-
if headerargv['tryonfirst'].empty?
|
67
|
-
headerargv['tryonfirst'] += Sisimai::Message::Email.makeorder(processing['header'])
|
68
|
-
end
|
69
|
-
|
70
|
-
# 4. Rewrite message body for detecting the bounce reason
|
71
|
-
methodargv = {
|
72
|
-
'hook' => hookmethod,
|
73
|
-
'mail' => processing,
|
74
|
-
'body' => aftersplit['body'],
|
75
|
-
'tryonfirst' => headerargv['tryonfirst'],
|
76
|
-
'tobeloaded' => tobeloaded,
|
77
|
-
}
|
78
|
-
return nil unless bouncedata = Sisimai::Message::Email.parse(methodargv)
|
79
|
-
return nil if bouncedata.empty?
|
80
|
-
processing['ds'] = bouncedata['ds']
|
81
|
-
processing['catch'] = bouncedata['catch']
|
82
|
-
|
83
|
-
# 5. Rewrite headers of the original message in the body part
|
84
|
-
rfc822part = bouncedata['rfc822']
|
85
|
-
rfc822part = aftersplit['body'] if rfc822part.empty?
|
86
|
-
|
87
|
-
processing['rfc822'] = if rfc822part.is_a? ::String
|
88
|
-
# The value returned from Sisimai::Bite::Email::* modules
|
89
|
-
Sisimai::Message::Email.takeapart(rfc822part)
|
90
|
-
else
|
91
|
-
# The value returned from Sisimai::Bite::JSON::* modules
|
92
|
-
rfc822part
|
93
|
-
end
|
94
|
-
return processing
|
95
|
-
end
|
96
|
-
|
97
|
-
# Load MTA modules which specified at 'order' and 'load' in the argument
|
98
|
-
# @param [Hash] argvs Module information to be loaded
|
99
|
-
# @options argvs [Array] load User defined MTA module list
|
100
|
-
# @options argvs [Array] order The order of MTA modules
|
101
|
-
# @return [Array] Module list
|
102
|
-
# @since v4.20.0
|
103
|
-
def self.load(argvs)
|
104
|
-
modulelist = []
|
105
|
-
tobeloaded = []
|
106
|
-
|
107
|
-
%w[load order].each do |e|
|
108
|
-
# The order of MTA modules specified by user
|
109
|
-
next unless argvs.key?(e)
|
110
|
-
next unless argvs[e].is_a? Array
|
111
|
-
next if argvs[e].empty?
|
112
|
-
|
113
|
-
modulelist += argvs['order'] if e == 'order'
|
114
|
-
next unless e == 'load'
|
115
|
-
|
116
|
-
# Load user defined MTA module
|
117
|
-
argvs['load'].each do |v|
|
118
|
-
# Load user defined MTA module
|
119
|
-
begin
|
120
|
-
require v.to_s.gsub('::', '/').downcase
|
121
|
-
rescue LoadError
|
122
|
-
warn ' ***warning: Failed to load ' << v
|
123
|
-
next
|
124
|
-
end
|
125
|
-
|
126
|
-
Module.const_get(v).headerlist.each do |w|
|
127
|
-
# Get header name which required user defined MTA module
|
128
|
-
ExtHeaders[w] ||= {}
|
129
|
-
ExtHeaders[w][v] = 1
|
130
|
-
end
|
131
|
-
tobeloaded << v
|
132
|
-
end
|
133
|
-
end
|
134
|
-
|
135
|
-
while e = modulelist.shift do
|
136
|
-
# Append the custom order of MTA modules
|
137
|
-
next if tobeloaded.index(e)
|
138
|
-
tobeloaded << e
|
139
|
-
end
|
140
|
-
|
141
|
-
return tobeloaded
|
142
|
-
end
|
143
|
-
|
144
|
-
# Divide email data up headers and a body part.
|
145
|
-
# @param [String] email Email data
|
146
|
-
# @return [Hash] Email data after split
|
147
|
-
def self.divideup(email)
|
148
|
-
return nil if email.empty?
|
149
|
-
|
150
|
-
block = { 'from' => '', 'header' => '', 'body' => '' }
|
151
|
-
email.scrub!('?')
|
152
|
-
email.gsub!(/\r\n/, "\n") if email.include?("\r\n")
|
153
|
-
email.gsub!(/[ \t]+$/, '') if email =~ /[ \t]+$/
|
154
|
-
|
155
|
-
(block['header'], block['body']) = email.split(/\n\n/, 2)
|
156
|
-
return nil unless block['header']
|
157
|
-
return nil unless block['body']
|
158
|
-
|
159
|
-
if block['header'][0, 5] == 'From '
|
160
|
-
# From MAILER-DAEMON Tue Feb 11 00:00:00 2014
|
161
|
-
block['from'] = block['header'].split(/\n/, 2)[0].delete("\r")
|
162
|
-
else
|
163
|
-
# Set pseudo UNIX From line
|
164
|
-
block['from'] = 'MAILER-DAEMON Tue Feb 11 00:00:00 2014'
|
165
|
-
end
|
166
|
-
|
167
|
-
block['body'] << "\n"
|
168
|
-
return block
|
169
|
-
end
|
170
|
-
|
171
|
-
# Convert email headers from text to hash reference
|
172
|
-
# @param [String] heads Email header data
|
173
|
-
# @param [Hash] argvs
|
174
|
-
# @param options extheaders [Array] External header table
|
175
|
-
# @return [Hash] Structured email header data
|
176
|
-
def self.headers(heads, argvs = {})
|
177
|
-
return nil unless heads
|
178
|
-
|
179
|
-
currheader = ''
|
180
|
-
allheaders = {}
|
181
|
-
structured = {}
|
182
|
-
extheaders = argvs['extheaders'] || []
|
183
|
-
extrafield = argvs['extrafield'] || []
|
184
|
-
hasdivided = heads.split("\n")
|
185
|
-
|
186
|
-
HeaderList.each { |e| structured[e] = nil }
|
187
|
-
HeaderList.each { |e| allheaders[e] = true }
|
188
|
-
RFC3834Set.each { |e| allheaders[e] = true }
|
189
|
-
IsMultiple.each_key { |e| structured[e] = [] }
|
190
|
-
extheaders.each_key { |e| allheaders[e] = true }
|
191
|
-
unless extrafield.empty?
|
192
|
-
extrafield.each { |e| allheaders[e] = true }
|
193
|
-
end
|
194
|
-
|
195
|
-
while e = hasdivided.shift do
|
196
|
-
# Convert email headers to hash
|
197
|
-
if cv = e.match(/\A[ \t]+(.+)\z/)
|
198
|
-
# Continued (foled) header value from the previous line
|
199
|
-
next unless allheaders.key?(currheader)
|
200
|
-
|
201
|
-
# Header line continued from the previous line
|
202
|
-
if structured[currheader].is_a? Array
|
203
|
-
# Concatenate a header which have multi-lines such as 'Received'
|
204
|
-
structured[currheader][-1] << ' ' << cv[1]
|
205
|
-
else
|
206
|
-
structured[currheader] ||= ''
|
207
|
-
structured[currheader] << ' ' << cv[1]
|
208
|
-
end
|
209
|
-
else
|
210
|
-
# split the line into a header name and a header content
|
211
|
-
(lhs, rhs) = e.split(/:[ ]*/, 2)
|
212
|
-
currheader = lhs ? lhs.downcase : ''
|
213
|
-
next unless allheaders.key?(currheader)
|
214
|
-
|
215
|
-
if IsMultiple.key?(currheader)
|
216
|
-
# Such as 'Received' header, there are multiple headers in a single
|
217
|
-
# email message.
|
218
|
-
#rhs = rhs.tr("\t", ' ').squeeze(' ')
|
219
|
-
rhs = rhs.tr("\t", ' ')
|
220
|
-
structured[currheader] << rhs
|
221
|
-
else
|
222
|
-
# Other headers except "Received" and so on
|
223
|
-
if extheaders[currheader]
|
224
|
-
# MTA specific header
|
225
|
-
extheaders[currheader].each do |r|
|
226
|
-
next if argvs['tryonfirst'].index(r)
|
227
|
-
argvs['tryonfirst'] << r
|
228
|
-
end
|
229
|
-
end
|
230
|
-
structured[currheader] = rhs
|
231
|
-
end
|
232
|
-
end
|
233
|
-
end
|
234
|
-
return structured
|
20
|
+
Sisimai::Message.warn(self.name)
|
21
|
+
return Sisimai::Message.make(argvs)
|
235
22
|
end
|
236
|
-
|
237
|
-
# Check headers for detecting MTA module and returns the order of modules
|
238
|
-
# @param [Hash] heads Email header data
|
239
|
-
# @return [Array] Order of MTA modules
|
240
|
-
def self.makeorder(heads)
|
241
|
-
return [] unless heads
|
242
|
-
return [] unless heads['subject']
|
243
|
-
return [] if heads['subject'].empty?
|
244
|
-
order = []
|
245
|
-
|
246
|
-
# Try to match the value of "Subject" with patterns generated by
|
247
|
-
# Sisimai::Order->by('subject') method
|
248
|
-
title = heads['subject'].downcase
|
249
|
-
SubjectTab.each_key do |e|
|
250
|
-
# Get MTA list from the subject header
|
251
|
-
next unless title.include?(e)
|
252
|
-
order += SubjectTab[e] # Matched and push MTA list
|
253
|
-
break
|
254
|
-
end
|
255
|
-
return order
|
256
|
-
end
|
257
|
-
|
258
|
-
# Take each email header in the original message apart
|
259
|
-
# @param [String] heads The original message header
|
260
|
-
# @return [Hash] Structured message headers
|
261
|
-
def self.takeapart(heads)
|
262
|
-
return {} unless heads
|
263
|
-
|
264
|
-
# 1. Scrub to avoid "invalid byte sequence in UTF-8" exception (#82)
|
265
|
-
# 2. Convert from string to hash reference
|
266
|
-
heads = heads.scrub('?').gsub(/^[>]+[ ]/m, '').gsub(/=[ ]+=/, "=\n =")
|
267
|
-
|
268
|
-
previousfn = '' # Previous field name
|
269
|
-
asciiarmor = {} # Header names which has MIME encoded value
|
270
|
-
headerpart = {} # Required headers in the original message part
|
271
|
-
hasdivided = heads.split("\n")
|
272
|
-
|
273
|
-
while e = hasdivided.shift do
|
274
|
-
# Header name as a key, The value of header as a value
|
275
|
-
if e.start_with?(' ', "\t")
|
276
|
-
# Continued (foled) header value from the previous line
|
277
|
-
next if previousfn.empty?
|
278
|
-
|
279
|
-
# Concatenate the line if it is the value of required header
|
280
|
-
if Sisimai::MIME.is_mimeencoded(e)
|
281
|
-
# The line is MIME-Encoded test
|
282
|
-
headerpart[previousfn] << if previousfn == 'subject'
|
283
|
-
# Subject: header
|
284
|
-
BorderLine + e
|
285
|
-
else
|
286
|
-
# Is not Subject header
|
287
|
-
e
|
288
|
-
end
|
289
|
-
asciiarmor[previousfn] = true
|
290
|
-
else
|
291
|
-
# ASCII Characters only: Not MIME-Encoded
|
292
|
-
headerpart[previousfn] << e.lstrip
|
293
|
-
asciiarmor[previousfn] ||= false
|
294
|
-
end
|
295
|
-
else
|
296
|
-
# Header name as a key, The value of header as a value
|
297
|
-
(lhs, rhs) = e.split(/:[ ]*/, 2)
|
298
|
-
next unless lhs
|
299
|
-
lhs.downcase!
|
300
|
-
previousfn = ''
|
301
|
-
|
302
|
-
next unless RFC822Head.key?(lhs)
|
303
|
-
previousfn = lhs
|
304
|
-
headerpart[previousfn] = rhs unless headerpart[previousfn]
|
305
|
-
end
|
306
|
-
end
|
307
|
-
return headerpart unless headerpart['subject']
|
308
|
-
|
309
|
-
# Convert MIME-Encoded subject
|
310
|
-
if Sisimai::String.is_8bit(headerpart['subject'])
|
311
|
-
# The value of ``Subject'' header is including multibyte character,
|
312
|
-
# is not MIME-Encoded text.
|
313
|
-
headerpart['subject'] = 'MULTIBYTE CHARACTERS HAVE BEEN REMOVED'
|
314
|
-
else
|
315
|
-
# MIME-Encoded subject field or ASCII characters only
|
316
|
-
r = []
|
317
|
-
if asciiarmor['subject']
|
318
|
-
# split the value of Subject by borderline
|
319
|
-
headerpart['subject'].split(BorderLine).each do |v|
|
320
|
-
# Insert value to the array if the string is MIME encoded text
|
321
|
-
r << v if Sisimai::MIME.is_mimeencoded(v)
|
322
|
-
end
|
323
|
-
else
|
324
|
-
# Subject line is not MIME encoded
|
325
|
-
r << headerpart['subject']
|
326
|
-
end
|
327
|
-
headerpart['subject'] = Sisimai::MIME.mimedecode(r)
|
328
|
-
end
|
329
|
-
return headerpart
|
330
|
-
end
|
331
|
-
|
332
|
-
# @abstract Parse bounce mail with each MTA module
|
333
|
-
# @param [Hash] argvs Processing message entity.
|
334
|
-
# @param options argvs [Hash] mail Email message entity
|
335
|
-
# @param options mail [String] from From line of mbox
|
336
|
-
# @param options mail [Hash] header Email header data
|
337
|
-
# @param options mail [String] rfc822 Original message part
|
338
|
-
# @param options mail [Array] ds Delivery status list(parsed data)
|
339
|
-
# @param options argvs [String] body Email message body
|
340
|
-
# @param options argvs [Array] tryonfirst MTA module list to load on first
|
341
|
-
# @param options argvs [Array] tobeloaded User defined MTA module list
|
342
|
-
# @return [Hash] Parsed and structured bounce mails
|
343
|
-
def self.parse(argvs)
|
344
|
-
mesgentity = argvs['mail']
|
345
|
-
bodystring = argvs['body']
|
346
|
-
hookmethod = argvs['hook'] || nil
|
347
|
-
havecaught = nil
|
348
|
-
tryonfirst = argvs['tryonfirst'] || []
|
349
|
-
tobeloaded = argvs['tobeloaded'] || []
|
350
|
-
mailheader = mesgentity['header']
|
351
|
-
|
352
|
-
return nil unless argvs['mail']
|
353
|
-
return nil unless argvs['body']
|
354
|
-
|
355
|
-
# PRECHECK_EACH_HEADER:
|
356
|
-
# Set empty string if the value is nil
|
357
|
-
mailheader['from'] ||= ''
|
358
|
-
mailheader['subject'] ||= ''
|
359
|
-
mailheader['content-type'] ||= ''
|
360
|
-
|
361
|
-
# Decode BASE64 Encoded message body, rewrite.
|
362
|
-
mesgformat = (mailheader['content-type'] || '').downcase
|
363
|
-
ctencoding = (mailheader['content-transfer-encoding'] || '').downcase
|
364
|
-
|
365
|
-
if mesgformat.start_with?('text/plain', 'text/html')
|
366
|
-
# Content-Type: text/plain; charset=UTF-8
|
367
|
-
if ctencoding == 'base64'
|
368
|
-
# Content-Transfer-Encoding: base64
|
369
|
-
bodystring = Sisimai::MIME.base64d(bodystring)
|
370
|
-
|
371
|
-
elsif ctencoding == 'quoted-printable'
|
372
|
-
# Content-Transfer-Encoding: quoted-printable
|
373
|
-
bodystring = Sisimai::MIME.qprintd(bodystring)
|
374
|
-
end
|
375
|
-
|
376
|
-
if mesgformat.start_with?('text/html;')
|
377
|
-
# Content-Type: text/html;...
|
378
|
-
bodystring = Sisimai::String.to_plain(bodystring, true)
|
379
|
-
end
|
380
|
-
else
|
381
|
-
# NOT text/plain
|
382
|
-
if mesgformat.start_with?('multipart/')
|
383
|
-
# In case of Content-Type: multipart/*
|
384
|
-
p = Sisimai::MIME.makeflat(mailheader['content-type'], bodystring)
|
385
|
-
bodystring = p unless p.empty?
|
386
|
-
end
|
387
|
-
end
|
388
|
-
|
389
|
-
# EXPAND_FORWARDED_MESSAGE:
|
390
|
-
# Check whether or not the message is a bounce mail.
|
391
|
-
# Pre-Process email body if it is a forwarded bounce message.
|
392
|
-
# Get the original text when the subject begins from 'fwd:' or 'fw:'
|
393
|
-
if mailheader['subject'].downcase =~ /\A[ \t]*fwd?:/
|
394
|
-
# Delete quoted strings, quote symbols(>)
|
395
|
-
bodystring = bodystring.gsub(/^[>]+[ ]/m, '').gsub(/^[>]$/m, '')
|
396
|
-
elsif Sisimai::MIME.is_mimeencoded(mailheader['subject'])
|
397
|
-
# Decode MIME-Encoded "Subject:" header
|
398
|
-
mailheader['subject'] = Sisimai::MIME.mimedecode(mailheader['subject'].split(/[ ]/))
|
399
|
-
mailheader['subject'].scrub!('?')
|
400
|
-
end
|
401
|
-
bodystring = bodystring.scrub('?').delete("\r")
|
402
|
-
|
403
|
-
if hookmethod.is_a? Proc
|
404
|
-
# Call the hook method
|
405
|
-
begin
|
406
|
-
p = {
|
407
|
-
'datasrc' => 'email',
|
408
|
-
'headers' => mailheader,
|
409
|
-
'message' => bodystring,
|
410
|
-
'bounces' => nil
|
411
|
-
}
|
412
|
-
havecaught = hookmethod.call(p)
|
413
|
-
rescue StandardError => ce
|
414
|
-
warn ' ***warning: Something is wrong in hook method :' << ce.to_s
|
415
|
-
end
|
416
|
-
end
|
417
|
-
|
418
|
-
bodystring << EndOfEmail
|
419
|
-
haveloaded = {}
|
420
|
-
scannedset = nil
|
421
|
-
|
422
|
-
catch :SCANNER do
|
423
|
-
while true
|
424
|
-
# 1. Sisimai::ARF
|
425
|
-
# 2. User-Defined Module
|
426
|
-
# 3. MTA Module Candidates to be tried on first
|
427
|
-
# 4. Sisimai::Bite::Email::*
|
428
|
-
# 5. Sisimai::RFC3464
|
429
|
-
# 6. Sisimai::RFC3834
|
430
|
-
if Sisimai::ARF.is_arf(mailheader)
|
431
|
-
# Feedback Loop message
|
432
|
-
scannedset = Sisimai::ARF.scan(mailheader, bodystring)
|
433
|
-
throw :SCANNER if scannedset
|
434
|
-
end
|
435
|
-
|
436
|
-
while r = tobeloaded.shift do
|
437
|
-
# Call user defined MTA modules
|
438
|
-
next if haveloaded[r]
|
439
|
-
scannedset = Module.const_get(r).scan(mailheader, bodystring)
|
440
|
-
haveloaded[r] = true
|
441
|
-
throw :SCANNER if scannedset
|
442
|
-
end
|
443
|
-
|
444
|
-
tryonfirst.concat(DefaultSet)
|
445
|
-
while r = tryonfirst.shift do
|
446
|
-
# Try MTA module candidates
|
447
|
-
next if haveloaded.key?(r)
|
448
|
-
require r.gsub('::', '/').downcase
|
449
|
-
scannedset = Module.const_get(r).scan(mailheader, bodystring)
|
450
|
-
haveloaded[r] = true
|
451
|
-
throw :SCANNER if scannedset
|
452
|
-
end
|
453
|
-
|
454
|
-
# When the all of Sisimai::Bite::Email::* modules did not return
|
455
|
-
# bounce data, call Sisimai::RFC3464;
|
456
|
-
require 'sisimai/rfc3464'
|
457
|
-
scannedset = Sisimai::RFC3464.scan(mailheader, bodystring)
|
458
|
-
break if scannedset
|
459
|
-
|
460
|
-
# Try to parse the message as auto reply message defined in RFC3834
|
461
|
-
require 'sisimai/rfc3834'
|
462
|
-
scannedset = Sisimai::RFC3834.scan(mailheader, bodystring)
|
463
|
-
break if scannedset
|
464
|
-
|
465
|
-
# as of now, we have no sample email for coding this block
|
466
|
-
break
|
467
|
-
end
|
468
|
-
end
|
469
|
-
|
470
|
-
scannedset['catch'] = havecaught if scannedset
|
471
|
-
return scannedset
|
472
|
-
end
|
473
|
-
|
474
23
|
end
|
475
24
|
end
|
476
25
|
end
|