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
@@ -13,7 +13,7 @@ module Sisimai::Bite::Email
13
13
  message: ['The following message to <', 'An error occurred while trying to deliver the mail'],
14
14
  rfc822: ['content-type: message/rfc822'],
15
15
  }.freeze
16
- MessagesOf = { expired: ['Delivery expired'] }.freeze
16
+ MessagesOf = { 'expired' => ['Delivery expired'] }.freeze
17
17
 
18
18
  def description; return 'Amazon SES(Sending): http://aws.amazon.com/ses/'; end
19
19
  def smtpagent; return Sisimai::Bite.smtpagent(self); end
@@ -22,7 +22,7 @@ module Sisimai::Bite::Email
22
22
  # X-Original-To: 000001321defbd2a-788e31c8-2be1-422f-a8d4-cf7765cc9ed7-000000@email-bounces.amazonses.com
23
23
  # X-AWS-Outgoing: 199.255.192.156
24
24
  # X-SES-Outgoing: 2016.10.12-54.240.27.6
25
- def headerlist; return ['X-AWS-Outgoing', 'X-SES-Outgoing', 'x-amz-sns-message-id']; end
25
+ def headerlist; return %w[x-aws-outgoing x-ses-outgoing x-amz-sns-message-id]; end
26
26
 
27
27
  # Parse bounce messages from Amazon SES
28
28
  # @param [Hash] mhead Message headers of a bounce email
@@ -45,6 +45,10 @@ module Sisimai::Bite::Email
45
45
  match += 1 if mhead['x-ses-outgoing']
46
46
  return nil unless match > 0
47
47
 
48
+ require 'sisimai/rfc1894'
49
+ fieldtable = Sisimai::RFC1894.FIELDTABLE
50
+ permessage = {} # (Hash) Store values of each Per-Message field
51
+
48
52
  dscontents = [Sisimai::Bite.DELIVERYSTATUS]
49
53
  hasdivided = mbody.split("\n")
50
54
  havepassed = ['']
@@ -52,10 +56,6 @@ module Sisimai::Bite::Email
52
56
  blanklines = 0 # (Integer) The number of blank lines
53
57
  readcursor = 0 # (Integer) Points the current cursor position
54
58
  recipients = 0 # (Integer) The number of 'Final-Recipient' header
55
- connvalues = 0 # (Integer) Flag, 1 if all the value of connheader have been set
56
- connheader = {
57
- 'lhost' => '', # The value of Reporting-MTA header
58
- }
59
59
  v = nil
60
60
 
61
61
  while e = hasdivided.shift do
@@ -64,7 +64,7 @@ module Sisimai::Bite::Email
64
64
  p = havepassed[-2]
65
65
 
66
66
  if readcursor == 0
67
- # Beginning of the bounce message or delivery status part
67
+ # Beginning of the bounce message or message/delivery-status part
68
68
  if e.start_with?(StartingOf[:message][0], StartingOf[:message][1])
69
69
  readcursor |= Indicators[:deliverystatus]
70
70
  next
@@ -72,7 +72,7 @@ module Sisimai::Bite::Email
72
72
  end
73
73
 
74
74
  if (readcursor & Indicators[:'message-rfc822']) == 0
75
- # Beginning of the original message part
75
+ # Beginning of the original message part(message/rfc822)
76
76
  if e == StartingOf[:rfc822][0]
77
77
  readcursor |= Indicators[:'message-rfc822']
78
78
  next
@@ -80,8 +80,7 @@ module Sisimai::Bite::Email
80
80
  end
81
81
 
82
82
  if readcursor & Indicators[:'message-rfc822'] > 0
83
- # After "message/rfc822"
84
- # After "message/rfc822"
83
+ # message/rfc822 OR text/rfc822-headers part
85
84
  if e.empty?
86
85
  blanklines += 1
87
86
  break if blanklines > 1
@@ -89,86 +88,51 @@ module Sisimai::Bite::Email
89
88
  end
90
89
  rfc822list << e
91
90
  else
92
- # Before "message/rfc822"
91
+ # message/delivery-status part
93
92
  next if (readcursor & Indicators[:deliverystatus]) == 0
