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
@@ -49,6 +49,10 @@ module Sisimai::Bite::Email
49
49
  # :from => %r/ [(]Mail Delivery System[)]\z/,
50
50
  return nil unless mhead['subject'] == 'Undelivered Mail Returned to Sender'
51
51
 
52
+ require 'sisimai/rfc1894'
53
+ fieldtable = Sisimai::RFC1894.FIELDTABLE
54
+ permessage = {} # (Hash) Store values of each Per-Message field
55
+
52
56
  dscontents = [Sisimai::Bite.DELIVERYSTATUS]
53
57
  hasdivided = mbody.split("\n")
54
58
  havepassed = ['']
@@ -57,11 +61,6 @@ module Sisimai::Bite::Email
57
61
  readcursor = 0 # (Integer) Points the current cursor position
58
62
  recipients = 0 # (Integer) The number of 'Final-Recipient' header
59
63
  commandset = [] # (Array) ``in reply to * command'' list
60
- connvalues = 0 # (Integer) Flag, 1 if all the value of connheader have been set
61
- connheader = {
62
- 'date' => '', # The value of Arrival-Date header
63
- 'lhost' => '', # The value of Received-From-MTA header
64
- }
65
64
  anotherset = {} # Another error information
66
65
  v = nil
67
66
 
@@ -71,7 +70,7 @@ module Sisimai::Bite::Email
71
70
  p = havepassed[-2]
72
71
 
73
72
  if readcursor == 0
74
- # Beginning of the bounce message or delivery status part
73
+ # Beginning of the bounce message or message/delivery-status part
75
74
  if e =~ MarkingsOf[:message]
76
75
  readcursor |= Indicators[:deliverystatus]
77
76
  next
@@ -79,7 +78,7 @@ module Sisimai::Bite::Email
79
78
  end
80
79
 
81
80
  if (readcursor & Indicators[:'message-rfc822']) == 0
82
- # Beginning of the original message part
81
+ # Beginning of the original message part(message/rfc822)
83
82
  if e.start_with?(StartingOf[:rfc822][0], StartingOf[:rfc822][1])
84
83
  readcursor |= Indicators[:'message-rfc822']
85
84
  next
@@ -87,7 +86,7 @@ module Sisimai::Bite::Email
87
86
  end
88
87
 
89
88
  if readcursor & Indicators[:'message-rfc822'] > 0
90
- # After "message/rfc822"
89
+ # message/rfc822 OR text/rfc822-headers part
91
90
  if e.empty?
92
91
  blanklines += 1
93
92
  break if blanklines > 1
@@ -95,71 +94,43 @@ module Sisimai::Bite::Email
95
94
  end
96
95
  rfc822list << e
97
96
  else
98
- # Before "message/rfc822"
97
+ # message/delivery-status part
99
98
  next if (readcursor & Indicators[:deliverystatus]) == 0
100
99
  next if e.empty?
101
100
 
102
- if connvalues == connheader.keys.size
103
- # Final-Recipient: RFC822; userunknown@example.jp
104
- # X-Actual-Recipient: RFC822; kijitora@example.co.jp
105
- # Action: failed
106
- # Status: 5.1.1
107
- # Remote-MTA: DNS; mx.example.jp
108
- # Diagnostic-Code: SMTP; 550 5.1.1 <userunknown@example.jp>... User Unknown
109
- # Last-Attempt-Date: Fri, 14 Feb 2014 12:30:08 -0500
101
+ if f = Sisimai::RFC1894.match(e)
102
+ # "e" matched with any field defined in RFC3464
103
+ next unless o = Sisimai::RFC1894.field(e)
110
104
  v = dscontents[-1]
111
- if cv = e.match(/\AFinal-Recipient:[ ]*(?:RFC|rfc)822;[ ]*(.+)\z/)
112
- # Final-Recipient: RFC822; userunknown@example.jp
113
- if v['recipient']
114
- # There are multiple recipient addresses in the message body.
115
- dscontents << Sisimai::Bite.DELIVERYSTATUS
116
- v = dscontents[-1]
117
- end
118
- v['recipient'] = cv[1]
119
- recipients += 1
120
-
121
- elsif cv = e.match(/\AX-Actual-Recipient:[ ]*(?:RFC|rfc)822;[ ]*([^ ]+)\z/) ||
122
- e.match(/\AOriginal-Recipient:[ ]*(?:RFC|rfc)822;[ ]*([^ ]+)\z/)
123
- # X-Actual-Recipient: RFC822; kijitora@example.co.jp
124
- # Original-Recipient: rfc822;kijitora@example.co.jp
125
- v['alias'] = cv[1]
126
-
127
- elsif cv = e.match(/\AAction:[ ]*(.+)\z/)
128
- # Action: failed
129
- v['action'] = cv[1].downcase
130
105
 
