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
@@ -17,7 +17,7 @@ module Sisimai::Bite::Email
17
17
 
18
18
  # Return-Path: <apps@sendgrid.net>
19
19
  # X-Mailer: MIME-tools 5.502 (Entity 5.502)
20
- def headerlist; return ['Return-Path', 'X-Mailer']; end
20
+ def headerlist; return %w[return-path x-mailer]; end
21
21
 
22
22
  # Parse bounce messages from SendGrid
23
23
  # @param [Hash] mhead Message headers of a bounce email
@@ -36,6 +36,10 @@ module Sisimai::Bite::Email
36
36
  return nil unless mhead['return-path'] == '<apps@sendgrid.net>'
37
37
  return nil unless mhead['subject'] == 'Undelivered Mail Returned to Sender'
38
38
 
39
+ require 'sisimai/rfc1894'
40
+ fieldtable = Sisimai::RFC1894.FIELDTABLE
41
+ permessage = {} # (Hash) Store values of each Per-Message field
42
+
39
43
  dscontents = [Sisimai::Bite.DELIVERYSTATUS]
40
44
  hasdivided = mbody.split("\n")
41
45
  havepassed = ['']
@@ -44,10 +48,6 @@ module Sisimai::Bite::Email
44
48
  readcursor = 0 # (Integer) Points the current cursor position
45
49
  recipients = 0 # (Integer) The number of 'Final-Recipient' header
46
50
  commandtxt = '' # (String) SMTP Command name begin with the string '>>>'
47
- connvalues = 0 # (Integer) Flag, 1 if all the value of connheader have been set
48
- connheader = {
49
- 'date' => '', # The value of Arrival-Date header
50
- }
51
51
  v = nil
52
52
 
53
53
  while e = hasdivided.shift do
@@ -56,7 +56,7 @@ module Sisimai::Bite::Email
56
56
  p = havepassed[-2]
57
57
 
58
58
  if readcursor == 0
59
- # Beginning of the bounce message or delivery status part
59
+ # Beginning of the bounce message or message/delivery-status part
60
60
  if e == StartingOf[:message][0]
61
61
  readcursor |= Indicators[:deliverystatus]
62
62
  next
@@ -64,7 +64,7 @@ module Sisimai::Bite::Email
64
64
  end
65
65
 
66
66
  if (readcursor & Indicators[:'message-rfc822']) == 0
67
- # Beginning of the original message part
67
+ # Beginning of the original message part(message/rfc822)
68
68
  if e == StartingOf[:rfc822][0]
69
69
  readcursor |= Indicators[:'message-rfc822']
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
- # After "message/rfc822"
75
+ # message/rfc822 OR text/rfc822-headers part
76
76
  if e.empty?
77
77
  blanklines += 1
78
78
  break if blanklines > 1
@@ -80,97 +80,79 @@ module Sisimai::Bite::Email
80
80
  end
81
81
  rfc822list << e
82
82
  else
83
- # Before "message/rfc822"
83
+ # message/delivery-status part
84
84
  next if (readcursor & Indicators[:deliverystatus]) == 0
85
85
  next if e.empty?
86
86
 
87
- if connvalues == connheader.keys.size
88
- # Final-Recipient: rfc822; kijitora@example.jp
89
- # Original-Recipient: rfc822; kijitora@example.jp
90
- # Action: failed
91
- # Status: 5.1.1
92
- # Diagnostic-Code: 550 5.1.1 <kijitora@example.jp>... User Unknown
87
+ if f = Sisimai::RFC1894.match(e)
88
+ # "e" matched with any field defined in RFC3464
89
+ o = Sisimai::RFC1894.field(e)
93
90
  v = dscontents[-1]
94
91
 
95
- if cv = e.match(/\AFinal-Recipient:[ ]*(?:RFC|rfc)822;[ ]*([^ ]+)\z/)
96
- # Final-Recipient: RFC822; userunknown@example.jp
97
- if v['recipient']
98
- # There are multiple recipient addresses in the message body.
99
- dscontents << Sisimai::Bite.DELIVERYSTATUS
100
- v = dscontents[-1]
101
- end
102
- v['recipient'] = cv[1]
103
- recipients += 1
104
-
105
- elsif cv = e.match(/\AAction:[ ]*(.+)\z/)
106
- # Action: failed
107
- v['action'] = cv[1].downcase
108
-
109
- elsif cv = e.match(/\AStatus:[ ]*(\d[.]\d+[.]\d+)/)
110
- # Status: 5.1.1
111
- # Status:5.2.0
112
- # Status: 5.1.0 (permanent failure)
113
- v['status'] = cv[1]
92
+ unless o
93
+ # Fallback code for empty value or invalid formatted value
94
+ # - Status: (empty)
95
+ # - Diagnostic-Code: 550 5.1.1 ... (No "diagnostic-type" sub field)
96
+ next unless cv = e.match(/\ADiagnostic-Code:[ ]*(.+)/)
97
+ v['diagnosis'] = cv[1]
98
+ next;
99
+ end
114
100
 