94
93
  next if e.empty?
95
94
 
96
- if connvalues == connheader.keys.size
97
- # Final-Recipient: rfc822;kijitora@example.jp
98
- # Action: failed
99
- # Status: 5.0.0 (permanent failure)
100
- # Remote-MTA: dns; [192.0.2.9]
101
- # Diagnostic-Code: smtp; 5.1.0 - Unknown address error 550-'5.7.1
102
- # <000001321defbd2a-788e31c8-2be1-422f-a8d4-cf7765cc9ed7-000000@email-bounces.amazonses.com>...
103
- # Access denied' (delivery attempts: 0)
104
- #
105
- # --JuU8e.4gyIcCrxq.1RFbQY.3Vu7Hs+
106
- # content-type: message/rfc822
95
+ if f = Sisimai::RFC1894.match(e)
96
+ # "e" matched with any field defined in RFC3464
97
+ next unless o = Sisimai::RFC1894.field(e)
107
98
  v = dscontents[-1]
108
99
 
109
- if cv = e.match(/\AFinal-Recipient:[ ]*(?:RFC|rfc)822;[ ]*([^ ]+)\z/)
110
- # Final-Recipient: RFC822; userunknown@example.jp
111
- if v['recipient']
112
- # There are multiple recipient addresses in the message body.
113
- dscontents << Sisimai::Bite.DELIVERYSTATUS
114
- v = dscontents[-1]
100
+ if o[-1] == 'addr'
101
+ # Final-Recipient: rfc822; kijitora@example.jp
102
+ # X-Actual-Recipient: rfc822; kijitora@example.co.jp
103
+ if o[0] == 'final-recipient'
104
+ # Final-Recipient: rfc822; kijitora@example.jp
105
+ if v['recipient']
106
+ # There are multiple recipient addresses in the message body.
107
+ dscontents << Sisimai::Bite.DELIVERYSTATUS
108
+ v = dscontents[-1]
109
+ end
110
+ v['recipient'] = o[2]
111
+ recipients += 1
112
+ else
113
+ # X-Actual-Recipient: rfc822; kijitora@example.co.jp
114
+ v['alias'] = o[2]
115
115
  end
116
- v['recipient'] = cv[1]
117
- recipients += 1
118
-
119
- elsif cv = e.match(/\AX-Actual-Recipient:[ ]*(?:RFC|rfc)822;[ ]*([^ ]+)\z/)
120
- # X-Actual-Recipient: RFC822; kijitora@example.co.jp
121
- v['alias'] = cv[1]
122
-
123
- elsif cv = e.match(/\AAction:[ ]*(.+)\z/)
124
- # Action: failed
125
- v['action'] = cv[1].downcase
126
-
127
- elsif cv = e.match(/\AStatus:[ ]*(\d[.]\d+[.]\d+)/)
128
- # Status: 5.1.1
129
- # Status:5.2.0
130
- # Status: 5.1.0 (permanent failure)
131
- v['status'] = cv[1]
132
-
133
- elsif cv = e.match(/\ARemote-MTA:[ ]*(?:DNS|dns);[ ]*(.+)\z/)
134
- # Remote-MTA: DNS; mx.example.jp
135
- v['rhost'] = cv[1].downcase
136
-
137
- elsif cv = e.match(/\ALast-Attempt-Date:[ ]*(.+)\z/)
138
- # Last-Attempt-Date: Fri, 14 Feb 2014 12:30:08 -0500
139
- v['date'] = cv[1]
116
+ elsif o[-1] == 'code'
117
+ # Diagnostic-Code: SMTP; 550 5.1.1 <userunknown@example.jp>... User Unknown
118
+ v['spec'] = o[1]
119
+ v['diagnosis'] = o[2]
140
120
  else
