sisimai 4.25.6 → 4.25.11

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 (104) hide show
  1. checksums.yaml +4 -4
  2. data/.travis.yml +4 -3
  3. data/ANALYTICAL-PRECISION +1 -1
  4. data/Benchmarks.mk +1 -1
  5. data/ChangeLog.md +71 -2
  6. data/Developers.mk +2 -2
  7. data/Makefile +1 -1
  8. data/README-JA.md +16 -16
  9. data/README.md +16 -16
  10. data/Repository.mk +1 -1
  11. data/lib/sisimai.rb +3 -3
  12. data/lib/sisimai/address.rb +6 -3
  13. data/lib/sisimai/arf.rb +25 -21
  14. data/lib/sisimai/data.rb +8 -29
  15. data/lib/sisimai/datetime.rb +18 -17
  16. data/lib/sisimai/lhost/amazonses.rb +1 -1
  17. data/lib/sisimai/lhost/domino.rb +29 -4
  18. data/lib/sisimai/lhost/einsundeins.rb +1 -1
  19. data/lib/sisimai/lhost/exchange2007.rb +1 -1
  20. data/lib/sisimai/lhost/exim.rb +12 -8
  21. data/lib/sisimai/lhost/ezweb.rb +1 -1
  22. data/lib/sisimai/lhost/fml.rb +0 -2
  23. data/lib/sisimai/lhost/gsuite.rb +5 -0
  24. data/lib/sisimai/lhost/imailserver.rb +1 -1
  25. data/lib/sisimai/lhost/mailfoundry.rb +3 -4
  26. data/lib/sisimai/lhost/mailmarshalsmtp.rb +2 -2
  27. data/lib/sisimai/lhost/mxlogic.rb +1 -1
  28. data/lib/sisimai/lhost/notes.rb +1 -1
  29. data/lib/sisimai/lhost/office365.rb +1 -1
  30. data/lib/sisimai/lhost/postfix.rb +3 -10
  31. data/lib/sisimai/lhost/qmail.rb +7 -7
  32. data/lib/sisimai/lhost/sendgrid.rb +2 -2
  33. data/lib/sisimai/lhost/sendmail.rb +1 -1
  34. data/lib/sisimai/lhost/verizon.rb +4 -4
  35. data/lib/sisimai/lhost/x3.rb +4 -0
  36. data/lib/sisimai/lhost/x4.rb +8 -8
  37. data/lib/sisimai/lhost/x5.rb +7 -3
  38. data/lib/sisimai/mail.rb +2 -21
  39. data/lib/sisimai/mda.rb +2 -2
  40. data/lib/sisimai/message.rb +47 -83
  41. data/lib/sisimai/reason/blocked.rb +40 -26
  42. data/lib/sisimai/reason/exceedlimit.rb +4 -1
  43. data/lib/sisimai/reason/expired.rb +2 -0
  44. data/lib/sisimai/reason/filtered.rb +1 -0
  45. data/lib/sisimai/reason/hostunknown.rb +3 -0
  46. data/lib/sisimai/reason/mailererror.rb +1 -1
  47. data/lib/sisimai/reason/norelaying.rb +5 -0
  48. data/lib/sisimai/reason/notaccept.rb +2 -0
  49. data/lib/sisimai/reason/policyviolation.rb +4 -0
  50. data/lib/sisimai/reason/rejected.rb +5 -1
  51. data/lib/sisimai/reason/securityerror.rb +5 -6
  52. data/lib/sisimai/reason/spamdetected.rb +17 -15
  53. data/lib/sisimai/reason/suspend.rb +1 -0
  54. data/lib/sisimai/reason/systemerror.rb +2 -0
  55. data/lib/sisimai/reason/userunknown.rb +25 -21
  56. data/lib/sisimai/rfc1894.rb +24 -22
  57. data/lib/sisimai/rfc3464.rb +4 -5
  58. data/lib/sisimai/rfc3834.rb +1 -1
  59. data/lib/sisimai/rfc5322.rb +8 -4
  60. data/lib/sisimai/rhost.rb +6 -8
  61. data/lib/sisimai/rhost/cox.rb +112 -0
  62. data/lib/sisimai/rhost/exchangeonline.rb +8 -1
  63. data/lib/sisimai/rhost/googleapps.rb +4 -1
  64. data/lib/sisimai/rhost/spectrum.rb +73 -0
  65. data/lib/sisimai/smtp/error.rb +8 -6
  66. data/lib/sisimai/smtp/reply.rb +1 -1
  67. data/lib/sisimai/smtp/status.rb +1 -1
  68. data/lib/sisimai/time.rb +1 -2
  69. data/lib/sisimai/version.rb +1 -1
  70. data/set-of-emails/README.md +5 -4
  71. data/set-of-emails/maildir/bsd/arf-25.eml +61 -0
  72. data/set-of-emails/maildir/bsd/lhost-aol-04.eml +23 -23
  73. data/set-of-emails/maildir/bsd/lhost-domino-02.eml +1 -2
  74. data/set-of-emails/maildir/bsd/lhost-exim-61.eml +40 -0
  75. data/set-of-emails/maildir/bsd/lhost-postfix-38.eml +1 -1
  76. data/set-of-emails/maildir/bsd/lhost-postfix-66.eml +80 -0
  77. data/set-of-emails/maildir/bsd/lhost-postfix-67.eml +80 -0
  78. data/set-of-emails/maildir/bsd/lhost-postfix-68.eml +82 -0
  79. data/set-of-emails/maildir/bsd/lhost-postfix-69.eml +80 -0
  80. data/set-of-emails/maildir/bsd/lhost-postfix-70.eml +87 -0
  81. data/set-of-emails/maildir/bsd/lhost-postfix-71.eml +81 -0
  82. data/set-of-emails/maildir/bsd/lhost-postfix-72.eml +81 -0
  83. data/set-of-emails/maildir/bsd/lhost-postfix-73.eml +79 -0
  84. data/set-of-emails/maildir/bsd/lhost-postfix-74.eml +83 -0
  85. data/set-of-emails/maildir/bsd/lhost-sendmail-08.eml +1 -1
  86. data/set-of-emails/maildir/bsd/lhost-sendmail-11.eml +1 -1
  87. data/set-of-emails/maildir/bsd/lhost-sendmail-57.eml +59 -0
  88. data/set-of-emails/maildir/bsd/lhost-sendmail-58.eml +70 -0
  89. data/set-of-emails/maildir/bsd/lhost-sendmail-59.eml +68 -0
  90. data/set-of-emails/maildir/bsd/lhost-x3-06.eml +53 -0
  91. data/set-of-emails/maildir/bsd/rhost-cox-01.eml +192 -0
  92. data/set-of-emails/maildir/bsd/{rhost-exchange-online-01.eml → rhost-exchangeonline-01.eml} +0 -0
  93. data/set-of-emails/maildir/bsd/{rhost-exchange-online-02.eml → rhost-exchangeonline-02.eml} +0 -0
  94. data/set-of-emails/maildir/bsd/{rhost-exchange-online-03.eml → rhost-exchangeonline-03.eml} +0 -0
  95. data/set-of-emails/maildir/bsd/{rhost-google-apps-01.eml → rhost-googleapps-01.eml} +0 -0
  96. data/set-of-emails/maildir/bsd/{rhost-google-apps-02.eml → rhost-googleapps-02.eml} +0 -0
  97. data/set-of-emails/maildir/bsd/rhost-spectrum-01.eml +111 -0
  98. data/set-of-emails/maildir/dos/{rhost-exchange-online-01.eml → rhost-exchangeonline-01.eml} +0 -0
  99. data/set-of-emails/maildir/dos/{rhost-google-apps-01.eml → rhost-googleapps-01.eml} +0 -0
  100. data/set-of-emails/maildir/mac/{rhost-exchange-online-01.eml → rhost-exchangeonline-01.eml} +0 -0
  101. data/set-of-emails/maildir/mac/{rhost-google-apps-01.eml → rhost-googleapps-01.eml} +0 -0
  102. data/sisimai-java.gemspec +4 -4
  103. data/sisimai.gemspec +4 -4
  104. metadata +39 -20
