sisimai 4.24.1-java → 4.25.0-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.

Files changed (155) hide show
  1. checksums.yaml +4 -4
  2. data/.travis.yml +3 -2
  3. data/ANALYTICAL-PRECISION +16 -25
  4. data/ChangeLog.md +41 -0
  5. data/Developers.mk +2 -2
  6. data/README-JA.md +13 -13
  7. data/README.md +13 -13
  8. data/lib/sisimai.rb +3 -7
  9. data/lib/sisimai/address.rb +25 -41
  10. data/lib/sisimai/arf.rb +58 -59
  11. data/lib/sisimai/bite.rb +0 -1
  12. data/lib/sisimai/bite/email.rb +7 -7
  13. data/lib/sisimai/bite/email/activehunter.rb +4 -3
  14. data/lib/sisimai/bite/email/amavis.rb +133 -0
  15. data/lib/sisimai/bite/email/amazonses.rb +53 -87
  16. data/lib/sisimai/bite/email/amazonworkmail.rb +51 -57
  17. data/lib/sisimai/bite/email/aol.rb +50 -76
  18. data/lib/sisimai/bite/email/apachejames.rb +2 -2
  19. data/lib/sisimai/bite/email/bigfoot.rb +47 -74
  20. data/lib/sisimai/bite/email/biglobe.rb +8 -9
  21. data/lib/sisimai/bite/email/courier.rb +56 -101
  22. data/lib/sisimai/bite/email/domino.rb +7 -8
  23. data/lib/sisimai/bite/email/einsundeins.rb +4 -5
  24. data/lib/sisimai/bite/email/exchange2003.rb +21 -22
  25. data/lib/sisimai/bite/email/exchange2007.rb +26 -28
  26. data/lib/sisimai/bite/email/exim.rb +48 -47
  27. data/lib/sisimai/bite/email/ezweb.rb +24 -36
  28. data/lib/sisimai/bite/email/facebook.rb +54 -79
  29. data/lib/sisimai/bite/email/fml.rb +10 -10
  30. data/lib/sisimai/bite/email/gmx.rb +6 -6
  31. data/lib/sisimai/bite/email/google.rb +12 -13
  32. data/lib/sisimai/bite/email/gsuite.rb +80 -108
  33. data/lib/sisimai/bite/email/imailserver.rb +16 -16
  34. data/lib/sisimai/bite/email/interscanmss.rb +4 -6
  35. data/lib/sisimai/bite/email/kddi.rb +9 -11
  36. data/lib/sisimai/bite/email/mailfoundry.rb +2 -2
  37. data/lib/sisimai/bite/email/mailmarshalsmtp.rb +2 -2
  38. data/lib/sisimai/bite/email/mailru.rb +12 -13
  39. data/lib/sisimai/bite/email/mcafee.rb +31 -25
  40. data/lib/sisimai/bite/email/messagelabs.rb +48 -87
  41. data/lib/sisimai/bite/email/messagingserver.rb +9 -10
  42. data/lib/sisimai/bite/email/mfilter.rb +16 -16
  43. data/lib/sisimai/bite/email/mxlogic.rb +11 -11
  44. data/lib/sisimai/bite/email/notes.rb +5 -6
  45. data/lib/sisimai/bite/email/office365.rb +25 -42
  46. data/lib/sisimai/bite/email/opensmtpd.rb +8 -8
  47. data/lib/sisimai/bite/email/outlook.rb +49 -67
  48. data/lib/sisimai/bite/email/postfix.rb +78 -112
  49. data/lib/sisimai/bite/email/qmail.rb +23 -23
  50. data/lib/sisimai/bite/email/receivingses.rb +53 -86
  51. data/lib/sisimai/bite/email/sendgrid.rb +65 -84
  52. data/lib/sisimai/bite/email/sendmail.rb +89 -117
  53. data/lib/sisimai/bite/email/surfcontrol.rb +15 -18
  54. data/lib/sisimai/bite/email/userdefined.rb +1 -1
  55. data/lib/sisimai/bite/email/v5sendmail.rb +3 -4
  56. data/lib/sisimai/bite/email/verizon.rb +7 -8
  57. data/lib/sisimai/bite/email/x1.rb +2 -2
  58. data/lib/sisimai/bite/email/x2.rb +2 -2
  59. data/lib/sisimai/bite/email/x3.rb +3 -3
  60. data/lib/sisimai/bite/email/x4.rb +22 -22
  61. data/lib/sisimai/bite/email/x5.rb +40 -49
  62. data/lib/sisimai/bite/email/yahoo.rb +3 -3
  63. data/lib/sisimai/bite/email/yandex.rb +54 -82
  64. data/lib/sisimai/bite/email/zoho.rb +6 -6
  65. data/lib/sisimai/bite/json/amazonses.rb +20 -20
  66. data/lib/sisimai/bite/json/sendgrid.rb +2 -2
  67. data/lib/sisimai/data.rb +27 -40
  68. data/lib/sisimai/datetime.rb +146 -162
  69. data/lib/sisimai/mda.rb +30 -31
  70. data/lib/sisimai/message/email.rb +83 -123
  71. data/lib/sisimai/message/json.rb +2 -4
  72. data/lib/sisimai/mime.rb +31 -34
  73. data/lib/sisimai/order/email.rb +23 -22
  74. data/lib/sisimai/reason.rb +61 -61
  75. data/lib/sisimai/reason/blocked.rb +139 -135
  76. data/lib/sisimai/reason/contenterror.rb +11 -10
  77. data/lib/sisimai/reason/exceedlimit.rb +4 -4
  78. data/lib/sisimai/reason/expired.rb +20 -20
  79. data/lib/sisimai/reason/filtered.rb +19 -18
  80. data/lib/sisimai/reason/hasmoved.rb +3 -3
  81. data/lib/sisimai/reason/hostunknown.rb +19 -19
  82. data/lib/sisimai/reason/mailboxfull.rb +49 -49
  83. data/lib/sisimai/reason/mailererror.rb +16 -16
  84. data/lib/sisimai/reason/mesgtoobig.rb +17 -17
  85. data/lib/sisimai/reason/networkerror.rb +19 -19
  86. data/lib/sisimai/reason/norelaying.rb +16 -16
  87. data/lib/sisimai/reason/notaccept.rb +9 -10
  88. data/lib/sisimai/reason/onhold.rb +1 -1
  89. data/lib/sisimai/reason/policyviolation.rb +21 -20
  90. data/lib/sisimai/reason/rejected.rb +53 -53
  91. data/lib/sisimai/reason/securityerror.rb +29 -29
  92. data/lib/sisimai/reason/spamdetected.rb +127 -127
  93. data/lib/sisimai/reason/suspend.rb +17 -17
  94. data/lib/sisimai/reason/systemerror.rb +22 -21
  95. data/lib/sisimai/reason/systemfull.rb +6 -6
  96. data/lib/sisimai/reason/toomanyconn.rb +19 -18
  97. data/lib/sisimai/reason/userunknown.rb +122 -121
  98. data/lib/sisimai/reason/vacation.rb +8 -8
  99. data/lib/sisimai/reason/virusdetected.rb +8 -8
  100. data/lib/sisimai/rfc1894.rb +142 -0
  101. data/lib/sisimai/rfc3464.rb +70 -70
  102. data/lib/sisimai/rfc3834.rb +15 -15
  103. data/lib/sisimai/rfc5322.rb +20 -36
  104. data/lib/sisimai/rhost.rb +1 -0
  105. data/lib/sisimai/rhost/exchangeonline.rb +31 -33
  106. data/lib/sisimai/rhost/franceptt.rb +23 -23
  107. data/lib/sisimai/rhost/godaddy.rb +28 -28
  108. data/lib/sisimai/rhost/googleapps.rb +39 -41
  109. data/lib/sisimai/rhost/kddi.rb +3 -3
  110. data/lib/sisimai/rhost/tencentqq.rb +51 -0
  111. data/lib/sisimai/smtp/error.rb +14 -21
  112. data/lib/sisimai/smtp/reply.rb +14 -13
  113. data/lib/sisimai/smtp/status.rb +178 -179
  114. data/lib/sisimai/string.rb +13 -12
  115. data/lib/sisimai/version.rb +1 -1
  116. data/set-of-emails/README.md +1 -5
  117. data/set-of-emails/maildir/bsd/arf-23.eml +49 -0
  118. data/set-of-emails/maildir/bsd/email-amavis-01.eml +78 -0
  119. data/set-of-emails/maildir/bsd/email-amavis-02.eml +78 -0
  120. data/set-of-emails/maildir/bsd/email-exchange2007-04.eml +146 -0
  121. data/set-of-emails/maildir/bsd/email-exim-60.eml +94 -0
  122. data/set-of-emails/maildir/bsd/email-ezweb-08.eml +49 -0
  123. data/set-of-emails/maildir/bsd/email-google-19.eml +67 -0
  124. data/set-of-emails/maildir/bsd/email-mcafee-05.eml +74 -0
  125. data/set-of-emails/maildir/bsd/email-messagingserver-12.eml +99 -0
  126. data/set-of-emails/maildir/bsd/email-postfix-46.eml +81 -0
  127. data/set-of-emails/maildir/bsd/email-postfix-47.eml +79 -0
  128. data/set-of-emails/maildir/bsd/email-postfix-48.eml +79 -0
  129. data/set-of-emails/maildir/bsd/email-postfix-49.eml +141 -0
  130. data/set-of-emails/maildir/bsd/email-postfix-50.eml +143 -0
  131. data/set-of-emails/maildir/bsd/email-postfix-51.eml +73 -0
  132. data/set-of-emails/maildir/bsd/email-postfix-52.eml +79 -0
  133. data/set-of-emails/maildir/bsd/email-postfix-53.eml +76 -0
  134. data/set-of-emails/maildir/bsd/email-postfix-54.eml +73 -0
  135. data/set-of-emails/maildir/bsd/email-postfix-55.eml +74 -0
  136. data/set-of-emails/maildir/bsd/email-postfix-56.eml +78 -0
  137. data/set-of-emails/maildir/bsd/email-qmail-10.eml +50 -0
  138. data/set-of-emails/maildir/bsd/email-x2-05.eml +38 -0
  139. data/set-of-emails/maildir/bsd/rhost-google-apps-02.eml +88 -0
  140. data/set-of-emails/maildir/bsd/rhost-tencentqq-01.eml +84 -0
  141. data/set-of-emails/maildir/bsd/rhost-tencentqq-02.eml +84 -0
  142. data/set-of-emails/maildir/bsd/rhost-tencentqq-03.eml +81 -0
  143. data/set-of-emails/maildir/dos/email-amavis-01.eml +78 -0
  144. data/set-of-emails/maildir/dos/email-apachejames-01.eml +1 -2
  145. data/set-of-emails/maildir/dos/email-messagelabs-01.eml +67 -50
  146. data/set-of-emails/maildir/dos/email-x4-01.eml +31 -76
  147. data/set-of-emails/maildir/dos/rhost-tencentqq-01.eml +84 -0
  148. data/set-of-emails/maildir/mac/email-amavis-01.eml +1 -4
  149. data/set-of-emails/maildir/mac/email-apachejames-01.eml +1 -9
  150. data/set-of-emails/maildir/mac/email-messagelabs-01.eml +1 -9
  151. data/set-of-emails/maildir/mac/email-x4-01.eml +1 -5
  152. data/set-of-emails/maildir/mac/rhost-tencentqq-01.eml +1 -4
  153. metadata +35 -4
  154. data/set-of-emails/logo/horizontalversions.png +0 -0
  155. data/set-of-emails/logo/icon.png +0 -0