141
- if cv = e.match(/\ADiagnostic-Code:[ ]*(.+?);[ ]*(.+)\z/)
142
- # Diagnostic-Code: SMTP; 550 5.1.1 <userunknown@example.jp>... User Unknown
143
- v['spec'] = cv[1].upcase
144
- v['diagnosis'] = cv[2]
145
-
146
- elsif p.start_with?('Diagnostic-Code:') && cv = e.match(/\A[ \t]+(.+)\z/)
147
- # Continued line of the value of Diagnostic-Code header
148
- v['diagnosis'] << ' ' << cv[1]
149
- havepassed[-1] = 'Diagnostic-Code: ' << e
150
- end
121
+ # Other DSN fields defined in RFC3464
122
+ next unless fieldtable.key?(o[0])
123
+ v[fieldtable[o[0]]] = o[2]
124
+
125
+ next unless f == 1
126
+ permessage[fieldtable[o[0]]] = o[2]
151
127
  end
152
128
  else
153
- # The following message to <kijitora@example.jp> was undeliverable.
154
- # The reason for the problem:
155
- # 5.1.0 - Unknown address error 550-'5.7.1 <0000000000000000-00000000-0000-00=
156
- # 00-0000-000000000000-000000@email-bounces.amazonses.com>... Access denied'
157
- #
158
- # --JuU8e.4gyIcCrxq.1RFbQY.3Vu7Hs+
159
- # content-type: message/delivery-status
160
- #
161
- # Reporting-MTA: dns; a192-79.smtp-out.amazonses.com
162
- #
163
- if cv = e.match(/\AReporting-MTA:[ ]*[DNSdns]+;[ ]*(.+)\z/)
164
- # Reporting-MTA: dns; mx.example.jp
165
- next unless connheader['lhost'].empty?
166
- connheader['lhost'] = cv[1].downcase
167
- connvalues += 1
168
- end
169
-
129
+ # Continued line of the value of Diagnostic-Code field
130
+ next unless p.start_with?('Diagnostic-Code:')
131
+ next unless cv = e.match(/\A[ \t]+(.+)\z/)
132
+ v['diagnosis'] << ' ' << cv[1]
133
+ havepassed[-1] = 'Diagnostic-Code: ' << e
170
134
  end
171
- end
135
+ end # End of message/delivery-status
172
136
  end
173
137
 
174
138
  if recipients == 0 && mbody =~ /notificationType/
@@ -186,25 +150,27 @@ module Sisimai::Bite::Email
186
150
 
187
151
  dscontents.each do |e|
188
152
  # Set default values if each value is empty.
189
- connheader.each_key { |a| e[a] ||= connheader[a] || '' }
153
+ e['lhost'] ||= permessage['rhost']
154
+ permessage.each_key { |a| e[a] ||= permessage[a] || '' }
190
155
 
191
156
  e['agent'] = self.smtpagent
192
- e['diagnosis'] = Sisimai::String.sweep(e['diagnosis'].to_s.gsub(/\\n/, ' '))
157
+ e['diagnosis'] = Sisimai::String.sweep(e['diagnosis'].to_s.tr("\n", ' '))
193
158
 
194
159
  if e['status'].to_s.start_with?('5.0.0', '5.1.0', '4.0.0', '4.1.0')
195
160
  # Get other D.S.N. value from the error message
196
161
  errormessage = e['diagnosis']
197
162
 
198
- # 5.1.0 - Unknown address error 550-'5.7.1 ...
199
- if cv = e['diagnosis'].match(/["'](\d[.]\d[.]\d.+)['"]/) then errormessage = cv[1] end
200
- pseudostatus = Sisimai::SMTP::Status.find(errormessage)
201
- e['status'] = pseudostatus unless pseudostatus.empty?
163
+ if cv = e['diagnosis'].match(/["'](\d[.]\d[.]\d.+)['"]/)
164
+ # 5.1.0 - Unknown address error 550-'5.7.1 ...
165
+ errormessage = cv[1]
166
+ end
167
+ e['status'] = Sisimai::SMTP::Status.find(errormessage) || e['status']
202
168
  end
203
169
 
204
170
  MessagesOf.each_key do |r|
205
171
  # Verify each regular expression of session errors
206
172
  next unless MessagesOf[r].any? { |a| e['diagnosis'].include?(a) }
