sisimai 4.25.17-java → 5.0.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.
- checksums.yaml +5 -5
- data/.travis.yml +3 -3
- data/ANALYTICAL-PRECISION +2 -2
- data/Benchmarks.mk +3 -3
- data/CONTRIBUTING +1 -1
- data/ChangeLog.md +406 -407
- data/Developers.mk +5 -6
- data/Gemfile +1 -1
- data/Makefile +12 -12
- data/README-JA.md +142 -94
- data/README.md +282 -150
- data/Rakefile +9 -3
- data/Repository.mk +2 -3
- data/lib/sisimai/address.rb +118 -74
- data/lib/sisimai/arf.rb +84 -82
- data/lib/sisimai/datetime.rb +5 -52
- data/lib/sisimai/{data → fact}/json.rb +7 -9
- data/lib/sisimai/fact/yaml.rb +31 -0
- data/lib/sisimai/fact.rb +468 -0
- data/lib/sisimai/lhost/activehunter.rb +12 -14
- data/lib/sisimai/lhost/amavis.rb +11 -14
- data/lib/sisimai/lhost/amazonses.rb +37 -41
- data/lib/sisimai/lhost/amazonworkmail.rb +15 -18
- data/lib/sisimai/lhost/aol.rb +12 -14
- data/lib/sisimai/lhost/apachejames.rb +19 -21
- data/lib/sisimai/lhost/barracuda.rb +10 -12
- data/lib/sisimai/lhost/bigfoot.rb +21 -21
- data/lib/sisimai/lhost/biglobe.rb +15 -16
- data/lib/sisimai/lhost/courier.rb +20 -20
- data/lib/sisimai/lhost/domino.rb +23 -19
- data/lib/sisimai/lhost/einsundeins.rb +20 -16
- data/lib/sisimai/lhost/exchange2003.rb +30 -29
- data/lib/sisimai/lhost/exchange2007.rb +70 -58
- data/lib/sisimai/lhost/exim.rb +175 -161
- data/lib/sisimai/lhost/ezweb.rb +31 -56
- data/lib/sisimai/lhost/facebook.rb +21 -33
- data/lib/sisimai/lhost/fml.rb +43 -48
- data/lib/sisimai/lhost/gmail.rb +29 -29
- data/lib/sisimai/lhost/gmx.rb +18 -17
- data/lib/sisimai/lhost/googlegroups.rb +9 -10
- data/lib/sisimai/lhost/gsuite.rb +21 -27
- data/lib/sisimai/lhost/imailserver.rb +25 -39
- data/lib/sisimai/lhost/interscanmss.rb +28 -31
- data/lib/sisimai/lhost/kddi.rb +22 -28
- data/lib/sisimai/lhost/mailfoundry.rb +11 -12
- data/lib/sisimai/lhost/mailmarshalsmtp.rb +25 -29
- data/lib/sisimai/lhost/mailru.rb +33 -27
- data/lib/sisimai/lhost/mcafee.rb +21 -31
- data/lib/sisimai/lhost/messagelabs.rb +17 -20
- data/lib/sisimai/lhost/messagingserver.rb +40 -37
- data/lib/sisimai/lhost/mfilter.rb +15 -16
- data/lib/sisimai/lhost/mxlogic.rb +24 -23
- data/lib/sisimai/lhost/notes.rb +17 -17
- data/lib/sisimai/lhost/office365.rb +63 -27
- data/lib/sisimai/lhost/opensmtpd.rb +12 -13
- data/lib/sisimai/lhost/outlook.rb +12 -15
- data/lib/sisimai/lhost/postfix.rb +179 -129
- data/lib/sisimai/lhost/powermta.rb +12 -14
- data/lib/sisimai/lhost/qmail.rb +44 -47
- data/lib/sisimai/lhost/receivingses.rb +15 -20
- data/lib/sisimai/lhost/sendgrid.rb +34 -32
- data/lib/sisimai/lhost/sendmail.rb +66 -53
- data/lib/sisimai/lhost/surfcontrol.rb +19 -19
- data/lib/sisimai/lhost/v5sendmail.rb +45 -39
- data/lib/sisimai/lhost/verizon.rb +35 -39
- data/lib/sisimai/lhost/x1.rb +18 -17
- data/lib/sisimai/lhost/x2.rb +17 -14
- data/lib/sisimai/lhost/x3.rb +19 -19
- data/lib/sisimai/lhost/x4.rb +72 -57
- data/lib/sisimai/lhost/x5.rb +17 -19
- data/lib/sisimai/lhost/x6.rb +41 -17
- data/lib/sisimai/lhost/yahoo.rb +17 -16
- data/lib/sisimai/lhost/yandex.rb +16 -20
- data/lib/sisimai/lhost/zoho.rb +16 -15
- data/lib/sisimai/lhost.rb +8 -10
- data/lib/sisimai/mail/maildir.rb +1 -3
- data/lib/sisimai/mail/mbox.rb +3 -4
- data/lib/sisimai/mail/memory.rb +0 -1
- data/lib/sisimai/mail/stdin.rb +1 -3
- data/lib/sisimai/mail.rb +3 -7
- data/lib/sisimai/mda.rb +28 -42
- data/lib/sisimai/message.rb +435 -325
- data/lib/sisimai/order.rb +5 -5
- data/lib/sisimai/reason/authfailure.rb +64 -0
- data/lib/sisimai/reason/badreputation.rb +53 -0
- data/lib/sisimai/reason/blocked.rb +94 -160
- data/lib/sisimai/reason/contenterror.rb +8 -9
- data/lib/sisimai/reason/delivered.rb +4 -6
- data/lib/sisimai/reason/exceedlimit.rb +10 -12
- data/lib/sisimai/reason/expired.rb +6 -8
- data/lib/sisimai/reason/feedback.rb +2 -3
- data/lib/sisimai/reason/filtered.rb +17 -19
- data/lib/sisimai/reason/hasmoved.rb +9 -10
- data/lib/sisimai/reason/hostunknown.rb +15 -15
- data/lib/sisimai/reason/mailboxfull.rb +10 -12
- data/lib/sisimai/reason/mailererror.rb +18 -20
- data/lib/sisimai/reason/mesgtoobig.rb +9 -11
- data/lib/sisimai/reason/networkerror.rb +5 -8
- data/lib/sisimai/reason/norelaying.rb +8 -11
- data/lib/sisimai/reason/notaccept.rb +13 -14
- data/lib/sisimai/reason/notcompliantrfc.rb +43 -0
- data/lib/sisimai/reason/onhold.rb +6 -9
- data/lib/sisimai/reason/policyviolation.rb +14 -12
- data/lib/sisimai/reason/rejected.rb +26 -24
- data/lib/sisimai/reason/requireptr.rb +69 -0
- data/lib/sisimai/reason/securityerror.rb +33 -36
- data/lib/sisimai/reason/spamdetected.rb +114 -147
- data/lib/sisimai/reason/speeding.rb +49 -0
- data/lib/sisimai/reason/suspend.rb +11 -11
- data/lib/sisimai/reason/syntaxerror.rb +11 -10
- data/lib/sisimai/reason/systemerror.rb +7 -9
- data/lib/sisimai/reason/systemfull.rb +7 -8
- data/lib/sisimai/reason/toomanyconn.rb +9 -11
- data/lib/sisimai/reason/undefined.rb +2 -3
- data/lib/sisimai/reason/userunknown.rb +129 -146
- data/lib/sisimai/reason/vacation.rb +3 -4
- data/lib/sisimai/reason/virusdetected.rb +10 -11
- data/lib/sisimai/reason.rb +59 -64
- data/lib/sisimai/rfc1894.rb +55 -28
- data/lib/sisimai/rfc2045.rb +373 -0
- data/lib/sisimai/rfc3464.rb +250 -308
- data/lib/sisimai/rfc3834.rb +42 -45
- data/lib/sisimai/rfc5322.rb +75 -100
- data/lib/sisimai/rfc5965.rb +31 -0
- data/lib/sisimai/rhost/cox.rb +5 -6
- data/lib/sisimai/rhost/franceptt.rb +6 -8
- data/lib/sisimai/rhost/godaddy.rb +12 -12
- data/lib/sisimai/rhost/{googleapps.rb → google.rb} +80 -72
- data/lib/sisimai/rhost/iua.rb +9 -10
- data/lib/sisimai/rhost/kddi.rb +6 -8
- data/lib/sisimai/rhost/{exchangeonline.rb → microsoft.rb} +115 -114
- data/lib/sisimai/rhost/mimecast.rb +42 -40
- data/lib/sisimai/rhost/nttdocomo.rb +13 -18
- data/lib/sisimai/rhost/spectrum.rb +10 -12
- data/lib/sisimai/rhost/{tencentqq.rb → tencent.rb} +7 -8
- data/lib/sisimai/rhost.rb +23 -31
- data/lib/sisimai/smtp/command.rb +59 -0
- data/lib/sisimai/smtp/error.rb +4 -7
- data/lib/sisimai/smtp/reply.rb +161 -74
- data/lib/sisimai/smtp/status.rb +504 -393
- data/lib/sisimai/smtp/transcript.rb +124 -0
- data/lib/sisimai/smtp.rb +0 -1
- data/lib/sisimai/string.rb +74 -5
- data/lib/sisimai/time.rb +1 -2
- data/lib/sisimai/version.rb +1 -1
- data/lib/sisimai.rb +35 -21
- data/set-of-emails/maildir/bsd/lhost-domino-02.eml +6 -3
- data/set-of-emails/maildir/bsd/lhost-googlegroups-15.eml +174 -0
- data/set-of-emails/maildir/bsd/lhost-gsuite-15.eml +229 -0
- data/set-of-emails/maildir/bsd/lhost-postfix-75.eml +51 -0
- data/set-of-emails/maildir/bsd/lhost-postfix-76.eml +101 -0
- data/set-of-emails/maildir/bsd/lhost-postfix-77.eml +74 -0
- data/set-of-emails/maildir/bsd/lhost-postfix-78.eml +91 -0
- data/set-of-emails/maildir/bsd/lhost-receivingses-08.eml +88 -0
- data/set-of-emails/maildir/bsd/rfc3464-43.eml +88 -0
- data/set-of-emails/maildir/bsd/rhost-google-03.eml +101 -0
- data/set-of-emails/maildir/bsd/rhost-google-04.eml +102 -0
- data/set-of-emails/maildir/bsd/rhost-google-05.eml +82 -0
- data/set-of-emails/maildir/bsd/rhost-google-06.eml +102 -0
- data/set-of-emails/maildir/bsd/rhost-google-07.eml +69 -0
- data/set-of-emails/maildir/bsd/rhost-google-08.eml +99 -0
- data/sisimai-java.gemspec +1 -1
- data/sisimai.gemspec +1 -1
- metadata +42 -23
- data/.rspec +0 -2
- data/lib/sisimai/data/yaml.rb +0 -33
- data/lib/sisimai/data.rb +0 -411
- data/lib/sisimai/mime.rb +0 -456
- data/set-of-emails/maildir/mac/reported-from-nick4tech-san-01.eml +0 -6
- /data/set-of-emails/maildir/bsd/{rfc3464-41.eml → rfc3834-05.eml} +0 -0
- /data/set-of-emails/maildir/bsd/{rhost-googleapps-01.eml → rhost-google-01.eml} +0 -0
- /data/set-of-emails/maildir/bsd/{rhost-googleapps-02.eml → rhost-google-02.eml} +0 -0
- /data/set-of-emails/maildir/bsd/{rhost-exchangeonline-01.eml → rhost-microsoft-01.eml} +0 -0
- /data/set-of-emails/maildir/bsd/{rhost-exchangeonline-02.eml → rhost-microsoft-02.eml} +0 -0
- /data/set-of-emails/maildir/bsd/{rhost-exchangeonline-03.eml → rhost-microsoft-03.eml} +0 -0
- /data/set-of-emails/maildir/bsd/{rhost-tencentqq-01.eml → rhost-tencent-01.eml} +0 -0
- /data/set-of-emails/maildir/bsd/{rhost-tencentqq-02.eml → rhost-tencent-02.eml} +0 -0
- /data/set-of-emails/maildir/bsd/{rhost-tencentqq-03.eml → rhost-tencent-03.eml} +0 -0
data/lib/sisimai/mime.rb
DELETED
@@ -1,456 +0,0 @@
|
|
1
|
-
module Sisimai
|
2
|
-
# Sisimai::MIME is MIME Utilities for Sisimai.
|
3
|
-
module MIME
|
4
|
-
# Imported from p5-Sisimail/lib/Sisimai/MIME.pm
|
5
|
-
class << self
|
6
|
-
require 'base64'
|
7
|
-
require 'sisimai/string'
|
8
|
-
|
9
|
-
ReE = {
|
10
|
-
:'7bit-encoded' => %r/^content-transfer-encoding:[ ]*7bit/m,
|
11
|
-
:'quoted-print' => %r/^content-transfer-encoding:[ ]*quoted-printable/m,
|
12
|
-
:'some-iso2022' => %r/^content-type:[ ]*.+;[ ]*charset=["']?(iso-2022-[-a-z0-9]+?)['"]?\b/m,
|
13
|
-
:'another-8bit' => %r/^content-type:[ ]*.+;[ ]*charset=["']?(.+?)['"]?\b/m,
|
14
|
-
:'with-charset' => %r/^content[-]type:[ ]*.+[;][ ]*charset=['"]?(.+?)['"]?\b/,
|
15
|
-
:'only-charset' => %r/^[\s\t]+charset=['"]?(.+?)['"]?\b/,
|
16
|
-
:'html-message' => %r|^content-type:[ ]*text/html;|m,
|
17
|
-
}.freeze
|
18
|
-
AlsoAppend = %r{\A(?:text/rfc822-headers|message/)}.freeze
|
19
|
-
ThisFormat = %r/\A(?:Content-Transfer-Encoding:\s*.+\n)?Content-Type:\s*([^ ;\s]+)/.freeze
|
20
|
-
LeavesOnly = %r{\A(?>
|
21
|
-
text/(?:plain|html|rfc822-headers)
|
22
|
-
|message/(?:x?delivery-status|rfc822|partial|feedback-report)
|
23
|
-
|multipart/(?:report|alternative|mixed|related|partial)
|
24
|
-
)
|
25
|
-
}x.freeze
|
26
|
-
|
27
|
-
# Check that the argument is MIME-Encoded string or not
|
28
|
-
# @param [String] argvs String to be checked
|
29
|
-
# @return [True,False] false: Not MIME encoded string
|
30
|
-
# true: MIME encoded string
|
31
|
-
def is_mimeencoded(argv1)
|
32
|
-
return nil unless argv1
|
33
|
-
|
34
|
-
text1 = argv1.delete('"')
|
35
|
-
mime1 = false
|
36
|
-
piece = []
|
37
|
-
|
38
|
-
if text1.include?(' ')
|
39
|
-
# Multiple MIME-Encoded strings in a line
|
40
|
-
piece = text1.split(' ')
|
41
|
-
else
|
42
|
-
piece << text1
|
43
|
-
end
|
44
|
-
|
45
|
-
while e = piece.shift do
|
46
|
-
# Check all the string in the array
|
47
|
-
next unless e =~ /[ \t]*=[?][-_0-9A-Za-z]+[?][BbQq][?].+[?]=?[ \t]*/
|
48
|
-
mime1 = true
|
49
|
-
end
|
50
|
-
return mime1
|
51
|
-
end
|
52
|
-
|
53
|
-
# Decode MIME-Encoded string
|
54
|
-
# @param [Array] argvs An array including MIME-Encoded text
|
55
|
-
# @return [String] MIME-Decoded text
|
56
|
-
def mimedecode(argvs = [])
|
57
|
-
characterset = nil
|
58
|
-
encodingname = nil
|
59
|
-
decodedtext0 = []
|
60
|
-
|
61
|
-
while e = argvs.shift do
|
62
|
-
# Check and decode each element
|
63
|
-
e = e.strip.delete('"')
|
64
|
-
|
65
|
-
if self.is_mimeencoded(e)
|
66
|
-
# MIME Encoded string like "=?utf-8?B?55m954yr44Gr44KD44KT44GT?="
|
67
|
-
next unless cv = e.match(/\A(.*)=[?]([-_0-9A-Za-z]+)[?]([BbQq])[?](.+)[?]=?(.*)\z/)
|
68
|
-
|
69
|
-
characterset ||= cv[2]
|
70
|
-
encodingname ||= cv[3]
|
71
|
-
mimeencoded0 = cv[4]
|
72
|
-
|
73
|
-
decodedtext0 << cv[1]
|
74
|
-
decodedtext0 << if encodingname.upcase == 'B'
|
75
|
-
Base64.decode64(mimeencoded0)
|
76
|
-
else
|
77
|
-
mimeencoded0.unpack('M').first
|
78
|
-
end
|
79
|
-
decodedtext0[-1].gsub!(/\r\n/, '')
|
80
|
-
decodedtext0 << cv[5]
|
81
|
-
else
|
82
|
-
decodedtext0 << if decodedtext0.empty? then e else ' ' << e end
|
83
|
-
end
|
84
|
-
end
|
85
|
-
|
86
|
-
return '' if decodedtext0.empty?
|
87
|
-
decodedtext1 = decodedtext0.join('')
|
88
|
-
|
89
|
-
if characterset && encodingname
|
90
|
-
# utf8 => UTF-8
|
91
|
-
characterset = 'UTF-8' if characterset.casecmp('UTF8') == 0
|
92
|
-
|
93
|
-
unless characterset.casecmp('UTF-8') == 0
|
94
|
-
# Characterset is not UTF-8
|
95
|
-
begin
|
96
|
-
decodedtext1.encode!('UTF-8', characterset)
|
97
|
-
rescue
|
98
|
-
decodedtext1 = 'FAILED TO CONVERT THE SUBJECT'
|
99
|
-
end
|
100
|
-
end
|
101
|
-
end
|
102
|
-
|
103
|
-
return decodedtext1.force_encoding('UTF-8').scrub('?')
|
104
|
-
end
|
105
|
-
|
106
|
-
# Decode MIME Quoted-Printable Encoded string
|
107
|
-
# @param [String] argv1 MIME Encoded text
|
108
|
-
# @param [Hash] heads Email header
|
109
|
-
# @return [String] MIME Decoded text
|
110
|
-
def qprintd(argv1 = nil, heads = {})
|
111
|
-
return nil unless argv1
|
112
|
-
return argv1.unpack('M').first.scrub('?') unless heads['content-type']
|
113
|
-
return argv1.unpack('M').first.scrub('?') if heads['content-type'].empty?
|
114
|
-
|
115
|
-
# Quoted-printable encoded part is the part of the text
|
116
|
-
boundary00 = Sisimai::MIME.boundary(heads['content-type'], 0)
|
117
|
-
|
118
|
-
# Decoded using unpack('M') entire body string when the boundary string
|
119
|
-
# or "Content-Transfer-Encoding: quoted-printable" are not included in
|
120
|
-
# the message body.
|
121
|
-
return argv1.unpack('M').first.scrub('?') if boundary00.empty?
|
122
|
-
return argv1.unpack('M').first.scrub('?') unless argv1.downcase =~ ReE[:'quoted-print']
|
123
|
-
|
124
|
-
boundary01 = Sisimai::MIME.boundary(heads['content-type'], 1)
|
125
|
-
bodystring = ''
|
126
|
-
notdecoded = ''
|
127
|
-
|
128
|
-
encodename = nil
|
129
|
-
ctencoding = nil
|
130
|
-
mimeinside = false
|
131
|
-
textslices = argv1.split("\n")
|
132
|
-
|
133
|
-
while e = textslices.shift do
|
134
|
-
# This is a multi-part message in MIME format. Your mail reader does not
|
135
|
-
# understand MIME message format.
|
136
|
-
# --=_gy7C4Gpes0RP4V5Bs9cK4o2Us2ZT57b-3OLnRN+4klS8dTmQ
|
137
|
-
# Content-Type: text/plain; charset=iso-8859-15
|
138
|
-
# Content-Transfer-Encoding: quoted-printable
|
139
|
-
if mimeinside
|
140
|
-
# Quoted-Printable encoded text block
|
141
|
-
if e == boundary00
|
142
|
-
# The next boundary string has appeared
|
143
|
-
# --=_gy7C4Gpes0RP4V5Bs9cK4o2Us2ZT57b-3OLnRN+4klS8dTmQ
|
144
|
-
hasdecoded = Sisimai::String.to_utf8(notdecoded.unpack('M').first, encodename)
|
145
|
-
bodystring << hasdecoded << e + "\n"
|
146
|
-
|
147
|
-
notdecoded = ''
|
148
|
-
mimeinside = false
|
149
|
-
ctencoding = false
|
150
|
-
encodename = nil
|
151
|
-
else
|
152
|
-
# Inside of Quoted-Printable encoded text
|
153
|
-
if e.size > 76
|
154
|
-
# Invalid line exists in "quoted-printable" part
|
155
|
-
e = [e].pack('M').chomp
|
156
|
-
else
|
157
|
-
# A bounce message generated by Office365(Outlook) include lines
|
158
|
-
# which are not proper as Quoted-Printable:
|
159
|
-
# - `=` is not encoded
|
160
|
-
# - Longer than 76 charaters a line
|
161
|
-
#
|
162
|
-
# Content-Transfer-Encoding: quoted-printable
|
163
|
-
# X-Microsoft-Exchange-Diagnostics:
|
164
|
-
# 1;SLXP216MB0381;27:IdH7U/WHGgJu6J...LiiA8rYgU/E7SQ==
|
165
|
-
# ...
|
166
|
-
mustencode = true
|
167
|
-
while true do
|
168
|
-
break if e.end_with?(' ', "\t")
|
169
|
-
break if e.split('').any? { |c| c.ord < 32 || c.ord > 126 }
|
170
|
-
if e.end_with?('=')
|
171
|
-
# Padding character of Base64 or not
|
172
|
-
break if e =~ /[\+\/0-9A-Za-z]{32,}[=]+\z/
|
173
|
-
else
|
174
|
-
# Including "=" not as "=3D"
|
175
|
-
break if e.include?('=') && ! e.upcase.include?('=3D')
|
176
|
-
end
|
177
|
-
mustencode = false
|
178
|
-
break
|
179
|
-
end
|
180
|
-
e = [e].pack('M').chomp if mustencode
|
181
|
-
mustencode = false
|
182
|
-
end
|
183
|
-
notdecoded << e + "\n"
|
184
|
-
end
|
185
|
-
else
|
186
|
-
# NOT Quoted-Printable encoded text block
|
187
|
-
lowercased = e.downcase
|
188
|
-
if e =~ /\A[-]{2}[^\s]+[^-]\z/
|
189
|
-
# Start of the boundary block
|
190
|
-
# --=_gy7C4Gpes0RP4V5Bs9cK4o2Us2ZT57b-3OLnRN+4klS8dTmQ
|
191
|
-
unless e == boundary00
|
192
|
-
# New boundary string has appeared
|
193
|
-
boundary00 = e
|
194
|
-
boundary01 = e + '--'
|
195
|
-
end
|
196
|
-
elsif cv = lowercased.match(ReE[:'with-charset']) || lowercased.match(ReE[:'only-charset'])
|
197
|
-
# Content-Type: text/plain; charset=ISO-2022-JP
|
198
|
-
encodename = cv[1]
|
199
|
-
mimeinside = true if ctencoding
|
200
|
-
|
201
|
-
elsif lowercased =~ ReE[:'quoted-print']
|
202
|
-
# Content-Transfer-Encoding: quoted-printable
|
203
|
-
ctencoding = true
|
204
|
-
mimeinside = true if encodename
|
205
|
-
|
206
|
-
elsif e == boundary01
|
207
|
-
# The end of boundary block
|
208
|
-
# --=_gy7C4Gpes0RP4V5Bs9cK4o2Us2ZT57b-3OLnRN+4klS8dTmQ--
|
209
|
-
mimeinside = false
|
210
|
-
end
|
211
|
-
|
212
|
-
bodystring << e + "\n"
|
213
|
-
end
|
214
|
-
end
|
215
|
-
|
216
|
-
bodystring << notdecoded unless notdecoded.empty?
|
217
|
-
return bodystring.scrub('?')
|
218
|
-
end
|
219
|
-
|
220
|
-
# Decode MIME BASE64 Encoded string
|
221
|
-
# @param [String] argv1 MIME Encoded text
|
222
|
-
# @return [String] MIME-Decoded text
|
223
|
-
def base64d(argv1)
|
224
|
-
return nil unless argv1
|
225
|
-
|
226
|
-
plain = nil
|
227
|
-
if cv = argv1.match(%r|([+/\=0-9A-Za-z\r\n]+)|) then plain = Base64.decode64(cv[1]) end
|
228
|
-
return plain.scrub('?')
|
229
|
-
end
|
230
|
-
|
231
|
-
# Get boundary string
|
232
|
-
# @param [String] argv1 The value of Content-Type header
|
233
|
-
# @param [Integer] start -1: boundary string itself
|
234
|
-
# 0: Start of boundary
|
235
|
-
# 1: End of boundary
|
236
|
-
# @return [String] Boundary string
|
237
|
-
def boundary(argv1 = nil, start = -1)
|
238
|
-
return nil unless argv1
|
239
|
-
value = ''
|
240
|
-
|
241
|
-
if cv = argv1.match(/\bboundary=([^ ]+)/i)
|
242
|
-
# Content-Type: multipart/mixed; boundary=Apple-Mail-5--931376066
|
243
|
-
# Content-Type: multipart/report; report-type=delivery-status;
|
244
|
-
# boundary="n6H9lKZh014511.1247824040/mx.example.jp"
|
245
|
-
value = cv[1]
|
246
|
-
value = value.split(/\n/, 2).shift if value =~ /\n/
|
247
|
-
value.delete!(%q|'";\\|)
|
248
|
-
value = '--' + value if start > -1
|
249
|
-
value = value + '--' if start > 0
|
250
|
-
end
|
251
|
-
|
252
|
-
return value
|
253
|
-
end
|
254
|
-
|
255
|
-
# Breaks up each multipart/* block
|
256
|
-
# @param [String] argv0 Text block of multipart/*
|
257
|
-
# @param [String] argv1 MIME type of the outside part
|
258
|
-
# @return [String] Decoded part as a plain text(text part only)
|
259
|
-
def breaksup(argv0 = nil, argv1 = '')
|
260
|
-
return nil unless argv0
|
261
|
-
|
262
|
-
hasflatten = '' # Message body including only text/plain and message/*
|
263
|
-
mimeformat = '' # MIME type string of this part
|
264
|
-
alternates = argv1.start_with?('multipart/alternative') ? true : false
|
265
|
-
|
266
|
-
# Get MIME type string from Content-Type: "..." field at the first line
|
267
|
-
# or the second line of the part.
|
268
|
-
if cv = argv0.match(ThisFormat) then mimeformat = cv[1].downcase end
|
269
|
-
|
270
|
-
# Sisimai require only MIME types defined in LeavesOnly variable
|
271
|
-
return '' unless mimeformat =~ LeavesOnly
|
272
|
-
return '' if alternates && mimeformat == 'text/html'
|
273
|
-
|
274
|
-
(upperchunk, lowerchunk) = argv0.split(/^$/, 2)
|
275
|
-
upperchunk.tr("\n", ' ').squeeze!(' ')
|
276
|
-
|
277
|
-
# Content-Description: Undelivered Message
|
278
|
-
# Content-Type: message/rfc822
|
279
|
-
# <EOM>
|
280
|
-
lowerchunk ||= ''
|
281
|
-
|
282
|
-
if mimeformat.start_with?('multipart/')
|
283
|
-
# Content-Type: multipart/*
|
284
|
-
mpboundary = Regexp.new(Regexp.escape(Sisimai::MIME.boundary(upperchunk, 0)) << "\n")
|
285
|
-
innerparts = lowerchunk.split(mpboundary)
|
286
|
-
innerparts.shift if innerparts[0].empty? || innerparts[0] == "\n"
|
287
|
-
|
288
|
-
while e = innerparts.shift do
|
289
|
-
# Find internal multipart/* blocks and decode
|
290
|
-
if cv = e.match(ThisFormat)
|
291
|
-
# Found "Content-Type" field at the first or second line of this
|
292
|
-
# splitted part
|
293
|
-
nextformat = cv[1].downcase
|
294
|
-
next unless nextformat =~ LeavesOnly
|
295
|
-
next if nextformat == 'text/html'
|
296
|
-
hasflatten << Sisimai::MIME.breaksup(e, mimeformat)
|
297
|
-
else
|
298
|
-
# The content of this part is almost '--': a part of boundary
|
299
|
-
# string which is used for splitting multipart/* blocks.
|
300
|
-
hasflatten << "\n"
|
301
|
-
end
|
302
|
-
end
|
303
|
-
else
|
304
|
-
# Is not "Content-Type: multipart/*"
|
305
|
-
if cv = upperchunk.match(/Content-Transfer-Encoding: ([^\s;]+)/)
|
306
|
-
# Content-Transfer-Encoding: quoted-printable|base64|7bit|...
|
307
|
-
ctencoding = cv[1].downcase
|
308
|
-
getdecoded = ''
|
309
|
-
|
310
|
-
if ctencoding == 'quoted-printable'
|
311
|
-
# Content-Transfer-Encoding: quoted-printable
|
312
|
-
getdecoded = Sisimai::MIME.qprintd(lowerchunk)
|
313
|
-
|
314
|
-
elsif ctencoding == 'base64'
|
315
|
-
# Content-Transfer-Encoding: base64
|
316
|
-
getdecoded = Sisimai::MIME.base64d(lowerchunk)
|
317
|
-
|
318
|
-
elsif ctencoding == '7bit'
|
319
|
-
# Content-Transfer-Encoding: 7bit
|
320
|
-
if cv = upperchunk.downcase.match(ReE[:'some-iso2022'])
|
321
|
-
# Content-Type: text/plain; charset=ISO-2022-JP
|
322
|
-
getdecoded = Sisimai::String.to_utf8(lowerchunk, cv[1])
|
323
|
-
else
|
324
|
-
# No "charset" parameter in Content-Type field
|
325
|
-
getdecoded = lowerchunk
|
326
|
-
end
|
327
|
-
else
|
328
|
-
# Content-Transfer-Encoding: 8bit, binary, and so on
|
329
|
-
getdecoded = lowerchunk
|
330
|
-
end
|
331
|
-
getdecoded.gsub!(/\r\n/, "\n") if getdecoded.include?("\r\n") # Convert CRLF to LF
|
332
|
-
|
333
|
-
if mimeformat =~ AlsoAppend
|
334
|
-
# Append field when the value of Content-Type: begins with
|
335
|
-
# message/ or equals text/rfc822-headers.
|
336
|
-
upperchunk.sub!(/Content-Transfer-Encoding:\s*[^\s]+./, '').strip!
|
337
|
-
hasflatten << upperchunk
|
338
|
-
|
339
|
-
elsif mimeformat == 'text/html'
|
340
|
-
# Delete HTML tags inside of text/html part whenever possible
|
341
|
-
getdecoded.gsub!(/[<][^@ ]+?[>]/, '')
|
342
|
-
end
|
343
|
-
|
344
|
-
unless getdecoded.empty?
|
345
|
-
# The string will be encoded to UTF-8 forcely and call String#scrub
|
346
|
-
# method to avoid the following errors:
|
347
|
-
# - incompatible character encodings: ASCII-8BIT and UTF-8
|
348
|
-
# - invalid byte sequence in UTF-8
|
349
|
-
unless getdecoded.encoding.to_s == 'UTF-8'
|
350
|
-
if cv = upperchunk.downcase.match(ReE[:'another-8bit'])
|
351
|
-
# ISO-8859-1, GB2312, and so on
|
352
|
-
getdecoded = Sisimai::String.to_utf8(getdecoded, cv[1])
|
353
|
-
end
|
354
|
-
end
|
355
|
-
# A part which has no "charset" parameter causes an ArgumentError:
|
356
|
-
# invalid byte sequence in UTF-8 so String#scrub should be called
|
357
|
-
hasflatten << getdecoded.scrub!('?') << "\n\n"
|
358
|
-
end
|
359
|
-
else
|
360
|
-
# Content-Type: text/plain OR text/rfc822-headers OR message/*
|
361
|
-
if mimeformat.start_with?('message/') || mimeformat == 'text/rfc822-headers'
|
362
|
-
# Append headers of multipart/* when the value of "Content-Type"
|
363
|
-
# is inlucded in the following MIME types:
|
364
|
-
# - message/delivery-status
|
365
|
-
# - message/rfc822
|
366
|
-
# - text/rfc822-headers
|
367
|
-
hasflatten << upperchunk
|
368
|
-
end
|
369
|
-
lowerchunk.sub!(/^--\z/m, '')
|
370
|
-
lowerchunk << "\n" unless lowerchunk =~ /\n\z/
|
371
|
-
hasflatten << lowerchunk
|
372
|
-
end
|
373
|
-
end
|
374
|
-
|
375
|
-
return hasflatten
|
376
|
-
end
|
377
|
-
|
378
|
-
# MIME decode entire message body
|
379
|
-
# @param [String] argv0 Content-Type header
|
380
|
-
# @param [String] argv1 Entire message body
|
381
|
-
# @return [String] Decoded message body
|
382
|
-
def makeflat(argv0 = nil, argv1 = nil)
|
383
|
-
return nil unless argv0
|
384
|
-
return nil unless argv1
|
385
|
-
|
386
|
-
ehboundary = Sisimai::MIME.boundary(argv0, 0)
|
387
|
-
mimeformat = ''
|
388
|
-
bodystring = ''
|
389
|
-
|
390
|
-
# Get MIME type string from an email header given as the 1st argument
|
391
|
-
if cv = argv0.match(%r|\A([0-9a-z]+/[^ ;]+)|) then mimeformat = cv[1] end
|
392
|
-
|
393
|
-
return '' unless mimeformat.include?('multipart/')
|
394
|
-
return '' if ehboundary.empty?
|
395
|
-
|
396
|
-
# Some bounce messages include lower-cased "content-type:" field such as
|
397
|
-
# content-type: message/delivery-status
|
398
|
-
# content-transfer-encoding: quoted-printable
|
399
|
-
argv1.gsub!(/[Cc]ontent-[Tt]ype:/, 'Content-Type:')
|
400
|
-
argv1.gsub!(/[Cc]ontent-[Tt]ransfer-[Ee]ncodeing:/, 'Content-Transfer-Encoding:')
|
401
|
-
|
402
|
-
# 1. Some bounce messages include upper-cased "Content-Transfer-Encoding",
|
403
|
-
# and "Content-Type" value such as
|
404
|
-
# - Content-Type: multipart/RELATED;
|
405
|
-
# - Content-Transfer-Encoding: 7BIT
|
406
|
-
# 2. Unused fields inside of mutipart/* block should be removed
|
407
|
-
argv1.gsub!(/(Content-[A-Za-z-]+?):[ ]*([^\s]+)/) do "#{$1}: #{$2.downcase}" end
|
408
|
-
argv1.gsub!(/^Content-(?:Description|Disposition):.+?\n$/, '')
|
409
|
-
|
410
|
-
multiparts = argv1.split(Regexp.new(Regexp.escape(ehboundary) << "\n?"))
|
411
|
-
multiparts.shift if multiparts[0].empty?
|
412
|
-
while e = multiparts.shift do
|
413
|
-
# Find internal multipart blocks and decode
|
414
|
-
catch :XCCT do
|
415
|
-
while true
|
416
|
-
# Remove fields except Content-Type, Content-Transfer-Encoding in
|
417
|
-
# each part such as the following:
|
418
|
-
# Date: Thu, 29 Apr 2018 22:22:22 +0900
|
419
|
-
# MIME-Version: 1.0
|
420
|
-
# Message-ID: ...
|
421
|
-
# Content-Transfer-Encoding: quoted-printable
|
422
|
-
# Content-Type: text/plain; charset=us-ascii
|
423
|
-
#
|
424
|
-
# Fields before "Content-Type:" in each part should have been removed
|
425
|
-
# and "Content-Type:" should be exist at the first line of each part.
|
426
|
-
# The field works as a delimiter to decode contents of each part.
|
427
|
-
#
|
428
|
-
throw :XCCT if e =~ /\AContent-T[ry]/ # The first field is "Content-Type:"
|
429
|
-
if cv = e.match(/\A(.+?)Content-Type:/m) # Capture lines before "Content-Type:"
|
430
|
-
throw :XCCT if cv[1] =~ /\n\n/ # There is no field before "Content-Type:"
|
431
|
-
e.sub!(/\A.+?(Content-T[ry].+)\z/m, '\1') # Remove fields before "Content-Type:"
|
432
|
-
end
|
433
|
-
throw :XCCT
|
434
|
-
end
|
435
|
-
end
|
436
|
-
|
437
|
-
if e =~ /\A(?:Content-[A-Za-z-]+:.+?\r\n)?Content-Type:[ ]*[^\s]+/
|
438
|
-
# Content-Type: multipart/*
|
439
|
-
bodystring << Sisimai::MIME.breaksup(e, mimeformat)
|
440
|
-
else
|
441
|
-
# Is not multipart/* block
|
442
|
-
e.sub!(%r|^Content-Transfer-Encoding:.+?\n|mi, '')
|
443
|
-
e.sub!(%r|^Content-Type:\s*text/plain.+?\n|mi, '')
|
444
|
-
bodystring << e
|
445
|
-
end
|
446
|
-
end
|
447
|
-
bodystring.gsub!(%r{^(Content-Type:\s*message/(?:rfc822|delivery-status)).+$}, '\1')
|
448
|
-
bodystring.gsub!(/^\n{2,}/, "\n")
|
449
|
-
|
450
|
-
return bodystring
|
451
|
-
end
|
452
|
-
|
453
|
-
end
|
454
|
-
end
|
455
|
-
end
|
456
|
-
|
@@ -1 +0,0 @@
|
|
1
|
-
Date: Tue, 06 Feb 2024 07:23:29 +0000
|
2
|
boundary="--==_mimepart_65c1fd2aee126_72f949768";
|
3
1
|
charset=UTF-8
|
4
|
-
---==_mimepart_65c1fd2aee126_72f949768
|
5
2
|
boundary="--==_mimepart_65c1fd2aee07b_72f9497553";
|
6
3
|
charset=UTF-8
|
7
|
-
---==_mimepart_65c1fd2aee07b_72f9497553
|
8
4
|
charset=UTF-8
|
9
5
|
The mail system
|
10
|
-
---==_mimepart_65c1fd2aee07b_72f9497553
|
11
6
|
charset=UTF-8
|
12
|
-
---==_mimepart_65c1fd2aee07b_72f9497553--
|
13
|
-
---==_mimepart_65c1fd2aee126_72f949768--
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|