@@ -17,15 +17,10 @@ module Sisimai
17
17
  BorderLine = '__MIME_ENCODED_BOUNDARY__'
18
18
  EndOfEmail = Sisimai::String.EOM
19
19
  RFC822Head = Sisimai::RFC5322.HEADERFIELDS
20
- RFC3834Set = Sisimai::RFC3834.headerlist.map(&:downcase)
20
+ RFC3834Set = Sisimai::RFC3834.headerlist
21
21
  HeaderList = %w[from to date subject content-type reply-to message-id
22
- received content-transfer-encoding return-path x-mailer].freeze
23
- MultiHeads = { 'received' => true }.freeze
24
- IgnoreList = { 'dkim-signature' => true }.freeze
25
- Indicators = {
26
- :begin => (1 << 1),
27
- :endof => (1 << 2),
28
- }.freeze
22
+ received content-transfer-encoding return-path x-mailer]
23
+ IsMultiple = { 'received' => true }
29
24
  DefaultSet = Sisimai::Order::Email.another
30
25
  SubjectTab = Sisimai::Order::Email.by('subject')
31
26
  ExtHeaders = Sisimai::Order::Email.headers
@@ -56,8 +51,7 @@ module Sisimai
56
51
  tobeloaded = Sisimai::Message::Email.load(methodargv)