data/lib/sisimai/data.rb CHANGED
@@ -41,7 +41,6 @@ module Sisimai
41
41
 
42
42
  RetryIndex = Sisimai::Reason.retry
43
43
  RFC822Head = Sisimai::RFC5322.HEADERFIELDS(:all)
44
- AddrHeader = { addresser: RFC822Head[:addresser], recipient: RFC822Head[:recipient] }.freeze
45
44
 
46
45
  # Constructor of Sisimai::Data
47
46
  # @param [Hash] argvs Data
@@ -96,35 +95,12 @@ module Sisimai
96
95
 
97
96
  messageobj = data
98
97
  rfc822data = messageobj.rfc822
99
- fieldorder = { :recipient => [], :addresser => [] }
100
98
  objectlist = []
101
- givenorder = argvs[:order] || {}
102
99
  delivered1 = argvs[:delivered] || false
103
100
 
104
101
  return nil unless messageobj.ds
105
102
  return nil unless messageobj.rfc822
106
103
 
107
- # Decide the order of email headers: user specified or system default.
108
- if givenorder.is_a?(Hash) && !givenorder.empty?
109
- # If the order of headers for searching is specified, use the order
110
- # for detecting an email address.
111
- fieldorder.each_key do |e|
112
- # The order should be "Array Reference".
113
- next unless givenorder[e]
114
- next unless givenorder[e].is_a? Array
115
- next if givenorder[e].empty?
116
- fieldorder[e] += givenorder[e]
117
- end
118
- end
119
-
120
- fieldorder.each_key do |e|
121
- # If the order is empty, use default order.
122
- next unless fieldorder[e].empty?
123
-
124
- # Load default order of each accessor.
125
- fieldorder[e] = AddrHeader[e]
126
- end
127
-
128
104
  eachobject = messageobj.ds.dup
