sisimai 4.24.1 → 4.25.0

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
@@ -12,13 +12,13 @@ module Sisimai::Bite::Email
12
12
  rfc822: ['Content-Type: message/delivery-status'],
13
13
  }.freeze
14
14
  MessagesOf = {
15
- userunknown: [
15
+ 'userunknown' => [
16
16
  'not listed in Domino Directory',
17
17
  'not listed in public Name & Address Book',
18
18
  'Domino ディレクトリには見つかりません',
19
19
  ],
20
- filtered: ['Cannot route mail to user'],
21
- systemerror: ['Several matches found in Domino Directory'],
20
+ 'filtered' => ['Cannot route mail to user'],
21
+ 'systemerror' => ['Several matches found in Domino Directory'],
22
22
  }.freeze
23
23
 
24
24
  def description; return 'IBM Domino Server'; end
@@ -68,7 +68,7 @@ module Sisimai::Bite::Email
68
68
  end
69
69
 
70
70
  if readcursor & Indicators[:'message-rfc822'] > 0
71
- # After "message/rfc822"
71
+ # Inside of the original message part
72
72
  if e.empty?
73
73
  blanklines += 1
74
74
  break if blanklines > 1
@@ -76,7 +76,7 @@ module Sisimai::Bite::Email
76
76
  end
77
77
  rfc822list << e
78
78
  else
79
- # Before "message/rfc822"
79
+ # Error message part
80
80
  next if (readcursor & Indicators[:deliverystatus]) == 0
81
81
 
82
82
  # Your message
@@ -133,9 +133,8 @@ module Sisimai::Bite::Email
133
133
  MessagesOf.each_key do |r|
134
134
  # Check each regular expression of Domino error messages
135
135
  next unless MessagesOf[r].any? { |a| e['diagnosis'].include?(a) }
136
- e['reason'] = r.to_s
137
- pseudostatus = Sisimai::SMTP::Status.code(r.to_s, false)
138
- e['status'] = pseudostatus unless pseudostatus.empty?
136
+ e['reason'] = r
137
+ e['status'] = Sisimai::SMTP::Status.code(r.to_s, false) || ''
139
138
  break
140
139
  end
141
140
  e.each_key { |a| e[a] ||= '' }
@@ -12,11 +12,10 @@ module Sisimai::Bite::Email
12
12
  error: ['For the following reason:'],
13
13
  rfc822: ['--- The header of the original message is following'],
14
14
  }.freeze
15
- MessagesOf = { mesgtoobig: ['Mail size limit exceeded'] }.freeze
15
+ MessagesOf = { 'mesgtoobig' => ['Mail size limit exceeded'] }.freeze
16
16
 
17
17
  def description; return '1&1: http://www.1and1.de'; end
18
18
  def smtpagent; return Sisimai::Bite.smtpagent(self); end
19
- # X-UI-Out-Filterresults: unknown:0;
20
19
  def headerlist; return []; end
21
20
 
22
21
  # Parse bounce messages from 1&1
@@ -60,7 +59,7 @@ module Sisimai::Bite::Email
60
59
  end
61
60
 
62
61
  if readcursor & Indicators[:'message-rfc822'] > 0
63
- # After "message/rfc822"
62
+ # Inside of the original message part
64
63
  if e.empty?
65
64
  blanklines += 1
66
65
  break if blanklines > 1
@@ -68,7 +67,7 @@ module Sisimai::Bite::Email
68
67
  end
69
68
  rfc822list << e
70
69
  else
71
- # Before "message/rfc822"
70
+ # Error message part
72
71
  next if (readcursor & Indicators[:deliverystatus]) == 0
73
72
  next if e.empty?
74
73
 
@@ -110,7 +109,7 @@ module Sisimai::Bite::Email
110
109
  MessagesOf.each_key do |r|
111
110
  # Verify each regular expression of session errors
112
111
  next unless MessagesOf[r].any? { |a| e['diagnosis'].include?(a) }