57
52
 
58
53
  # 1. Split email data to headers and a body part.
59
- aftersplit = Sisimai::Message::Email.divideup(email)
60
- return nil if aftersplit.empty?
54
+ return nil unless aftersplit = Sisimai::Message::Email.divideup(email)
61
55
 
62
56
  # 2. Convert email headers from text to hash reference
63
57
  headerargv = {
@@ -81,9 +75,7 @@ module Sisimai
81
75
  'tryonfirst' => headerargv['tryonfirst'],
82
76
  'tobeloaded' => tobeloaded,
83
77
  }
84
- bouncedata = Sisimai::Message::Email.parse(methodargv)
85
-
86
- return nil unless bouncedata
78
+ return nil unless bouncedata = Sisimai::Message::Email.parse(methodargv)
87
79
  return nil if bouncedata.empty?
88
80
  processing['ds'] = bouncedata['ds']
89
81
  processing['catch'] = bouncedata['catch']
@@ -153,48 +145,27 @@ module Sisimai
153
145
  # @param [String] email Email data
154
146
  # @return [Hash] Email data after split
155
147
  def self.divideup(email)
156
- return {} if email.empty?
148
+ return nil if email.empty?
157
149
 
150
+ block = { 'from' => '', 'header' => '', 'body' => '' }
158
151
  email.scrub!('?')