129
105
  while e = eachobject.shift do
130
106
  # Create parameters for new() constructor.
@@ -152,7 +128,7 @@ module Sisimai
152
128
 
153
129
  # EMAIL_ADDRESS:
154
130
  # Detect email address from message/rfc822 part
155
- fieldorder[:addresser].each do |f|
131
+ RFC822Head[:addresser].each do |f|
156
132
  # Check each header in message/rfc822 part
157
133
  next unless rfc822data[f]
158
134
  next if rfc822data[f].empty?
@@ -313,10 +289,13 @@ module Sisimai
313
289
 
314
290
  if o.reason.empty? || RetryIndex[o.reason]
315
291
  # Decide the reason of email bounce
316
- r = ''
317
- r = Sisimai::Rhost.get(o) if Sisimai::Rhost.match(o.rhost) # Remote host dependent error
318
- r = Sisimai::Reason.get(o) if r.empty?
319
- r = 'undefined' if r.empty?
292
+ r = ''; r = Sisimai::Rhost.get(o) if Sisimai::Rhost.match(o.rhost)
293
+ if r.empty?
294
+ # Failed to detect a bounce reason by the value of "rhost"
295
+ r = Sisimai::Rhost.get(o, o.destination) if Sisimai::Rhost.match(o.destination)
296
+ r = Sisimai::Reason.get(o) if r.empty?
297
+ r = 'undefined' if r.empty?
298
+ end
320
299
  o.reason = r
321
300
  end
322
301
 
@@ -43,7 +43,7 @@ module Sisimai
43
43
  abbr: %w[Sun Mon Tue Wed Thu Fri Sat],
44
44
  }.freeze
45
45
 