131
- elsif cv = e.match(/\AStatus:[ ]*(\d[.]\d+[.]\d+)/)
132
- # Status: 5.1.1
133
- # Status:5.2.0
134
- # Status: 5.1.0 (permanent failure)
135
- v['status'] = cv[1]
136
-
137
- elsif cv = e.match(/\ARemote-MTA:[ ]*(?:DNS|dns);[ ]*(.+)\z/)
138
- # Remote-MTA: DNS; mx.example.jp
139
- v['rhost'] = cv[1].downcase
140
-
141
- elsif cv = e.match(/\ALast-Attempt-Date:[ ]*(.+)\z/)
142
- # Last-Attempt-Date: Fri, 14 Feb 2014 12:30:08 -0500
143
- #
144
- # src/bounce/bounce_notify_util.c:
145
- # 681 #if 0
146
- # 682 if (dsn->time > 0)
147
- # 683 post_mail_fprintf(bounce, "Last-Attempt-Date: %s",
148
- # 684 mail_date(dsn->time));
149
- # 685 #endif
150
- v['date'] = cv[1]
106
+ if o[-1] == 'addr'
107
+ # Final-Recipient: rfc822; kijitora@example.jp
108
+ # X-Actual-Recipient: rfc822; kijitora@example.co.jp
109
+ if o[0] == 'final-recipient'
110
+ # Final-Recipient: rfc822; kijitora@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]
115
+ end
116
+ v['recipient'] = o[2]
117
+ recipients += 1
118
+ else
119
+ # X-Actual-Recipient: rfc822; kijitora@example.co.jp
120
+ v['alias'] = o[2]
121
+ end
122
+ elsif o[-1] == 'code'
123
+ # Diagnostic-Code: SMTP; 550 5.1.1 <userunknown@example.jp>... User Unknown
124
+ v['spec'] = o[1]
125
+ v['spec'] = 'SMTP' if v['spec'] == 'X-POSTFIX'
126
+ v['diagnosis'] = o[2]
151
127
  else
152
- if cv = e.match(/\ADiagnostic-Code:[ ]*(.+?);[ ]*(.*)\z/)
153
- # Diagnostic-Code: SMTP; 550 5.1.1 <userunknown@example.jp>... User Unknown
154
- v['spec'] = cv[1].upcase
155
- v['diagnosis'] = cv[2]
156
- v['spec'] = 'SMTP' if v['spec'] == 'X-POSTFIX'
128
+ # Other DSN fields defined in RFC3464
129
+ next unless fieldtable.key?(o[0])
130
+ v[fieldtable[o[0]]] = o[2]
157
131
 
158
- elsif p.start_with?('Diagnostic-Code:') && cv = e.match(/\A[ \t]+(.+)\z/)
159
- # Continued line of the value of Diagnostic-Code header
160
- v['diagnosis'] << ' ' << cv[1]
161
- havepassed[-1] = 'Diagnostic-Code: ' << e
162
- end
132
+ next unless f == 1
133
+ permessage[fieldtable[o[0]]] = o[2]
163
134
  end
164
135
  else
165
136
  # If you do so, please include this problem report. You can
@@ -170,58 +141,52 @@ module Sisimai::Bite::Email
170
141
  # <userunknown@example.co.jp>: host mx.example.co.jp[192.0.2.153] said: 550
171
142
  # 5.1.1 <userunknown@example.co.jp>... User Unknown (in reply to RCPT TO
172
143
  # command)