159
152
  email.gsub!(/\r\n/, "\n") if email.include?("\r\n")
160
153
  email.gsub!(/[ \t]+$/, '') if email =~ /[ \t]+$/
161
154
 
162
- hasdivided = email.split("\n")
163
- return {} if hasdivided.empty?
164
-
165
- readcursor = 0
166
- aftersplit = { 'from' => '', 'header' => '', 'body' => '' }
155
+ (block['header'], block['body']) = email.split(/\n\n/, 2)
156
+ return nil unless block['header']
157
+ return nil unless block['body']
167
158
 
168
- if hasdivided[0][0, 5] == 'From '
159
+ if block['header'][0, 5] == 'From '
169
160
  # From MAILER-DAEMON Tue Feb 11 00:00:00 2014
170
- aftersplit['from'] = hasdivided.shift
171
- aftersplit['from'] = aftersplit['from'].delete("\n").delete("\r")
172
- end
173
-
174
- # Split email data to headers and a body part.
175
- while e = hasdivided.shift do
176
- # Split email data to headers and a body part.
177
- if readcursor & Indicators[:endof] > 0
178
- # The body part of the email
179
- aftersplit['body'] << e + "\n"
180
- else
181
- # The boundary for splitting headers and a body part does not
182
- # appeare yet.
183
- if e.empty?
184
- # Blank line, it is a boundary of headers and a body part
185
- readcursor |= Indicators[:endof] if readcursor & Indicators[:begin] > 0
186
- else
187
- # The header part of the email
188
- aftersplit['header'] << e + "\n"
189
- readcursor |= Indicators[:begin]
190
- end
191
- end
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'
192
165
  end