115
- else
116
- if cv = e.match(/\ADiagnostic-Code:[ ]*(.+)\z/)
117
- # Diagnostic-Code: 550 5.1.1 <userunknown@example.jp>... User Unknown
118
- v['diagnosis'] = cv[1]
119
-
120
- elsif p.start_with?('Diagnostic-Code:') && cv = e.match(/\A[ \t]+(.+)\z/)
121
- # Continued line of the value of Diagnostic-Code header
122
- v['diagnosis'] << ' ' << cv[1]
123
- havepassed[-1] = 'Diagnostic-Code: ' << e
101
+ if o[-1] == 'addr'
102
+ # Final-Recipient: rfc822; kijitora@example.jp
103
+ # X-Actual-Recipient: rfc822; kijitora@example.co.jp
104
+ if o[0] == 'final-recipient'
105
+ # Final-Recipient: rfc822; kijitora@example.jp
106
+ if v['recipient']
107
+ # There are multiple recipient addresses in the message body.
108
+ dscontents << Sisimai::Bite.DELIVERYSTATUS
109
+ v = dscontents[-1]
110
+ end
111
+ v['recipient'] = o[2]
112
+ recipients += 1
113
+ else
114
+ # X-Actual-Recipient: rfc822; kijitora@example.co.jp
115
+ v['alias'] = o[2]
124
116
  end
117
+ elsif o[-1] == 'code'
118
+ # Diagnostic-Code: SMTP; 550 5.1.1 <userunknown@example.jp>... User Unknown
119
+ v['spec'] = o[1]
120
+ v['diagnosis'] = o[2]
121
+ elsif o[-1] == 'date'
122
+ # Arrival-Date: 2012-12-31 23-59-59
123
+ next unless cv = e.match(/\AArrival-Date: (\d{4})[-](\d{2})[-](\d{2}) (\d{2})[-](\d{2})[-](\d{2})\z/)
124
+ o[1] << 'Thu, ' << cv[3] + ' '
125
+ o[1] << Sisimai::DateTime.monthname(0)[cv[2].to_i - 1]
126
+ o[1] << ' ' << cv[1] + ' ' << [cv[4], cv[5], cv[6]].join(':')
127
+ o[1] << ' ' << Sisimai::DateTime.abbr2tz('CDT')
128
+ else
129
+ # Other DSN fields defined in RFC3464
130
+ next unless fieldtable.key?(o[0])
131
+ v[fieldtable[o[0]]] = o[2]
132
+
133
+ next unless f == 1
134
+ permessage[fieldtable[o[0]]] = o[2]
125
135
  end
126
136
  else
127
- # This is an automatically generated message from SendGrid.
128
- #
129
- # I'm sorry to have to tell you that your message was not able to be
130
- # delivered to one of its intended recipients.
131
- #
132
- # If you require assistance with this, please contact SendGrid support.
133
- #
134
- # shironekochan:000000:<kijitora@example.jp> : 192.0.2.250 : mx.example.jp:[192.0.2.153] :
135
- # 550 5.1.1 <userunknown@cubicroot.jp>... User Unknown in RCPT TO
136
- #
137
- # ------------=_1351676802-30315-116783
138
- # Content-Type: message/delivery-status
139
- # Content-Disposition: inline
140
- # Content-Transfer-Encoding: 7bit
141
- # Content-Description: Delivery Report
142
- #
143
- # X-SendGrid-QueueID: 959479146
144
- # X-SendGrid-Sender: <bounces+61689-10be-kijitora=example.jp@sendgrid.info>
145
- # Arrival-Date: 2012-12-31 23-59-59
137
+ # The line does not begin with a DSN field defined in RFC3464
146
138
  if cv = e.match(/.+ in (?:End of )?([A-Z]{4}).*\z/)
