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
@@ -10,7 +10,7 @@ module Sisimai::Bite::Email
10
10
  Indicators = Sisimai::Bite::Email.INDICATORS
11
11
  StartingOf = { message: ['This report relates to a message you sent with the following header fields:'] }.freeze
12
12
  MarkingsOf = { rfc822: %r!\A(?:Content-type:[ ]*message/rfc822|Return-path:[ ]*)! }.freeze
13
- MessagesOf = { hostunknown: ['Illegal host/domain name found'] }.freeze
13
+ MessagesOf = { 'hostunknown' => ['Illegal host/domain name found'] }.freeze
14
14
 
15
15
  def description; return 'Oracle Communications Messaging Server'; end
16
16
  def smtpagent; return Sisimai::Bite.smtpagent(self); end
@@ -60,7 +60,7 @@ module Sisimai::Bite::Email
60
60
  end
61
61
 
62
62
  if readcursor & Indicators[:'message-rfc822'] > 0
63
- # After "message/rfc822"
63
+ # Inside of the original message part
64
64
  if e.empty?
65
65
  blanklines += 1
66
66
  break if blanklines > 1
@@ -68,7 +68,7 @@ module Sisimai::Bite::Email
68
68
  end
69
69
  rfc822list << e
70
70
  else
71
- # Before "message/rfc822"
71
+ # Error message part
72
72
  next if (readcursor & Indicators[:deliverystatus]) == 0
73
73
  next if e.empty?
74
74
 
@@ -126,12 +126,11 @@ module Sisimai::Bite::Email
126
126
  sessionlog = cv[2] # smtp session
127
127
  v['rhost'] = remotehost
128
128
 
129
- if cv = sessionlog.match(/\A[(]TCP|(.+)|\d+|(.+)|\d+[)]/)
130
- # The value does not include ".", use IP address instead.
131
- # (TCP|17.111.174.67|47323|192.0.2.225|25)
132
- v['lhost'] = cv[1]
133
- v['rhost'] = cv[2] unless remotehost =~ /[^.]+[.][^.]+/
134
- end
129
+ # The value does not include ".", use IP address instead.
130
+ # (TCP|17.111.174.67|47323|192.0.2.225|25)
131
+ next unless cv = sessionlog.match(/\A[(]TCP|(.+)|\d+|(.+)|\d+[)]/)
132
+ v['lhost'] = cv[1]
133
+ v['rhost'] = cv[2] unless remotehost =~ /[^.]+[.][^.]+/
135
134
  else
136
135
  # Original-envelope-id: 0NFC009FLKOUVMA0@mr21p30im-asmtp004.me.com
137
136
  # Reporting-MTA: dns;mr21p30im-asmtp004.me.com (tcp-daemon)
@@ -172,7 +171,7 @@ module Sisimai::Bite::Email
172
171
  MessagesOf.each_key do |r|
173
172
  # Verify each regular expression of session errors
174
173
  next unless MessagesOf[r].any? { |a| e['diagnosis'].include?(a) }
175
- e['reason'] = r.to_s
174
+ e['reason'] = r
176
175
  break
177
176
  end
178
177
  e.each_key { |a| e[a] ||= '' }
@@ -17,7 +17,7 @@ module Sisimai::Bite::Email
17
17
 
18
18
  def description; return 'Digital Arts m-FILTER'; end
19
19
  def smtpagent; return 'Email::mFILTER'; end
20
- def headerlist; return ['X-Mailer']; end
20
+ def headerlist; return %w[x-mailer]; end
21
21
 
22
22
  # Parse bounce messages from Digital Arts m-FILTER
23
23
  # @param [Hash] mhead Message headers of a bounce email
@@ -59,7 +59,7 @@ module Sisimai::Bite::Email
59
59
  end
60
60
 
61
61
  if readcursor & Indicators[:'message-rfc822'] > 0
62
- # After "message/rfc822"
62
+ # Inside of the original message part
63
63
  if e.empty?