113
- e['reason'] = r.to_s
112
+ e['reason'] = r
114
113
  break
115
114
  end
116
115
  end
@@ -14,36 +14,36 @@ module Sisimai::Bite::Email
14
14
  rfc822: ['Content-Type: message/rfc822'],
15
15
  }.freeze
16
16
  ErrorCodes = {
17
- onhold: [
17
+ 'onhold' => [
18
18
  '000B099C', # Host Unknown, Message exceeds size limit, ...
19
19
  '000B09AA', # Unable to relay for, Message exceeds size limit,...
20
20
  '000B09B6', # Error messages by remote MTA
21
21
  ],
22
- userunknown: [
22
+ 'userunknown' => [
23
23
  '000C05A6', # Unknown Recipient,
24
24
  ],
25
- systemerror: [
25
+ 'systemerror' => [
26
26
  '00010256', # Too many recipients.
27
27
  '000D06B5', # No proxy for recipient (non-smtp mail?)
28
28
  ],
29
- networkerror: [
29
+ 'networkerror' => [
30
30
  '00120270', # Too Many Hops
31
31
  ],
32
- contenterr: [
32
+ 'contenterr' => [
33
33
  '00050311', # Conversion to Internet format failed
34
34
  '000502CC', # Conversion to Internet format failed
35
35
  ],
36
- securityerr: [
36
+ 'securityerr' => [
37
37
  '000B0981', # 502 Server does not support AUTH
38
38
  ],
39
- filtered: [
39
+ 'filtered' => [
40
40
  '000C0595', # Ambiguous Recipient
41
41
  ],
42
42
  }.freeze
43
43
 
44
44
  def description; return 'Microsoft Exchange Server 2003'; end
45
45
  def smtpagent; return Sisimai::Bite.smtpagent(self); end
46
- def headerlist; return ['X-MS-Embedded-Report', 'X-MimeOLE']; end
46
+ def headerlist; return %w[x-ms-embedded-report x-mimeole]; end
47
47
 
48
48
  # Parse bounce messages from Microsoft Exchange Server 2003
49
49
  # @param [Hash] mhead Message headers of a bounce email
@@ -124,7 +124,7 @@ module Sisimai::Bite::Email
124
124
  end
125
125
 
126
126
  if readcursor & Indicators[:'message-rfc822'] > 0
127
- # After "message/rfc822"
127
+ # Inside of the original message part
128
128
  if e.empty?
129
129
  blanklines += 1
130
130
  break if blanklines > 1
@@ -132,7 +132,7 @@ module Sisimai::Bite::Email
132
132
  end
133
133
  rfc822list << e
134
134
  else
135
- # Before "message/rfc822"
135
+ # Error message part
136
136
  next if (readcursor & Indicators[:deliverystatus]) == 0
137
137
  next if statuspart
138
138
 
@@ -214,18 +214,18 @@ module Sisimai::Bite::Email
214
214
  return nil unless recipients > 0
215
215
 
216
216
  dscontents.each do |e|
217
+ e['agent'] = self.smtpagent
218
+ e.delete('msexch')
217
219
  if cv = e['diagnosis'].match(/\AMSEXCH:.+[ \t]*[(]([0-9A-F]{8})[)][ \t]*(.*)\z/)
218
220
  # MSEXCH:IMS:KIJITORA CAT:EXAMPLE:EXCHANGE 0 (000C05A6) Unknown Recipient
219
221
  capturedcode = cv[1]
220
222
  errormessage = cv[2]
221
- pseudostatus = ''
222
223
 
223
224
  ErrorCodes.each_key do |r|
224
225
  # Find captured code from the error code table
225
226
  next unless ErrorCodes[r].index(capturedcode)
226
- e['reason'] = r.to_s
227
- pseudostatus = Sisimai::SMTP::Status.code(r.to_s)
228
- e['status'] = pseudostatus unless pseudostatus.empty?
227
+ e['reason'] = r
228
+ e['status'] = Sisimai::SMTP::Status.code(r.to_s) || ''
229
229
  break
230
230
  end
231
231
  e['diagnosis'] = errormessage
@@ -233,15 +233,14 @@ module Sisimai::Bite::Email
233
233
 
234
234
  unless e['reason']
235
235
  # Could not detect the reason from the value of "diagnosis".
236
- if e['alterrors']
237
- # Copy alternative error message
238
- e['diagnosis'] = e['alterrors'] + ' ' + e['diagnosis']
239
- e['diagnosis'] = Sisimai::String.sweep(e['diagnosis'])
240
- e.delete('alterrors')
241
- end
236
+ next unless e['alterrors']
237
+ next if e['alterrors'].empty?
238
+
239
+ # Copy alternative error message
240
+ e['diagnosis'] = e['alterrors'] + ' ' + e['diagnosis']
241
+ e['diagnosis'] = Sisimai::String.sweep(e['diagnosis'])
242
+ e.delete('alterrors')
242
243
  end
243
- e['agent'] = self.smtpagent
244
- e.delete('msexch')
245
244
  e.each_key { |a| e[a] ||= '' }
246
245
  end
247
246
 
@@ -10,28 +10,28 @@ module Sisimai::Bite::Email
10
10
  Indicators = Sisimai::Bite::Email.INDICATORS
11
11
  StartingOf = { rfc822: ['Original message headers:'] }.freeze
12
12
  MarkingsOf = {
13
- message: %r/ Microsoft Exchange Server 20\d{2}/,
13
+ message: %r/\ADiagnostic[ ]information[ ]for[ ]administrators:/,
14
14
  error: %r/ ((?:RESOLVER|QUEUE)[.][A-Za-z]+(?:[.]\w+)?);/,
15
15
  rhost: %r/\AGenerating server:[ ]?(.*)/,
16
16
  }.freeze
17
17
  NDRSubject = {
18
- :'SMTPSEND.DNS.NonExistentDomain'=> 'hostunknown', # 554 5.4.4 SMTPSEND.DNS.NonExistentDomain
19
- :'SMTPSEND.DNS.MxLoopback' => 'networkerror', # 554 5.4.4 SMTPSEND.DNS.MxLoopback
20
- :'RESOLVER.ADR.BadPrimary' => 'systemerror', # 550 5.2.0 RESOLVER.ADR.BadPrimary
21
- :'RESOLVER.ADR.RecipNotFound' => 'userunknown', # 550 5.1.1 RESOLVER.ADR.RecipNotFound
22
- :'RESOLVER.ADR.ExRecipNotFound' => 'userunknown', # 550 5.1.1 RESOLVER.ADR.ExRecipNotFound
23
- :'RESOLVER.ADR.RecipLimit' => 'toomanyconn', # 550 5.5.3 RESOLVER.ADR.RecipLimit
24
- :'RESOLVER.ADR.InvalidInSmtp' => 'systemerror', # 550 5.1.0 RESOLVER.ADR.InvalidInSmtp
25
- :'RESOLVER.ADR.Ambiguous' => 'systemerror', # 550 5.1.4 RESOLVER.ADR.Ambiguous, 420 4.2.0 RESOLVER.ADR.Ambiguous
26
- :'RESOLVER.RST.AuthRequired' => 'filtered', # 550 5.7.1 RESOLVER.RST.AuthRequired
27
- :'RESOLVER.RST.NotAuthorized' => 'rejected', # 550 5.7.1 RESOLVER.RST.NotAuthorized
28
- :'RESOLVER.RST.RecipSizeLimit' => 'mesgtoobig', # 550 5.2.3 RESOLVER.RST.RecipSizeLimit
29
- :'QUEUE.Expired' => 'expired', # 550 4.4.7 QUEUE.Expired
18
+ 'SMTPSEND.DNS.NonExistentDomain' => 'hostunknown', # 554 5.4.4 SMTPSEND.DNS.NonExistentDomain
19
+ 'SMTPSEND.DNS.MxLoopback' => 'networkerror', # 554 5.4.4 SMTPSEND.DNS.MxLoopback
20
+ 'RESOLVER.ADR.BadPrimary' => 'systemerror', # 550 5.2.0 RESOLVER.ADR.BadPrimary
21
+ 'RESOLVER.ADR.RecipNotFound' => 'userunknown', # 550 5.1.1 RESOLVER.ADR.RecipNotFound
22
+ 'RESOLVER.ADR.ExRecipNotFound' => 'userunknown', # 550 5.1.1 RESOLVER.ADR.ExRecipNotFound
23
+ 'RESOLVER.ADR.RecipLimit' => 'toomanyconn', # 550 5.5.3 RESOLVER.ADR.RecipLimit
24
+ 'RESOLVER.ADR.InvalidInSmtp' => 'systemerror', # 550 5.1.0 RESOLVER.ADR.InvalidInSmtp
25
+ 'RESOLVER.ADR.Ambiguous' => 'systemerror', # 550 5.1.4 RESOLVER.ADR.Ambiguous, 420 4.2.0 RESOLVER.ADR.Ambiguous
26
+ 'RESOLVER.RST.AuthRequired' => 'securityerror', # 550 5.7.1 RESOLVER.RST.AuthRequired
27
+ 'RESOLVER.RST.NotAuthorized' => 'rejected', # 550 5.7.1 RESOLVER.RST.NotAuthorized
28
+ 'RESOLVER.RST.RecipSizeLimit' => 'mesgtoobig', # 550 5.2.3 RESOLVER.RST.RecipSizeLimit
29
+ 'QUEUE.Expired' => 'expired', # 550 4.4.7 QUEUE.Expired
30
30
  }.freeze
31
31
 
32
32
  def description; return 'Microsoft Exchange Server 2007'; end
33
33
  def smtpagent; return Sisimai::Bite.smtpagent(self); end
34
- def headerlist; return ['Content-Language']; end
34
+ def headerlist; return %w[content-language]; end
35
35
 
36
36
  # Parse bounce messages from Microsoft Exchange Server 2007
37
37
  # @param [Hash] mhead Message headers of a bounce email
@@ -79,7 +79,7 @@ module Sisimai::Bite::Email
79
79
  end
80
80
 
81
81
  if readcursor & Indicators[:'message-rfc822'] > 0
82
- # After "message/rfc822"
82
+ # Inside of the original message part
83
83
  if e.empty?
84
84
  blanklines += 1
85
85
  break if blanklines > 1
@@ -87,7 +87,7 @@ module Sisimai::Bite::Email
87
87
  end
88
88
  rfc822list << e
89
89
  else
90
- # Before "message/rfc822"
90
+ # Error message part
91
91
  next if (readcursor & Indicators[:deliverystatus]) == 0
92
92
 
93
93
  if connvalues == connheader.keys.size
@@ -120,22 +120,20 @@ module Sisimai::Bite::Email
120
120
  v['status'] = cv[2]
121
121
  v['diagnosis'] = e
122
122
  else
123
- if !v['diagnosis'].to_s.empty? && v['diagnosis'].end_with?('=')
124
- # Continued line of error messages
125
- v['diagnosis'] = v['diagnosis'].chomp('=')
126
- v['diagnosis'] << e
127
- end
123
+ # Continued line of error messages
124
+ next if v['diagnosis'].to_s.empty?
125
+ next unless v['diagnosis'].end_with?('=')
126
+ v['diagnosis'] = v['diagnosis'].chomp('=')
127
+ v['diagnosis'] << e
128
128
  end
129
129
  else
130
130
  # Diagnostic information for administrators:
131
131
  #
132
132
  # Generating server: mta22.neko.example.org
133
- if cv = e.match(MarkingsOf[:rhost])
134
- # Generating server: mta22.neko.example.org
135
- next unless connheader['rhost'].empty?
136
- connheader['rhost'] = cv[1]
137
- connvalues += 1
138
- end
133
+ next unless cv = e.match(MarkingsOf[:rhost])
134
+ next unless connheader['rhost'].empty?
135
+ connheader['rhost'] = cv[1]
136
+ connvalues += 1
139
137
  end
140
138
  end
141
139
  end
@@ -147,7 +145,7 @@ module Sisimai::Bite::Email
147
145
  f = cv[1]
148
146
  NDRSubject.each_key do |r|
149
147
  # Try to match with error subject strings
150
- next unless f == r.to_s
148
+ next unless f == r
151
149
  e['reason'] = NDRSubject[r]
152
150
  break
153
151
  end
@@ -62,11 +62,11 @@ module Sisimai::Bite::Email
62
62
  MessagesOf = {
63
63
  # find exim/ -type f -exec grep 'message = US' {} /dev/null \;
64
64
  # route.c:1158| DEBUG(D_uid) debug_printf("getpwnam() returned NULL (user not found)\n");
65
- userunknown: ['user not found'],
65
+ 'userunknown' => ['user not found'],
66
66
  # transports/smtp.c:3524| addr->message = US"all host address lookups failed permanently";
67
67
  # routers/dnslookup.c:331| addr->message = US"all relevant MX records point to non-existent hosts";
68
68
  # route.c:1826| uschar *message = US"Unrouteable address";
69
- hostunknown: [
69
+ 'hostunknown' => [
70
70
  'all host address lookups failed permanently',
71
71
  'all relevant MX records point to non-existent hosts',
72
72
  'Unrouteable address',
@@ -74,10 +74,10 @@ module Sisimai::Bite::Email
74
74
  # transports/appendfile.c:2567| addr->user_message = US"mailbox is full";
75
75
  # transports/appendfile.c:3049| addr->message = string_sprintf("mailbox is full "
76
76
  # transports/appendfile.c:3050| "(quota exceeded while writing to file %s)", filename);
77
- mailboxfull: ['mailbox is full', 'error: quota exceed'],
77
+ 'mailboxfull' => ['mailbox is full', 'error: quota exceed'],
78
78
  # routers/dnslookup.c:328| addr->message = US"an MX or SRV record indicated no SMTP service";
79
79
  # transports/smtp.c:3502| addr->message = US"no host found for existing SMTP connection";
80
- notaccept: [
80
+ 'notaccept' => [
81
81
  'an MX or SRV record indicated no SMTP service',
82
82
  'no host found for existing SMTP connection',
83
83
  ],
@@ -85,7 +85,7 @@ module Sisimai::Bite::Email
85
85
  # parser.c:701| if(bracket_count++ > 5) FAILED(US"angle-brackets nested too deep");
86
86
  # parser.c:738| FAILED(US"domain missing in source-routed address");
87
87
  # parser.c:747| : string_sprintf("malformed address: %.32s may not follow %.*s",
88
- syntaxerror: [
88
+ 'syntaxerror' => [
89
89
  'angle-brackets nested too deep',
90
90
  'expected word or "<"',
91
91
  'domain missing in source-routed address',
@@ -94,14 +94,14 @@ module Sisimai::Bite::Email
94
94
  # deliver.c:5614| addr->message = US"delivery to file forbidden";
95
95
  # deliver.c:5624| addr->message = US"delivery to pipe forbidden";
96
96
  # transports/pipe.c:1156| addr->user_message = US"local delivery failed";
97
- systemerror: [
97
+ 'systemerror' => [
98
98
  'delivery to file forbidden',
99
99
  'delivery to pipe forbidden',
100
100
  'local delivery failed',
101
101
  'LMTP error after ',
102
102
  ],
103
103
  # deliver.c:5425| new->message = US"Too many \"Received\" headers - suspected mail loop";
104
- contenterror: ['Too many "Received" headers'],
104
+ 'contenterror' => ['Too many "Received" headers'],
105
105
  }.freeze
106
106
 
107
107
  # retry.c:902| addr->message = (addr->message == NULL)? US"retry timeout exceeded" :
@@ -125,7 +125,7 @@ module Sisimai::Bite::Email
125
125
 
126
126
  def description; return 'Exim'; end
127
127
  def smtpagent; return Sisimai::Bite.smtpagent(self); end
128
- def headerlist; return ['X-Failed-Recipients']; end
128
+ def headerlist; return %w[x-failed-recipients]; end
129
129
 
130
130
  # Parse bounce messages from Exim
131
131
  # @param [Hash] mhead Message headers of a bounce email
@@ -152,6 +152,8 @@ module Sisimai::Bite::Email
152
152
  )
153
153
  }x
154
154
 
155
+ require 'sisimai/rfc1894'
156
+ fieldtable = Sisimai::RFC1894.FIELDTABLE
155
157
  dscontents = [Sisimai::Bite.DELIVERYSTATUS]
156
158
  hasdivided = mbody.split("\n")
157
159
  rfc822list = [] # (Array) Each line in message/rfc822 part string
@@ -175,7 +177,7 @@ module Sisimai::Bite::Email
175
177
  break if e == StartingOf[:endof][0]
176
178
 
177
179
  if readcursor == 0
178
- # Beginning of the bounce message or delivery status part
180
+ # Beginning of the bounce message or message/delivery-status part
179
181
  if e =~ MarkingsOf[:message]
180
182
  readcursor |= Indicators[:deliverystatus]
181
183
  next unless e =~ MarkingsOf[:frozen]
@@ -183,7 +185,7 @@ module Sisimai::Bite::Email
183
185
  end
184
186
 
185
187
  if (readcursor & Indicators[:'message-rfc822']) == 0
186
- # Beginning of the original message part
188
+ # Beginning of the original message part(message/rfc822)
187
189
  if e =~ MarkingsOf[:rfc822]
188
190
  readcursor |= Indicators[:'message-rfc822']
189
191
  next
@@ -191,7 +193,7 @@ module Sisimai::Bite::Email
191
193
  end
192
194
 
193
195
  if readcursor & Indicators[:'message-rfc822'] > 0
194
- # After "message/rfc822"
196
+ # message/rfc822 OR text/rfc822-headers part
195
197
  if e.empty?
196
198
  blanklines += 1
197
199
  break if blanklines > 1
@@ -199,7 +201,7 @@ module Sisimai::Bite::Email
199
201
  end
200
202
  rfc822list << e
201
203
  else
202
- # Before "message/rfc822"
204
+ # message/delivery-status part
203
205
  next if (readcursor & Indicators[:deliverystatus]) == 0
204
206
  next if e.empty?
205
207
 
@@ -266,34 +268,34 @@ module Sisimai::Bite::Email
266
268
  # --NNNNNNNNNN-eximdsn-MMMMMMMMMM
267
269
  # Content-type: message/delivery-status
268
270
  # ...
269
- if cv = e.match(/\AReporting-MTA:[ ]*(?:DNS|dns);[ ]*(.+)\z/)
270
- # Reporting-MTA: dns; mx.example.jp
271
- v['lhost'] = cv[1]
272
-
273
- elsif cv = e.match(/\AAction:[ ]*(.+)\z/)
274
- # Action: failed
275
- v['action'] = cv[1].downcase
276
-
277
- elsif cv = e.match(/\AStatus:[ ]*(\d[.]\d+[.]\d+)/)
278
- # Status: 5.0.0
279
- v['status'] = cv[1]
280
-
281
- elsif cv = e.match(/\ADiagnostic-Code:[ ]*(.+?);[ ]*(.+)\z/)
282
- # Diagnostic-Code: SMTP; 550 5.1.1 <userunknown@example.jp>... User Unknown
283
- v['spec'] = cv[1].upcase
284
- v['diagnosis'] = cv[2]
285
-
286
- elsif cv = e.match(/\AFinal-Recipient:[ ]*(?:RFC|rfc)822;[ ]*(.+)\z/)
287
- # Final-Recipient: rfc822;|/bin/echo "Some pipe output"
288
- v['spec'] ||= cv[1].include?('@') ? 'SMTP' : 'X-UNIX'
271
+ if Sisimai::RFC1894.match(e)
272
+ # "e" matched with any field defined in RFC3464
273
+ next unless o = Sisimai::RFC1894.field(e)
274
+
275
+ if o[-1] == 'addr'
276
+ # Final-Recipient: rfc822; kijitora@example.jp
277
+ # X-Actual-Recipient: rfc822; kijitora@example.co.jp
278
+ next unless o[0] == 'final-recipient'
279
+ v['spec'] ||= o[2].include?('@') ? 'SMTP' : 'X-UNIX'
280
+
281
+ elsif o[-1] == 'code'
282
+ # Diagnostic-Code: SMTP; 550 5.1.1 <userunknown@example.jp>... User Unknown
283
+ v['spec'] = o[1]
284
+ v['diagnosis'] = o[2]
285
+
286
+ else
287
+ # Other DSN fields defined in RFC3464
288
+ next unless fieldtable.key?(o[0])
289
+ v[fieldtable[o[0]]] = o[2]
290
+ end
289
291
  else
290
292
  # Error message ?
291
- if havepassed[:deliverystatus] == 0
292
- # Content-type: message/delivery-status
293
- havepassed[:deliverystatus] = 1 if e.start_with?(StartingOf[:deliverystatus][0])
294
- v['alterrors'] ||= ''
295
- v['alterrors'] << e + ' ' if e.start_with?(' ')
296
- end
293
+ next if havepassed[:deliverystatus] == 1
294
+
295
+ # Content-type: message/delivery-status
296
+ havepassed[:deliverystatus] = 1 if e.start_with?(StartingOf[:deliverystatus][0])
297
+ v['alterrors'] ||= ''
298
+ v['alterrors'] << e + ' ' if e.start_with?(' ')
297
299
  end
298
300
  else
299
301
  if dscontents.size == recipients
@@ -313,12 +315,11 @@ module Sisimai::Bite::Email
313
315
  v['alterrors'] ||= ''
314
316
  v['alterrors'] << e + ' '
315
317
  end
316
-
317
318
  end
318
319
  end
319
320
  end
320
321
  end
321
- end
322
+ end # End of message/delivery-status
322
323
  end
323
324
 
324
325
  if recipients > 0
@@ -448,7 +449,7 @@ module Sisimai::Bite::Email
448
449
  MessagesOf.each_key do |r|
449
450
  # Check each regular expression
450
451
  next unless MessagesOf[r].any? { |a| e['diagnosis'].include?(a) }
451
- e['reason'] = r.to_s
452
+ e['reason'] = r
452
453
  break
453
454
  end
454
455
 
@@ -473,10 +474,10 @@ module Sisimai::Bite::Email
473
474
  s1 = 0 # First character of Status as integer
474
475
  r1 = 0 # First character of SMTP reply code as integer
475
476
 
476
- # "Status:" field did not exist in the bounce message
477
477
  while true
478
- break unless sv.empty?
479
- break if rv.empty?
478
+ # "Status:" field did not exist in the bounce message
479
+ break if sv
480
+ break unless rv
480
481
 
481
482
  # Check SMTP reply code
482
483
  # Generate pseudo DSN code from SMTP reply code
@@ -492,8 +493,8 @@ module Sisimai::Bite::Email
492
493
  break
493
494
  end
494
495
 
495
- s1 = sv[0, 1].to_i unless sv.empty?
496
- v1 = s1 + r1
496
+ s1 = sv[0, 1].to_i if sv
497
+ v1 = s1 + r1
497
498
  v1 << e['status'][0, 1].to_i if e['status']
498
499
 
499
500
  if v1 > 0
@@ -510,7 +511,7 @@ module Sisimai::Bite::Email
510
511
  Sisimai::SMTP::Status.code(e['reason'], false)
511
512
  end
512
513
  end
513
- e['status'] ||= sv
514
+ e['status'] ||= sv.to_s
514
515
  e.each_key { |a| e[a] ||= '' }
515
516
  end
516
517