147
139
  # in RCPT TO, in MAIL FROM, end of DATA
148
140
  commandtxt = cv[1]
149
-
150
- elsif cv = e.match(/\AArrival-Date:[ ]*(.+)\z/)
151
- # Arrival-Date: Wed, 29 Apr 2009 16:03:18 +0900
152
- next unless connheader['date'].empty?
153
- arrivaldate = cv[1]
154
-
155
- if cv = e.match(/\AArrival-Date: (\d{4})[-](\d{2})[-](\d{2}) (\d{2})[-](\d{2})[-](\d{2})\z/)
156
- # Arrival-Date: 2011-08-12 01-05-05
157
- arrivaldate << 'Thu, ' << cv[3] + ' '
158
- arrivaldate << Sisimai::DateTime.monthname(0)[cv[2].to_i - 1]
159
- arrivaldate << ' ' << cv[1] + ' ' << [cv[4], cv[5], cv[6]].join(':')
160
- arrivaldate << ' ' << Sisimai::DateTime.abbr2tz('CDT')
161
- end
162
- connheader['date'] = arrivaldate
163
- connvalues += 1
141
+ else
142
+ # Continued line of the value of Diagnostic-Code field
143
+ next unless p.start_with?('Diagnostic-Code:')
144
+ next unless cv = e.match(/\A[ \t]+(.+)\z/)
145
+ v['diagnosis'] << ' ' << cv[1]
146
+ havepassed[-1] = 'Diagnostic-Code: ' << e
164
147
  end
165
148
  end
166
- end
149
+ end # End of message/delivery-status
167
150
  end
168
151
  return nil unless recipients > 0
169
152
 
170
153
  dscontents.each do |e|
171
- e['diagnosis'] = Sisimai::String.sweep(e['diagnosis'])
172
-
173
154
  # Get the value of SMTP status code as a pseudo D.S.N.
155
+ e['diagnosis'] = Sisimai::String.sweep(e['diagnosis'])
174
156
  if cv = e['diagnosis'].match(/\b([45])\d\d[ \t]*/)
175
157
  # 4xx or 5xx
176
158
  e['status'] = cv[1] + '.0.0'
@@ -179,8 +161,7 @@ module Sisimai::Bite::Email
179
161
  if e['status'] == '5.0.0' || e['status'] == '4.0.0'
180
162
  # Get the value of D.S.N. from the error message or the value of
181
163
  # Diagnostic-Code header.
182
- pseudostatus = Sisimai::SMTP::Status.find(e['diagnosis'])
183
- e['status'] = pseudostatus unless pseudostatus.empty?
164
+ e['status'] = Sisimai::SMTP::Status.find(e['diagnosis']) || ''
184
165
  end
185
166
 
186
167
  if e['action'] == 'expired'
@@ -189,11 +170,11 @@ module Sisimai::Bite::Email
189
170
  if !e['status'] || e['status'].end_with?('.0.0')
190
171
  # Set pseudo Status code value if the value of Status is not
191
172
  # defined or 4.0.0 or 5.0.0.
192
- pseudostatus = Sisimai::SMTP::Status.code('expired')
193
- e['status'] = pseudostatus unless pseudostatus.empty?
173
+ e['status'] = Sisimai::SMTP::Status.code('expired') || e['status']
194
174
  end
195
175
  end
196
176
 
177
+ e['lhost'] ||= permessage['rhost']
197
178
  e['agent'] = self.smtpagent
198
179
  e['command'] = commandtxt
199
180
  end
@@ -12,7 +12,10 @@ module Sisimai::Bite::Email
12
12
  # savemail.c:1040|if (printheader && !putline(" ----- Transcript of session follows -----\n",
13
13
  # savemail.c:1041| mci))
14
14
  # savemail.c:1042| goto writeerr;
15
- #
15
+ # savemail.c:1360|if (!putline(
16
+ # savemail.c:1361| sendbody
17
+ # savemail.c:1362| ? " ----- Original message follows -----\n"
18
+ # savemail.c:1363| : " ----- Message header follows -----\n",
16
19
  rfc822: ['Content-Type: message/rfc822', 'Content-Type: text/rfc822-headers'],
17
20
  message: [' ----- Transcript of session follows -----'],
18
21
  error: ['... while talking to '],
@@ -41,6 +44,10 @@ module Sisimai::Bite::Email
41
44
  return nil unless mhead['from'].start_with?('Mail Delivery Subsystem')
42
45
  end