64
64
  blanklines += 1
65
65
  break if blanklines > 1
@@ -67,7 +67,7 @@ module Sisimai::Bite::Email
67
67
  end
68
68
  rfc822list << e
69
69
  else
70
- # Before "message/rfc822"
70
+ # Error message part
71
71
  next if (readcursor & Indicators[:deliverystatus]) == 0
72
72
  next if e.empty?
73
73
 
@@ -125,21 +125,21 @@ module Sisimai::Bite::Email
125
125
  return nil unless recipients > 0
126
126
 
127
127
  dscontents.each do |e|
128
- unless mhead['received'].empty?
129
- # Get localhost and remote host name from Received header.
130
- rheads = mhead['received']
131
- rhosts = Sisimai::RFC5322.received(rheads[-1])
132
-
133
- e['lhost'] ||= Sisimai::RFC5322.received(rheads[0]).shift
134
- while ee = rhosts.shift do
135
- # Avoid "... by m-FILTER"
136
- next unless ee.include?('.')
137
- e['rhost'] = ee
138
- end
139
- end
128
+ e.each_key { |a| e[a] ||= '' }
140
129
  e['diagnosis'] = Sisimai::String.sweep(e['diagnosis'])
141
130
  e['agent'] = self.smtpagent
142
- e.each_key { |a| e[a] ||= '' }
131
+
132
+ # Get localhost and remote host name from Received header.
133
+ next if mhead['received'].empty?
134
+ rheads = mhead['received']
135
+ rhosts = Sisimai::RFC5322.received(rheads[-1])
136
+
137
+ e['lhost'] ||= Sisimai::RFC5322.received(rheads[0]).shift
138
+ while ee = rhosts.shift do
139
+ # Avoid "... by m-FILTER"
140
+ next unless ee.include?('.')
141
+ e['rhost'] = ee
142
+ end
143
143
  end
144
144
 
145
145
  rfc822part = Sisimai::RFC5322.weedout(rfc822list)