207
- e['reason'] = r.to_s
173
+ e['reason'] = r
208
174
  break
209
175
  end
210
176
 
@@ -19,7 +19,7 @@ module Sisimai::Bite::Email
19
19
  # X-Mailer: Amazon WorkMail
20
20
  # X-Original-Mailer: Amazon WorkMail
21
21
  # X-Ses-Outgoing: 2016.01.14-54.240.27.159
22
- def headerlist; return ['X-SES-Outgoing', 'X-Original-Mailer']; end
22
+ def headerlist; return %w[x-ses-outgoing x-original-mailer]; end
23
23
 
24
24
  # Parse bounce messages from Amazon WorkMail
25
25
  # @param [Hash] mhead Message headers of a bounce email
@@ -47,21 +47,21 @@ module Sisimai::Bite::Email
47
47
  end
48
48
  return nil if match < 2
49
49
 
50
+ require 'sisimai/rfc1894'
51
+ fieldtable = Sisimai::RFC1894.FIELDTABLE
52
+ permessage = {} # (Hash) Store values of each Per-Message field
53
+
50
54
  dscontents = [Sisimai::Bite.DELIVERYSTATUS]
51
55
  hasdivided = mbody.split("\n")
52
56
  rfc822list = [] # (Array) Each line in message/rfc822 part string
53
57
  blanklines = 0 # (Integer) The number of blank lines
54
58
  readcursor = 0 # (Integer) Points the current cursor position
55
59
  recipients = 0 # (Integer) The number of 'Final-Recipient' header
56
- connvalues = 0 # (Integer) Flag, 1 if all the value of connheader have been set
57
- connheader = {
58
- 'lhost' => '', # The value of Reporting-MTA header
59
- }
60
60
  v = nil
61
61
 
62
62
  while e = hasdivided.shift do
63
63
  if readcursor == 0
64
- # Beginning of the bounce message or delivery status part
64
+ # Beginning of the bounce message or message/delivery-status part
65
65
  if e == StartingOf[:message][0]
66
66
  readcursor |= Indicators[:deliverystatus]
67
67
  next
@@ -69,7 +69,7 @@ module Sisimai::Bite::Email
69
69
  end
70
70
 
71
71
  if (readcursor & Indicators[:'message-rfc822']) == 0
72
- # Beginning of the original message part
72
+ # Beginning of the original message part(message/rfc822)
73
73
  if e == StartingOf[:rfc822][0]
74
74
  readcursor |= Indicators[:'message-rfc822']
75
75
  next
@@ -77,7 +77,7 @@ module Sisimai::Bite::Email
77
77
  end
78
78
 
79
79
  if readcursor & Indicators[:'message-rfc822'] > 0
80
- # After "message/rfc822"
80
+ # message/rfc822 OR text/rfc822-headers part
81
81
  if e.empty?
82
82
  blanklines += 1
83
83
  break if blanklines > 1
@@ -85,51 +85,42 @@ module Sisimai::Bite::Email
85
85
  end
86
86
  rfc822list << e
87
87
  else
88
- # Before "message/rfc822"
88
+ # message/delivery-status part
89
89
  next if (readcursor & Indicators[:deliverystatus]) == 0
90
90
  next if e.empty?
91
91
 
92
- if connvalues == connheader.keys.size
93
- # Action: failed
94
- # Final-Recipient: rfc822; kijitora@libsisimai.org
95
- # Diagnostic-Code: smtp; 554 4.4.7 Message expired: unable to deliver in 840 minutes.<421 4.4.2 Connection timed out>
96
- # Status: 4.4.7
92
+ if f = Sisimai::RFC1894.match(e)
93
+ # "e" matched with any field defined in RFC3464
94
+ o = Sisimai::RFC1894.field(e) || next
97
95
  v = dscontents[-1]
98
96
 