46
- TimeZoneAbbr = {
46
+ TimeZones = {
47
47
  # http://en.wikipedia.org/wiki/List_of_time_zone_abbreviations
48
48
  #'ACDT' => '+1030', # Australian Central Daylight Time UTC+10:30
49
49
  #'ACST' => '+0930', # Australian Central Standard Time UTC+09:30
@@ -207,24 +207,24 @@ module Sisimai
207
207
  end
208
208
 
209
209
  # Month name list
210
- # @param [Integer] argv1 Require full name or not
210
+ # @param [Boolean] argv1 Require full name or not
211
211
  # @return [Array, String] Month name list or month name
212
212
  # @example Get the names of each month
213
- # monthname() #=> [ 'Jan', 'Feb', ... ]
214
- # monthname(1) #=> [ 'January', 'February', 'March', ... ]
215
- def monthname(argv1 = 0)
216
- value = argv1 > 0 ? :full : :abbr
213
+ # monthname() #=> [ 'Jan', 'Feb', ... ]
214
+ # monthname(true) #=> [ 'January', 'February', 'March', ... ]
215
+ def monthname(argv1 = false)
216
+ value = argv1 ? :full : :abbr
217
217
  return MonthName[value]
218
218
  end
219
219
 
220
220
  # List of day of week
221
- # @param [Integer] argv1 Require full name
221
+ # @param [Boolean] argv1 Require full name
222
222
  # @return [Array, String] List of day of week or day of week
223
223
  # @example Get the names of each day of week
224
- # dayofweek() #=> [ 'Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat' ]
225
- # dayofweek(1) #=> [ 'Sunday', 'Monday', 'Tuesday', ... ]
226
- def dayofweek(argv1 = 0)
227
- value = argv1 > 0 ? :full : :abbr
224
+ # dayofweek() #=> [ 'Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat' ]
225
+ # dayofweek(true) #=> [ 'Sunday', 'Monday', 'Tuesday', ... ]
226
+ def dayofweek(argv1 = false)
227
+ value = argv1 ? :full : :abbr
228
228
  return DayOfWeek[value]
229
229
  end
230
230
 
@@ -257,9 +257,10 @@ module Sisimai
257
257
 
258
258
  while p = timetokens.shift do
259
259
  # Parse each piece of time
260
- if p =~ /\A[A-Z][a-z]{2}[,]?\z/
260
+ if p =~ /\A[A-Z][a-z]{2,}[,]?\z/
261
261
  # Day of week or Day of week; Thu, Apr, ...
262
- p.chop if p.size == 4 # Thu, -> Thu
262
+ p.gsub!(/,\z/, '') if p.end_with?(',') # "Thu," => "Thu"
263
+ p = p[0,3] if p.size > 3
263
264
 
264
265
  if DayOfWeek[:abbr].include?(p)
265
266
  # Day of week; Mon, Thu, Sun,...
@@ -273,7 +274,7 @@ module Sisimai
273
274
  # Year or Day; 2005, 31, 04, 1, ...
274
275
  if p.to_i > 31
275
276
  # The piece is the value of an year
276
- v[:Y] = p
277
+ v[:Y] = p.to_i
277
278
  else
278
279
  # The piece is the value of a day
279
280
  if v[:d]
@@ -399,7 +400,7 @@ module Sisimai
399
400
  # abbr2tz('JST') #=> '+0900'
400
401
  def abbr2tz(argv1)
401
402
  return nil unless argv1.is_a?(::String)
402
- return TimeZoneAbbr[argv1]
403
+ return TimeZones[argv1]
403
404
  end
404
405
 
405
406
  # Convert to second
@@ -428,7 +429,7 @@ module Sisimai
428
429
  return ztime
429
430
 
430
431
  elsif argv1 =~ /\A[A-Za-z]+\z/
431
- return tz2second(TimeZoneAbbr[argv1])
432
+ return tz2second(TimeZones[argv1])
432
433
  else
433
434
  return nil
434
435
  end
@@ -441,7 +442,7 @@ module Sisimai
441
442
  # @example Get timezone offset string of specified seconds
442
443
  # second2tz(12345) #=> '+0325'
443
444
  def second2tz(argv1)
444
- return '+0000' unless argv1.is_a?(Number)
445
+ return '+0000' unless argv1.is_a?(::Integer)
445
446
  return nil if argv1.abs > TZ_OFFSET # UTC+14 + 1(DST?)
446
447
 
447
448
  digit = { :operator => '+' }
@@ -182,7 +182,7 @@ module Sisimai::Lhost
182
182
  v['status'] = Sisimai::SMTP::Status.find(v['diagnosis']) || ''
183
183
  v['replycode'] = Sisimai::SMTP::Reply.find(v['diagnosis']) || ''
184
184
  v['reason'] = 'delivered'
185
- v['action'] = 'deliverable'
185
+ v['action'] = 'delivered'
186
186
 
187
187
  v['date'] = o['timestamp'] || p['mail']['timestamp']
188
188
  v['date'].sub!(/[.]\d+Z\z/, '')
@@ -7,12 +7,13 @@ module Sisimai::Lhost
7
7
  require 'sisimai/lhost'
8
8
 
9
9
  Indicators = Sisimai::Lhost.INDICATORS
10
- ReBackbone = %r|^Content-Type:[ ]message/delivery-status|.freeze
10
+ ReBackbone = %r|^Content-Type:[ ]message/rfc822|.freeze
11
11
  StartingOf = { message: ['Your message'] }.freeze
12
12
  MessagesOf = {
13
13
  'userunknown' => [
14
14
  'not listed in Domino Directory',
15
15
  'not listed in public Name & Address Book',
16
+ "dans l'annuaire Domino", # TODO: "non répertorié dans l'annuaire Domino",
16
17
  'Domino ディレクトリには見つかりません',
17
18
  ],
18
19
  'filtered' => ['Cannot route mail to user'],
@@ -25,7 +26,11 @@ module Sisimai::Lhost
25
26
  # @return [Hash] Bounce data list and message/rfc822 part
26
27
  # @return [Nil] it failed to parse or the arguments are missing
27
28
  def make(mhead, mbody)
28
- return nil unless mhead['subject'].start_with?('DELIVERY FAILURE:')
29
+ return nil unless mhead['subject'].start_with?('DELIVERY FAILURE:', 'DELIVERY_FAILURE:')
30
+
31
+ require 'sisimai/rfc1894'
32
+ fieldtable = Sisimai::RFC1894.FIELDTABLE
33
+ permessage = {} # (Hash) Store values of each Per-Message field
29
34
 
30
35
  dscontents = [Sisimai::Lhost.DELIVERYSTATUS]
31
36
  emailsteak = Sisimai::RFC5322.fillet(mbody, ReBackbone)
@@ -87,6 +92,24 @@ module Sisimai::Lhost
87
92
  elsif cv = e.match(/\A[ ][ ]Subject: (.+)\z/)
88
93
  # Subject: Nyaa
89
94
  subjecttxt = cv[1]
95
+
96
+ elsif f = Sisimai::RFC1894.match(e)
97
+ # There are some fields defined in RFC3464, try to match
98
+ o = Sisimai::RFC1894.field(e) || next
99
+ next if o[-1] == 'addr'
100
+
101
+ if o[-1] == 'code'
102
+ # Diagnostic-Code: SMTP; 550 5.1.1 <userunknown@example.jp>... User Unknown
103
+ v['spec'] = o[1] if v['spec'].to_s.empty?
104
+ v['diagnosis'] = o[2] if v['diagnosis'].to_s.empty?
105
+ else
106
+ # Other DSN fields defined in RFC3464
107
+ next unless fieldtable[o[0]]
108
+ v[fieldtable[o[0]]] = o[2]
109
+
110
+ next unless f == 1
111
+ permessage[fieldtable[o[0]]] = o[2]
112
+ end
90
113
  end
91
114
  end
92
115
  end
@@ -95,12 +118,14 @@ module Sisimai::Lhost
95
118
  dscontents.each do |e|
96
119
  e['diagnosis'] = Sisimai::String.sweep(e['diagnosis'])
97
120
  e['recipient'] = Sisimai::Address.s3s4(e['recipient'])
121
+ e['lhost'] ||= permessage['rhost']
122
+ permessage.each_key { |a| e[a] ||= permessage[a] || '' }
98
123
 
99
124
  MessagesOf.each_key do |r|
100
125
  # Check each regular expression of Domino error messages
101
126
  next unless MessagesOf[r].any? { |a| e['diagnosis'].include?(a) }
102
- e['reason'] = r
103
- e['status'] = Sisimai::SMTP::Status.code(r.to_s, false) || ''
127
+ e['reason'] = r
128
+ e['status'] ||= Sisimai::SMTP::Status.code(r.to_s, false) || ''
104
129
  break
105
130
  end
106
131
  end
@@ -52,7 +52,7 @@ module Sisimai::Lhost
52
52
  # http://postmaster.1and1.com/en/error-messages?ip=%1s
53
53
  v = dscontents[-1]
54
54
 
55
- if cv = e.match(/\A([^ ]+[@][^ ]+)\z/)
55
+ if cv = e.match(/\A([^ ]+[@][^ ]+?)[:]?\z/)
56
56
  # general@example.eu
57
57
  if v['recipient']
58
58
  # There are multiple recipient addresses in the message body.
@@ -24,7 +24,7 @@ module Sisimai::Lhost
24
24
  error: %r/ ((?:RESOLVER|QUEUE)[.][A-Za-z]+(?:[.]\w+)?);/,
25
25
  rhost: %r{\A(?:
26
26
  Generating[ ]server # en-US
27
- |Serveur[ ]de[ ]g.+ration[ ] # fr-FR/Serveur de génération
27
+ |Serveur[ ]de[ ]g[^ ]+ration[ ] # fr-FR/Serveur de génération
28
28
  |Server[ ]di[ ]generazione # it-CH
29
29
  ):[ ]?(.*)
30
30
  }x,
@@ -12,7 +12,7 @@ module Sisimai::Lhost
12
12
  # deliver.c:6425| else fprintf(f,
13
13
  # deliver.c:6426|"------ This is a copy of the message's headers. ------\n");
14
14
  ReBackbone = %r{^(?:
15
- [-]+[ ]This[ ]is[ ]a[ ]copy[ ]of[ ](?:the|your)[ ]message.+?headers[.][ ][-]+
15
+ [-]+[ ]This[ ]is[ ]a[ ]copy[ ]of[ ](?:the|your)[ ]message,[ ]including[ ]all[ ]the[ ]headers[.][ ][-]+
16
16
  |Content-Type:[ ]*message/rfc822\n(?:[\s\t]+.*?\n\n)?
17
17
  )
18
18
  }x.freeze
@@ -36,13 +36,14 @@ module Sisimai::Lhost
36
36
  # deliver.c:6305|"address(es) failed:\n", sender_address);
37
37
  # deliver.c:6306| }
38
38
  alias: %r/\A([ ]+an undisclosed address)\z/,
39
- frozen: %r/\AMessage .+ (?:has been frozen|was frozen on arrival)/,
39
+ frozen: %r/\AMessage [^ ]+ (?:has been frozen|was frozen on arrival)/,
40
40
  message: %r{\A(?>
41
41
  This[ ]message[ ]was[ ]created[ ]automatically[ ]by[ ]mail[ ]delivery[ ]software[.]
42
42
  |A[ ]message[ ]that[ ]you[ ]sent[ ]was[ ]rejected[ ]by[ ]the[ ]local[ ]scanning[ ]code
43
43
  |A[ ]message[ ]that[ ]you[ ]sent[ ]contained[ ]one[ ]or[ ]more[ ]recipient[ ]addresses[ ]
44
- |Message[ ].+[ ](?:has[ ]been[ ]frozen|was[ ]frozen[ ]on[ ]arrival)
45
- |The[ ].+[ ]router[ ]encountered[ ]the[ ]following[ ]error[(]s[)]:
44
+ |A[ ]message[ ]that[ ]you[ ]sent[ ]could[ ]not[ ]be[ ]delivered[ ]to[ ]all[ ]of[ ]its[ ]recipients
45
+ |Message[ ][^ ]+[ ](?:has[ ]been[ ]frozen|was[ ]frozen[ ]on[ ]arrival)
46
+ |The[ ][^ ]+[ ]router[ ]encountered[ ]the[ ]following[ ]error[(]s[)]:
46
47
  )
47
48
  }x,
48
49
  }.freeze
@@ -134,7 +135,7 @@ module Sisimai::Lhost
134
135
  match += 1 if mhead['message-id'].to_s =~ %r/\A[<]\w{7}[-]\w{6}[-]\w{2}[@]/
135
136
  match += 1 if mhead['subject'] =~ %r{(?:
136
137
  Mail[ ]delivery[ ]failed(:[ ]returning[ ]message[ ]to[ ]sender)?
137
- |Warning:[ ]message[ ].+[ ]delayed[ ]+
138
+ |Warning:[ ]message[ ][^ ]+[ ]delayed[ ]+
138
139
  |Delivery[ ]Status[ ]Notification
139
140
  |Mail[ ]failure
140
141
  |Message[ ]frozen
@@ -264,7 +265,10 @@ module Sisimai::Lhost
264
265
  # Content-type: message/delivery-status
265
266
  nextcursor = 1 if e.start_with?(StartingOf[:deliverystatus][0])
266
267
  v['alterrors'] ||= ''
267
- v['alterrors'] << e + ' ' if e.start_with?(' ')
268
+ if e.start_with?("\s", "\t")
269
+ e.sub!(/\A[\s\t]+/, '')
270
+ v['alterrors'] << e + ' ' unless v['alterrors'].include?(e)
271
+ end
268
272
  end
269
273
  else
270
274
  if dscontents.size == recipients
@@ -275,7 +279,7 @@ module Sisimai::Lhost
275
279
  else
276
280
  # Error message when email address above does not include '@'
277
281
  # and domain part.
278
- if e =~ %r<\A[ ]+pipe[ ]to[ ][|]/.+>
282
+ if e =~ %r<\A[ ]+pipe[ ]to[ ][|]/[^ ]+>
279
283
  # pipe to |/path/to/prog ...
280
284
  # generated by kijitora@example.com
281
285
  v['diagnosis'] = e
@@ -374,7 +378,7 @@ module Sisimai::Lhost
374
378
  rxdiagnosis = %r/e['diagnosis']/i
375
379
  # Override the value of diagnostic code message because
376
380
  # the value of alterrors includes the value of diagnosis.
377
- e['diagnosis'] = e['alterrors'] if e['alterrors'] =~ rxdiagnosis
381
+ e['diagnosis'] = e['alterrors'] if e['alterrors'].downcase.include?(e['diagnosis'].downcase)
378
382
  end
379
383
  end
380
384
  e.delete('alterrors')
@@ -125,7 +125,7 @@ module Sisimai::Lhost
125
125
  v['command'] = cv[1]
126
126
  else
127
127
  # Check error message
128
- if rxmessages.any? { |a| e =~ a }
128
+ if rxmessages.any? { |messages| messages.any? { |message| e =~ message } }
129
129
  # Check with regular expressions of each error
130
130
  v['diagnosis'] ||= ''
131
131
  v['diagnosis'] << ' ' << e
@@ -59,14 +59,12 @@ module Sisimai::Lhost
59
59
  dscontents = [Sisimai::Lhost.DELIVERYSTATUS]
60
60
  emailsteak = Sisimai::RFC5322.fillet(mbody, ReBackbone)
61
61
  bodyslices = emailsteak[0].split("\n")
62
- readcursor = 0 # (Integer) Points the current cursor position
63
62
  recipients = 0 # (Integer) The number of 'Final-Recipient' header
64
63
  v = nil
65
64
 
66
65
  while e = bodyslices.shift do
67
66
  # Read error messages and delivery status lines from the head of the email
68
67
  # to the previous line of the beginning of the original message.
69
- next if (readcursor & Indicators[:deliverystatus]) == 0
70
68
  next if e.empty?
71
69
 
72
70
  # Duplicated Message-ID in <2ndml@example.com>.
@@ -82,6 +82,11 @@ module Sisimai::Lhost
82
82
  next unless fieldtable[o[0]]
83
83
  v[fieldtable[o[0]]] = o[2]
84
84
 
85
+ if fieldtable[o[0]] == 'lhost'
86
+ # Do not set an email address as a hostname in "lhost" value
87
+ v['lhost'] = '' if v['lhost'].include?('@')
88
+ end
89
+
85
90
  next unless f == 1
86
91
  permessage[fieldtable[o[0]]] = o[2]
87
92
  end
@@ -71,7 +71,7 @@ module Sisimai::Lhost
71
71
  dscontents << Sisimai::Lhost.DELIVERYSTATUS
72
72
  v = dscontents[-1]
73
73
  end
74
- v['recipient'] = cv[1]
74
+ v['recipient'] = Sisimai::Address.s3s4(cv[1])
75
75
  recipients += 1
76
76
  else
77
77
  # Other error message text
@@ -9,7 +9,7 @@ module Sisimai::Lhost
9
9
  Indicators = Sisimai::Lhost.INDICATORS
10
10
  ReBackbone = %r|^Content-Type:[ ]message/rfc822|.freeze
11
11
  StartingOf = {
12
- message: ['This is a MIME encoded message'],
12
+ message: ['Unable to deliver message to:'],
13
13
  error: ['Delivery failed for the following reason:'],
14
14
  }.freeze
15
15
 
@@ -34,8 +34,7 @@ module Sisimai::Lhost
34
34
  # to the previous line of the beginning of the original message.
35
35
  if readcursor == 0
36
36
  # Beginning of the bounce message or delivery status part
37
- readcursor |= Indicators[:deliverystatus] if e == StartingOf[:message][0]
38
- next
37
+ readcursor |= Indicators[:deliverystatus] if e.start_with?(StartingOf[:message][0])
39
38
  end
40
39
  next if (readcursor & Indicators[:deliverystatus]) == 0
41
40
  next if e.empty?
@@ -44,7 +43,7 @@ module Sisimai::Lhost
44
43
  # Delivery failed for the following reason:
45
44
  # Server mx22.example.org[192.0.2.222] failed with: 550 <kijitora@example.org> No such user here
46
45
  #
47
- # This has been a permanent failure. No further delivery attempts will be made.
46
+ # This has been a permanent failure. No further delivery attempts will be made.
48
47
  v = dscontents[-1]
49
48
 
50
49
  if cv = e.match(/\AUnable to deliver message to: [<]([^ ]+[@][^ ]+)[>]\z/)
@@ -32,10 +32,10 @@ module Sisimai::Lhost
32
32
  regularexp = nil
33
33
  v = nil
34
34
 
35
- boundary00 = Sisimai::MIME.boundary(mhead['content-type']) || ''
35
+ boundary00 = Sisimai::MIME.boundary(mhead['content-type'], 1) || ''
36
36
  regularexp = if boundary00.size > 0
37
37
  # Convert to regular expression
38
- Regexp.new('\A' << Regexp.escape('--' << boundary00 << '--') << '\z')
38
+ Regexp.new('\A' << Regexp.escape(boundary00) << '\z')
39
39
  else
40
40
  regularexp = %r/\A[ \t]*[+]+[ \t]*\z/
41
41
  end
@@ -85,7 +85,7 @@ module Sisimai::Lhost
85
85
  match += 1 if mhead['from'].start_with?('Mail Delivery System')
86
86
  match += 1 if mhead['subject'] =~ %r{(?:
87
87
  Mail[ ]delivery[ ]failed(:[ ]returning[ ]message[ ]to[ ]sender)?
88
- |Warning:[ ]message[ ].+[ ]delayed[ ]+
88
+ |Warning:[ ]message[ ][^ ]+[ ]delayed[ ]+
89
89
  |Delivery[ ]Status[ ]Notification
90
90
  )
91
91
  }x
@@ -14,7 +14,7 @@ module Sisimai::Lhost
14
14
  'User not listed in public Name & Address Book',
15
15
  'ディレクトリのリストにありません',
16
16
  ],
17
- networkerror: ['Message has exceeded maximum hop count'],
17
+ 'networkerror' => ['Message has exceeded maximum hop count'],
18
18
  }.freeze
19
19
 
20
20
  # Parse bounce messages from Lotus Notes
@@ -188,7 +188,7 @@ module Sisimai::Lhost
188
188
  e['diagnosis'] = Sisimai::String.sweep(e['diagnosis']) || ''
189
189
  if e['status'].empty? || e['status'].end_with?('.0.0')
190
190
  # There is no value of Status header or the value is 5.0.0, 4.0.0
191
- e['status'] = Sisimai::SMTP::Status.find(e['diagnosis']) || ''
191
+ e['status'] = Sisimai::SMTP::Status.find(e['diagnosis']) || e['status']
192
192
  end
193
193
 
194
194
  ReCommands.each_key do |p|