@@ -21,11 +21,11 @@ module Sisimai::Bite::Email
21
21
  MessagesOf = {
22
22
  # find exim/ -type f -exec grep 'message = US' {} /dev/null \;
23
23
  # route.c:1158| DEBUG(D_uid) debug_printf("getpwnam() returned NULL (user not found)\n");
24
- userunknown: ['user not found'],
24
+ 'userunknown' => ['user not found'],
25
25
  # transports/smtp.c:3524| addr->message = US"all host address lookups failed permanently";
26
26
  # routers/dnslookup.c:331| addr->message = US"all relevant MX records point to non-existent hosts";
27
27
  # route.c:1826| uschar *message = US"Unrouteable address";
28
- hostunknown: [
28
+ 'hostunknown' => [
29
29
  'all host address lookups failed permanently',
30
30
  'all relevant MX records point to non-existent hosts',
31
31
  'Unrouteable address',
@@ -33,10 +33,10 @@ module Sisimai::Bite::Email
33
33
  # transports/appendfile.c:2567| addr->user_message = US"mailbox is full";
34
34
  # transports/appendfile.c:3049| addr->message = string_sprintf("mailbox is full "
35
35
  # transports/appendfile.c:3050| "(quota exceeded while writing to file %s)", filename);
36
- mailboxfull: ['mailbox is full', 'error: quota exceed'],
36
+ 'mailboxfull' => ['mailbox is full', 'error: quota exceed'],
37
37
  # routers/dnslookup.c:328| addr->message = US"an MX or SRV record indicated no SMTP service";
38
38
  # transports/smtp.c:3502| addr->message = US"no host found for existing SMTP connection";
39
- notaccept: [
39
+ 'notaccept' => [
40
40
  'an MX or SRV record indicated no SMTP service',
41
41
  'no host found for existing SMTP connection',
42
42
  ],
@@ -44,7 +44,7 @@ module Sisimai::Bite::Email
44
44
  # parser.c:701| if(bracket_count++ > 5) FAILED(US"angle-brackets nested too deep");
45
45
  # parser.c:738| FAILED(US"domain missing in source-routed address");
46
46
  # parser.c:747| : string_sprintf("malformed address: %.32s may not follow %.*s",
47
- syntaxerror: [
47
+ 'syntaxerror' => [
48
48
  'angle-brackets nested too deep',
49
49
  'expected word or "<"',
50
50
  'domain missing in source-routed address',
@@ -53,14 +53,14 @@ module Sisimai::Bite::Email
53
53
  # deliver.c:5614| addr->message = US"delivery to file forbidden";
54
54
  # deliver.c:5624| addr->message = US"delivery to pipe forbidden";
55
55
  # transports/pipe.c:1156| addr->user_message = US"local delivery failed";
56
- systemerror: [
56
+ 'systemerror' => [
57
57
  'delivery to file forbidden',
58
58
  'delivery to pipe forbidden',
59
59
  'local delivery failed',
60
60
  'LMTP error after ',
61
61
  ],
62
62
  # deliver.c:5425| new->message = US"Too many \"Received\" headers - suspected mail loop";
63
- contenterror: ['Too many "Received" headers'],
63
+ 'contenterror' => ['Too many "Received" headers'],
64
64
  }.freeze
65
65
  DelayedFor = [
66
66
  'retry timeout exceeded',
@@ -77,7 +77,7 @@ module Sisimai::Bite::Email
77
77
  # X-MX-Bounce: mta/src/queue/bounce
78
78
  # X-MXL-NoteHash: ffffffffffffffff-0000000000000000000000000000000000000000
79
79
  # X-MXL-Hash: 4c9d4d411993da17-bbd4212b6c887f6c23bab7db4bd87ef5edc00758
80
- def headerlist; return ['X-MXL-NoteHash', 'X-MXL-Hash', 'X-MX-Bounce']; end
80
+ def headerlist; return %w[x-mxl-notehash x-mxl-hash x-mx-bounce]; end
81
81
 
82
82
  # Parse bounce messages from MXLogic
83
83
  # @param [Hash] mhead Message headers of a bounce email
@@ -132,7 +132,7 @@ module Sisimai::Bite::Email
132
132
  end
133
133
 
134
134
  if readcursor & Indicators[:'message-rfc822'] > 0
135
- # After "message/rfc822"
135
+ # Inside of the original message part
136
136
  if e.empty?
137
137
  blanklines += 1
138
138
  break if blanklines > 1
@@ -140,7 +140,7 @@ module Sisimai::Bite::Email
140
140
  end
141
141
  rfc822list << e
142
142
  else
143
- # Before "message/rfc822"
143
+ # Error message part
144
144
  next if (readcursor & Indicators[:deliverystatus]) == 0
145
145
  next if e.empty?
146
146
 
@@ -222,7 +222,7 @@ module Sisimai::Bite::Email
222
222
  MessagesOf.each_key do |r|
223
223
  # Check each regular expression
224
224
  next unless MessagesOf[r].any? { |a| e['diagnosis'].include?(a) }
225
- e['reason'] = r.to_s
225
+ e['reason'] = r
226
226
  break
227
227
  end
228
228
 
@@ -12,7 +12,7 @@ module Sisimai::Bite::Email
12
12
  rfc822: ['------- Returned Message '],
13
13
  }.freeze
14
14
  MessagesOf = {
15
- userunknown: [
15
+ 'userunknown' => [
16
16
  'User not listed in public Name & Address Book',
17
17
  'ディレクトリのリストにありません',
18
18
  ],
@@ -74,7 +74,7 @@ module Sisimai::Bite::Email
74
74
  end
75
75
 
76
76
  if readcursor & Indicators[:'message-rfc822'] > 0
77
- # After "message/rfc822"
77
+ # Inside of the original message part
78
78
  if e.empty?
79
79
  blanklines += 1
80
80
  break if blanklines > 1
@@ -82,7 +82,7 @@ module Sisimai::Bite::Email
82
82
  end
83
83
  rfc822list << e
84
84
  else
85
- # Before "message/rfc822"
85
+ # Error message part
86
86
  next if (readcursor & Indicators[:deliverystatus]) == 0
87
87
 
88
88
  # ------- Failure Reasons --------
@@ -150,9 +150,8 @@ module Sisimai::Bite::Email
150
150
  MessagesOf.each_key do |r|
151
151
  # Check each regular expression of Notes error messages
152
152
  next unless MessagesOf[r].any? { |a| e['diagnosis'].include?(a) }
153
- e['reason'] = r.to_s
154
- pseudostatus = Sisimai::SMTP::Status.code(r.to_s)
155
- e['status'] = pseudostatus unless pseudostatus.empty?
153
+ e['reason'] = r
154
+ e['status'] = Sisimai::SMTP::Status.code(r.to_s) || ''
156
155
  break
157
156
  end
158
157
  e.each_key { |a| e[a] ||= '' }
@@ -65,14 +65,15 @@ module Sisimai::Bite::Email
65
65
  # X-MS-Exchange-CrossTenant-OriginalArrivalTime: 29 Apr 2015 23:34:45.6789 (JST)
66
66
  # X-MS-Exchange-CrossTenant-FromEntityHeader: Hosted
67
67
  # X-MS-Exchange-Transport-CrossTenantHeadersStamped: ...
68
- return [
69
- 'X-MS-Exchange-Message-Is-Ndr',
70
- 'X-Microsoft-Antispam-PRVS',
71
- 'X-Exchange-Antispam-Report-Test',
72
- 'X-Exchange-Antispam-Report-CFA-Test',
73
- 'X-MS-Exchange-CrossTenant-OriginalArrivalTime',
74
- 'X-MS-Exchange-CrossTenant-FromEntityHeader',
75
- 'X-MS-Exchange-Transport-CrossTenantHeadersStamped',
68
+ return %w[
69
+ content-language
70
+ x-ms-exchange-message-is-ndr
71
+ x-microsoft-antispam-prvs
72
+ x-exchange-antispam-report-test
73
+ x-exchange-antispam-report-cfa-test
74
+ x-ms-exchange-crosstenant-originalarrivaltime
75
+ x-ms-exchange-crosstenant-fromentityheader
76
+ x-ms-exchange-transport-crosstenantheadersstamped
76
77
  ]
77
78
  end
78
79
 
@@ -105,6 +106,10 @@ module Sisimai::Bite::Email
105
106
  end
106
107
  return nil if match < 2
107
108
 
109
+ require 'sisimai/rfc1894'
110
+ fieldtable = Sisimai::RFC1894.FIELDTABLE
111
+ permessage = {} # (Hash) Store values of each Per-Message field
112
+
108
113
  dscontents = [Sisimai::Bite.DELIVERYSTATUS]
109
114
  hasdivided = mbody.split("\n")
110
115
  rfc822list = [] # (Array) Each line in message/rfc822 part string
@@ -134,7 +139,7 @@ module Sisimai::Bite::Email
134
139
  end
135
140
 
136
141
  if readcursor & Indicators[:'message-rfc822'] > 0
137
- # After "message/rfc822"
142
+ # Inside of the original message part
138
143
  if e.empty?
139
144
  blanklines += 1
140
145
  break if blanklines > 1
@@ -142,7 +147,7 @@ module Sisimai::Bite::Email
142
147
  end
143
148
  rfc822list << e
144
149
  else
145
- # Before "message/rfc822"
150
+ # Error message part
146
151
  next if (readcursor & Indicators[:deliverystatus]) == 0
147
152
  next if e.empty?
148
153
 
@@ -168,35 +173,14 @@ module Sisimai::Bite::Email
168
173
  else
169
174
  if endoferror
170
175
  # After "Original message headers:"
171
- if htmlbegins
172
- # <html> .. </html>
173
- htmlbegins = false if e.start_with?('</html>')
174
- next
175
- end
176
-
177
- if cv = e.match(/\AAction:[ ]*(.+)\z/)
178
- # Action: failed
179
- v['action'] = cv[1].downcase
180
-
181
- elsif cv = e.match(/\AStatus:[ ]*(\d[.]\d+[.]\d+)/)
182
- # Status:5.2.0
183
- v['status'] = cv[1]
184
-
185
- elsif cv = e.match(/\AReporting-MTA:[ ]*(?:DNS|dns);[ ]*(.+)\z/)
186
- # Reporting-MTA: dns;BLU004-OMC3S13.hotmail.example.com
187
- connheader['lhost'] = cv[1].downcase
188
-
189
- elsif cv = e.match(/\AReceived-From-MTA:[ ]*(?:DNS|dns);[ ]*(.+)\z/)
190
- # Reporting-MTA: dns;BLU004-OMC3S13.hotmail.example.com
191
- connheader['rhost'] = cv[1].downcase
192
-
193
- elsif cv = e.match(/\AArrival-Date:[ ]*(.+)\z/)
194
- # Arrival-Date: Wed, 29 Apr 2009 16:03:18 +0900
195
- next if connheader['date']
196
- connheader['date'] = cv[1]
197
- else
198
- htmlbegins = true if e.start_with?('<html>')
199
- end
176
+ next unless f = Sisimai::RFC1894.match(e)
177
+ next unless o = Sisimai::RFC1894.field(e)
178
+ next unless fieldtable.key?(o[0])
179
+ next if o[0] =~ /\A(?:diagnostic-code|final-recipient)\z/
180
+ v[fieldtable[o[0]]] = o[2]
181
+
182
+ next unless f == 1
183
+ permessage[fieldtable[o[0]]] = o[2]
200
184
  else
201
185
  if e == StartingOf[:error][0]
202
186
  # Diagnostic information for administrators:
@@ -222,7 +206,7 @@ module Sisimai::Bite::Email
222
206
 
223
207
  dscontents.each do |e|
224
208
  # Set default values if each value is empty.
225
- connheader.each_key { |a| e[a] ||= connheader[a] || '' }
209
+ permessage.each_key { |a| e[a] ||= permessage[a] || '' }
226
210
 
227
211
  e['agent'] = self.smtpagent
228
212
  e['status'] ||= ''
@@ -230,8 +214,7 @@ module Sisimai::Bite::Email
230
214
 
231
215
  if e['status'].empty? || e['status'].end_with?('.0.0')
232
216
  # There is no value of Status header or the value is 5.0.0, 4.0.0
233
- pseudostatus = Sisimai::SMTP::Status.find(e['diagnosis'])
234
- e['status'] = pseudostatus unless pseudostatus.empty?
217
+ e['status'] = Sisimai::SMTP::Status.find(e['diagnosis']) || ''
235
218
  end
236
219
 
237
220
  ReCommands.each_key do |p|
@@ -38,17 +38,17 @@ module Sisimai::Bite::Email
38
38
  }.freeze
39
39
  MessagesOf = {
40
40
  # smtpd/queue.c:221| envelope_set_errormsg(&evp, "Envelope expired");
41
- expired: ['Envelope expired'],
41
+ 'expired' => ['Envelope expired'],
42
42
  # smtpd/mta.c:976| relay->failstr = "Invalid domain name";
43
43
  # smtpd/mta.c:980| relay->failstr = "Domain does not exist";
44
- hostunknown: [
44
+ 'hostunknown' => [
45
45
  'Invalid domain name',
46
46
  'Domain does not exist',
47
47
  ],
48
48
  # smtp/mta.c:1085| relay->failstr = "Destination seem to reject all mails";
49
- notaccept: ['Destination seem to reject all mails'],
49
+ 'notaccept' => ['Destination seem to reject all mails'],
50
50
  # smtpd/mta.c:972| relay->failstr = "Temporary failure in MX lookup";
51
- networkerror: [
51
+ 'networkerror' => [
52
52
  'Address family mismatch on destination MXs',
53
53
  'All routes to destination blocked',
54
54
  'bad DNS lookup error code',
@@ -62,7 +62,7 @@ module Sisimai::Bite::Email
62
62
  'Temporary failure in MX lookup',
63
63
  ],
64
64
  # smtpd/mta.c:1013| relay->failstr = "Could not retrieve credentials";
65
- securityerror: ['Could not retrieve credentials'],
65
+ 'securityerror' => ['Could not retrieve credentials'],
66
66
  }.freeze
67
67
 
68
68
  def description; return 'OpenSMTPD'; end
@@ -111,7 +111,7 @@ module Sisimai::Bite::Email
111
111
  end
112
112
 
113
113
  if readcursor & Indicators[:'message-rfc822'] > 0
114
- # After "message/rfc822"
114
+ # Inside of the original message part
115
115
  if e.empty?
116
116
  blanklines += 1
117
117
  break if blanklines > 1
@@ -119,7 +119,7 @@ module Sisimai::Bite::Email
119
119
  end
120
120
  rfc822list << e
121
121
  else
122
- # Before "message/rfc822"
122
+ # Error message part
123
123
  next if (readcursor & Indicators[:deliverystatus]) == 0
124
124
  next if e.empty?
125
125
 
@@ -157,7 +157,7 @@ module Sisimai::Bite::Email
157
157
  MessagesOf.each_key do |r|
158
158
  # Verify each regular expression of session errors
159
159
  next unless MessagesOf[r].any? { |a| e['diagnosis'].include?(a) }
160
- e['reason'] = r.to_s
160
+ e['reason'] = r
161
161
  break
162
162
  end
163
163
  e.each_key { |a| e[a] ||= '' }
@@ -13,8 +13,8 @@ module Sisimai::Bite::Email
13
13
  rfc822: ['Content-Type: message/rfc822'],
14
14
  }.freeze
15
15
  MessagesOf = {
16
- hostunknown: ['The mail could not be delivered to the recipient because the domain is not reachable'],
17
- userunknown: ['Requested action not taken: mailbox unavailable'],
16
+ 'hostunknown' => ['The mail could not be delivered to the recipient because the domain is not reachable'],
17
+ 'userunknown' => ['Requested action not taken: mailbox unavailable'],
18
18
  }.freeze
19
19
 
20
20
  def description; return 'Microsoft Outlook.com: https://www.outlook.com/'; end
@@ -22,7 +22,7 @@ module Sisimai::Bite::Email
22
22
 
23
23
  # X-Message-Delivery: Vj0xLjE7RD0wO0dEPTA7U0NMPTk7bD0xO3VzPTE=
24
24
  # X-Message-Info: AuEzbeVr9u5fkDpn2vR5iCu5wb6HBeY4iruBjnutBzpStnUabbM...
25
- def headerlist; return ['X-Message-Delivery', 'X-Message-Info']; end
25
+ def headerlist; return %w[x-message-delivery x-message-info]; end
26
26
 
27
27
  # Parse bounce messages from Microsoft Outlook.com
28
28
  # @param [Hash] mhead Message headers of a bounce email
@@ -44,6 +44,10 @@ module Sisimai::Bite::Email
44
44
  match += 1 if mhead['received'].any? { |a| a.include?('.hotmail.com') }
45
45
  return nil if match < 2
46
46
 
47
+ require 'sisimai/rfc1894'
48
+ fieldtable = Sisimai::RFC1894.FIELDTABLE
49
+ permessage = {} # (Hash) Store values of each Per-Message field
50
+
47
51
  dscontents = [Sisimai::Bite.DELIVERYSTATUS]
48
52
  hasdivided = mbody.split("\n")
49
53
  havepassed = ['']
@@ -51,11 +55,6 @@ module Sisimai::Bite::Email
51
55
  blanklines = 0 # (Integer) The number of blank lines
52
56
  readcursor = 0 # (Integer) Points the current cursor position
53
57
  recipients = 0 # (Integer) The number of 'Final-Recipient' header
54
- connvalues = 0 # (Integer) Flag, 1 if all the value of connheader have been set
55
- connheader = {
56
- 'date' => '', # The value of Arrival-Date header
57
- 'lhost' => '', # The value of Reporting-MTA header
58
- }
59
58
  v = nil
60
59
 
61
60
  while e = hasdivided.shift do
@@ -64,7 +63,7 @@ module Sisimai::Bite::Email
64
63
  p = havepassed[-2]
65
64
 
66
65
  if readcursor == 0
67
- # Beginning of the bounce message or delivery status part
66
+ # Beginning of the bounce message or message/delivery-status part
68
67
  if e.start_with?(StartingOf[:message][0])
69
68
  readcursor |= Indicators[:deliverystatus]
70
69
  next
@@ -72,7 +71,7 @@ module Sisimai::Bite::Email
72
71
  end
73
72
 
74
73
  if (readcursor & Indicators[:'message-rfc822']) == 0
75
- # Beginning of the original message part
74
+ # Beginning of the original message part(message/rfc822)
76
75
  if e == StartingOf[:rfc822][0]
77
76
  readcursor |= Indicators[:'message-rfc822']
78
77
  next
@@ -80,7 +79,7 @@ module Sisimai::Bite::Email
80
79
  end
81
80
 
82
81
  if readcursor & Indicators[:'message-rfc822'] > 0
83
- # After "message/rfc822"
82
+ # message/rfc822 OR text/rfc822-headers part
84
83
  if e.empty?
85
84
  blanklines += 1
86
85
  break if blanklines > 1
@@ -88,76 +87,59 @@ module Sisimai::Bite::Email
88
87
  end
89
88
  rfc822list << e
90
89
  else
91
- # Before "message/rfc822"
90
+ # message/delivery-status part
92
91
  next if (readcursor & Indicators[:deliverystatus]) == 0
93
92
  next if e.empty?
94
93
 
95
- if connvalues == connheader.keys.size
96
- # This is an automatically generated Delivery Status Notification.
97
- #
98
- # Delivery to the following recipients failed.
99
- #
100
- # kijitora@example.jp
101
- #
102
- # Final-Recipient: rfc822;kijitora@example.jp
103
- # Action: failed
104
- # Status: 5.2.2
105
- # Diagnostic-Code: smtp;550 5.2.2 <kijitora@example.jp>... Mailbox Full
94
+ if f = Sisimai::RFC1894.match(e)
95
+ # "e" matched with any field defined in RFC3464
96
+ next unless o = Sisimai::RFC1894.field(e)
106
97
  v = dscontents[-1]
107
98
 
108
- if cv = e.match(/\AFinal-Recipient:[ ]*(?:RFC|rfc)822;[ ]*([^ ]+)\z/)
109
- # Final-Recipient: rfc822;kijitora@example.jp
110
- if v['recipient']
111
- # There are multiple recipient addresses in the message body.
112
- dscontents << Sisimai::Bite.DELIVERYSTATUS
113
- v = dscontents[-1]
99
+ if o[-1] == 'addr'
100
+ # Final-Recipient: rfc822; kijitora@example.jp
101
+ # X-Actual-Recipient: rfc822; kijitora@example.co.jp
102
+ if o[0] == 'final-recipient'
103
+ # Final-Recipient: rfc822; kijitora@example.jp
104
+ if v['recipient']
105
+ # There are multiple recipient addresses in the message body.
106
+ dscontents << Sisimai::Bite.DELIVERYSTATUS
107
+ v = dscontents[-1]
108
+ end
109
+ v['recipient'] = o[2]
110
+ recipients += 1
111
+ else
112
+ # X-Actual-Recipient: rfc822; kijitora@example.co.jp
113
+ v['alias'] = o[2]
114
114
  end
115
- v['recipient'] = cv[1]
116
- recipients += 1
117
-
118
- elsif cv = e.match(/\AAction:[ ]*(.+)\z/)
119
- # Action: failed
120
- v['action'] = cv[1].downcase
121
-
122
- elsif cv = e.match(/\AStatus:[ ]*(\d[.]\d+[.]\d+)/)
123
- # Status:5.2.0
124
- v['status'] = cv[1]
115
+ elsif o[-1] == 'code'
116
+ # Diagnostic-Code: SMTP; 550 5.1.1 <userunknown@example.jp>... User Unknown
117
+ v['spec'] = o[1]
118
+ v['diagnosis'] = o[2]
125
119
  else
126
- if cv = e.match(/\ADiagnostic-Code:[ ]*(.+?);[ ]*(.+)\z/)
127
- # Diagnostic-Code: SMTP; 550 5.1.1 <userunknown@example.jp>... User Unknown
128
- v['spec'] = cv[1].upcase
129
- v['diagnosis'] = cv[2]
130
-
131
- elsif p.start_with?('Diagnostic-Code:') && cv = e.match(/\A[ \t]+(.+)\z/)
132
- # Continued line of the value of Diagnostic-Code header
133
- v['diagnosis'] << ' ' << cv[1]
134
- havepassed[-1] = 'Diagnostic-Code: ' << e
135
- end
120
+ # Other DSN fields defined in RFC3464
121
+ next unless fieldtable.key?(o[0])
122
+ v[fieldtable[o[0]]] = o[2]
123
+
124
+ next unless f == 1
125
+ permessage[fieldtable[o[0]]] = o[2]
136
126
  end
137
127
  else
138
- # Reporting-MTA: dns;BLU004-OMC3S13.hotmail.example.com
139
- # Received-From-MTA: dns;BLU436-SMTP66
140
- # Arrival-Date: Fri, 21 Nov 2014 14:17:34 -0800
141
- if cv = e.match(/\AReporting-MTA:[ ]*(?:DNS|dns);[ ]*(.+)\z/)
142
- # Reporting-MTA: dns;BLU004-OMC3S13.hotmail.example.com
143
- next unless connheader['lhost'].empty?
144
- connheader['lhost'] = cv[1].downcase
145
- connvalues += 1
146
-
147
- elsif cv = e.match(/\AArrival-Date:[ ]*(.+)\z/)
148
- # Arrival-Date: Wed, 29 Apr 2009 16:03:18 +0900
149
- next unless connheader['date'].empty?
150
- connheader['date'] = cv[1]
151
- connvalues += 1
152
- end
128
+ # Continued line of the value of Diagnostic-Code field
129
+ next unless p.start_with?('Diagnostic-Code:')
130
+ next unless cv = e.match(/\A[ \t]+(.+)\z/)
131
+ v['diagnosis'] << ' ' << cv[1]
132
+ havepassed[-1] = 'Diagnostic-Code: ' << e
153
133
  end
154
- end
134
+ end # End of message/delivery-status
155
135
  end
156
136
  return nil unless recipients > 0
157
137
 
158
138
  dscontents.each do |e|
159
139
  # Set default values if each value is empty.
160
- connheader.each_key { |a| e[a] ||= connheader[a] || '' }
140
+ e['lhost'] ||= permessage['rhost']
141
+ permessage.each_key { |a| e[a] ||= permessage[a] || '' }
142
+
161
143
  e['agent'] = self.smtpagent
162
144
  e['diagnosis'] = Sisimai::String.sweep(e['diagnosis']) || ''
163
145
 
@@ -176,7 +158,7 @@ module Sisimai::Bite::Email
176
158
  MessagesOf.each_key do |r|
177
159
  # Verify each regular expression of session errors
178
160
  next unless MessagesOf[r].any? { |a| e['diagnosis'].include?(a) }
179
- e['reason'] = r.to_s
161
+ e['reason'] = r
180
162
  break
181
163
  end
182
164
  end