99
- if cv = e.match(/\AFinal-Recipient:[ ]*(?:RFC|rfc)822;[ ]*([^ ]+)\z/)
100
- # Final-Recipient: RFC822; kijitora@example.jp
101
- if v['recipient']
102
- # There are multiple recipient addresses in the message body.
103
- dscontents << Sisimai::Bite.DELIVERYSTATUS
104
- v = dscontents[-1]
97
+ if o[-1] == 'addr'
98
+ # Final-Recipient: rfc822; kijitora@example.jp
99
+ # X-Actual-Recipient: rfc822; kijitora@example.co.jp
100
+ if o[0] == 'final-recipient'
101
+ # Final-Recipient: rfc822; kijitora@example.jp
102
+ if v['recipient']
103
+ # There are multiple recipient addresses in the message body.
104
+ dscontents << Sisimai::Bite.DELIVERYSTATUS
105
+ v = dscontents[-1]
106
+ end
107
+ v['recipient'] = o[2]
108
+ recipients += 1
109
+ else
110
+ # X-Actual-Recipient: rfc822; kijitora@example.co.jp
111
+ v['alias'] = o[2]
105
112
  end
106
- v['recipient'] = cv[1]
107
- recipients += 1
108
-
109
- elsif cv = e.match(/\AAction:[ ]*(.+)\z/)
110
- # Action: failed
111
- v['action'] = cv[1].downcase
112
-
113
- elsif cv = e.match(/\AStatus:[ ]*(\d[.]\d+[.]\d+)/)
114
- # Status: 5.1.1
115
- v['status'] = cv[1]
113
+ elsif o[-1] == 'code'
114
+ # Diagnostic-Code: SMTP; 550 5.1.1 <userunknown@example.jp>... User Unknown
115
+ v['spec'] = o[1]
116
+ v['diagnosis'] = o[2]
116
117
  else
117
- if cv = e.match(/\ADiagnostic-Code:[ ]*(.+?);[ ]*(.+)\z/)
118
- # Diagnostic-Code: SMTP; 550 5.1.1 <kijitora@example.jp>... User Unknown
119
- v['spec'] = cv[1].upcase
120
- v['diagnosis'] = cv[2]
121
- end
122
- end
123
- else
124
- # Technical report:
125
- #
126
- # Reporting-MTA: dsn; a27-85.smtp-out.us-west-2.amazonses.com
127
- #
128
- if cv = e.match(/\AReporting-MTA:[ ]*[DNSdns]+;[ ]*(.+)\z/)
129
- # Reporting-MTA: dns; mx.example.jp
130
- next unless connheader['lhost'].empty?
131
- connheader['lhost'] = cv[1].downcase
132
- connvalues += 1
118
+ # Other DSN fields defined in RFC3464
119
+ next unless fieldtable.key?(o[0])
120
+ v[fieldtable[o[0]]] = o[2]
121
+
122
+ next unless f == 1
123
+ permessage[fieldtable[o[0]]] = o[2]
133
124
  end
134
125
  end
135
126
 
@@ -138,31 +129,34 @@ module Sisimai::Bite::Email
138
129
  # <meta name="Generator" content="Amazon WorkMail v3.0-2023.77">
139
130
  # <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
140
131
  break if e.start_with?('<!DOCTYPE HTML><html>')
141
- end
132
+ end # End of message/delivery-status
142
133
  end
143
134
  return nil unless recipients > 0
144
135
 
145
136
  dscontents.each do |e|
146
137
  # Set default values if each value is empty.
147
- connheader.each_key { |a| e[a] ||= connheader[a] || '' }
138
+ e['lhost'] ||= permessage['rhost']
139
+ permessage.each_key { |a| e[a] ||= permessage[a] || '' }
148
140
 
149
141
  e['diagnosis'] = Sisimai::String.sweep(e['diagnosis'])
150
142
  if e['status'].to_s.start_with?('5.0.0', '5.1.0', '4.0.0', '4.1.0')
151
143
  # Get other D.S.N. value from the error message
152
144
  errormessage = e['diagnosis']
153
145
 
