sisimai 4.22.3 → 4.22.4

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.

Files changed (130) hide show
  1. checksums.yaml +4 -4
  2. data/.travis.yml +1 -1
  3. data/Benchmarks.mk +54 -0
  4. data/ChangeLog.md +23 -2
  5. data/Developers.mk +42 -35
  6. data/Makefile +10 -0
  7. data/README-JA.md +13 -13
  8. data/README.md +14 -14
  9. data/lib/sisimai.rb +12 -18
  10. data/lib/sisimai/address.rb +64 -82
  11. data/lib/sisimai/arf.rb +27 -42
  12. data/lib/sisimai/bite/email.rb +2 -4
  13. data/lib/sisimai/bite/email/activehunter.rb +12 -17
  14. data/lib/sisimai/bite/email/amazonses.rb +30 -48
  15. data/lib/sisimai/bite/email/amazonworkmail.rb +20 -27
  16. data/lib/sisimai/bite/email/aol.rb +27 -35
  17. data/lib/sisimai/bite/email/apachejames.rb +17 -28
  18. data/lib/sisimai/bite/email/bigfoot.rb +20 -33
  19. data/lib/sisimai/bite/email/biglobe.rb +15 -24
  20. data/lib/sisimai/bite/email/courier.rb +37 -61
  21. data/lib/sisimai/bite/email/domino.rb +19 -28
  22. data/lib/sisimai/bite/email/einsundeins.rb +20 -34
  23. data/lib/sisimai/bite/email/exchange2003.rb +25 -43
  24. data/lib/sisimai/bite/email/exchange2007.rb +15 -23
  25. data/lib/sisimai/bite/email/exim.rb +101 -120
  26. data/lib/sisimai/bite/email/ezweb.rb +28 -44
  27. data/lib/sisimai/bite/email/facebook.rb +26 -37
  28. data/lib/sisimai/bite/email/fml.rb +11 -20
  29. data/lib/sisimai/bite/email/gmx.rb +17 -27
  30. data/lib/sisimai/bite/email/google.rb +19 -29
  31. data/lib/sisimai/bite/email/gsuite.rb +39 -48
  32. data/lib/sisimai/bite/email/imailserver.rb +25 -39
  33. data/lib/sisimai/bite/email/interscanmss.rb +19 -26
  34. data/lib/sisimai/bite/email/kddi.rb +20 -33
  35. data/lib/sisimai/bite/email/mailfoundry.rb +14 -24
  36. data/lib/sisimai/bite/email/mailmarshalsmtp.rb +15 -24
  37. data/lib/sisimai/bite/email/mailru.rb +40 -59
  38. data/lib/sisimai/bite/email/mcafee.rb +21 -35
  39. data/lib/sisimai/bite/email/messagelabs.rb +23 -38
  40. data/lib/sisimai/bite/email/messagingserver.rb +15 -27
  41. data/lib/sisimai/bite/email/mfilter.rb +19 -28
  42. data/lib/sisimai/bite/email/mxlogic.rb +31 -49
  43. data/lib/sisimai/bite/email/notes.rb +16 -24
  44. data/lib/sisimai/bite/email/office365.rb +29 -38
  45. data/lib/sisimai/bite/email/opensmtpd.rb +50 -67
  46. data/lib/sisimai/bite/email/outlook.rb +24 -36
  47. data/lib/sisimai/bite/email/postfix.rb +33 -42
  48. data/lib/sisimai/bite/email/qmail.rb +44 -59
  49. data/lib/sisimai/bite/email/receivingses.rb +28 -36
  50. data/lib/sisimai/bite/email/sendgrid.rb +28 -37
  51. data/lib/sisimai/bite/email/sendmail.rb +35 -51
  52. data/lib/sisimai/bite/email/surfcontrol.rb +17 -25
  53. data/lib/sisimai/bite/email/userdefined.rb +17 -28
  54. data/lib/sisimai/bite/email/v5sendmail.rb +32 -41
  55. data/lib/sisimai/bite/email/verizon.rb +31 -56
  56. data/lib/sisimai/bite/email/x1.rb +11 -18
  57. data/lib/sisimai/bite/email/x2.rb +11 -23
  58. data/lib/sisimai/bite/email/x3.rb +10 -19
  59. data/lib/sisimai/bite/email/x4.rb +46 -65
  60. data/lib/sisimai/bite/email/x5.rb +26 -37
  61. data/lib/sisimai/bite/email/yahoo.rb +11 -19
  62. data/lib/sisimai/bite/email/yandex.rb +19 -30
  63. data/lib/sisimai/bite/email/zoho.rb +21 -30
  64. data/lib/sisimai/bite/json.rb +1 -2
  65. data/lib/sisimai/bite/json/amazonses.rb +20 -25
  66. data/lib/sisimai/bite/json/sendgrid.rb +1 -1
  67. data/lib/sisimai/data.rb +36 -55
  68. data/lib/sisimai/data/json.rb +3 -3
  69. data/lib/sisimai/data/yaml.rb +1 -1
  70. data/lib/sisimai/datetime.rb +5 -21
  71. data/lib/sisimai/mail.rb +4 -6
  72. data/lib/sisimai/mail/maildir.rb +1 -1
  73. data/lib/sisimai/mda.rb +41 -44
  74. data/lib/sisimai/message.rb +2 -3
  75. data/lib/sisimai/message/email.rb +42 -52
  76. data/lib/sisimai/message/json.rb +7 -7
  77. data/lib/sisimai/mime.rb +25 -23
  78. data/lib/sisimai/order/email.rb +2 -2
  79. data/lib/sisimai/order/json.rb +2 -7
  80. data/lib/sisimai/reason.rb +41 -46
  81. data/lib/sisimai/reason/blocked.rb +60 -71
  82. data/lib/sisimai/reason/contenterror.rb +4 -8
  83. data/lib/sisimai/reason/delivered.rb +1 -3
  84. data/lib/sisimai/reason/exceedlimit.rb +10 -20
  85. data/lib/sisimai/reason/expired.rb +5 -9
  86. data/lib/sisimai/reason/feedback.rb +1 -3
  87. data/lib/sisimai/reason/filtered.rb +19 -38
  88. data/lib/sisimai/reason/hasmoved.rb +5 -8
  89. data/lib/sisimai/reason/hostunknown.rb +11 -18
  90. data/lib/sisimai/reason/mailboxfull.rb +14 -24
  91. data/lib/sisimai/reason/mailererror.rb +3 -5
  92. data/lib/sisimai/reason/mesgtoobig.rb +15 -25
  93. data/lib/sisimai/reason/networkerror.rb +8 -10
  94. data/lib/sisimai/reason/norelaying.rb +9 -14
  95. data/lib/sisimai/reason/notaccept.rb +9 -21
  96. data/lib/sisimai/reason/onhold.rb +3 -8
  97. data/lib/sisimai/reason/policyviolation.rb +8 -10
  98. data/lib/sisimai/reason/rejected.rb +36 -49
  99. data/lib/sisimai/reason/securityerror.rb +11 -13
  100. data/lib/sisimai/reason/spamdetected.rb +23 -37
  101. data/lib/sisimai/reason/suspend.rb +9 -10
  102. data/lib/sisimai/reason/syntaxerror.rb +3 -4
  103. data/lib/sisimai/reason/systemerror.rb +7 -9
  104. data/lib/sisimai/reason/systemfull.rb +2 -4
  105. data/lib/sisimai/reason/toomanyconn.rb +17 -30
  106. data/lib/sisimai/reason/undefined.rb +1 -3
  107. data/lib/sisimai/reason/userunknown.rb +28 -38
  108. data/lib/sisimai/reason/vacation.rb +4 -6
  109. data/lib/sisimai/reason/virusdetected.rb +4 -6
  110. data/lib/sisimai/rfc2606.rb +1 -2
  111. data/lib/sisimai/rfc3464.rb +87 -101
  112. data/lib/sisimai/rfc3834.rb +29 -39
  113. data/lib/sisimai/rfc5322.rb +17 -24
  114. data/lib/sisimai/rhost.rb +10 -7
  115. data/lib/sisimai/rhost/exchangeonline.rb +124 -255
  116. data/lib/sisimai/rhost/franceptt.rb +2 -2
  117. data/lib/sisimai/rhost/godaddy.rb +12 -25
  118. data/lib/sisimai/rhost/googleapps.rb +82 -183
  119. data/lib/sisimai/smtp.rb +4 -4
  120. data/lib/sisimai/smtp/error.rb +8 -8
  121. data/lib/sisimai/smtp/reply.rb +1 -1
  122. data/lib/sisimai/smtp/status.rb +1 -0
  123. data/lib/sisimai/string.rb +5 -7
  124. data/lib/sisimai/version.rb +1 -1
  125. data/set-of-emails/README.md +1 -1
  126. data/set-of-emails/maildir/bsd/README.md +50 -50
  127. data/sisimai-java.gemspec +1 -1
  128. data/sisimai.gemspec +1 -1
  129. metadata +5 -5
  130. data/lib/sisimai/skeleton.rb +0 -43