193
- return {} if aftersplit['header'].empty?
194
- return {} if aftersplit['body'].empty?
195
166
 
196
- aftersplit['from'] = 'MAILER-DAEMON Tue Feb 11 00:00:00 2014' if aftersplit['from'].empty?
197
- return aftersplit
167
+ block['body'] << "\n"
168
+ return block
198
169
  end
199
170
 
200
171
  # Convert email headers from text to hash reference
@@ -205,6 +176,7 @@ module Sisimai
205
176
  def self.headers(heads, argvs = {})
206
177
  return nil unless heads
207
178
 
179
+ currheader = ''
208
180
  allheaders = {}
209
181
  structured = {}
210
182
  extheaders = argvs['extheaders'] || []
@@ -214,50 +186,49 @@ module Sisimai
214
186
  HeaderList.each { |e| structured[e] = nil }
215
187
  HeaderList.each { |e| allheaders[e] = true }
216
188
  RFC3834Set.each { |e| allheaders[e] = true }
217
- MultiHeads.each_key { |e| structured[e.downcase] = [] }
189
+ IsMultiple.each_key { |e| structured[e] = [] }
218
190
  extheaders.each_key { |e| allheaders[e] = true }
219
191
  unless extrafield.empty?
220
- extrafield.each { |e| allheaders[e.downcase] = true }
192
+ extrafield.each { |e| allheaders[e] = true }
221
193
  end
222
194
 
223
195
  while e = hasdivided.shift do
224
196
  # Convert email headers to hash
225
- if cv = e.match(/\A([^ ]+?)[:][ ]*(.*?)\z/)
226
- # split the line into a header name and a header content
227
- lhs = cv[1]
228
- rhs = cv[2]
197
+ if cv = e.match(/\A[ \t]+(.+)\z/)
198
+ # Continued (foled) header value from the previous line
199
+ next unless allheaders.key?(currheader)
229
200
 
230
- currheader = lhs.downcase
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 : ''
231
213
  next unless allheaders.key?(currheader)
232
214
 
233
- if MultiHeads.key?(currheader)
215
+ if IsMultiple.key?(currheader)
234
216
  # Such as 'Received' header, there are multiple headers in a single
235
217
  # email message.
236
- rhs = rhs.tr("\t", ' ').squeeze(' ')
218
+ #rhs = rhs.tr("\t", ' ').squeeze(' ')
219
+ rhs = rhs.tr("\t", ' ')
237
220
  structured[currheader] << rhs
238
221
  else
239
222
  # Other headers except "Received" and so on
240
223
  if extheaders[currheader]
241
224
  # MTA specific header
242
- extheaders[currheader].keys.each do |r|
225
+ extheaders[currheader].each do |r|
243
226
  next if argvs['tryonfirst'].index(r)
244
227
  argvs['tryonfirst'] << r
245
228
  end
246
229
  end
247
230
  structured[currheader] = rhs
248
231
  end
249
- elsif cv = e.match(/\A[ \t]+(.+?)\z/)
250
- # Ignore header?
251
- next if IgnoreList[currheader]
252
-
253
- # Header line continued from the previous line
254
- if structured[currheader].is_a? Array
255
- # Concatenate a header which have multi-lines such as 'Received'
256
- structured[currheader][-1] << ' ' << cv[1]
257
- else
258
- structured[currheader] ||= ''
259
- structured[currheader] << ' ' << cv[1]
260
- end
261
232
  end
262
233
  end
263
234
  return structured
@@ -294,72 +265,68 @@ module Sisimai
294
265
  # 2. Convert from string to hash reference
295
266
  heads = heads.scrub('?').gsub(/^[>]+[ ]/m, '')
296
267
 
297
- takenapart = {}
298
- hasdivided = heads.split("\n")
299
268
  previousfn = '' # Previous field name
