sisimai 4.22.3-java → 4.22.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 +4 -4
- data/.travis.yml +1 -1
- data/Benchmarks.mk +54 -0
- data/ChangeLog.md +23 -2
- data/Developers.mk +42 -35
- data/Makefile +10 -0
- data/README-JA.md +13 -13
- data/README.md +14 -14
- data/lib/sisimai.rb +12 -18
- data/lib/sisimai/address.rb +64 -82
- data/lib/sisimai/arf.rb +27 -42
- data/lib/sisimai/bite/email.rb +2 -4
- data/lib/sisimai/bite/email/activehunter.rb +12 -17
- data/lib/sisimai/bite/email/amazonses.rb +30 -48
- data/lib/sisimai/bite/email/amazonworkmail.rb +20 -27
- data/lib/sisimai/bite/email/aol.rb +27 -35
- data/lib/sisimai/bite/email/apachejames.rb +17 -28
- data/lib/sisimai/bite/email/bigfoot.rb +20 -33
- data/lib/sisimai/bite/email/biglobe.rb +15 -24
- data/lib/sisimai/bite/email/courier.rb +37 -61
- data/lib/sisimai/bite/email/domino.rb +19 -28
- data/lib/sisimai/bite/email/einsundeins.rb +20 -34
- data/lib/sisimai/bite/email/exchange2003.rb +25 -43
- data/lib/sisimai/bite/email/exchange2007.rb +15 -23
- data/lib/sisimai/bite/email/exim.rb +101 -120
- data/lib/sisimai/bite/email/ezweb.rb +28 -44
- data/lib/sisimai/bite/email/facebook.rb +26 -37
- data/lib/sisimai/bite/email/fml.rb +11 -20
- data/lib/sisimai/bite/email/gmx.rb +17 -27
- data/lib/sisimai/bite/email/google.rb +19 -29
- data/lib/sisimai/bite/email/gsuite.rb +39 -48
- data/lib/sisimai/bite/email/imailserver.rb +25 -39
- data/lib/sisimai/bite/email/interscanmss.rb +19 -26
- data/lib/sisimai/bite/email/kddi.rb +20 -33
- data/lib/sisimai/bite/email/mailfoundry.rb +14 -24
- data/lib/sisimai/bite/email/mailmarshalsmtp.rb +15 -24
- data/lib/sisimai/bite/email/mailru.rb +40 -59
- data/lib/sisimai/bite/email/mcafee.rb +21 -35
- data/lib/sisimai/bite/email/messagelabs.rb +23 -38
- data/lib/sisimai/bite/email/messagingserver.rb +15 -27
- data/lib/sisimai/bite/email/mfilter.rb +19 -28
- data/lib/sisimai/bite/email/mxlogic.rb +31 -49
- data/lib/sisimai/bite/email/notes.rb +16 -24
- data/lib/sisimai/bite/email/office365.rb +29 -38
- data/lib/sisimai/bite/email/opensmtpd.rb +50 -67
- data/lib/sisimai/bite/email/outlook.rb +24 -36
- data/lib/sisimai/bite/email/postfix.rb +33 -42
- data/lib/sisimai/bite/email/qmail.rb +44 -59
- data/lib/sisimai/bite/email/receivingses.rb +28 -36
- data/lib/sisimai/bite/email/sendgrid.rb +28 -37
- data/lib/sisimai/bite/email/sendmail.rb +35 -51
- data/lib/sisimai/bite/email/surfcontrol.rb +17 -25
- data/lib/sisimai/bite/email/userdefined.rb +17 -28
- data/lib/sisimai/bite/email/v5sendmail.rb +32 -41
- data/lib/sisimai/bite/email/verizon.rb +31 -56
- data/lib/sisimai/bite/email/x1.rb +11 -18
- data/lib/sisimai/bite/email/x2.rb +11 -23
- data/lib/sisimai/bite/email/x3.rb +10 -19
- data/lib/sisimai/bite/email/x4.rb +46 -65
- data/lib/sisimai/bite/email/x5.rb +26 -37
- data/lib/sisimai/bite/email/yahoo.rb +11 -19
- data/lib/sisimai/bite/email/yandex.rb +19 -30
- data/lib/sisimai/bite/email/zoho.rb +21 -30
- data/lib/sisimai/bite/json.rb +1 -2
- data/lib/sisimai/bite/json/amazonses.rb +20 -25
- data/lib/sisimai/bite/json/sendgrid.rb +1 -1
- data/lib/sisimai/data.rb +36 -55
- data/lib/sisimai/data/json.rb +3 -3
- data/lib/sisimai/data/yaml.rb +1 -1
- data/lib/sisimai/datetime.rb +5 -21
- data/lib/sisimai/mail.rb +4 -6
- data/lib/sisimai/mail/maildir.rb +1 -1
- data/lib/sisimai/mda.rb +41 -44
- data/lib/sisimai/message.rb +2 -3
- data/lib/sisimai/message/email.rb +42 -52
- data/lib/sisimai/message/json.rb +7 -7
- data/lib/sisimai/mime.rb +25 -23
- data/lib/sisimai/order/email.rb +2 -2
- data/lib/sisimai/order/json.rb +2 -7
- data/lib/sisimai/reason.rb +41 -46
- data/lib/sisimai/reason/blocked.rb +60 -71
- data/lib/sisimai/reason/contenterror.rb +4 -8
- data/lib/sisimai/reason/delivered.rb +1 -3
- data/lib/sisimai/reason/exceedlimit.rb +10 -20
- data/lib/sisimai/reason/expired.rb +5 -9
- data/lib/sisimai/reason/feedback.rb +1 -3
- data/lib/sisimai/reason/filtered.rb +19 -38
- data/lib/sisimai/reason/hasmoved.rb +5 -8
- data/lib/sisimai/reason/hostunknown.rb +11 -18
- data/lib/sisimai/reason/mailboxfull.rb +14 -24
- data/lib/sisimai/reason/mailererror.rb +3 -5
- data/lib/sisimai/reason/mesgtoobig.rb +15 -25
- data/lib/sisimai/reason/networkerror.rb +8 -10
- data/lib/sisimai/reason/norelaying.rb +9 -14
- data/lib/sisimai/reason/notaccept.rb +9 -21
- data/lib/sisimai/reason/onhold.rb +3 -8
- data/lib/sisimai/reason/policyviolation.rb +8 -10
- data/lib/sisimai/reason/rejected.rb +36 -49
- data/lib/sisimai/reason/securityerror.rb +11 -13
- data/lib/sisimai/reason/spamdetected.rb +23 -37
- data/lib/sisimai/reason/suspend.rb +9 -10
- data/lib/sisimai/reason/syntaxerror.rb +3 -4
- data/lib/sisimai/reason/systemerror.rb +7 -9
- data/lib/sisimai/reason/systemfull.rb +2 -4
- data/lib/sisimai/reason/toomanyconn.rb +17 -30
- data/lib/sisimai/reason/undefined.rb +1 -3
- data/lib/sisimai/reason/userunknown.rb +28 -38
- data/lib/sisimai/reason/vacation.rb +4 -6
- data/lib/sisimai/reason/virusdetected.rb +4 -6
- data/lib/sisimai/rfc2606.rb +1 -2
- data/lib/sisimai/rfc3464.rb +87 -101
- data/lib/sisimai/rfc3834.rb +29 -39
- data/lib/sisimai/rfc5322.rb +17 -24
- data/lib/sisimai/rhost.rb +10 -7
- data/lib/sisimai/rhost/exchangeonline.rb +124 -255
- data/lib/sisimai/rhost/franceptt.rb +2 -2
- data/lib/sisimai/rhost/godaddy.rb +12 -25
- data/lib/sisimai/rhost/googleapps.rb +82 -183
- data/lib/sisimai/smtp.rb +4 -4
- data/lib/sisimai/smtp/error.rb +8 -8
- data/lib/sisimai/smtp/reply.rb +1 -1
- data/lib/sisimai/smtp/status.rb +1 -0
- data/lib/sisimai/string.rb +5 -7
- data/lib/sisimai/version.rb +1 -1
- data/set-of-emails/README.md +1 -1
- data/set-of-emails/maildir/bsd/README.md +50 -50
- data/sisimai-java.gemspec +1 -1
- data/sisimai.gemspec +1 -1
- metadata +4 -4
- data/lib/sisimai/skeleton.rb +0 -43
data/lib/sisimai.rb
CHANGED
@@ -29,25 +29,22 @@ module Sisimai
|
|
29
29
|
require 'sisimai/data'
|
30
30
|
require 'sisimai/message'
|
31
31
|
|
32
|
-
rtype = nil
|
33
32
|
input = argv1[:input] || nil
|
34
33
|
field = argv1[:field] || []
|
35
34
|
raise ' ***error: "field" accepts an array reference only' unless field.is_a? Array
|
36
35
|
|
37
36
|
unless input
|
38
37
|
# "input" did not specified, try to detect automatically.
|
39
|
-
|
40
|
-
if rtype == 'String' || rtype == 'IO'
|
38
|
+
if argv0.is_a?(::String) || argv0.is_a?(IO)
|
41
39
|
# The argument may be a path to email
|
42
40
|
input = 'email'
|
43
41
|
|
44
|
-
elsif
|
42
|
+
elsif argv0.is_a?(Array) || argv0.is_a?(Hash)
|
45
43
|
# The argument may be a decoded JSON object
|
46
44
|
input = 'json'
|
47
45
|
end
|
48
46
|
end
|
49
47
|
|
50
|
-
methodargv = {}
|
51
48
|
delivered1 = argv1[:delivered] || false
|
52
49
|
hookmethod = argv1[:hook] || nil
|
53
50
|
bouncedata = []
|
@@ -77,14 +74,14 @@ module Sisimai
|
|
77
74
|
|
78
75
|
if type == 'Array'
|
79
76
|
# [ {...}, {...}, ... ]
|
80
|
-
argv0.
|
77
|
+
while e = argv0.shift do
|
81
78
|
list << e
|
82
79
|
end
|
83
80
|
else
|
84
81
|
list << argv0
|
85
82
|
end
|
86
83
|
|
87
|
-
list.
|
84
|
+
while e = list.shift do
|
88
85
|
methodargv = { data: e, hook: hookmethod, input: 'json' }
|
89
86
|
mesg = Sisimai::Message.new(methodargv)
|
90
87
|
next if mesg.void
|
@@ -95,7 +92,6 @@ module Sisimai
|
|
95
92
|
next unless data
|
96
93
|
bouncedata.concat(data) if data.size > 0
|
97
94
|
end
|
98
|
-
|
99
95
|
else
|
100
96
|
# The value of "input" neither "email" nor "json"
|
101
97
|
raise ' ***error: invalid value of "input"'
|
@@ -118,7 +114,7 @@ module Sisimai
|
|
118
114
|
return nil unless argv0
|
119
115
|
|
120
116
|
nyaan = Sisimai.make(argv0, argv1) || []
|
121
|
-
if RUBY_PLATFORM
|
117
|
+
if RUBY_PLATFORM.include?('java')
|
122
118
|
# java-based ruby environment like JRuby.
|
123
119
|
require 'jrjackson'
|
124
120
|
jsonstring = JrJackson::Json.dump(nyaan)
|
@@ -132,18 +128,17 @@ module Sisimai
|
|
132
128
|
# Parser engine list (MTA modules)
|
133
129
|
# @return [Hash] Parser engine table
|
134
130
|
def engine
|
135
|
-
names = %w|Bite::Email Bite::JSON ARF RFC3464 RFC3834|
|
136
131
|
table = {}
|
137
132
|
|
138
|
-
|
139
|
-
r = 'Sisimai::'
|
133
|
+
%w[Bite::Email Bite::JSON ARF RFC3464 RFC3834].each do |e|
|
134
|
+
r = 'Sisimai::' << e
|
140
135
|
require r.gsub('::', '/').downcase
|
141
136
|
|
142
|
-
if e
|
137
|
+
if e.start_with?('Bite::Email', 'Bite::JSON')
|
143
138
|
# Sisimai::Bite::Email or Sisimai::Bite::JSON
|
144
139
|
Module.const_get(r).send(:index).each do |ee|
|
145
140
|
# Load and get the value of "description" from each module
|
146
|
-
rr =
|
141
|
+
rr = 'Sisimai::' << e + '::' << ee
|
147
142
|
require rr.gsub('::', '/').downcase
|
148
143
|
table[rr.to_sym] = Module.const_get(rr).send(:description)
|
149
144
|
end
|
@@ -164,11 +159,10 @@ module Sisimai
|
|
164
159
|
names = Sisimai::Reason.index
|
165
160
|
|
166
161
|
# These reasons are not included in the results of Sisimai::Reason.index
|
167
|
-
names.concat(%w
|
168
|
-
|
162
|
+
names.concat(%w[Delivered Feedback Undefined Vacation])
|
169
163
|
names.each do |e|
|
170
164
|
# Call .description() method of Sisimai::Reason::*
|
171
|
-
r = 'Sisimai::Reason::'
|
165
|
+
r = 'Sisimai::Reason::' << e
|
172
166
|
require r.gsub('::', '/').downcase
|
173
167
|
table[e.to_sym] = Module.const_get(r).send(:description)
|
174
168
|
end
|
@@ -182,7 +176,7 @@ module Sisimai
|
|
182
176
|
def match(argvs = '')
|
183
177
|
return nil if argvs.empty?
|
184
178
|
require 'sisimai/reason'
|
185
|
-
return Sisimai::Reason.match(argvs)
|
179
|
+
return Sisimai::Reason.match(argvs.downcase)
|
186
180
|
end
|
187
181
|
end
|
188
182
|
end
|
data/lib/sisimai/address.rb
CHANGED
@@ -3,6 +3,12 @@ module Sisimai
|
|
3
3
|
class Address
|
4
4
|
# Imported from p5-Sisimail/lib/Sisimai/Address.pm
|
5
5
|
require 'sisimai/rfc5322'
|
6
|
+
Indicators = {
|
7
|
+
:'email-address' => (1 << 0), # <neko@example.org>
|
8
|
+
:'quoted-string' => (1 << 1), # "Neko, Nyaan"
|
9
|
+
:'comment-block' => (1 << 2), # (neko)
|
10
|
+
}
|
11
|
+
|
6
12
|
@@undisclosed = 'libsisimai.org.invalid'
|
7
13
|
|
8
14
|
# Return pseudo recipient or sender address
|
@@ -11,7 +17,7 @@ module Sisimai
|
|
11
17
|
# nil when the argv1 is neither :r nor :s
|
12
18
|
def self.undisclosed(argv1)
|
13
19
|
return nil unless argv1
|
14
|
-
return nil unless %w
|
20
|
+
return nil unless %w[r s].index(argv1.to_s)
|
15
21
|
|
16
22
|
local = argv1 == :r ? 'recipient' : 'sender'
|
17
23
|
return sprintf('undisclosed-%s-in-headers@%s', local, @@undisclosed)
|
@@ -49,46 +55,30 @@ module Sisimai
|
|
49
55
|
# find('Neko <neko(nyaan)@example.org>')
|
50
56
|
# #=> [{ address: 'neko@example.org', name: 'Neko', comment: '(nyaan)'}]
|
51
57
|
return nil unless argv1
|
52
|
-
argv1 = argv1.gsub(/[\r\n]/, '')
|
53
58
|
|
59
|
+
characters = argv1.gsub(/[\r\n]/, '').split('')
|
54
60
|
emailtable = { address: '', name: '', comment: '' }
|
55
61
|
addrtables = []
|
56
62
|
readbuffer = []
|
57
63
|
readcursor = 0
|
58
|
-
delimiters = ['<', '>', '(', ')', '"', ',']
|
59
|
-
validemail = %r{(?>
|
60
|
-
(?:([^\s]+|["].+?["])) # local part
|
61
|
-
[@]
|
62
|
-
(?:([^@\s]+|[0-9A-Za-z:\.]+)) # domain part
|
63
|
-
)
|
64
|
-
}x
|
65
|
-
indicators = {
|
66
|
-
:'email-address' => (1 << 0), # <neko@example.org>
|
67
|
-
:'quoted-string' => (1 << 1), # "Neko, Nyaan"
|
68
|
-
:'comment-block' => (1 << 2), # (neko)
|
69
|
-
}
|
70
64
|
|
71
65
|
v = emailtable # temporary buffer
|
72
66
|
p = '' # current position
|
73
67
|
|
74
|
-
|
68
|
+
while e = characters.shift do
|
75
69
|
# Check each characters
|
76
|
-
|
77
|
-
if delimiters.detect { |r| r == e }
|
70
|
+
if %w[< > ( ) " ,].detect { |r| r == e }
|
78
71
|
# The character is a delimiter character
|
79
72
|
if e == ','
|
80
73
|
# Separator of email addresses or not
|
81
|
-
if v[:address]
|
74
|
+
if v[:address].start_with?('<') && v[:address].end_with?('>') && v[:address].include?('@')
|
82
75
|
# An email address has already been picked
|
83
|
-
|
84
|
-
if readcursor & indicators[:'comment-block'] > 0
|
76
|
+
if readcursor & Indicators[:'comment-block'] > 0
|
85
77
|
# The cursor is in the comment block (Neko, Nyaan)
|
86
|
-
v[:comment]
|
87
|
-
|
88
|
-
elsif readcursor & indicators[:'quoted-string'] > 0
|
78
|
+
v[:comment] << e
|
79
|
+
elsif readcursor & Indicators[:'quoted-string'] > 0
|
89
80
|
# "Neko, Nyaan"
|
90
|
-
v[:name]
|
91
|
-
|
81
|
+
v[:name] << e
|
92
82
|
else
|
93
83
|
# The cursor is not in neither the quoted-string nor the comment block
|
94
84
|
readcursor = 0 # reset cursor position
|
@@ -98,7 +88,7 @@ module Sisimai
|
|
98
88
|
end
|
99
89
|
else
|
100
90
|
# "Neko, Nyaan" <neko@nyaan.example.org> OR <"neko,nyaan"@example.org>
|
101
|
-
p.size > 0 ? (v[p]
|
91
|
+
p.size > 0 ? (v[p] << e) : (v[:name] << e)
|
102
92
|
end
|
103
93
|
next
|
104
94
|
end # End of if(',')
|
@@ -106,12 +96,11 @@ module Sisimai
|
|
106
96
|
if e == '<'
|
107
97
|
# <: The beginning of an email address or not
|
108
98
|
if v[:address].size > 0
|
109
|
-
p.size > 0 ? (v[p]
|
110
|
-
|
99
|
+
p.size > 0 ? (v[p] << e) : (v[:name] << e)
|
111
100
|
else
|
112
101
|
# <neko@nyaan.example.org>
|
113
|
-
readcursor |=
|
114
|
-
v[:address]
|
102
|
+
readcursor |= Indicators[:'email-address']
|
103
|
+
v[:address] << e
|
115
104
|
p = :address
|
116
105
|
end
|
117
106
|
next
|
@@ -120,47 +109,45 @@ module Sisimai
|
|
120
109
|
|
121
110
|
if e == '>'
|
122
111
|
# >: The end of an email address or not
|
123
|
-
if readcursor &
|
112
|
+
if readcursor & Indicators[:'email-address'] > 0
|
124
113
|
# <neko@example.org>
|
125
|
-
readcursor &= ~
|
126
|
-
v[:address]
|
114
|
+
readcursor &= ~Indicators[:'email-address']
|
115
|
+
v[:address] << e
|
127
116
|
p = ''
|
128
117
|
else
|
129
118
|
# a comment block or a display name
|
130
|
-
p.size > 0 ? (v[:comment]
|
119
|
+
p.size > 0 ? (v[:comment] << e) : (v[:name] << e)
|
131
120
|
end
|
132
121
|
next
|
133
122
|
end # End of if('>')
|
134
123
|
|
135
124
|
if e == '('
|
136
125
|
# The beginning of a comment block or not
|
137
|
-
if readcursor &
|
126
|
+
if readcursor & Indicators[:'email-address'] > 0
|
138
127
|
# <"neko(nyaan)"@example.org> or <neko(nyaan)@example.org>
|
139
|
-
if v[:address]
|
128
|
+
if v[:address].include?('"')
|
140
129
|
# Quoted local part: <"neko(nyaan)"@example.org>
|
141
|
-
v[:address]
|
142
|
-
|
130
|
+
v[:address] << e
|
143
131
|
else
|
144
132
|
# Comment: <neko(nyaan)@example.org>
|
145
|
-
readcursor |=
|
146
|
-
v[:comment]
|
147
|
-
v[:comment]
|
133
|
+
readcursor |= Indicators[:'comment-block']
|
134
|
+
v[:comment] << ' ' if v[:comment].end_with?(')')
|
135
|
+
v[:comment] << e
|
148
136
|
p = :comment
|
149
137
|
end
|
150
|
-
elsif readcursor &
|
138
|
+
elsif readcursor & Indicators[:'comment-block'] > 0
|
151
139
|
# Comment at the outside of an email address (...(...)
|
152
|
-
v[:comment]
|
153
|
-
v[:comment]
|
140
|
+
v[:comment] << ' ' if v[:comment].end_with?(')')
|
141
|
+
v[:comment] << e
|
154
142
|
|
155
|
-
elsif readcursor &
|
143
|
+
elsif readcursor & Indicators[:'quoted-string'] > 0
|
156
144
|
# "Neko, Nyaan(cat)", Deal as a display name
|
157
|
-
v[:name]
|
158
|
-
|
145
|
+
v[:name] << e
|
159
146
|
else
|
160
147
|
# The beginning of a comment block
|
161
|
-
readcursor |=
|
162
|
-
v[:comment]
|
163
|
-
v[:comment]
|
148
|
+
readcursor |= Indicators[:'comment-block']
|
149
|
+
v[:comment] << ' ' if v[:comment].end_with?(')')
|
150
|
+
v[:comment] << e
|
164
151
|
p = :comment
|
165
152
|
end
|
166
153
|
next
|
@@ -168,27 +155,25 @@ module Sisimai
|
|
168
155
|
|
169
156
|
if e == ')'
|
170
157
|
# The end of a comment block or not
|
171
|
-
if readcursor &
|
158
|
+
if readcursor & Indicators[:'email-address'] > 0
|
172
159
|
# <"neko(nyaan)"@example.org> OR <neko(nyaan)@example.org>
|
173
|
-
if v[:address]
|
160
|
+
if v[:address].include?('"')
|
174
161
|
# Quoted string in the local part: <"neko(nyaan)"@example.org>
|
175
|
-
v[:address]
|
176
|
-
|
162
|
+
v[:address] << e
|
177
163
|
else
|
178
164
|
# Comment: <neko(nyaan)@example.org>
|
179
|
-
readcursor &= ~
|
180
|
-
v[:comment]
|
165
|
+
readcursor &= ~Indicators[:'comment-block']
|
166
|
+
v[:comment] << e
|
181
167
|
p = :address
|
182
168
|
end
|
183
|
-
elsif readcursor &
|
169
|
+
elsif readcursor & Indicators[:'comment-block'] > 0
|
184
170
|
# Comment at the outside of an email address (...(...)
|
185
|
-
readcursor &= ~
|
186
|
-
v[:comment]
|
171
|
+
readcursor &= ~Indicators[:'comment-block']
|
172
|
+
v[:comment] << e
|
187
173
|
p = ''
|
188
|
-
|
189
174
|
else
|
190
175
|
# Deal as a display name
|
191
|
-
readcursor &= ~
|
176
|
+
readcursor &= ~Indicators[:'comment-block']
|
192
177
|
v[:name] = e
|
193
178
|
p = ''
|
194
179
|
end
|
@@ -199,15 +184,15 @@ module Sisimai
|
|
199
184
|
# The beginning or the end of a quoted-string
|
200
185
|
if p.size > 0
|
201
186
|
# email-address or comment-block
|
202
|
-
v[p]
|
187
|
+
v[p] << e
|
203
188
|
else
|
204
189
|
# Display name
|
205
|
-
v[:name]
|
206
|
-
if readcursor &
|
190
|
+
v[:name] << e
|
191
|
+
if readcursor & Indicators[:'quoted-string'] > 0
|
207
192
|
# "Neko, Nyaan"
|
208
|
-
unless v[:name]
|
193
|
+
unless v[:name].end_with?(%Q|\x5c"|)
|
209
194
|
# "Neko, Nyaan \"...
|
210
|
-
readcursor &= ~
|
195
|
+
readcursor &= ~Indicators[:'quoted-string']
|
211
196
|
p = ''
|
212
197
|
end
|
213
198
|
end
|
@@ -216,7 +201,7 @@ module Sisimai
|
|
216
201
|
end # End of if('"')
|
217
202
|
else
|
218
203
|
# The character is not a delimiter
|
219
|
-
p.size > 0 ? (v[p]
|
204
|
+
p.size > 0 ? (v[p] << e) : (v[:name] << e)
|
220
205
|
next
|
221
206
|
end
|
222
207
|
end
|
@@ -226,9 +211,9 @@ module Sisimai
|
|
226
211
|
readbuffer << v
|
227
212
|
else
|
228
213
|
# No email address like <neko@example.org> in the argument
|
229
|
-
if cv = v[:name].match(
|
214
|
+
if cv = v[:name].match(/(?>(?:([^\s]+|["].+?["]))[@](?:([^@\s]+|[0-9A-Za-z:\.]+)))/)
|
230
215
|
# String like an email address will be set to the value of "address"
|
231
|
-
v[:address] =
|
216
|
+
v[:address] = cv[1] + '@' + cv[2]
|
232
217
|
|
233
218
|
elsif Sisimai::RFC5322.is_mailerdaemon(v[:name])
|
234
219
|
# Allow if the argument is MAILER-DAEMON
|
@@ -240,14 +225,14 @@ module Sisimai
|
|
240
225
|
if cv = v[:address].match(/(.*)([(].+[)])(.*)/)
|
241
226
|
# (nyaan)nekochan@example.org, nekochan(nyaan)cat@example.org or
|
242
227
|
# nekochan(nyaan)@example.org
|
243
|
-
v[:address] = cv[1]
|
228
|
+
v[:address] = cv[1] << cv[3]
|
244
229
|
v[:comment] = cv[2]
|
245
230
|
end
|
246
231
|
readbuffer << v
|
247
232
|
end
|
248
233
|
end
|
249
234
|
|
250
|
-
readbuffer.
|
235
|
+
while e = readbuffer.shift do
|
251
236
|
# The element must not include any character except from 0x20 to 0x7e.
|
252
237
|
next if e[:address] =~ /[^\x20-\x7e]/
|
253
238
|
|
@@ -260,12 +245,12 @@ module Sisimai
|
|
260
245
|
# except a domain part is an IP address like neko@[192.0.2.222]
|
261
246
|
e[:address] = e[:address].sub(/\A[\[<{('`]/, '')
|
262
247
|
e[:address] = e[:address].sub(/['`>})]\z/, '')
|
263
|
-
e[:address] = e[:address].
|
248
|
+
e[:address] = e[:address].chomp(']') unless e[:address] =~ /[@]\[[0-9A-Za-z:\.]+\]\z/
|
264
249
|
|
265
250
|
unless e[:address] =~ /\A["].+["][@]/
|
266
251
|
# Remove double-quotations
|
267
252
|
e[:address] = e[:address].sub(/\A["]/, '')
|
268
|
-
e[:address] = e[:address].
|
253
|
+
e[:address] = e[:address].chomp('"')
|
269
254
|
end
|
270
255
|
|
271
256
|
if addrs
|
@@ -275,14 +260,13 @@ module Sisimai
|
|
275
260
|
else
|
276
261
|
# Remove double-quotations, trailing spaces.
|
277
262
|
[:name, :comment].each do |f|
|
278
|
-
e[f] = e[f].
|
279
|
-
e[f] = e[f].sub(/\s*\z/, '')
|
263
|
+
e[f] = e[f].strip
|
280
264
|
end
|
281
265
|
e[:comment] = '' unless e[:comment] =~ /\A[(].+[)]/
|
282
266
|
|
283
267
|
e[:name] = e[:name].squeeze(' ') unless e[:name] =~ /\A["].+["]\z/
|
284
268
|
e[:name] = e[:name].sub(/\A["]/, '') unless e[:name] =~ /\A["].+["][@]/
|
285
|
-
e[:name] = e[:name].
|
269
|
+
e[:name] = e[:name].chomp('"')
|
286
270
|
end
|
287
271
|
addrtables << e
|
288
272
|
end
|
@@ -311,10 +295,9 @@ module Sisimai
|
|
311
295
|
# expand_verp('bounce+neko=example.org@example.org') #=> 'neko@example.org'
|
312
296
|
def self.expand_verp(email)
|
313
297
|
local = email.split('@', 2).first
|
314
|
-
verp0 = ''
|
315
298
|
|
316
299
|
if cv = local.match(/\A[-_\w]+?[+](\w[-._\w]+\w)[=](\w[-.\w]+\w)\z/)
|
317
|
-
verp0 =
|
300
|
+
verp0 = cv[1] + '@' + cv[2]
|
318
301
|
return verp0 if Sisimai::RFC5322.is_emailaddress(verp0)
|
319
302
|
else
|
320
303
|
return ''
|
@@ -332,7 +315,7 @@ module Sisimai
|
|
332
315
|
local = email.split('@')
|
333
316
|
value = ''
|
334
317
|
if cv = local[0].match(/\A([-_\w]+?)[+].+\z/)
|
335
|
-
value =
|
318
|
+
value = cv[1] + '@' + local[1]
|
336
319
|
end
|
337
320
|
return value
|
338
321
|
end
|
@@ -389,12 +372,11 @@ module Sisimai
|
|
389
372
|
end
|
390
373
|
@user = lpart
|
391
374
|
@host = dpart
|
392
|
-
@address =
|
393
|
-
|
375
|
+
@address = lpart + '@' + dpart
|
394
376
|
else
|
395
377
|
# The argument does not include "@"
|
396
378
|
return nil unless Sisimai::RFC5322.is_mailerdaemon(thing[:address])
|
397
|
-
return nil if thing[:address]
|
379
|
+
return nil if thing[:address].include?(' ')
|
398
380
|
|
399
381
|
# The argument does not include " "
|
400
382
|
@user = thing[:address]
|
data/lib/sisimai/arf.rb
CHANGED
@@ -6,16 +6,6 @@ module Sisimai
|
|
6
6
|
require 'sisimai/bite/email'
|
7
7
|
require 'sisimai/rfc5322'
|
8
8
|
|
9
|
-
Re0 = {
|
10
|
-
:'content-type' => %r|multipart/mixed|,
|
11
|
-
:'report-type' => %r/report-type=["]?feedback-report["]?/,
|
12
|
-
:'from' => %r{(?:
|
13
|
-
staff[@]hotmail[.]com\z
|
14
|
-
|complaints[@]email-abuse[.]amazonses[.]com\z
|
15
|
-
)
|
16
|
-
}x,
|
17
|
-
:'subject' => %r/complaint[ ]about[ ]message[ ]from[ ]/,
|
18
|
-
}.freeze
|
19
9
|
# http://tools.ietf.org/html/rfc5965
|
20
10
|
# http://en.wikipedia.org/wiki/Feedback_loop_(email)
|
21
11
|
# http://en.wikipedia.org/wiki/Abuse_Reporting_Format
|
@@ -23,8 +13,10 @@ module Sisimai
|
|
23
13
|
# Netease DMARC uses: This is a spf/dkim authentication-failure report for an email message received from IP
|
24
14
|
# OpenDMARC 1.3.0 uses: This is an authentication failure report for an email message received from IP
|
25
15
|
# Abusix ARF uses this is an autogenerated email abuse complaint regarding your network.
|
26
|
-
|
27
|
-
|
16
|
+
Indicators = Sisimai::Bite::Email.INDICATORS
|
17
|
+
StartingOf = { rfc822: ['Content-Type: message/rfc822', 'Content-Type: text/rfc822-headers'] }.freeze
|
18
|
+
MarkingsOf = {
|
19
|
+
message: %r{\A(?>
|
28
20
|
[Tt]his[ ]is[ ].+[ ]email[ ]abuse[ ]report
|
29
21
|
|[Tt]his[ ]is[ ](?:
|
30
22
|
an[ ]autogenerated[ ]email[ ]abuse[ ]complaint
|
@@ -33,17 +25,13 @@ module Sisimai
|
|
33
25
|
)
|
34
26
|
)
|
35
27
|
}x,
|
36
|
-
:rfc822 => %r[\AContent-Type: (:?message/rfc822|text/rfc822-headers)],
|
37
|
-
:endof => %r/\A__END_OF_EMAIL_MESSAGE__\z/,
|
38
28
|
}.freeze
|
39
|
-
Indicators = Sisimai::Bite::Email.INDICATORS
|
40
29
|
LongFields = Sisimai::RFC5322.LONGFIELDS
|
41
30
|
RFC822Head = Sisimai::RFC5322.HEADERFIELDS
|
42
31
|
|
43
32
|
def description; return 'Abuse Feedback Reporting Format'; end
|
44
33
|
def smtpagent; return 'FeedBack-Loop'; end
|
45
34
|
def headerlist; return []; end
|
46
|
-
def pattern; return Re0; end
|
47
35
|
|
48
36
|
# Email is a Feedback-Loop message or not
|
49
37
|
# @param [Hash] heads Email header including "Content-Type", "From",
|
@@ -54,14 +42,19 @@ module Sisimai
|
|
54
42
|
return false unless heads
|
55
43
|
match = false
|
56
44
|
|
57
|
-
if heads['content-type'] =~
|
45
|
+
if heads['content-type'] =~ /report-type=["]?feedback-report["]?/
|
58
46
|
# Content-Type: multipart/report; report-type=feedback-report; ...
|
59
47
|
match = true
|
60
48
|
|
61
|
-
elsif heads['content-type']
|
49
|
+
elsif heads['content-type'].include?('multipart/mixed')
|
62
50
|
# Microsoft (Hotmail, MSN, Live, Outlook) uses its own report format.
|
63
51
|
# Amazon SES Complaints bounces
|
64
|
-
|
52
|
+
mfrom = %r{(?:
|
53
|
+
staff[@]hotmail[.]com\z
|
54
|
+
|complaints[@]email-abuse[.]amazonses[.]com\z
|
55
|
+
)
|
56
|
+
}x
|
57
|
+
if heads['from'] =~ mfrom && heads['subject'].include?('complaint about message from ')
|
65
58
|
# From: staff@hotmail.com
|
66
59
|
# From: complaints@email-abuse.amazonses.com
|
67
60
|
# Subject: complaint about message from 192.0.2.1
|
@@ -127,10 +120,10 @@ module Sisimai
|
|
127
120
|
# generator is using to generate the report. The version number in
|
128
121
|
# this specification is set to "1".
|
129
122
|
#
|
130
|
-
hasdivided.
|
123
|
+
while e = hasdivided.shift do
|
131
124
|
if readcursor.zero?
|
132
125
|
# Beginning of the bounce message or delivery status part
|
133
|
-
if e =~
|
126
|
+
if e =~ MarkingsOf[:message]
|
134
127
|
readcursor |= Indicators[:deliverystatus]
|
135
128
|
next
|
136
129
|
end
|
@@ -138,7 +131,7 @@ module Sisimai
|
|
138
131
|
|
139
132
|
if (readcursor & Indicators[:'message-rfc822']).zero?
|
140
133
|
# Beginning of the original message part
|
141
|
-
if e
|
134
|
+
if e.start_with?(StartingOf[:rfc822][0], StartingOf[:rfc822][1])
|
142
135
|
readcursor |= Indicators[:'message-rfc822']
|
143
136
|
next
|
144
137
|
end
|
@@ -159,9 +152,7 @@ module Sisimai
|
|
159
152
|
|
160
153
|
elsif cv = e.match(/\AFrom:[ ]*(.+)\z/)
|
161
154
|
# Microsoft ARF: original sender.
|
162
|
-
if commondata[:from].empty?
|
163
|
-
commondata[:from] = Sisimai::Address.s3s4(cv[1])
|
164
|
-
end
|
155
|
+
commondata[:from] = Sisimai::Address.s3s4(cv[1]) if commondata[:from].empty?
|
165
156
|
|
166
157
|
elsif cv = e.match(/\A([-0-9A-Za-z]+?)[:][ ]*(.+)\z/)
|
167
158
|
# Get required headers only
|
@@ -172,16 +163,15 @@ module Sisimai
|
|
172
163
|
next unless RFC822Head.key?(lhs)
|
173
164
|
|
174
165
|
previousfn = lhs
|
175
|
-
rfc822part
|
166
|
+
rfc822part << e + "\n"
|
176
167
|
rcptintext = rhs if lhs == 'to'
|
177
168
|
|
178
|
-
elsif e
|
169
|
+
elsif e.start_with?(' ', "\t")
|
179
170
|
# Continued line from the previous line
|
180
|
-
rfc822part
|
171
|
+
rfc822part << e + "\n" if LongFields.key?(previousfn)
|
181
172
|
next if e.size > 0
|
182
|
-
rcptintext
|
173
|
+
rcptintext << e if previousfn == 'to'
|
183
174
|
end
|
184
|
-
|
185
175
|
else
|
186
176
|
# Before "message/rfc822"
|
187
177
|
next unless readcursor & Indicators[:deliverystatus] > 0
|
@@ -248,11 +238,9 @@ module Sisimai
|
|
248
238
|
elsif cv = e.match(/\AOriginal-Mail-From:[ ]*(.+)\z/)
|
249
239
|
# the header is optional and MUST NOT appear more than once.
|
250
240
|
# Original-Mail-From: <somespammer@example.net>
|
251
|
-
if commondata[:from].empty?
|
252
|
-
commondata[:from] = Sisimai::Address.s3s4(cv[1])
|
253
|
-
end
|
241
|
+
commondata[:from] = Sisimai::Address.s3s4(cv[1]) if commondata[:from].empty?
|
254
242
|
|
255
|
-
elsif e =~
|
243
|
+
elsif e =~ MarkingsOf[:message]
|
256
244
|
# This is an email abuse report for an email message with the
|
257
245
|
# message-id of 0000-000000000000000000000000000000000@mx
|
258
246
|
# received from IP address 192.0.2.1 on
|
@@ -264,7 +252,7 @@ module Sisimai
|
|
264
252
|
|
265
253
|
if arfheaders[:feedbacktype] == 'auth-failure' && arfheaders[:authres]
|
266
254
|
# Append the value of Authentication-Results header
|
267
|
-
commondata[:diagnosis]
|
255
|
+
commondata[:diagnosis] << ' ' << arfheaders[:authres]
|
268
256
|
end
|
269
257
|
|
270
258
|
if recipients.zero?
|
@@ -276,18 +264,15 @@ module Sisimai
|
|
276
264
|
|
277
265
|
unless rfc822part =~ /\bFrom: [^ ]+[@][^ ]+\b/
|
278
266
|
# There is no "From:" header in the original message
|
279
|
-
|
280
|
-
|
281
|
-
rfc822part += sprintf("From: %s\n", commondata[:from])
|
282
|
-
end
|
267
|
+
# Append the value of "Original-Mail-From" value as a sender address.
|
268
|
+
rfc822part << 'From: ' << commondata[:from] + "\n" if commondata[:from].size > 0
|
283
269
|
end
|
284
270
|
|
285
271
|
if cv = mhead['subject'].match(/complaint about message from (\d{1,3}[.]\d{1,3}[.]\d{1,3}[.]\d{1,3})/)
|
286
272
|
# Microsoft ARF: remote host address.
|
287
273
|
arfheaders[:rhost] = cv[1]
|
288
|
-
commondata[:diagnosis] =
|
289
|
-
'This is a Microsoft email abuse report for an email message received from IP
|
290
|
-
arfheaders[:rhost], mhead['date'])
|
274
|
+
commondata[:diagnosis] =
|
275
|
+
'This is a Microsoft email abuse report for an email message received from IP' << arfheaders[:rhost] + ' on ' << mhead['date']
|
291
276
|
end
|
292
277
|
|
293
278
|
dscontents.map do |e|
|