@@ -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
- rtype = argv0.class.to_s
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 rtype =~ /\A(?:Array|Hash)\z/
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.each do |e|
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.each do |e|
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 =~ /java/
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
- names.each do |e|
139
- r = 'Sisimai::' + e
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 =~ /\ABite::(?:Email|JSON)\z/
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 = sprintf('Sisimai::%s::%s', e, ee)
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|Delivered Feedback Undefined Vacation|)
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::' + e
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
@@ -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|r s|.index(argv1.to_s)
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
- argv1.split('').each do |e|
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] =~ /\A[<].+[@].+[>]\z/
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] += e
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] += e
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] += e) : (v[:name] += e)
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] += e) : (v[:name] += e)
110
-
99
+ p.size > 0 ? (v[p] << e) : (v[:name] << e)
111
100
  else
112
101
  # <neko@nyaan.example.org>
113
- readcursor |= indicators[:'email-address']
114
- v[:address] += e
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 & indicators[:'email-address'] > 0
112
+ if readcursor & Indicators[:'email-address'] > 0
124
113
  # <neko@example.org>
125
- readcursor &= ~indicators[:'email-address']
126
- v[:address] += e
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] += e) : (v[:name] += e)
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 & indicators[:'email-address'] > 0
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] += e
142
-
130
+ v[:address] << e
143
131
  else