300
- mimeborder = {}
269
+ asciiarmor = {} # Header names which has MIME encoded value
270
+ headerpart = {} # Required headers in the original message part
271
+ hasdivided = heads.split("\n")
301
272
 
302
273
  while e = hasdivided.shift do
303
274
  # Header name as a key, The value of header as a value
304
- if cv = e.match(/\A([-0-9A-Za-z]+?)[:][ ]*(.*)\z/)
305
- # Header
306
- lhs = cv[1].downcase
307
- rhs = cv[2]
308
- previousfn = ''
309
-
310
- next unless RFC822Head.key?(lhs)
311
- previousfn = lhs
312
- takenapart[previousfn] = rhs unless takenapart[previousfn]
313
- else
314
- # Continued line from the previous line
315
- next unless e.start_with?(' ', "\t")
275
+ if e.start_with?(' ', "\t")
276
+ # Continued (foled) header value from the previous line
316
277
  next if previousfn.empty?
317
278
 
318
279
  # Concatenate the line if it is the value of required header
319
280
  if Sisimai::MIME.is_mimeencoded(e)
320
281
  # The line is MIME-Encoded test
321
- takenapart[previousfn] << if previousfn == 'subject'
282
+ headerpart[previousfn] << if previousfn == 'subject'
322
283
  # Subject: header
323
284
  BorderLine + e
324
285
  else
325
286
  # Is not Subject header
326
287
  e
327
288
  end
328
- mimeborder[previousfn] = true
289
+ asciiarmor[previousfn] = true
329
290
  else
330
291
  # ASCII Characters only: Not MIME-Encoded
331
- takenapart[previousfn] << e.lstrip
332
- mimeborder[previousfn] ||= false
292
+ headerpart[previousfn] << e.lstrip
293
+ asciiarmor[previousfn] ||= false
333
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]
334
305
  end
335
306
  end
307
+ return headerpart unless headerpart['subject']
336
308
 
337
- if takenapart['subject']
338
- # Convert MIME-Encoded subject
339
- v = takenapart['subject']
340
-
341
- if Sisimai::String.is_8bit(v)
342
- # The value of ``Subject'' header is including multibyte character,
343
- # is not MIME-Encoded text.
344
- v = 'MULTIBYTE CHARACTERS HAVE BEEN REMOVED'
345
- else
346
- # MIME-Encoded subject field or ASCII characters only
347
- r = []
348
- if mimeborder['subject']
349
- # split the value of Subject by borderline
350
- v.split(BorderLine).each do |m|
351
- # Insert value to the array if the string is MIME encoded text
352
- r << m if Sisimai::MIME.is_mimeencoded(m)
353
- end
354
- else
355
- # Subject line is not MIME encoded
356
- r << v
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)
357
322
  end
358
- v = Sisimai::MIME.mimedecode(r)
323
+ else
324
+ # Subject line is not MIME encoded
325
+ r << headerpart['subject']
359
326
  end
360
- takenapart['subject'] = v
327
+ headerpart['subject'] = Sisimai::MIME.mimedecode(r)
361
328
  end
362
- return takenapart
329
+ return headerpart
363
330
  end
364
331
 
365
332
  # @abstract Parse bounce mail with each MTA module
@@ -441,10 +408,13 @@ module Sisimai
441
408
  if mailheader['subject'].downcase =~ /\A[ \t]*fwd?:/
442
409
  # Delete quoted strings, quote symbols(>)
443
410
  bodystring = bodystring.gsub(/^[>]+[ ]/m, '').gsub(/^[>]$/m, '')
411
+ elsif Sisimai::MIME.is_mimeencoded(mailheader['subject'])
412
+ # Decode MIME-Encoded "Subject:" header
413
+ mailheader['subject'] = Sisimai::MIME.mimedecode(mailheader['subject'].split(/[ ]/))
414
+ mailheader['subject'].scrub!('?')
444
415
  end
445
416
  bodystring << EndOfEmail
446
417
  haveloaded = {}
447
- defaultset = DefaultSet.dup
448
418
  scannedset = nil
