sisimai 4.25.16-java → 5.0.2-java

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (180) hide show
  1. checksums.yaml +5 -5
  2. data/.github/workflows/rake-test.yml +55 -0
  3. data/.travis.yml +3 -3
  4. data/ANALYTICAL-PRECISION +2 -2
  5. data/Benchmarks.mk +3 -3
  6. data/CONTRIBUTING +1 -1
  7. data/ChangeLog.md +451 -393
  8. data/Developers.mk +5 -6
  9. data/Gemfile +1 -1
  10. data/Makefile +15 -15
  11. data/README-JA.md +323 -149
  12. data/README.md +319 -149
  13. data/Rakefile +9 -3
  14. data/Repository.mk +2 -3
  15. data/lib/sisimai/address.rb +118 -74
  16. data/lib/sisimai/arf.rb +84 -82
  17. data/lib/sisimai/datetime.rb +5 -52
  18. data/lib/sisimai/{data → fact}/json.rb +7 -9
  19. data/lib/sisimai/fact/yaml.rb +31 -0
  20. data/lib/sisimai/fact.rb +506 -0
  21. data/lib/sisimai/lhost/activehunter.rb +12 -14
  22. data/lib/sisimai/lhost/amavis.rb +11 -14
  23. data/lib/sisimai/lhost/amazonses.rb +37 -42
  24. data/lib/sisimai/lhost/amazonworkmail.rb +15 -19
  25. data/lib/sisimai/lhost/aol.rb +12 -15
  26. data/lib/sisimai/lhost/apachejames.rb +19 -21
  27. data/lib/sisimai/lhost/barracuda.rb +10 -12
  28. data/lib/sisimai/lhost/bigfoot.rb +21 -22
  29. data/lib/sisimai/lhost/biglobe.rb +15 -16
  30. data/lib/sisimai/lhost/courier.rb +20 -20
  31. data/lib/sisimai/lhost/domino.rb +23 -20
  32. data/lib/sisimai/lhost/einsundeins.rb +23 -18
  33. data/lib/sisimai/lhost/exchange2003.rb +30 -29
  34. data/lib/sisimai/lhost/exchange2007.rb +70 -58
  35. data/lib/sisimai/lhost/exim.rb +179 -174
  36. data/lib/sisimai/lhost/ezweb.rb +31 -56
  37. data/lib/sisimai/lhost/facebook.rb +21 -34
  38. data/lib/sisimai/lhost/fml.rb +43 -48
  39. data/lib/sisimai/lhost/gmail.rb +29 -29
  40. data/lib/sisimai/lhost/gmx.rb +18 -17
  41. data/lib/sisimai/lhost/googlegroups.rb +11 -11
  42. data/lib/sisimai/lhost/gsuite.rb +21 -28
  43. data/lib/sisimai/lhost/imailserver.rb +25 -39
  44. data/lib/sisimai/lhost/interscanmss.rb +28 -31
  45. data/lib/sisimai/lhost/kddi.rb +22 -28
  46. data/lib/sisimai/lhost/mailfoundry.rb +11 -12
  47. data/lib/sisimai/lhost/mailmarshalsmtp.rb +25 -29
  48. data/lib/sisimai/lhost/mailru.rb +37 -40
  49. data/lib/sisimai/lhost/mcafee.rb +21 -31
  50. data/lib/sisimai/lhost/messagelabs.rb +17 -21
  51. data/lib/sisimai/lhost/messagingserver.rb +40 -37
  52. data/lib/sisimai/lhost/mfilter.rb +16 -17
  53. data/lib/sisimai/lhost/mxlogic.rb +24 -33
  54. data/lib/sisimai/lhost/notes.rb +17 -17
  55. data/lib/sisimai/lhost/office365.rb +64 -28
  56. data/lib/sisimai/lhost/opensmtpd.rb +12 -13
  57. data/lib/sisimai/lhost/outlook.rb +12 -16
  58. data/lib/sisimai/lhost/postfix.rb +179 -130
  59. data/lib/sisimai/lhost/powermta.rb +12 -14
  60. data/lib/sisimai/lhost/qmail.rb +44 -47
  61. data/lib/sisimai/lhost/receivingses.rb +15 -21
  62. data/lib/sisimai/lhost/sendgrid.rb +34 -34
  63. data/lib/sisimai/lhost/sendmail.rb +65 -53
  64. data/lib/sisimai/lhost/surfcontrol.rb +19 -19
  65. data/lib/sisimai/lhost/v5sendmail.rb +45 -39
  66. data/lib/sisimai/lhost/verizon.rb +35 -39
  67. data/lib/sisimai/lhost/x1.rb +18 -17
  68. data/lib/sisimai/lhost/x2.rb +17 -14
  69. data/lib/sisimai/lhost/x3.rb +19 -19
  70. data/lib/sisimai/lhost/x4.rb +72 -57
  71. data/lib/sisimai/lhost/x5.rb +17 -19
  72. data/lib/sisimai/lhost/x6.rb +41 -17
  73. data/lib/sisimai/lhost/yahoo.rb +17 -16
  74. data/lib/sisimai/lhost/yandex.rb +16 -21
  75. data/lib/sisimai/lhost/zoho.rb +16 -15
  76. data/lib/sisimai/lhost.rb +8 -10
  77. data/lib/sisimai/mail/maildir.rb +1 -3
  78. data/lib/sisimai/mail/mbox.rb +3 -4
  79. data/lib/sisimai/mail/memory.rb +0 -1
  80. data/lib/sisimai/mail/stdin.rb +1 -3
  81. data/lib/sisimai/mail.rb +3 -7
  82. data/lib/sisimai/mda.rb +28 -42
  83. data/lib/sisimai/message.rb +444 -326
  84. data/lib/sisimai/order.rb +5 -5
  85. data/lib/sisimai/reason/authfailure.rb +65 -0
  86. data/lib/sisimai/reason/badreputation.rb +53 -0
  87. data/lib/sisimai/reason/blocked.rb +96 -160
  88. data/lib/sisimai/reason/contenterror.rb +8 -9
  89. data/lib/sisimai/reason/delivered.rb +4 -6
  90. data/lib/sisimai/reason/exceedlimit.rb +10 -12
  91. data/lib/sisimai/reason/expired.rb +7 -8
  92. data/lib/sisimai/reason/feedback.rb +2 -3
  93. data/lib/sisimai/reason/filtered.rb +17 -19
  94. data/lib/sisimai/reason/hasmoved.rb +9 -10
  95. data/lib/sisimai/reason/hostunknown.rb +15 -15
  96. data/lib/sisimai/reason/mailboxfull.rb +11 -12
  97. data/lib/sisimai/reason/mailererror.rb +18 -20
  98. data/lib/sisimai/reason/mesgtoobig.rb +9 -11
  99. data/lib/sisimai/reason/networkerror.rb +5 -8
  100. data/lib/sisimai/reason/norelaying.rb +8 -11
  101. data/lib/sisimai/reason/notaccept.rb +13 -14
  102. data/lib/sisimai/reason/notcompliantrfc.rb +43 -0
  103. data/lib/sisimai/reason/onhold.rb +6 -9
  104. data/lib/sisimai/reason/policyviolation.rb +14 -12
  105. data/lib/sisimai/reason/rejected.rb +26 -24
  106. data/lib/sisimai/reason/requireptr.rb +69 -0
  107. data/lib/sisimai/reason/securityerror.rb +34 -36
  108. data/lib/sisimai/reason/spamdetected.rb +115 -147
  109. data/lib/sisimai/reason/speeding.rb +49 -0
  110. data/lib/sisimai/reason/suspend.rb +12 -11
  111. data/lib/sisimai/reason/syntaxerror.rb +11 -10
  112. data/lib/sisimai/reason/systemerror.rb +7 -9
  113. data/lib/sisimai/reason/systemfull.rb +7 -8
  114. data/lib/sisimai/reason/toomanyconn.rb +9 -11
  115. data/lib/sisimai/reason/undefined.rb +2 -3
  116. data/lib/sisimai/reason/userunknown.rb +129 -146
  117. data/lib/sisimai/reason/vacation.rb +3 -4
  118. data/lib/sisimai/reason/virusdetected.rb +10 -11
  119. data/lib/sisimai/reason.rb +59 -64
  120. data/lib/sisimai/rfc1894.rb +55 -28
  121. data/lib/sisimai/rfc2045.rb +373 -0
  122. data/lib/sisimai/rfc3464.rb +250 -308
  123. data/lib/sisimai/rfc3834.rb +42 -45
  124. data/lib/sisimai/rfc5322.rb +177 -146
  125. data/lib/sisimai/rfc5965.rb +31 -0
  126. data/lib/sisimai/rhost/cox.rb +5 -6
  127. data/lib/sisimai/rhost/franceptt.rb +6 -8
  128. data/lib/sisimai/rhost/godaddy.rb +12 -12
  129. data/lib/sisimai/rhost/google.rb +530 -0
  130. data/lib/sisimai/rhost/iua.rb +9 -10
  131. data/lib/sisimai/rhost/kddi.rb +6 -8
  132. data/lib/sisimai/rhost/{exchangeonline.rb → microsoft.rb} +115 -114
  133. data/lib/sisimai/rhost/mimecast.rb +51 -42
  134. data/lib/sisimai/rhost/nttdocomo.rb +12 -12
  135. data/lib/sisimai/rhost/spectrum.rb +10 -12
  136. data/lib/sisimai/rhost/{tencentqq.rb → tencent.rb} +7 -8
  137. data/lib/sisimai/rhost.rb +23 -31
  138. data/lib/sisimai/smtp/command.rb +59 -0
  139. data/lib/sisimai/smtp/error.rb +4 -7
  140. data/lib/sisimai/smtp/reply.rb +161 -74
  141. data/lib/sisimai/smtp/status.rb +507 -393
  142. data/lib/sisimai/smtp/transcript.rb +124 -0
  143. data/lib/sisimai/smtp.rb +0 -1
  144. data/lib/sisimai/string.rb +74 -5
  145. data/lib/sisimai/time.rb +1 -2
  146. data/lib/sisimai/version.rb +1 -1
  147. data/lib/sisimai.rb +46 -31
  148. data/set-of-emails/maildir/bsd/lhost-domino-02.eml +6 -3
  149. data/set-of-emails/maildir/bsd/lhost-googlegroups-15.eml +174 -0
  150. data/set-of-emails/maildir/bsd/lhost-gsuite-15.eml +229 -0
  151. data/set-of-emails/maildir/bsd/lhost-postfix-75.eml +51 -0
  152. data/set-of-emails/maildir/bsd/lhost-postfix-76.eml +101 -0
  153. data/set-of-emails/maildir/bsd/lhost-postfix-77.eml +74 -0
  154. data/set-of-emails/maildir/bsd/lhost-postfix-78.eml +91 -0
  155. data/set-of-emails/maildir/bsd/lhost-receivingses-08.eml +88 -0
  156. data/set-of-emails/maildir/bsd/lhost-sendmail-60.eml +85 -0
  157. data/set-of-emails/maildir/bsd/rfc3464-43.eml +88 -0
  158. data/set-of-emails/maildir/bsd/rhost-google-03.eml +101 -0
  159. data/set-of-emails/maildir/bsd/rhost-google-04.eml +102 -0
  160. data/set-of-emails/maildir/bsd/rhost-google-05.eml +82 -0
  161. data/set-of-emails/maildir/bsd/rhost-google-06.eml +102 -0
  162. data/set-of-emails/maildir/bsd/rhost-google-07.eml +69 -0
  163. data/set-of-emails/maildir/bsd/rhost-google-08.eml +99 -0
  164. data/sisimai-java.gemspec +1 -1
  165. data/sisimai.gemspec +1 -1
  166. metadata +48 -26
  167. data/.rspec +0 -2
  168. data/lib/sisimai/data/yaml.rb +0 -33
  169. data/lib/sisimai/data.rb +0 -411
  170. data/lib/sisimai/mime.rb +0 -456
  171. data/lib/sisimai/rhost/googleapps.rb +0 -261
  172. /data/set-of-emails/maildir/bsd/{rfc3464-41.eml → rfc3834-05.eml} +0 -0
  173. /data/set-of-emails/maildir/bsd/{rhost-googleapps-01.eml → rhost-google-01.eml} +0 -0
  174. /data/set-of-emails/maildir/bsd/{rhost-googleapps-02.eml → rhost-google-02.eml} +0 -0
  175. /data/set-of-emails/maildir/bsd/{rhost-exchangeonline-01.eml → rhost-microsoft-01.eml} +0 -0
  176. /data/set-of-emails/maildir/bsd/{rhost-exchangeonline-02.eml → rhost-microsoft-02.eml} +0 -0
  177. /data/set-of-emails/maildir/bsd/{rhost-exchangeonline-03.eml → rhost-microsoft-03.eml} +0 -0
  178. /data/set-of-emails/maildir/bsd/{rhost-tencentqq-01.eml → rhost-tencent-01.eml} +0 -0
  179. /data/set-of-emails/maildir/bsd/{rhost-tencentqq-02.eml → rhost-tencent-02.eml} +0 -0
  180. /data/set-of-emails/maildir/bsd/{rhost-tencentqq-03.eml → rhost-tencent-03.eml} +0 -0