144
132
  # Comment: <neko(nyaan)@example.org>
145
- readcursor |= indicators[:'comment-block']
146
- v[:comment] += ' ' if v[:comment] =~ /[)]\z/
147
- v[:comment] += e
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 & indicators[:'comment-block'] > 0
138
+ elsif readcursor & Indicators[:'comment-block'] > 0
151
139
  # Comment at the outside of an email address (...(...)
152
- v[:comment] += ' ' if v[:comment] =~ /[)]\z/
153
- v[:comment] += e
140
+ v[:comment] << ' ' if v[:comment].end_with?(')')
141
+ v[:comment] << e
154
142
 
155
- elsif readcursor & indicators[:'quoted-string'] > 0
143
+ elsif readcursor & Indicators[:'quoted-string'] > 0
156
144
  # "Neko, Nyaan(cat)", Deal as a display name
157
- v[:name] += e
158
-
145
+ v[:name] << e
159
146
  else
160
147
  # The beginning of a comment block
161
- readcursor |= indicators[:'comment-block']
162
- v[:comment] += ' ' if v[:comment] =~ /[)]\z/
163
- v[:comment] += e
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 & indicators[:'email-address'] > 0
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] += e
176
-
162
+ v[:address] << e
177
163
  else
178
164
  # Comment: <neko(nyaan)@example.org>
179
- readcursor &= ~indicators[:'comment-block']
180
- v[:comment] += e
165
+ readcursor &= ~Indicators[:'comment-block']
166
+ v[:comment] << e
181
167
  p = :address
182
168
  end
183
- elsif readcursor & indicators[:'comment-block'] > 0
169
+ elsif readcursor & Indicators[:'comment-block'] > 0
184
170
  # Comment at the outside of an email address (...(...)
185
- readcursor &= ~indicators[:'comment-block']
186
- v[:comment] += e
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 &= ~indicators[:'comment-block']
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] += e
187
+ v[p] << e
203
188
  else
204
189
  # Display name
205
- v[:name] += e
206
- if readcursor & indicators[:'quoted-string'] > 0
190
+ v[:name] << e
191
+ if readcursor & Indicators[:'quoted-string'] > 0
207
192
  # "Neko, Nyaan"
208
- unless v[:name] =~ /\x5c["]\z/
193
+ unless v[:name].end_with?(%Q|\x5c"|)
209
194
  # "Neko, Nyaan \"...
210
- readcursor &= ~indicators[:'quoted-string']
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] += e) : (v[:name] += e)
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(validemail)
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] = sprintf('%s@%s', cv[1], cv[2])
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] + cv[3]
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.each do |e|
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].sub(/\]\z/, '') unless e[:address] =~ /[@]\[[0-9A-Z:\.]+\]\z/i
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].sub(/["]\z/, '')
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].sub(/\A\s*/, '')
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].sub(/["]\z/, '')
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 = sprintf('%s@%s', cv[1], cv[2])
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 = sprintf('%s@%s', cv[1], local[1])
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 = sprintf('%s@%s', lpart, dpart)
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]
@@ -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
- Re1 = {
27
- :begin => %r{\A(?>
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'] =~ Re0[:'report-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'] =~ Re0[:'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
- if heads['from'] =~ Re0[:'from'] && heads['subject'] =~ Re0[:'subject']
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.each do |e|
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 =~ Re1[:begin]
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 =~ Re1[:rfc822]
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 += e + "\n"
166
+ rfc822part << e + "\n"
176
167
  rcptintext = rhs if lhs == 'to'
177
168
 
178
- elsif e =~ /\A[ \t]+/
169
+ elsif e.start_with?(' ', "\t")
179
170
  # Continued line from the previous line
180
- rfc822part += e + "\n" if LongFields.key?(previousfn)
171
+ rfc822part << e + "\n" if LongFields.key?(previousfn)
181
172
  next if e.size > 0
182
- rcptintext += e if previousfn == 'to'
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 =~ Re1[:begin]
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] += ' ' + arfheaders[:authres]
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
- if commondata[:from].size > 0
280
- # Append the value of "Original-Mail-From" value as a sender address.
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] = sprintf(
289
- 'This is a Microsoft email abuse report for an email message received from IP %s on %s',
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|