43
46
 
47
+ require 'sisimai/rfc1894'
48
+ fieldtable = Sisimai::RFC1894.FIELDTABLE
49
+ permessage = {} # (Hash) Store values of each Per-Message field
50
+
44
51
  dscontents = [Sisimai::Bite.DELIVERYSTATUS]
45
52
  hasdivided = mbody.split("\n")
46
53
  havepassed = ['']
@@ -51,11 +58,6 @@ module Sisimai::Bite::Email
51
58
  commandtxt = '' # (String) SMTP Command name begin with the string '>>>'
52
59
  esmtpreply = '' # (String) Reply from remote server on SMTP session
53
60
  sessionerr = false # (Boolean) Flag, "true" if it is SMTP session error
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
- 'rhost' => '', # The value of Reporting-MTA header
58
- }
59
61
  anotherset = {} # Another error information
60
62
  v = nil
61
63
 
@@ -65,7 +67,7 @@ module Sisimai::Bite::Email
65
67
  p = havepassed[-2]
66
68
 
67
69
  if readcursor == 0
68
- # Beginning of the bounce message or delivery status part
70
+ # Beginning of the bounce message or message/delivery-status part
69
71
  if e.start_with?(StartingOf[:message][0])
70
72
  readcursor |= Indicators[:deliverystatus]
71
73
  next
@@ -73,7 +75,7 @@ module Sisimai::Bite::Email
73
75
  end
74
76
 
75
77
  if (readcursor & Indicators[:'message-rfc822']) == 0
76
- # Beginning of the original message part
78
+ # Beginning of the original message part(message/rfc822)
77
79
  if e.start_with?(StartingOf[:rfc822][0], StartingOf[:rfc822][1])
78
80
  readcursor |= Indicators[:'message-rfc822']
79
81
  next
@@ -81,7 +83,7 @@ module Sisimai::Bite::Email
81
83
  end
82
84
 
83
85
  if readcursor & Indicators[:'message-rfc822'] > 0
84
- # After "message/rfc822"
86
+ # message/rfc822 OR text/rfc822-headers part
85
87
  if e.empty?
86
88
  blanklines += 1
87
89
  break if blanklines > 1
@@ -89,65 +91,46 @@ module Sisimai::Bite::Email
89
91
  end
90
92
  rfc822list << e
91
93
  else
92
- # Before "message/rfc822"
94
+ # message/delivery-status part
93
95
  next if (readcursor & Indicators[:deliverystatus]) == 0
94
96
  next if e.empty?
95
97
 
96
- if connvalues == connheader.keys.size
97
- # Final-Recipient: RFC822; userunknown@example.jp
98
- # X-Actual-Recipient: RFC822; kijitora@example.co.jp
99
- # Action: failed
100
- # Status: 5.1.1
101
- # Remote-MTA: DNS; mx.example.jp
102
- # Diagnostic-Code: SMTP; 550 5.1.1 <userunknown@example.jp>... User Unknown
103
- # Last-Attempt-Date: Fri, 14 Feb 2014 12:30:08 -0500
98
+ if f = Sisimai::RFC1894.match(e)
99
+ # "e" matched with any field defined in RFC3464
100
+ o = Sisimai::RFC1894.field(e) || next
104
101
  v = dscontents[-1]
105
102
 
106
- if cv = e.match(/\AFinal-Recipient:[ ]*(?:RFC|rfc)822;[ ]*([^ ]+)\z/)
107
- # Final-Recipient: RFC822; userunknown@example.jp
108
- if v['recipient']
109
- # There are multiple recipient addresses in the message body.
110
- dscontents << Sisimai::Bite.DELIVERYSTATUS
111
- v = dscontents[-1]
103
+ if o[-1] == 'addr'
104
+ # Final-Recipient: rfc822; kijitora@example.jp
105
+ # X-Actual-Recipient: rfc822; kijitora@example.co.jp
106
+ if o[0] == 'final-recipient'
107
+ # Final-Recipient: rfc822; kijitora@example.jp
108
+ if v['recipient']
109
+ # There are multiple recipient addresses in the message body.
110
+ dscontents << Sisimai::Bite.DELIVERYSTATUS
111
+ v = dscontents[-1]
112
+ end
113
+ v['recipient'] = o[2]
114
+ recipients += 1
115
+ else
116
+ # X-Actual-Recipient: rfc822; kijitora@example.co.jp
117
+ v['alias'] = o[2]
112
118
  end