154
- # 5.1.0 - Unknown address error 550-'5.7.1 ...
155
- if cv = e['diagnosis'].match(/["'](\d[.]\d[.]\d.+)['"]/) then errormessage = cv[1] end
156
-
157
- pseudostatus = Sisimai::SMTP::Status.find(errormessage)
158
- e['status'] = pseudostatus unless pseudostatus.empty?
146
+ if cv = e['diagnosis'].match(/["'](\d[.]\d[.]\d.+)['"]/)
147
+ # 5.1.0 - Unknown address error 550-'5.7.1 ...
148
+ errormessage = cv[1]
149
+ end
150
+ e['status'] = Sisimai::SMTP::Status.find(errormessage) || e['status']
159
151
  end
160
152
 
161
- # 554 4.4.7 Message expired: unable to deliver in 840 minutes.
162
- # <421 4.4.2 Connection timed out>
163
- if cv = e['diagnosis'].match(/[<]([245]\d\d)[ ].+[>]/) then e['replycode'] = cv[1] end
153
+ if cv = e['diagnosis'].match(/[<]([245]\d\d)[ ].+[>]/)
154
+ # 554 4.4.7 Message expired: unable to deliver in 840 minutes.
155
+ # <421 4.4.2 Connection timed out>
156
+ e['replycode'] = cv[1]
157
+ end
164
158
 
165
- e['reason'] ||= Sisimai::SMTP::Status.name(e['status'])
159
+ e['reason'] ||= Sisimai::SMTP::Status.name(e['status']) || ''
166
160
  e['agent'] = self.smtpagent
167
161
  end
168
162
 
@@ -12,8 +12,8 @@ module Sisimai::Bite::Email
12
12
  rfc822: ['Content-Type: message/rfc822'],
13
13
  }.freeze
14
14
  MessagesOf = {
15
- hostunknown: ['Host or domain name not found'],
16
- notaccept: ['type=MX: Malformed or unexpected name server reply'],
15
+ 'hostunknown' => ['Host or domain name not found'],
16
+ 'notaccept' => ['type=MX: Malformed or unexpected name server reply'],
17
17
  }.freeze
18
18
 
19
19
  def description; return 'Aol Mail: http://www.aol.com'; end
@@ -29,7 +29,7 @@ module Sisimai::Bite::Email
29
29
  # X-BounceIO-Id: 9D38DE46-21BC-4309-83E1-5F0D788EFF1F.1_0
30
30
  # X-Outbound-Mail-Relay-Queue-ID: 07391702BF4DC
31
31
  # X-Outbound-Mail-Relay-Sender: rfc822; shironeko@aol.example.jp
32
- def headerlist; return ['X-AOL-IP']; end
32
+ def headerlist; return %w[x-aol-ip]; end
33
33
 
34
34
  # Parse bounce messages from Aol Mail
35
35
  # @param [Hash] mhead Message headers of a bounce email
@@ -47,6 +47,10 @@ module Sisimai::Bite::Email
47
47
  # :subject => %r/\AUndeliverable: /,
48
48
  return nil unless mhead['x-aol-ip']
49
49
 
50
+ require 'sisimai/rfc1894'
51
+ fieldtable = Sisimai::RFC1894.FIELDTABLE
52
+ permessage = {} # (Hash) Store values of each Per-Message field
53
+
50
54
  dscontents = [Sisimai::Bite.DELIVERYSTATUS]
51
55
  hasdivided = mbody.split("\n")
52
56
  havepassed = ['']
@@ -54,11 +58,6 @@ module Sisimai::Bite::Email
54
58
  blanklines = 0 # (Integer) The number of blank lines
55
59
  readcursor = 0 # (Integer) Points the current cursor position
56
60
  recipients = 0 # (Integer) The number of 'Final-Recipient' header
57
- connvalues = 0 # (Integer) Flag, 1 if all the value of connheader have been set
58
- connheader = {
59
- 'date' => '', # The value of Arrival-Date header
60
- 'lhost' => '', # The value of Reporting-MTA header
61
- }
62
61
  v = nil
63
62
 
64
63
  while e = hasdivided.shift do
@@ -67,7 +66,7 @@ module Sisimai::Bite::Email
67
66
  p = havepassed[-2]
68
67
 
69
68
  if readcursor == 0