173
- if cv = e.match(/[ \t][(]in reply to ([A-Z]{4}).*/)
174
- # 5.1.1 <userunknown@example.co.jp>... User Unknown (in reply to RCPT TO
175
- commandset << cv[1]
176
- anotherset['diagnosis'] ||= ''
177
- anotherset['diagnosis'] << ' ' << e
144
+ if p.start_with?('Diagnostic-Code:') && cv = e.match(/\A[ \t]+(.+)\z/)
145
+ # Continued line of the value of Diagnostic-Code header
146
+ v['diagnosis'] << ' ' << cv[1]
147
+ havepassed[-1] = 'Diagnostic-Code: ' << e
178
148
 
179
- elsif cv = e.match(/([A-Z]{4})[ \t]*.*command[)]\z/)
180
- # to MAIL command)
181
- commandset << cv[1]
182
- anotherset['diagnosis'] ||= ''
183
- anotherset['diagnosis'] << ' ' << e
149
+ elsif cv = e.match(/\A(X-Postfix-Sender):[ ]*rfc822;[ ]*(.+)\z/)
150
+ # X-Postfix-Sender: rfc822; shironeko@example.org
151
+ rfc822list << cv[1] << ': ' << cv[2]
184
152
 
185
153
  else
186
- if cv = e.match(/\AReporting-MTA:[ ]*(?:DNS|dns);[ ]*(.+)\z/)
187
- # Reporting-MTA: dns; mx.example.jp
188
- next unless connheader['lhost'].empty?
189
- connheader['lhost'] = cv[1].downcase
190
- connvalues += 1
154
+ if cv = e.match(/[ \t][(]in reply to ([A-Z]{4}).*/)
155
+ # 5.1.1 <userunknown@example.co.jp>... User Unknown (in reply to RCPT TO
156
+ commandset << cv[1]
157
+ anotherset['diagnosis'] ||= ''
158
+ anotherset['diagnosis'] << ' ' << e
159
+
160
+ elsif cv = e.match(/([A-Z]{4})[ \t]*.*command[)]\z/)
161
+ # to MAIL command)
162
+ commandset << cv[1]
163
+ anotherset['diagnosis'] ||= ''
164
+ anotherset['diagnosis'] << ' ' << e
191
165
 
192
- elsif cv = e.match(/\AArrival-Date:[ ]*(.+)\z/)
193
- # Arrival-Date: Wed, 29 Apr 2009 16:03:18 +0900
194
- next unless connheader['date'].empty?
195
- connheader['date'] = cv[1]
196
- connvalues += 1
197
-
198
- elsif cv = e.match(/\A(X-Postfix-Sender):[ ]*rfc822;[ ]*(.+)\z/)
199
- # X-Postfix-Sender: rfc822; shironeko@example.org
200
- rfc822list << (cv[1] << ': ' << cv[2])
201
166
  else
202
- # Alternative error message and recipient
203
- if cv = e.match(/\A[<]([^ ]+[@][^ ]+)[>] [(]expanded from [<](.+)[>][)]:[ \t]*(.+)\z/)
204
- # <r@example.ne.jp> (expanded from <kijitora@example.org>): user ...
205
- anotherset['recipient'] = cv[1]
206
- anotherset['alias'] = cv[2]
207
- anotherset['diagnosis'] = cv[3]
208
-
209
- elsif cv = e.match(/\A[<]([^ ]+[@][^ ]+)[>]:(.*)\z/)
210
- # <kijitora@exmaple.jp>: ...
211
- anotherset['recipient'] = cv[1]
212
- anotherset['diagnosis'] = cv[2]
213
- else
214
- # Get error message continued from the previous line
215
- next unless anotherset['diagnosis']
216
- if e =~ /\A[ \t]{4}(.+)\z/
217
- # host mx.example.jp said:...
218
- anotherset['diagnosis'] << ' ' << e
167
+ # Alternative error message and recipient
168
+ if cv = e.match(/\A[<]([^ ]+[@][^ ]+)[>] [(]expanded from [<](.+)[>][)]:[ \t]*(.+)\z/)
169
+ # <r@example.ne.jp> (expanded from <kijitora@example.org>): user ...
170
+ anotherset['recipient'] = cv[1]
171
+ anotherset['alias'] = cv[2]
172
+ anotherset['diagnosis'] = cv[3]
173
+
174
+ elsif cv = e.match(/\A[<]([^ ]+[@][^ ]+)[>]:(.*)\z/)
175
+ # <kijitora@exmaple.jp>: ...
176
+ anotherset['recipient'] = cv[1]
177
+ anotherset['diagnosis'] = cv[2]
178
+ else
179
+ # Get error message continued from the previous line
180
+ next unless anotherset['diagnosis']
181
+ if e =~ /\A[ \t]{4}(.+)\z/
182
+ # host mx.example.jp said:...
183
+ anotherset['diagnosis'] << ' ' << e
184
+ end
219
185
  end
220
186
  end