113
- v['recipient'] = cv[1]
114
- recipients += 1
115
-
116
- elsif cv = e.match(/\AX-Actual-Recipient:[ ]*(?:RFC|rfc)822;[ ]*([^ ]+)\z/)
117
- # X-Actual-Recipient: RFC822; kijitora@example.co.jp
118
- v['alias'] = cv[1]
119
-
120
- elsif cv = e.match(/\AAction:[ ]*(.+)\z/)
121
- # Action: failed
122
- v['action'] = cv[1].downcase
123
-
124
- elsif cv = e.match(/\AStatus:[ ]*(\d[.]\d+[.]\d+)/)
125
- # Status: 5.1.1
126
- # Status:5.2.0
127
- # Status: 5.1.0 (permanent failure)
128
- v['status'] = cv[1]
129
-
130
- elsif cv = e.match(/\ARemote-MTA:[ ]*(?:DNS|dns);[ ]*(.+)\z/)
131
- # Remote-MTA: DNS; mx.example.jp
132
- v['rhost'] = cv[1].downcase
133
- v['rhost'] = '' if v['rhost'] =~ /\A\s+\z/ # Remote-MTA: DNS;
134
-
135
- elsif cv = e.match(/\ALast-Attempt-Date:[ ]*(.+)\z/)
136
- # Last-Attempt-Date: Fri, 14 Feb 2014 12:30:08 -0500
137
- v['date'] = cv[1]
119
+ elsif o[-1] == 'code'
120
+ # Diagnostic-Code: SMTP; 550 5.1.1 <userunknown@example.jp>... User Unknown
121
+ v['spec'] = o[1]
122
+ v['diagnosis'] = o[2]
138
123
  else
139
- if cv = e.match(/\ADiagnostic-Code:[ ]*(.+?);[ ]*(.+)\z/)
140
- # Diagnostic-Code: SMTP; 550 5.1.1 <userunknown@example.jp>... User Unknown
141
- v['spec'] = cv[1].upcase
142
- v['diagnosis'] = cv[2]
124
+ # Other DSN fields defined in RFC3464
125
+ next unless fieldtable.key?(o[0])
126
+ v[fieldtable[o[0]]] = o[2]
143
127
 
144
- elsif 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
148
- end
128
+ next unless f == 1
129
+ permessage[fieldtable[o[0]]] = o[2]
149
130
  end
150
131
  else
132
+ # The line does not begin with a DSN field defined in RFC3464
133
+ #
151
134
  # ----- Transcript of session follows -----
152
135
  # ... while talking to mta.example.org.:
153
136
  # >>> DATA
@@ -157,77 +140,66 @@ module Sisimai::Bite::Email
157
140
  # Reporting-MTA: dns; mx.example.jp
158
141
  # Received-From-MTA: DNS; x1x2x3x4.dhcp.example.ne.jp
159
142
  # Arrival-Date: Wed, 29 Apr 2009 16:03:18 +0900
160
- if cv = e.match(/\A[>]{3}[ ]+([A-Z]{4})[ ]?/)
161
- # >>> DATA
162
- commandtxt = cv[1]
163
-
164
- elsif cv = e.match(/\A[<]{3}[ ]+(.+)\z/)
165
- # <<< Response
166
- esmtpreply = cv[1]
167
-
168
- elsif cv = e.match(/\AReporting-MTA:[ ]*(?:DNS|dns);[ ]*(.+)\z/)
169
- # Reporting-MTA: dns; mx.example.jp
170
- next unless connheader['rhost'].empty?
171
- connheader['rhost'] = cv[1].downcase
172
- connvalues += 1
173
-
174
- elsif cv = e.match(/\AReceived-From-MTA:[ ]*(?:DNS|dns);[ ]*(.+)\z/)
175
- # Received-From-MTA: DNS; x1x2x3x4.dhcp.example.ne.jp
176
- next if connheader['lhost']
177
-
178
- # The value of "lhost" is optional
179
- connheader['lhost'] = cv[1].downcase
180
- connvalues += 1
181
-
182
- elsif cv = e.match(/\AArrival-Date:[ ]*(.+)\z/)
183
- # Arrival-Date: Wed, 29 Apr 2009 16:03:18 +0900
184
- next unless connheader['date'].empty?
185
- connheader['date'] = cv[1]
186
- connvalues += 1
187
- else
188
- # Detect SMTP session error or connection error
189
- next if sessionerr
190
- if e.start_with?(StartingOf[:error][0])
191
- # ----- Transcript of session follows -----
192
- # ... while talking to mta.example.org.:
193
- sessionerr = true
194
- next
195
- end
196
-
197
- if cv = e.match(/\A[<](.+)[>][.]+ (.+)\z/)
198
- # <kijitora@example.co.jp>... Deferred: Name server: example.co.jp.: host name lookup failure
199
- anotherset['recipient'] = cv[1]
200
- anotherset['diagnosis'] = cv[2]
143
+ unless e.start_with?(' ')
144
+ if cv = e.match(/\A[>]{3}[ ]+([A-Z]{4})[ ]?/)
145
+ # >>> DATA
146
+ commandtxt = cv[1]
147
+
148
+ elsif cv = e.match(/\A[<]{3}[ ]+(.+)\z/)
149
+ # <<< Response
150
+ esmtpreply = cv[1]
201
151
  else