449
419
 
450
420
  catch :SCANNER do
@@ -469,19 +439,9 @@ module Sisimai
469
439
  throw :SCANNER if scannedset
470
440
  end
471
441
 
442
+ tryonfirst.concat(DefaultSet)
472
443
  while r = tryonfirst.shift do
473
- # Try MTA module candidates which are detected from MTA specific
474
- # mail headers on first
475
- next if haveloaded.key?(r)
476
- require r.gsub('::', '/').downcase
477
- scannedset = Module.const_get(r).scan(mailheader, bodystring)
478
- haveloaded[r] = true
479
- throw :SCANNER if scannedset
480
- end
481
-
482
- while r = defaultset.shift do
483
- # MTA modules which does not have MTA specific header and did
484
- # not match with any regular expressions of Subject header.
444
+ # Try MTA module candidates
485
445
  next if haveloaded.key?(r)
486
446
  require r.gsub('::', '/').downcase
487
447
  scannedset = Module.const_get(r).scan(mailheader, bodystring)
@@ -37,14 +37,12 @@ module Sisimai
37
37
 
38
38
  # Rewrite message body for detecting the bounce reason
39
39
  methodargv = { 'hook' => hookmethod, 'json' => argvs['data'] }
40
- bouncedata = Sisimai::Message::JSON.parse(methodargv)
41
-
42
- return nil unless bouncedata
40
+ return nil unless bouncedata = Sisimai::Message::JSON.parse(methodargv)
43
41
  return nil if bouncedata.empty?
42
+
44
43
  processing['ds'] = bouncedata['ds']
45
44
  processing['catch'] = bouncedata['catch']
46
45
  processing['rfc822'] = bouncedata['rfc822']
47
-
48
46
  return processing
49
47
  end
50
48
 
data/lib/sisimai/mime.rb CHANGED
@@ -15,6 +15,14 @@ module Sisimai
15
15
  :'only-charset' => %r/^[\s\t]+charset=['"]?(.+?)['"]?\b/,
16
16
  :'html-message' => %r|^content-type:[ ]*text/html;|m,
17
17
  }.freeze
18
+ AlsoAppend = %r{\A(?:text/rfc822-headers|message/)}.freeze
19
+ ThisFormat = %r/\A(?:Content-Transfer-Encoding:\s*.+\n)?Content-Type:\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
18
26
 
19
27
  # Make MIME-Encoding and Content-Type related headers regurlar expression
20
28
  # @return [Array] Regular expressions related to MIME encoding
@@ -27,17 +35,17 @@ module Sisimai
27
35
  # @return [True,False] false: Not MIME encoded string
28
36
  # true: MIME encoded string
29
37
  def is_mimeencoded(argv1)
30
- return false unless argv1
38
+ return nil unless argv1
31
39
 
32
- argv1.delete!('"')
33
- piece = []
40
+ text1 = argv1.delete('"')
34
41
  mime1 = false
42
+ piece = []
35
43
 
36
- if argv1.include?(' ')
44
+ if text1.include?(' ')
37
45
  # Multiple MIME-Encoded strings in a line
38
- piece = argv1.split(' ')
46
+ piece = text1.split(' ')
39
47
  else
40
- piece << argv1
48
+ piece << text1
41
49
  end
42
50
 
43
51
  while e = piece.shift do
@@ -67,19 +75,17 @@ module Sisimai
67
75
  characterset ||= cv[2]
68
76
  encodingname ||= cv[3]
69
77
  mimeencoded0 = cv[4]
70
- decodedtext0 << cv[1]
71
78
 
72
- if encodingname == 'Q'
73
- # Quoted-Printable
74
- decodedtext0 << mimeencoded0.unpack('M').first
75
-
76
- elsif encodingname == 'B'
77
- # Base64
78
- decodedtext0 << Base64.decode64(mimeencoded0)
79
- end
79
+ decodedtext0 << cv[1]
80
+ decodedtext0 << if encodingname == 'B'
81
+ Base64.decode64(mimeencoded0)
82
+ else
83
+ mimeencoded0.unpack('M').first
84
+ end
85
+ decodedtext0[-1].gsub!(/\r\n/, '')
80
86
  decodedtext0 << cv[5]