70
- # Beginning of the bounce message or delivery status part
69
+ # Beginning of the bounce message or message/delivery-status part
71
70
  if e.start_with?(StartingOf[:message][0])
72
71
  readcursor |= Indicators[:deliverystatus]
73
72
  next
@@ -75,7 +74,7 @@ module Sisimai::Bite::Email
75
74
  end
76
75
 
77
76
  if (readcursor & Indicators[:'message-rfc822']) == 0
78
- # Beginning of the original message part
77
+ # Beginning of the original message part(message/rfc822)
79
78
  if e.start_with?(StartingOf[:rfc822][0])
80
79
  readcursor |= Indicators[:'message-rfc822']
81
80
  next
@@ -83,7 +82,7 @@ module Sisimai::Bite::Email
83
82
  end
84
83
 
85
84
  if readcursor & Indicators[:'message-rfc822'] > 0
86
- # After "message/rfc822"
85
+ # message/rfc822 OR text/rfc822-headers part
87
86
  if e.empty?
88
87
  blanklines += 1
89
88
  break if blanklines > 1
@@ -91,97 +90,72 @@ module Sisimai::Bite::Email
91
90
  end
92
91
  rfc822list << e
93
92
  else
94
- # Before "message/rfc822"
93
+ # message/delivery-status part
95
94
  next if (readcursor & Indicators[:deliverystatus]) == 0
96
95
  next if e.empty?
97
96
 
98
- if connvalues == connheader.keys.size
99
- # Final-Recipient: rfc822; kijitora@example.co.jp
100
- # Original-Recipient: rfc822;kijitora@example.co.jp
101
- # Action: failed
102
- # Status: 5.2.2
103
- # Remote-MTA: dns; mx.example.co.jp
104
- # Diagnostic-Code: smtp; 550 5.2.2 <kijitora@example.co.jp>... Mailbox Full
97
+ if f = Sisimai::RFC1894.match(e)
98
+ # "e" matched with any field defined in RFC3464
99
+ next unless o = Sisimai::RFC1894.field(e)
105
100
  v = dscontents[-1]
106
101
 
107
- if cv = e.match(/\AFinal-Recipient:[ ]*(?:RFC|rfc)822;[ ]*([^ ]+)\z/)
108
- # Final-Recipient: RFC822; userunknown@example.jp
109
- if v['recipient']
110
- # There are multiple recipient addresses in the message body.
111
- dscontents << Sisimai::Bite.DELIVERYSTATUS
112
- v = dscontents[-1]
102
+ if o[-1] == 'addr'
103
+ # Final-Recipient: rfc822; kijitora@example.jp
104
+ # X-Actual-Recipient: rfc822; kijitora@example.co.jp
105
+ if o[0] == 'final-recipient'
106
+ # Final-Recipient: rfc822; kijitora@example.jp
107
+ if v['recipient']
108
+ # There are multiple recipient addresses in the message body.
109
+ dscontents << Sisimai::Bite.DELIVERYSTATUS
110
+ v = dscontents[-1]
111
+ end
112
+ v['recipient'] = o[2]
113
+ recipients += 1
114
+ else
115
+ # X-Actual-Recipient: rfc822; kijitora@example.co.jp
116
+ v['alias'] = o[2]
113
117
  end
114
- v['recipient'] = cv[1]
115
- recipients += 1
116
-
117
- elsif cv = e.match(/\AAction:[ ]*(.+)\z/)
118
- # Action: failed
119
- v['action'] = cv[1].downcase
120
-
121
- elsif cv = e.match(/\AStatus:[ ]*(\d[.]\d+[.]\d+)/)
122
- # Status:5.2.0
123
- v['status'] = cv[1]
124
-
125
- elsif cv = e.match(/\ARemote-MTA:[ ]*(?:DNS|dns);[ ]*(.+)\z/)
126
- # Remote-MTA: DNS; mx.example.jp
127
- v['rhost'] = cv[1].downcase
118
+ elsif o[-1] == 'code'
119
+ # Diagnostic-Code: SMTP; 550 5.1.1 <userunknown@example.jp>... User Unknown
120
+ v['spec'] = o[1]
121
+ v['diagnosis'] = o[2]
128
122
  else