202
- # ----- Transcript of session follows -----
203
- # Message could not be delivered for too long
204
- # Message will be deleted from queue
205
- next if e =~ /\A[ \t]*[-]+/
206
- if cv = e.match(/\A[45]\d\d[ \t]([45][.]\d[.]\d)[ \t].+/)
207
- # 550 5.1.2 <kijitora@example.org>... Message
208
- #
209
- # DBI connect('dbname=...')
210
- # 554 5.3.0 unknown mailer error 255
211
- anotherset['status'] = cv[1]
212
- anotherset['diagnosis'] ||= ''
213
- anotherset['diagnosis'] << ' ' << e
152
+ # Detect SMTP session error or connection error
153
+ next if sessionerr
154
+ if e.start_with?(StartingOf[:error][0])
155
+ # ----- Transcript of session follows -----
156
+ # ... while talking to mta.example.org.:
157
+ sessionerr = true
158
+ next
159
+ end
214
160
 
215
- elsif e.start_with?('Message ', 'Warning: ')
161
+ if cv = e.match(/\A[<](.+)[>][.]+ (.+)\z/)
162
+ # <kijitora@example.co.jp>... Deferred: Name server: example.co.jp.: host name lookup failure
163
+ anotherset['recipient'] = cv[1]
164
+ anotherset['diagnosis'] = cv[2]
165
+ else
166
+ # ----- Transcript of session follows -----
216
167
  # Message could not be delivered for too long
217
- # Warning: message still undelivered after 4 hours
218
- anotherset['diagnosis'] ||= ''
219
- anotherset['diagnosis'] << ' ' << e
168
+ # Message will be deleted from queue
169
+ next if e =~ /\A[ \t]*[-]+/
170
+ if cv = e.match(/\A[45]\d\d[ \t]([45][.]\d[.]\d)[ \t].+/)
171
+ # 550 5.1.2 <kijitora@example.org>... Message
172
+ #
173
+ # DBI connect('dbname=...')
174
+ # 554 5.3.0 unknown mailer error 255
175
+ anotherset['status'] = cv[1]
176
+ anotherset['diagnosis'] ||= ''
177
+ anotherset['diagnosis'] << ' ' << e
178
+
179
+ elsif e.start_with?('Message ', 'Warning: ')
180
+ # Message could not be delivered for too long
181
+ # Warning: message still undelivered after 4 hours
182
+ anotherset['diagnosis'] ||= ''
183
+ anotherset['diagnosis'] << ' ' << e
184
+ end
220
185
  end
221
186
  end
187
+ else
188
+ # Continued line of the value of Diagnostic-Code field
189
+ next unless p.start_with?('Diagnostic-Code:')
190
+ next unless cv = e.match(/\A[ \t]+(.+)\z/)
191
+ v['diagnosis'] << ' ' << cv[1]
192
+ havepassed[-1] = 'Diagnostic-Code: ' << e
222
193
  end
223
194
  end
224
- end
195
+ end # End of message/delivery-status
225
196
  end
226
197
  return nil unless recipients > 0
227
198
 
228
199
  dscontents.each do |e|
229
200
  # Set default values if each value is empty.
230
- connheader.each_key { |a| e[a] ||= connheader[a] || '' }
201
+ e['lhost'] ||= permessage['rhost']
202
+ permessage.each_key { |a| e[a] ||= permessage[a] || '' }
231
203
 
232
204
  e['agent'] = self.smtpagent
233
205
  e['command'] ||= commandtxt