221
- end
222
187
  end
223
188
  end
224
- end # End of if: rfc822
189
+ end # End of message/delivery-status
225
190
  end
226
191
 
227
192
  unless recipients > 0
@@ -236,7 +201,8 @@ module Sisimai::Bite::Email
236
201
 
237
202
  dscontents.each do |e|
238
203
  # Set default values if each value is empty.
239
- connheader.each_key { |a| e[a] ||= connheader[a] || '' }
204
+ e['lhost'] ||= permessage['rhost']
205
+ permessage.each_key { |a| e[a] ||= permessage[a] || '' }
240
206
 
241
207
  e['agent'] = self.smtpagent
242
208
  e['command'] = commandset.shift || ''
@@ -258,7 +224,7 @@ module Sisimai::Bite::Email
258
224
  if e['status'] == '' || e['status'].start_with?('4.0.0', '5.0.0')
259
225
  # Check the value of D.S.N. in anotherset
260
226
  as = Sisimai::SMTP::Status.find(anotherset['diagnosis'])
261
- if !as.empty? && as[-3, 3] != '0.0'
227
+ if as && as[-3, 3] != '0.0'
262
228
  # The D.S.N. is neither an empty nor *.0.0
263
229
  e['status'] = as
264
230
  end
@@ -267,7 +233,7 @@ module Sisimai::Bite::Email
267
233
  if e['replycode'] == '' || e['replycode'].start_with?('400', '500')
268
234
  # Check the value of SMTP reply code in anotherset
269
235
  ar = Sisimai::SMTP::Reply.find(anotherset['diagnosis'])
270
- if !ar.empty? && ar[-2, 2].to_i != 0
236
+ if ar && ar[-2, 2].to_i != 0
271
237
  # The SMTP reply code is neither an empty nor *00
272
238
  e['replycode'] = ar
273
239
  end