81
87
  else
82
- decodedtext0 << e
88
+ decodedtext0 << if decodedtext0.empty? then e else ' ' << e end
83
89
  end
84
90
  end
85
91
 
@@ -261,28 +267,19 @@ module Sisimai
261
267
  return nil unless argv0
262
268
 
263
269
  hasflatten = '' # Message body including only text/plain and message/*
264
- alsoappend = %r{\A(?:text/rfc822-headers|message/)}
265
- thisformat = %r/\A(?:Content-Transfer-Encoding:\s*.+\n)?Content-Type:\s*([^ ;]+)/
266
- leavesonly = %r{\A(?>
267
- text/(?:plain|html|rfc822-headers)
268
- |message/(?:x?delivery-status|rfc822|partial|feedback-report)
269
- |multipart/(?:report|alternative|mixed|related|partial)
270
- )
271
- }x
272
-
273
270
  mimeformat = '' # MIME type string of this part
274
271
  alternates = argv1.start_with?('multipart/alternative') ? true : false
275
272
 
276
273
  # Get MIME type string from Content-Type: "..." field at the first line
277
274
  # or the second line of the part.
278
- if cv = argv0.match(thisformat) then mimeformat = cv[1].downcase end
275
+ if cv = argv0.match(ThisFormat) then mimeformat = cv[1].downcase end
279
276
 
280
- # Sisimai require only MIME types defined in $leavesonly variable
281
- return '' unless mimeformat =~ leavesonly
277
+ # Sisimai require only MIME types defined in LeavesOnly variable
278
+ return '' unless mimeformat =~ LeavesOnly
282
279
  return '' if alternates && mimeformat == 'text/html'
283
280
 
284
281
  (upperchunk, lowerchunk) = argv0.split(/^$/m, 2)
285
- upperchunk.gsub!("\n", ' ').squeeze(' ')
282
+ upperchunk.tr!("\n", ' ').squeeze(' ')
286
283
 
287
284
  # Content-Description: Undelivered Message
288
285
  # Content-Type: message/rfc822
@@ -297,12 +294,12 @@ module Sisimai
297
294
  innerparts.shift if innerparts[0].empty?
298
295
  while e = innerparts.shift do
299
296
  # Find internal multipart/* blocks and decode
300
- if cv = e.match(thisformat)
297
+ if cv = e.match(ThisFormat)
301
298
  # Found "Content-Type" field at the first or second line of this
302
299
  # splitted part
303
300
  nextformat = cv[1].downcase
304
301
 
305
- next unless nextformat =~ leavesonly
302
+ next unless nextformat =~ LeavesOnly
306
303
  next if nextformat == 'text/html'
307
304
 
308
305
  hasflatten << Sisimai::MIME.breaksup(e, mimeformat)
@@ -340,12 +337,12 @@ module Sisimai
340
337
  # Content-Transfer-Encoding: 8bit, binary, and so on
341
338
  getdecoded = lowerchunk
342
339
  end
343
- getdecoded.gsub!(/\r\n/, "\n") # Convert CRLF to LF
340
+ getdecoded.gsub!(/\r\n/, "\n") if getdecoded.include?("\r\n") # Convert CRLF to LF
344
341
 
345
- if mimeformat =~ alsoappend
342
+ if mimeformat =~ AlsoAppend
346
343
  # Append field when the value of Content-Type: begins with
347
344
  # message/ or equals text/rfc822-headers.
348
- upperchunk.sub!(/Content-Transfer-Encoding:.+\z/, '').gsub!(/[ ]\z/, '')
345
+ upperchunk.sub!(/Content-Transfer-Encoding:.+\z/, '').rstrip!
349
346
  hasflatten << upperchunk
350
347
 
351
348
  elsif mimeformat == 'text/html'