@@ -1,13 +1,15 @@
1
1
  module Sisimai::Lhost
2
- # Sisimai::Lhost::Sendmail parses a bounce email which created by
3
- # v8 Sendmail. Methods in the module are called from only Sisimai::Message.
2
+ # Sisimai::Lhost::Sendmail parses a bounce email which created by v8 Sendmail. Methods in the module
3
+ # are called from only Sisimai::Message.
4
4
  module Sendmail
5
5
  class << self
6
- # Imported from p5-Sisimail/lib/Sisimai/Lhost/Sendmail.pm
7
6
  require 'sisimai/lhost'
7
+ require 'sisimai/smtp/reply'
8
+ require 'sisimai/smtp/status'
9
+ require 'sisimai/smtp/command'
8
10
 
9
11
  Indicators = Sisimai::Lhost.INDICATORS
10
- ReBackbone = %r<^Content-Type:[ ](?:message/rfc822|text/rfc822-headers)>.freeze
12
+ Boundaries = ['Content-Type: message/rfc822', 'Content-Type: text/rfc822-headers'].freeze
11
13
  StartingOf = {
12
14
  # Error text regular expressions which defined in sendmail/savemail.c
13
15
  # savemail.c:1040|if (printheader && !putline(" ----- Transcript of session follows -----\n",
@@ -26,29 +28,30 @@ module Sisimai::Lhost
26
28
  # @param [String] mbody Message body of a bounce email
27
29
  # @return [Hash] Bounce data list and message/rfc822 part
28
30
  # @return [Nil] it failed to parse or the arguments are missing
29
- def make(mhead, mbody)
30
- return nil unless mhead['subject'] =~ /(?:see transcript for details\z|\AWarning: )/
31
+ def inquire(mhead, mbody)
31
32
  return nil if mhead['x-aol-ip']
33
+ match = nil
34
+ match ||= true if mhead['subject'].end_with?('see transcript for details')
35
+ match ||= true if mhead['subject'].start_with?('Warning: ')
36
+ return nil unless match
32
37
 
33
- require 'sisimai/rfc1894'
34
38
  fieldtable = Sisimai::RFC1894.FIELDTABLE
35
39
  permessage = {} # (Hash) Store values of each Per-Message field
36
-
37
40
  dscontents = [Sisimai::Lhost.DELIVERYSTATUS]
38
- emailsteak = Sisimai::RFC5322.fillet(mbody, ReBackbone)
39
- bodyslices = emailsteak[0].split("\n")
41
+ emailparts = Sisimai::RFC5322.part(mbody, Boundaries)
42
+ bodyslices = emailparts[0].split("\n")
40
43
  readslices = ['']
41
44
  readcursor = 0 # (Integer) Points the current cursor position
42
45
  recipients = 0 # (Integer) The number of 'Final-Recipient' header
43
- commandtxt = '' # (String) SMTP Command name begin with the string '>>>'
46
+ thecommand = '' # (String) SMTP Command name begin with the string '>>>'
44
47
  esmtpreply = [] # (Array) Reply from remote server on SMTP session
45
48
  sessionerr = false # (Boolean) Flag, "true" if it is SMTP session error
46
49
  anotherset = {} # Another error information
47
50
  v = nil
48
51
 
49
52
  while e = bodyslices.shift do
50
- # Read error messages and delivery status lines from the head of the email
51
- # to the previous line of the beginning of the original message.
53
+ # Read error messages and delivery status lines from the head of the email to the previous
54
+ # line of the beginning of the original message.
52
55
  readslices << e # Save the current line for the next loop
53
56
 
54
57
  if readcursor == 0
@@ -89,7 +92,7 @@ module Sisimai::Lhost
89
92
  next unless fieldtable[o[0]]
90
93
  v[fieldtable[o[0]]] = o[2]
91
94
 
92
- next unless f == 1
95
+ next unless f
93
96
  permessage[fieldtable[o[0]]] = o[2]
94
97
  end
95
98
  else
@@ -105,13 +108,14 @@ module Sisimai::Lhost
105
108
  # Received-From-MTA: DNS; x1x2x3x4.dhcp.example.ne.jp
106
109
  # Arrival-Date: Wed, 29 Apr 2009 16:03:18 +0900
107
110
  unless e.start_with?(' ')
108
- if cv = e.match(/\A[>]{3}[ ]+([A-Z]{4})[ ]?/)
111
+ if e.start_with?('>>> ')
109
112
  # >>> DATA
110
- commandtxt = cv[1]
113
+ thecommand = Sisimai::SMTP::Command.find(e)
111
114
 
112
- elsif cv = e.match(/\A[<]{3}[ ]+(.+)\z/)
115
+ elsif e.start_with?('<<< ')
113
116
  # <<< Response
114
- esmtpreply << cv[1] unless esmtpreply.index(cv[1])
117
+ cv = e[4, e.size - 4]
118
+ esmtpreply << cv unless esmtpreply.index(cv)
115
119
  else
116
120
  # Detect SMTP session error or connection error
117
121
  next if sessionerr
@@ -122,21 +126,23 @@ module Sisimai::Lhost
122
126
  next
123
127
  end
124
128
 
125
- if cv = e.match(/\A[<](.+)[>][.]+ (.+)\z/)
129
+ if e.start_with?('<') && Sisimai::String.aligned(e, ['@', '>.', ' '])
126
130
  # <kijitora@example.co.jp>... Deferred: Name server: example.co.jp.: host name lookup failure
127
- anotherset['recipient'] = cv[1]
128
- anotherset['diagnosis'] = cv[2]
131
+ anotherset['recipient'] = Sisimai::Address.s3s4(e[0, e.index('>')])
132
+ anotherset['diagnosis'] = e[e.index(' ') + 1, e.size]
129
133
  else
130
134
  # ----- Transcript of session follows -----
131
135
  # Message could not be delivered for too long
132
136
  # Message will be deleted from queue
133
- next if e =~ /\A[ \t]*[-]+/
134
- if cv = e.match(/\A[45]\d\d[ \t]([45][.]\d[.]\d)[ \t].+/)
137
+ cr = Sisimai::SMTP::Reply.find(e) || ''
138
+ cs = Sisimai::SMTP::Status.find(e) || ''
139
+
140
+ if cr.size + cs.size > 7
135
141
  # 550 5.1.2 <kijitora@example.org>... Message
136
142
  #
137
143
  # DBI connect('dbname=...')
138
144
  # 554 5.3.0 unknown mailer error 255
139
- anotherset['status'] = cv[1]
145
+ anotherset['status'] = cs
140
146
  anotherset['diagnosis'] ||= ''
141
147
  anotherset['diagnosis'] << ' ' << e
142
148
 
@@ -151,8 +157,8 @@ module Sisimai::Lhost
151
157
  else
152
158
  # Continued line of the value of Diagnostic-Code field
153
159
  next unless readslices[-2].start_with?('Diagnostic-Code:')
154
- next unless cv = e.match(/\A[ \t]+(.+)\z/)
155
- v['diagnosis'] << ' ' << cv[1]
160
+ next unless e.start_with?(' ')
161
+ v['diagnosis'] << ' ' << Sisimai::String.sweep(e)
156
162
  readslices[-1] = 'Diagnostic-Code: ' << e
157
163
  end
158
164
  end
@@ -161,47 +167,53 @@ module Sisimai::Lhost
161
167
 
162
168
  dscontents.each do |e|
163
169
  # Set default values if each value is empty.
164
- e['lhost'] ||= permessage['rhost']
170
+ e['diagnosis'] ||= ''
165
171
  permessage.each_key { |a| e[a] ||= permessage[a] || '' }
166
172
 
167
- e['command'] ||= commandtxt
168
- if e['command'].empty?
169
- e['command'] = 'EHLO' unless esmtpreply.empty?
170
- end
171
-
172
173
  if anotherset['diagnosis']
173
174
  # Copy alternative error message
174
- e['diagnosis'] = anotherset['diagnosis'] if e['diagnosis'] =~ /\A[ \t]+\z/
175
- e['diagnosis'] = anotherset['diagnosis'] unless e['diagnosis']
176
- e['diagnosis'] = anotherset['diagnosis'] if e['diagnosis'] =~ /\A\d+\z/
175
+ e['diagnosis'] = anotherset['diagnosis'] if e['diagnosis'].start_with?(' ')
176
+ e['diagnosis'] = anotherset['diagnosis'] if e['diagnosis'].=~ /\A\d+\z/
177
+ e['diagnosis'] = anotherset['diagnosis'] if e['diagnosis'].empty?
177
178
  end
178
- unless esmtpreply.empty?
179
- # Replace the error message in "diagnosis" with the ESMTP Reply
180
- r = esmtpreply.join(' ')
181
- e['diagnosis'] = r if r.size > e['diagnosis'].to_s.size
179
+
180
+ while true
181
+ # Replace or append the error message in "diagnosis" with the ESMTP Reply Code when the
182
+ # following conditions have matched
183
+ break if esmtpreply.empty?
184
+ break if recipients != 1
185
+
186
+ e['diagnosis'] = sprintf("%s %s", esmtpreply.join(' '), e['diagnosis'])
187
+ break
182
188
  end
183
- e['diagnosis'] = Sisimai::String.sweep(e['diagnosis'])
184
189
 
185
- if anotherset['status']
186
- # Check alternative status code
187
- if e['status'].empty? || e['status'] !~ /\A[45][.]\d[.]\d{1,3}\z/
188
- # Override alternative status code
189
- e['status'] = anotherset['status']
190
- end
190
+ e['diagnosis'] = Sisimai::String.sweep(e['diagnosis'])
191
+ e['command'] ||= thecommand || Sisimai::SMTP::Command.find(e['diagnosis']) || ''
192
+ if e['command'].empty?
193
+ e['command'] = 'EHLO' unless esmtpreply.empty?
191
194
  end
192
195
 
193
- unless e['recipient'] =~ /\A[^ ]+[@][^ ]+\z/
194
- # @example.jp, no local part
195
- if cv = e['diagnosis'].match(/[<]([^ ]+[@][^ ]+)[>]/)
196
- # Get email address from the value of Diagnostic-Code header
197
- e['recipient'] = cv[1]
198
- end
196
+ while true
197
+ # Check alternative status code and override it
198
+ break unless anotherset.has_key?('status')
199
+ break unless anotherset['status'].size > 0
200
+ break if Sisimai::SMTP::Status.test(e['status'])
201
+
202
+ e['status'] = anotherset['status']
203
+ break
199
204
  end
205
+
206
+ # @example.jp, no local part
207
+ # # Get email address from the value of Diagnostic-Code field
208
+ next unless e['recipient'].start_with?('@')
209
+ cv = Sisimai::Address.find(e['diagnosis'], true) || []
210
+ e['recipient'] = cv[0][:address] if cv.size > 0
200
211
  end
201
212
 
202
- return { 'ds' => dscontents, 'rfc822' => emailsteak[1] }
213
+ return { 'ds' => dscontents, 'rfc822' => emailparts[1] }
203
214
  end
204
215
  def description; return 'V8Sendmail: /usr/sbin/sendmail'; end
205
216
  end
206
217
  end
207
218
  end
219
+
@@ -1,14 +1,12 @@
1
1
  module Sisimai::Lhost
2
- # Sisimai::Lhost::SurfControl parses a bounce email which created by
3
- # WebSense SurfControl.
2
+ # Sisimai::Lhost::SurfControl parses a bounce email which created by WebSense SurfControl.
4
3
  # Methods in the module are called from only Sisimai::Message.
5
4
  module SurfControl
6
5
  class << self
7
- # Imported from p5-Sisimail/lib/Sisimai/Lhost/SurfControl.pm
8
6
  require 'sisimai/lhost'
9
7
 
10
8
  Indicators = Sisimai::Lhost.INDICATORS
11
- ReBackbone = %r|^Content-Type:[ ]message/rfc822|.freeze
9
+ Boundaries = ['Content-Type: message/rfc822'].freeze
12
10
  StartingOf = { message: ['Your message could not be sent.'] }.freeze
13
11
 
14
12
  # Parse bounce messages from SurfControl
@@ -16,7 +14,7 @@ module Sisimai::Lhost
16
14
  # @param [String] mbody Message body of a bounce email
17
15
  # @return [Hash] Bounce data list and message/rfc822 part
18
16
  # @return [Nil] it failed to parse or the arguments are missing
19
- def make(mhead, mbody)
17
+ def inquire(mhead, mbody)
20
18
  # X-SEF-ZeroHour-RefID: fgs=000000000
21
19
  # X-SEF-Processed: 0_0_0_000__2010_04_29_23_34_45
22
20
  # X-Mailer: SurfControl E-mail Filter
@@ -24,19 +22,18 @@ module Sisimai::Lhost
24
22
  return nil unless mhead['x-mailer']
25
23
  return nil unless mhead['x-mailer'] == 'SurfControl E-mail Filter'
26
24
 
27
- require 'sisimai/rfc1894'
28
25
  fieldtable = Sisimai::RFC1894.FIELDTABLE
29
26
  dscontents = [Sisimai::Lhost.DELIVERYSTATUS]
30
- emailsteak = Sisimai::RFC5322.fillet(mbody, ReBackbone)
31
- bodyslices = emailsteak[0].split("\n")
27
+ emailparts = Sisimai::RFC5322.part(mbody, Boundaries)
28
+ bodyslices = emailparts[0].split("\n")
32
29
  readslices = ['']
33
30
  readcursor = 0 # (Integer) Points the current cursor position
34
31
  recipients = 0 # (Integer) The number of 'Final-Recipient' header
35
32
  v = nil
36
33
 
37
34
  while e = bodyslices.shift do
38
- # Read error messages and delivery status lines from the head of the email
39
- # to the previous line of the beginning of the original message.
35
+ # Read error messages and delivery status lines from the head of the email to the previous
36
+ # line of the beginning of the original message.
40
37
  readslices << e # Save the current line for the next loop
41
38
 
42
39
  if readcursor == 0
@@ -58,24 +55,27 @@ module Sisimai::Lhost
58
55
  # --- Message non-deliverable.
59
56
  v = dscontents[-1]
60
57
 
61
- if cv = e.match(/\AAddressed To:[ ]*([^ ]+?[@][^ ]+?)\z/)
58
+ if e.start_with?('Addressed To:') && e.index('@') > 1
62
59
  # Addressed To: kijitora@example.com
63
60
  if v['recipient']
64
61
  # There are multiple recipient addresses in the message body.
65
62
  dscontents << Sisimai::Lhost.DELIVERYSTATUS
66
63
  v = dscontents[-1]
67
64
  end
68
- v['recipient'] = cv[1]
65
+ v['recipient'] = Sisimai::Address.s3s4(e[e.index(':') + 2, e.size])
69
66
  recipients += 1
70
67
 
71
- elsif e =~ /\A(?:Sun|Mon|Tue|Wed|Thu|Fri|Sat)[ ,]/
68
+ elsif %w[Sun Mon Tue Wed Thu Fri Sat].any? { |a| e.start_with?(a) }
72
69
  # Thu 29 Apr 2010 23:34:45 +0900
73
70
  v['date'] = e
74
71
 
75
- elsif cv = e.match(/\A[^ ]+[@][^ ]+:[ ]*\[(\d+[.]\d+[.]\d+[.]\d)\],[ ]*(.+)\z/)
72
+ elsif Sisimai::String.aligned(e, ['@', ':', ' ', '[', '],', '...'])
76
73
  # kijitora@example.com: [192.0.2.5], 550 kijitora@example.com... No such user
77
- v['rhost'] = cv[1]
78
- v['diagnosis'] = cv[2]
74
+ p1 = e.index('[')
75
+ p2 = e.index('],', p1 + 1)
76
+ v['rhost'] = e[p1 + 1, p2 - p1 - 1]
77
+ v['diagnosis'] = Sisimai::String.sweep(e[p2 + 2, e.size])
78
+
79
79
  else
80
80
  # Fallback, parse RFC3464 headers.
81
81
  if f = Sisimai::RFC1894.match(e)
@@ -87,8 +87,8 @@ module Sisimai::Lhost
87
87
  else
88
88
  # Continued line of the value of Diagnostic-Code field
89
89
  next unless readslices[-2].start_with?('Diagnostic-Code:')
90
- next unless cv = e.match(/\A[ \t]+(.+)\z/)
91
- v['diagnosis'] << ' ' << cv[1]
90
+ next unless e.start_with?(' ')
91
+ v['diagnosis'] << ' ' << Sisimai::String.sweep(e)
92
92
  readslices[-1] = 'Diagnostic-Code: ' << e
93
93
  end
94
94
  end
@@ -96,7 +96,7 @@ module Sisimai::Lhost
96
96
  return nil unless recipients > 0
97
97
 
98
98
  dscontents.each { |e| e['diagnosis'] = Sisimai::String.sweep(e['diagnosis']) }
99
- return { 'ds' => dscontents, 'rfc822' => emailsteak[1] }
99
+ return { 'ds' => dscontents, 'rfc822' => emailparts[1] }
100
100
  end
101
101
  def description; return 'WebSense SurfControl'; end
102
102
  end
@@ -1,16 +1,13 @@
1
1
  module Sisimai::Lhost
2
- # Sisimai::Lhost::V5sendmail parses a bounce email which created by
3
- # Sendmail version 5.
4
- # Methods in the module are called from only Sisimai::Message.
2
+ # Sisimai::Lhost::V5sendmail parses a bounce email which created by Sendmail version 5. Methods in
3
+ # the module are called from only Sisimai::Message.
5
4
  module V5sendmail
6
5
  class << self
7
- # Imported from p5-Sisimail/lib/Sisimai/Lhost/V5sendmail.pm
8
6
  require 'sisimai/lhost'
9
7
 
10
8
  Indicators = Sisimai::Lhost.INDICATORS
11
- ReBackbone = %r/^[ ]+-----[ ](?:Unsent[ ]message[ ]follows|No[ ]message[ ]was[ ]collected)[ ]-----/.freeze
12
- StartingOf = { message: ['----- Transcript of session follows -----'] }
13
- MarkingsOf = {
9
+ Boundaries = [' ----- Unsent message follows -----', ' ----- No message was collected -----'].freeze
10
+ StartingOf = {
14
11
  # Error text regular expressions which defined in src/savemail.c
15
12
  # savemail.c:485| (void) fflush(stdout);
16
13
  # savemail.c:486| p = queuename(e->e_parent, 'x');
@@ -27,7 +24,8 @@ module Sisimai::Lhost
27
24
  # savemail.c:497| while (fgets(buf, sizeof buf, xfile) != NULL)
28
25
  # savemail.c:498| putline(buf, fp, m);
29
26
  # savemail.c:499| (void) fclose(xfile);
30
- error: %r/\A[.]+ while talking to .+[:]\z/,
27
+ error: [' while talking to '],
28
+ message: ['----- Transcript of session follows -----'],
31
29
  }.freeze
32
30
 
33
31
  # Parse bounce messages from Sendmail version 5
@@ -35,26 +33,27 @@ module Sisimai::Lhost
35
33
  # @param [String] mbody Message body of a bounce email
36
34
  # @return [Hash] Bounce data list and message/rfc822 part
37
35
  # @return [Nil] it failed to parse or the arguments are missing
38
- def make(mhead, mbody)
36
+ def inquire(mhead, mbody)
39
37
  # :from => %r/\AMail Delivery Subsystem/,
40
- return nil unless mhead['subject'] =~ /\AReturned mail: [A-Z]/
38
+ return nil unless mhead['subject'].start_with?('Returned mail: ')
41
39
 
42
- emailsteak = Sisimai::RFC5322.fillet(mbody, ReBackbone)
43
- return nil if emailsteak[1].empty?
40
+ emailparts = Sisimai::RFC5322.part(mbody, Boundaries)
41
+ return nil unless emailparts[1].size > 0
44
42
 
43
+ require 'sisimai/smtp/command'
45
44
  dscontents = [Sisimai::Lhost.DELIVERYSTATUS]
46
- bodyslices = emailsteak[0].split("\n")
45
+ bodyslices = emailparts[0].split("\n")
47
46
  readcursor = 0 # (Integer) Points the current cursor position
48
47
  recipients = 0 # (Integer) The number of 'Final-Recipient' header
48
+ anotherset = {} # (Hash) Another error information
49
49
  responding = [] # (Array) Responses from remote server
50
50
  commandset = [] # (Array) SMTP command which is sent to remote server
51
- anotherset = {} # (Hash) Another error information
52
51
  errorindex = -1 # (Integer)
53
52
  v = nil
54
53
 
55
54
  while e = bodyslices.shift do
56
- # Read error messages and delivery status lines from the head of the email
57
- # to the previous line of the beginning of the original message.
55
+ # Read error messages and delivery status lines from the head of the email to the previous
56
+ # line of the beginning of the original message.
58
57
  if readcursor == 0
59
58
  # Beginning of the bounce message or delivery status part
60
59
  readcursor |= Indicators[:deliverystatus] if e.include?(StartingOf[:message][0])
@@ -71,57 +70,64 @@ module Sisimai::Lhost
71
70
  # 421 example.org (smtp)... Deferred: Connection timed out during user open with example.org
72
71
  v = dscontents[-1]
73
72
 
74
- if cv = e.match(/\A\d{3}[ ]+[<]([^ ]+[@][^ ]+)[>][.]{3}[ ]*(.+)\z/)
73
+ if e.start_with?('5', '4') && Sisimai::String.aligned(e, [' <', '@', '>...'])
75
74
  # 550 <kijitora@example.org>... User unknown
76
75
  if v['recipient']
77
76
  # There are multiple recipient addresses in the message body.
78
77
  dscontents << Sisimai::Lhost.DELIVERYSTATUS
79
78
  v = dscontents[-1]
80
79
  end
81
- v['recipient'] = cv[1]
82
- v['diagnosis'] = cv[2]
80
+ p1 = e.index('<', 0)
81
+ p2 = e.index('>...')
82
+ v['recipient'] = e[p1 + 1, p2 - p1 - 1]
83
+ v['diagnosis'] = e[p2 + 5, e.size]
83
84
 
84
85
  # Concatenate the response of the server and error message
85
86
  v['diagnosis'] << ': ' << responding[recipients] if responding[recipients]
86
87
  recipients += 1
87
88
 
88
- elsif cv = e.match(/\A[>]{3}[ ]*([A-Z]{4})[ ]*/)
89
+ elsif e.start_with?('>>> ')
89
90
  # >>> RCPT To:<kijitora@example.org>
90
- commandset[recipients] = cv[1]
91
+ cv = Sisimai::SMTP::Command.find(e); commandset[recipients] = cv if cv
91
92
 
92
- elsif cv = e.match(/\A[<]{3}[ ]+(.+)\z/)
93
+ elsif e.start_with?('<<< ')
93
94
  # <<< Response
94
95
  # <<< 501 <shironeko@example.co.jp>... no access from mail server [192.0.2.55] which is an open relay.
95
96
  # <<< 550 Requested User Mailbox not found. No such user here.
96
- responding[recipients] = cv[1]
97
+ responding[recipients] = e[4, e.size]
98
+
97
99
  else
98
100
  # Detect SMTP session error or connection error
99
101
  next if v['sessionerr']
100
102
 
101
- if e =~ MarkingsOf[:error]
103
+ if e.include?(StartingOf[:error][0])
102
104
  # ----- Transcript of session follows -----
103
105
  # ... while talking to mta.example.org.:
104
106
  v['sessionerr'] = true
105
107
  next
106
108
  end
107
109
 
108
- if cv = e.match(/\A\d{3}[ ]+.+[.]{3}[ \t]*(.+)\z/)
110
+ if e.start_with?('4', '5') && e.include?('... ')
109
111
  # 421 example.org (smtp)... Deferred: Connection timed out during user open with example.org
110
- anotherset['diagnosis'] = cv[1]
112
+ anotherset['replycode'] = e[0, 3]
113
+ anotherset['diagnosis'] = e[e.index('... ') + 4, e.size]
111
114
  end
112
115
  end
113
116
  end
114
117
 
115
- if recipients == 0 && cv = emailsteak[1].match(/^To:[ ]*(.+)$/)
118
+ p1 = emailparts[1].index("\nTo: ") || -1
119
+ p2 = emailparts[1].index("\n", p1 + 6) || -1
120
+ if recipients == 0 && p1 > 0
116
121
  # Get the recipient address from "To:" header at the original message
117
- dscontents[0]['recipient'] = Sisimai::Address.s3s4(cv[1])
122
+ dscontents[0]['recipient'] = Sisimai::Address.s3s4(emailparts[1][p1, p2 - p1 - 5])
118
123
  recipients = 1
119
124
  end
120
125
  return nil unless recipients > 0
121
126
 
122
127
  dscontents.each do |e|
123
128
  errorindex += 1
124
- e['command'] = commandset[errorindex] || ''
129
+ e.delete('sessionerr')
130
+
125
131
  e['diagnosis'] ||= if anotherset['diagnosis'].to_s.size > 0
126
132
  # Copy alternative error message
127
133
  anotherset['diagnosis']
@@ -130,18 +136,18 @@ module Sisimai::Lhost
130
136
  responding[errorindex]
131
137
  end
132
138
  e['diagnosis'] = Sisimai::String.sweep(e['diagnosis'])
133
-
134
- unless e['recipient'] =~ /\A[^ ]+[@][^ ]+\z/
135
- # @example.jp, no local part
136
- if cv = e['diagnosis'].match(/[<]([^ ]+[@][^ ]+)[>]/)
137
- # Get email address from the value of Diagnostic-Code header
138
- e['recipient'] = cv[1]
139
- end
140
- end
141
- e.delete('sessionerr')
139
+ e['replycode'] = Sisimai::SMTP::Reply.find(e['diagnosis']) || anotherset['replycode']
140
+ e['command'] = commandset[errorindex] || Sisimai::SMTP::Command.find(e['diagnosis']) || ''
141
+
142
+ # @example.jp, no local part
143
+ # Get email address from the value of Diagnostic-Code header
144
+ next if e['recipient'].include?('@')
145
+ p1 = e['diagnosis'].index('<'); next unless p1
146
+ p2 = e['diagnosis'].index('>'); next unless p2
147
+ e['recipient'] = Sisimai::Address.s3s4(e[p1, p2 - p1])
142
148
  end
143
149
 
144
- return { 'ds' => dscontents, 'rfc822' => emailsteak[1] }
150
+ return { 'ds' => dscontents, 'rfc822' => emailparts[1] }
145
151
  end
146
152
  def description; return 'Sendmail version 5'; end
147
153
  end