129
- # Get error message
130
- if cv = e.match(/\ADiagnostic-Code:[ ]*(.+?);[ ]*(.+)\z/)
131
- # Diagnostic-Code: SMTP; 550 5.1.1 <userunknown@example.jp>... User Unknown
132
- v['spec'] = cv[1].upcase
133
- v['diagnosis'] = cv[2]
134
-
135
- elsif p.start_with?('Diagnostic-Code:') && cv = e.match(/\A[ \t]+(.+)\z/)
136
- # Continued line of the value of Diagnostic-Code header
137
- v['diagnosis'] << ' ' << cv[1]
138
- havepassed[-1] = 'Diagnostic-Code: ' << e
139
- end
123
+ # Other DSN fields defined in RFC3464
124
+ next unless fieldtable.key?(o[0])
125
+ v[fieldtable[o[0]]] = o[2]
126
+
127
+ next unless f == 1
128
+ permessage[fieldtable[o[0]]] = o[2]
140
129
  end
141
130
  else
142
- # Content-Type: message/delivery-status
143
- # Content-Transfer-Encoding: 7bit
144
- #
145
- # Reporting-MTA: dns; omr-m5.mx.aol.com
146
- # X-Outbound-Mail-Relay-Queue-ID: CCBA43800007F
147
- # X-Outbound-Mail-Relay-Sender: rfc822; shironeko@aol.example.jp
148
- # Arrival-Date: Fri, 21 Nov 2014 17:14:34 -0500 (EST)
149
- if cv = e.match(/\AReporting-MTA:[ ]*(?:DNS|dns);[ ]*(.+)\z/)
150
- # Reporting-MTA: dns; mx.example.jp
151
- next unless connheader['lhost'].empty?
152
- connheader['lhost'] = cv[1].downcase
153
- connvalues += 1
154
-
155
- elsif cv = e.match(/\AArrival-Date:[ ]*(.+)\z/)
156
- # Arrival-Date: Wed, 29 Apr 2009 16:03:18 +0900
157
- next unless connheader['date'].empty?
158
- connheader['date'] = cv[1]
159
- connvalues += 1
160
- end
161
-
131
+ # Continued line of the value of Diagnostic-Code field
132
+ next unless p.start_with?('Diagnostic-Code:')
133
+ next unless cv = e.match(/\A[ \t]+(.+)\z/)
134
+ v['diagnosis'] << ' ' << cv[1]
135
+ havepassed[-1] = 'Diagnostic-Code: ' << e
162
136
  end
163
- end
137
+ end # End of message/delivery-status
164
138
  end
165
139
  return nil unless recipients > 0
166
140
 
167
141
  dscontents.each do |e|
168
142
  # Set default values if each value is empty.
169
- connheader.each_key { |a| e[a] ||= connheader[a] || '' }
143
+ e['lhost'] ||= permessage['rhost']
144
+ permessage.each_key { |a| e[a] ||= permessage[a] || '' }
170
145
 
171
146
  e['agent'] = self.smtpagent
172
- e['diagnosis'] = Sisimai::String.sweep(e['diagnosis'].gsub(/\\n/, ' '))
147
+ e['diagnosis'] = Sisimai::String.sweep(e['diagnosis'].tr("\n", ' '))
173
148
 
174
149
  MessagesOf.each_key do |r|
175
150
  # Verify each regular expression of session errors
176
151
  next unless MessagesOf[r].any? { |a| e['diagnosis'].include?(a) }
177
- e['reason'] = r.to_s
152
+ e['reason'] = r
178
153
  break
179
154
  end
180
155
 
181
156
  if e['status'].empty? || e['status'].end_with?('.0.0')
182
157
  # There is no value of Status header or the value is 5.0.0, 4.0.0
183
- pseudostatus = Sisimai::SMTP::Status.find(e['diagnosis'])
184
- e['status'] = pseudostatus unless pseudostatus.empty?
158
+ e['status'] = Sisimai::SMTP::Status.find(e['diagnosis']) || ''
185
159
  end
186
160
  end
187
161