@@ -23,21 +23,21 @@ module Sisimai::Bite::Email
23
23
  ReSMTP = {
24
24
  # Error text regular expressions which defined in qmail-remote.c
25
25
  # qmail-remote.c:225| if (smtpcode() != 220) quit("ZConnected to "," but greeting failed");
26
- conn: %r/(?:Error:)?Connected to .+ but greeting failed[.]/,
26
+ 'conn' => %r/(?:Error:)?Connected to .+ but greeting failed[.]/,
27
27
  # qmail-remote.c:231| if (smtpcode() != 250) quit("ZConnected to "," but my name was rejected");
28
- ehlo: %r/(?:Error:)?Connected to .+ but my name was rejected[.]/,
28
+ 'ehlo' => %r/(?:Error:)?Connected to .+ but my name was rejected[.]/,
29
29
  # qmail-remote.c:238| if (code >= 500) quit("DConnected to "," but sender was rejected");
30
30
  # reason = rejected
31
- mail: %r/(?:Error:)?Connected to .+ but sender was rejected[.]/,
31
+ 'mail' => %r/(?:Error:)?Connected to .+ but sender was rejected[.]/,
32
32
  # qmail-remote.c:249| out("h"); outhost(); out(" does not like recipient.\n");
33
33
  # qmail-remote.c:253| out("s"); outhost(); out(" does not like recipient.\n");
34
34
  # reason = userunknown
35
- rcpt: %r/(?:Error:)?.+ does not like recipient[.]/,
35
+ 'rcpt' => %r/(?:Error:)?.+ does not like recipient[.]/,
36
36
  # qmail-remote.c:265| if (code >= 500) quit("D"," failed on DATA command");
37
37
  # qmail-remote.c:266| if (code >= 400) quit("Z"," failed on DATA command");
38
38
  # qmail-remote.c:271| if (code >= 500) quit("D"," failed after I sent the message");
39
39
  # qmail-remote.c:272| if (code >= 400) quit("Z"," failed after I sent the message");
40
- data: %r{(?:
40
+ 'data' => %r{(?:
41
41
  (?:Error:)?.+[ ]failed[ ]on[ ]DATA[ ]command[.]
42
42
  |(?:Error:)?.+[ ]failed[ ]after[ ]I[ ]sent[ ]the[ ]message[.]
43
43
  )
@@ -57,10 +57,10 @@ module Sisimai::Bite::Email
57
57
  ReIsOnHold = %r/\A[^ ]+ does not like recipient[.][ ]+.+this message has been in the queue too long[.]\z/
58
58
  FailOnLDAP = {
59
59
  # qmail-ldap-1.03-20040101.patch:19817 - 19866
60
- suspend: ['Mailaddress is administrative?le?y disabled'], # 5.2.1
61
- userunknown: ['Sorry, no mailbox here by that name'], # 5.1.1
62
- exceedlimit: ['The message exeeded the maximum size the user accepts'], # 5.2.3
63
- systemerror: [
60
+ 'suspend' => ['Mailaddress is administrative?le?y disabled'], # 5.2.1
61
+ 'userunknown' => ['Sorry, no mailbox here by that name'], # 5.1.1
62
+ 'exceedlimit' => ['The message exeeded the maximum size the user accepts'], # 5.2.3
63
+ 'systemerror' => [
64
64
  'Automatic homedir creator crashed', # 4.3.0
65
65
  'Illegal value in LDAP attribute', # 5.3.5
66
66
  'LDAP attribute is not given but mandatory', # 5.3.5
@@ -77,22 +77,22 @@ module Sisimai::Bite::Email
77
77
  MessagesOf = {
78
78
  # qmail-local.c:589| strerr_die1x(100,"Sorry, no mailbox here by that name. (#5.1.1)");
79
79
  # qmail-remote.c:253| out("s"); outhost(); out(" does not like recipient.\n");
80
- userunknown: ['no mailbox here by that name', 'does not like recipient.'],
80
+ 'userunknown' => ['no mailbox here by that name', 'does not like recipient.'],
81
81
  # error_str.c:192| X(EDQUOT,"disk quota exceeded")
82
- mailboxfull: ['disk quota exceeded'],
82
+ 'mailboxfull' => ['disk quota exceeded'],
83
83
  # qmail-qmtpd.c:233| ... result = "Dsorry, that message size exceeds my databytes limit (#5.3.4)";
84
84
  # qmail-smtpd.c:391| ... out("552 sorry, that message size exceeds my databytes limit (#5.3.4)\r\n"); return;
85
- mesgtoobig: ['Message size exceeds fixed maximum message size:'],
85
+ 'mesgtoobig' => ['Message size exceeds fixed maximum message size:'],
86
86
  # qmail-remote.c:68| Sorry, I couldn't find any host by that name. (#4.1.2)\n"); zerodie();
87
87
  # qmail-remote.c:78| Sorry, I couldn't find any host named ");
88
- hostunknown: ["Sorry, I couldn't find any host "],
89
- systemfull: ['Requested action not taken: mailbox unavailable (not enough free space)'],
90
- systemerror: [
88
+ 'hostunknown' => ["Sorry, I couldn't find any host "],
89
+ 'systemfull' => ['Requested action not taken: mailbox unavailable (not enough free space)'],
90
+ 'systemerror' => [
91
91
  'bad interpreter: No such file or directory',
92
92
  'system error',
93
93
  'Unable to',
94
94
  ],
95
- networkerror: [
95
+ 'networkerror' => [
96
96
  "Sorry, I wasn't able to establish an SMTP connection",
97
97
  "Sorry, I couldn't find a mail exchanger or IP address",
98
98
  "Sorry. Although I'm listed as a best-preference MX or A for that host",
@@ -151,7 +151,7 @@ module Sisimai::Bite::Email
151
151
  end
152
152
 
153
153
  if readcursor & Indicators[:'message-rfc822'] > 0
154
- # After "message/rfc822"
154
+ # Inside of the original message part
155
155
  if e.empty?
156
156
  blanklines += 1
157
157
  break if blanklines > 1
@@ -159,7 +159,7 @@ module Sisimai::Bite::Email
159
159
  end
160
160
  rfc822list << e
161
161
  else
162
- # Before "message/rfc822"
162
+ # Error message part
163
163
  next if (readcursor & Indicators[:deliverystatus]) == 0
164
164
  next if e.empty?
165
165
 
@@ -203,7 +203,7 @@ module Sisimai::Bite::Email
203
203
  ReSMTP.each_key do |r|
204
204
  # Verify each regular expression of SMTP commands
205
205
  next unless e['diagnosis'] =~ ReSMTP[r]
206
- e['command'] = r.to_s.upcase
206
+ e['command'] = r.upcase
207
207
  break
208
208
  end
209
209
 
@@ -234,12 +234,12 @@ module Sisimai::Bite::Email
234
234
  if e['alterrors']
235
235
  # Check the value of "alterrors"
236
236
  next unless MessagesOf[r].any? { |a| e['alterrors'].include?(a) }
237
- e['reason'] = r.to_s
237
+ e['reason'] = r
238
238
  end
239
239
  break if e['reason']
240
240
 
241
241
  next unless MessagesOf[r].any? { |a| e['diagnosis'].include?(a) }
242
- e['reason'] = r.to_s
242
+ e['reason'] = r
243
243
  break
244
244
  end
245
245
 
@@ -247,7 +247,7 @@ module Sisimai::Bite::Email
247
247
  FailOnLDAP.each_key do |r|
248
248
  # Verify each regular expression of LDAP errors
249
249
  next unless FailOnLDAP[r].any? { |a| e['diagnosis'].include?(a) }
250
- e['reason'] = r.to_s
250
+ e['reason'] = r
251
251
  break
252
252
  end
253
253
  end
@@ -258,7 +258,7 @@ module Sisimai::Bite::Email
258
258
  end
259
259
  end
260
260
 
261
- e['status'] = Sisimai::SMTP::Status.find(e['diagnosis'])
261
+ e['status'] = Sisimai::SMTP::Status.find(e['diagnosis']) || ''
262
262
  e.each_key { |a| e[a] ||= '' }
263
263
  end
264
264
 
@@ -15,10 +15,10 @@ module Sisimai::Bite::Email
15
15
  }.freeze
16
16
  MessagesOf = {
17
17
  # The followings are error messages in Rule sets/*/Actions/Template
18
- filtered: ['Mailbox does not exist'],
19
- mesgtoobig: ['Message too large'],
20
- mailboxfull: ['Mailbox full'],
21
- contenterror: ['Message content rejected'],
18
+ 'filtered' => ['Mailbox does not exist'],
19
+ 'mesgtoobig' => ['Message too large'],
20
+ 'mailboxfull' => ['Mailbox full'],
21
+ 'contenterror' => ['Message content rejected'],
22
22
  }.freeze
23
23
 
24
24
  def description; return 'Amazon SES(Receiving): http://aws.amazon.com/ses/'; end
@@ -26,7 +26,7 @@ module Sisimai::Bite::Email
26
26
 
27
27
  # X-SES-Outgoing: 2015.10.01-54.240.27.7
28
28
  # Feedback-ID: 1.us-west-2.HX6/J9OVlHTadQhEu1+wdF9DBj6n6Pa9sW5Y/0pSOi8=:AmazonSES
29
- def headerlist; return ['X-SES-Outgoing']; end
29
+ def headerlist; return %w[x-ses-outgoing]; end
30
30
 
31
31
  # Parse bounce messages from Amazon SES/Receiving
32
32
  # @param [Hash] mhead Message headers of a bounce email
@@ -44,6 +44,10 @@ module Sisimai::Bite::Email
44
44
  # :received => %r/.+[.]smtp-out[.].+[.]amazonses[.]com\b/,
45
45
  return nil unless mhead['x-ses-outgoing']
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 == 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,90 +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
- # Action: failed
97
- # Final-Recipient: rfc822; kijitora@neko.example.jp
98
- # Original-Recipient: rfc822; kijitora@neko.example.jp
99
- # Diagnostic-Code: smtp; 550 5.1.1 Mailbox does not exist
100
- # Status: 5.1.1
94
+ if f = Sisimai::RFC1894.match(e)
95
+ # "e" matched with any field defined in RFC3464
96
+ next unless o = Sisimai::RFC1894.field(e)
101
97
  v = dscontents[-1]
102
98
 
103
- if cv = e.match(/\AFinal-Recipient:[ ]*(?:RFC|rfc)822;[ ]*([^ ]+)\z/)
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]
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]
109
114
  end
110
- v['recipient'] = cv[1]
111
- recipients += 1
112
-
113
- elsif cv = e.match(/\AX-Actual-Recipient:[ ]*(?:RFC|rfc)822;[ ]*([^ ]+)\z/) ||
114
- e.match(/\AOriginal-Recipient:[ ]*(?:RFC|rfc)822;[ ]*([^ ]+)\z/)
115
- # X-Actual-Recipient: RFC822; kijitora@example.co.jp
116
- # Original-Recipient: rfc822; kijitora@example.co.jp
117
- v['alias'] = cv[1]
118
-
119
- elsif cv = e.match(/\AAction:[ ]*(.+)\z/)
120
- # Action: failed
121
- v['action'] = cv[1].downcase
122
-
123
- elsif cv = e.match(/\AStatus:[ ]*(\d[.]\d+[.]\d+)/)
124
- # Status: 5.1.1
125
- v['status'] = cv[1]
126
-
127
- elsif cv = e.match(/\ARemote-MTA:[ ]*(?:DNS|dns);[ ]*(.+)\z/)
128
- # Remote-MTA: DNS; mx.example.jp
129
- v['rhost'] = cv[1].downcase
130
-
131
- elsif cv = e.match(/\ALast-Attempt-Date:[ ]*(.+)\z/)
132
- # Last-Attempt-Date: Fri, 14 Feb 2014 12:30:08 -0500
133
- v['date'] = 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]
134
119
  else
135
- if cv = e.match(/\ADiagnostic-Code:[ ]*(.+?);[ ]*(.+)\z/)
136
- # Diagnostic-Code: SMTP; 550 5.1.1 <kijitora@example.jp>... User Unknown
137
- v['spec'] = cv[1].upcase
138
- v['diagnosis'] = cv[2]
139
-
140
- elsif p.start_with?('Diagnostic-Code:') && cv = e.match(/\A[ \t]+(.+)\z/)
141
- # Continued line of the value of Diagnostic-Code header
142
- v['diagnosis'] << ' ' << cv[1]
143
- havepassed[-1] = 'Diagnostic-Code: ' << e
144
- 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]
145
126
  end
146
127
  else
147
- # This message could not be delivered.
148
- # ------=_Part_0_1984813963.1443707337938
149
- # Content-Type: message/delivery-status
150
- # Content-Transfer-Encoding: 7bit
151
- # Content-Description: Delivery Status Notification
152
- #
153
- # Reporting-MTA: dns; inbound-smtp.us-west-2.amazonaws.com
154
- if cv = e.match(/\AReporting-MTA:[ ]*(?:DNS|dns);[ ]*(.+)\z/)
155
- # Reporting-MTA: dns; mx.example.jp
156
- next unless connheader['lhost'].empty?
157
- connheader['lhost'] = cv[1].downcase
158
- connvalues += 1
159
-
160
- elsif cv = e.match(/\AArrival-Date:[ ]*(.+)\z/)
161
- # Arrival-Date: Wed, 29 Apr 2009 16:03:18 +0900
162
- next unless connheader['date'].empty?
163
- connheader['date'] = cv[1]
164
- connvalues += 1
165
- 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
166
133
  end
167
- end
134
+ end # End of message/delivery-status
168
135
  end
169
136
  return nil unless recipients > 0
170
137
 
171
138
  dscontents.each do |e|
172
139
  # Set default values if each value is empty.
173
- connheader.each_key { |a| e[a] ||= connheader[a] || '' }
174
- e['diagnosis'] = Sisimai::String.sweep(e['diagnosis'].gsub(/\\n/, ' '))
140
+ e['lhost'] ||= permessage['rhost']
141
+ permessage.each_key { |a| e[a] ||= permessage[a] || '' }
142
+ e['diagnosis'] = Sisimai::String.sweep(e['diagnosis'].tr("\n", ' '))
175
143
 
176
144
  if e['status'].to_s.start_with?('5.0.0', '5.1.0', '4.0.0', '4.1.0')
177
145
  # Get other D.S.N. value from the error message
@@ -181,18 +149,17 @@ module Sisimai::Bite::Email
181
149
  # 5.1.0 - Unknown address error 550-'5.7.1 ...
182
150
  errormessage = cv[1]
183
151
  end
184
- pseudostatus = Sisimai::SMTP::Status.find(errormessage)
185
- e['status'] = pseudostatus unless pseudostatus.empty?
152
+ e['status'] = Sisimai::SMTP::Status.find(errormessage) || e['status']
186
153
  end
187
154
 
188
155
  MessagesOf.each_key do |r|
189
156
  # Verify each regular expression of session errors
190
157
  next unless MessagesOf[r].any? { |a| e['diagnosis'].include?(a) }
191
- e['reason'] = r.to_s
158
+ e['reason'] = r
192
159
  break
193
160
  end
194
161
 
195
- e['reason'] ||= Sisimai::SMTP::Status.name(e['status'])
162
+ e['reason'] ||= Sisimai::SMTP::Status.name(e['status']) || ''
196
163
  e['agent'] = self.smtpagent
197
164
  end
198
165