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,24 +1,21 @@
1
1
  module Sisimai::Lhost
2
- # Sisimai::Lhost::Exim parses a bounce email which created by Exim.
3
- # Methods in the module are called from only Sisimai::Message.
2
+ # Sisimai::Lhost::Exim parses a bounce email which created by Exim. Methods in the module are called
3
+ # from only Sisimai::Message.
4
4
  module Exim
5
5
  class << self
6
- # Imported from p5-Sisimail/lib/Sisimai/Lhost/Exim.pm
7
6
  require 'sisimai/lhost'
8
7
 
9
8
  Indicators = Sisimai::Lhost.INDICATORS
10
- # deliver.c:6423| if (bounce_return_body) fprintf(f,
11
- # deliver.c:6424|"------ This is a copy of the message, including all the headers. ------\n");
12
- # deliver.c:6425| else fprintf(f,
13
- # deliver.c:6426|"------ This is a copy of the message's headers. ------\n");
14
- ReBackbone = %r{^(?:
15
- [-]+[ ]This[ ]is[ ]a[ ]copy[ ]of[ ](?:the|your)[ ]message,[ ]including[ ]all[ ]the[ ]headers[.][ ][-]+
16
- |Content-Type:[ ]*message/rfc822\n(?:[\s\t]+.*?\n\n)?
17
- )
18
- }x.freeze
19
- StartingOf = { deliverystatus: ['Content-type: message/delivery-status'] }.freeze
20
- MarkingsOf = {
21
- # Error text regular expressions which defined in exim/src/deliver.c
9
+ Boundaries = [
10
+ # deliver.c:6423| if (bounce_return_body) fprintf(f,
11
+ # deliver.c:6424|"------ This is a copy of the message, including all the headers. ------\n");
12
+ # deliver.c:6425| else fprintf(f,
13
+ # deliver.c:6426|"------ This is a copy of the message's headers. ------\n");
14
+ '------ This is a copy of the message, including all the headers. ------',
15
+ 'Content-Type: message/rfc822',
16
+ ].freeze
17
+ StartingOf = {
18
+ # Error text strings which defined in exim/src/deliver.c
22
19
  #
23
20
  # deliver.c:6292| fprintf(f,
24
21
  # deliver.c:6293|"This message was created automatically by mail delivery software.\n");
@@ -35,19 +32,19 @@ module Sisimai::Lhost
35
32
  # deliver.c:6304|"could not be delivered to one or more of its recipients. The following\n"
36
33
  # deliver.c:6305|"address(es) failed:\n", sender_address);
37
34
  # deliver.c:6306| }
38
- alias: %r/\A([ ]+an undisclosed address)\z/,
39
- frozen: %r/\AMessage [^ ]+ (?:has been frozen|was frozen on arrival)/,
40
- message: %r{\A(?>
41
- This[ ]message[ ]was[ ]created[ ]automatically[ ]by[ ]mail[ ]delivery[ ]software[.]
42
- |A[ ]message[ ]that[ ]you[ ]sent[ ]was[ ]rejected[ ]by[ ]the[ ]local[ ]scanning[ ]code
43
- |A[ ]message[ ]that[ ]you[ ]sent[ ]contained[ ]one[ ]or[ ]more[ ]recipient[ ]addresses[ ]
44
- |A[ ]message[ ]that[ ]you[ ]sent[ ]could[ ]not[ ]be[ ]delivered[ ]to[ ]all[ ]of[ ]its[ ]recipients
45
- |Message[ ][^ ]+[ ](?:has[ ]been[ ]frozen|was[ ]frozen[ ]on[ ]arrival)
46
- |The[ ][^ ]+[ ]router[ ]encountered[ ]the[ ]following[ ]error[(]s[)]:
47
- )
48
- }x,
35
+ deliverystatus: ['Content-Type: message/delivery-status'],
36
+ frozen: [' has been frozen', ' was frozen on arrival'],
37
+ message: [
38
+ 'This message was created automatically by mail delivery software.',
39
+ 'A message that you sent was rejected by the local scannning code',
40
+ 'A message that you sent contained one or more recipient addresses ',
41
+ 'A message that you sent could not be delivered to all of its recipients',
42
+ ' has been frozen',
43
+ ' was frozen on arrival',
44
+ ' router encountered the following error(s):',
45
+ ],
49
46
  }.freeze
50
-
47
+ MarkingsOf = { alias: ' an undisclosed address' }.freeze
51
48
  ReCommands = [
52
49
  # transports/smtp.c:564| *message = US string_sprintf("SMTP error from remote mail server after %s%s: "
53
50
  # transports/smtp.c:837| string_sprintf("SMTP error from remote mail server after RCPT TO:<%s>: "
@@ -125,51 +122,47 @@ module Sisimai::Lhost
125
122
  # @param [String] mbody Message body of a bounce email
126
123
  # @return [Hash] Bounce data list and message/rfc822 part
127
124
  # @return [Nil] it failed to parse or the arguments are missing
128
- def make(mhead, mbody)
129
- return nil if mhead['from'] =~ /[@].+[.]mail[.]ru[>]?/
125
+ def inquire(mhead, mbody)
126
+ return nil if mhead['from'].include?('.mail.ru')
130
127
 
131
128
  # Message-Id: <E1P1YNN-0003AD-Ga@example.org>
132
129
  # X-Failed-Recipients: kijitora@example.ed.jp
133
130
  match = 0
131
+ msgid = mhead['message-id'] || ''
134
132
  match += 1 if mhead['from'].start_with?('Mail Delivery System')
135
- match += 1 if mhead['message-id'].to_s =~ %r/\A[<]\w{7}[-]\w{6}[-]\w{2}[@]/
136
- match += 1 if mhead['subject'] =~ %r{(?:
137
- Mail[ ]delivery[ ]failed(:[ ]returning[ ]message[ ]to[ ]sender)?
138
- |Warning:[ ]message[ ][^ ]+[ ]delayed[ ]+
139
- |Delivery[ ]Status[ ]Notification
140
- |Mail[ ]failure
141
- |Message[ ]frozen
142
- |error[(]s[)][ ]in[ ]forwarding[ ]or[ ]filtering
143
- )
144
- }x
133
+ match += 1 if msgid.start_with?('<') && msgid.index('-') == 8 && msgid.index('@') == 18
134
+ match += 1 if %w[
135
+ 'Delivery Status Notification',
136
+ 'Mail delivery failed',
137
+ 'Mail failure',
138
+ 'Message frozen',
139
+ 'Warning: message ',
140
+ 'error(s) in forwarding or filtering'].any? { |a| mhead['subject'].include?(a) }
145
141
  return nil if match < 2
146
142
 
147
- require 'sisimai/rfc1894'
148
143
  fieldtable = Sisimai::RFC1894.FIELDTABLE
149
144
  dscontents = [Sisimai::Lhost.DELIVERYSTATUS]
150
- emailsteak = Sisimai::RFC5322.fillet(mbody, ReBackbone)
151
- bodyslices = emailsteak[0].split("\n")
145
+ emailparts = Sisimai::RFC5322.part(mbody, Boundaries)
146
+ bodyslices = emailparts[0].split("\n")
152
147
  readcursor = 0 # (Integer) Points the current cursor position
153
148
  nextcursor = 0
154
149
  recipients = 0 # (Integer) The number of 'Final-Recipient' header
155
- localhost0 = '' # (String) Local MTA
156
150
  boundary00 = '' # (String) Boundary string
157
151
  v = nil
158
152
 
159
153
  if mhead['content-type']
160
- # Get the boundary string and set regular expression for matching with
161
- # the boundary string.
162
- boundary00 = Sisimai::MIME.boundary(mhead['content-type']) || ''
154
+ # Get the boundary string and set regular expression for matching with the boundary string.
155
+ boundary00 = Sisimai::RFC2045.boundary(mhead['content-type']) || ''
163
156
  end
164
157
 
165
158
  while e = bodyslices.shift do
166
- # Read error messages and delivery status lines from the head of the email
167
- # to the previous line of the beginning of the original message.
159
+ # Read error messages and delivery status lines from the head of the email to the previous
160
+ # line of the beginning of the original message.
168
161
  if readcursor == 0
169
162
  # Beginning of the bounce message or message/delivery-status part
170
- if e =~ MarkingsOf[:message]
163
+ if StartingOf[:message].any? { |a| e.include?(a) }
171
164
  readcursor |= Indicators[:deliverystatus]
172
- next unless e =~ MarkingsOf[:frozen]
165
+ next unless StartingOf[:frozen].any? { |a| e.include?(a) }
173
166
  end
174
167
  end
175
168
  next if (readcursor & Indicators[:deliverystatus]) == 0
@@ -185,109 +178,130 @@ module Sisimai::Lhost
185
178
  # host neko.example.jp [192.0.2.222]: 550 5.1.1 <kijitora@example.jp>... User Unknown
186
179
  v = dscontents[-1]
187
180
 
188
- if cv = e.match(/\A[ \t]{2}([^ \t]+[@][^ \t]+[.]?[a-zA-Z]+)(:.+)?\z/) ||
189
- e.match(/\A[ \t]{2}[^ \t]+[@][^ \t]+[.][a-zA-Z]+[ ]<(.+?[@].+?)>:.+\z/) ||
190
- e.match(MarkingsOf[:alias])
191
- # kijitora@example.jp
192
- # sabineko@example.jp: forced freeze
193
- # mikeneko@example.jp <nekochan@example.org>: ...
194
- #
195
- # deliver.c:4549| printed = US"an undisclosed address";
196
- # an undisclosed address
197
- # (generated from kijitora@example.jp)
198
- r = cv[1]
181
+ cv = ''
182
+ ce = false
183
+ while true
184
+ # Check if the line matche the following patterns:
185
+ break unless e.start_with?(' ') # The line should start with " " (2 spaces)
186
+ break unless e.include?('@') # "@" should be included (email)
187
+ break unless e.include?('.') # "." should be included (domain part)
188
+ break unless e.include?('pipe to |') == false # Exclude "pipe to /path/to/prog" line
189
+
190
+ break unless e[2, 1] != ' '
191
+ break unless e[2, 1] != '<'
192
+ ce = true
193
+ break
194
+ end
199
195
 
196
+ if ce || e.include?(MarkingsOf[:alias])
197
+ # The line is including an email address
200
198
  if v['recipient']
201
199
  # There are multiple recipient addresses in the message body.
202
200
  dscontents << Sisimai::Lhost.DELIVERYSTATUS
203
201
  v = dscontents[-1]
204
202
  end
205
- # v['recipient'] = cv[1]
206
-
207
- if cv = e.match(/\A[ \t]+[^ \t]+[@][^ \t]+[.][a-zA-Z]+[ ]<(.+?[@].+?)>:.+\z/)
208
- # parser.c:743| while (bracket_count-- > 0) if (*s++ != '>')
209
- # parser.c:744| {
210
- # parser.c:745| *errorptr = s[-1] == 0
211
- # parser.c:746| ? US"'>' missing at end of address"
212
- # parser.c:747| : string_sprintf("malformed address: %.32s may not follow %.*s",
213
- # parser.c:748| s-1, (int)(s - US mailbox - 1), mailbox);
214
- # parser.c:749| goto PARSE_FAILED;
215
- # parser.c:750| }
216
- r = cv[1]
217
- v['diagnosis'] = e
203
+
204
+ if e.include?(MarkingsOf[:alias])
205
+ # The line does not include an email address
206
+ # deliver.c:4549| printed = US"an undisclosed address";
207
+ # an undisclosed address
208
+ # (generated from kijitora@example.jp)
209
+ cv = e[2, e.size]
210
+ else
211
+ # kijitora@example.jp
212
+ # sabineko@example.jp: forced freeze
213
+ # mikeneko@example.jp <nekochan@example.org>: ...
214
+ p1 = e.index(' <') || -1
215
+ p2 = e.index('>:') || -1
216
+
217
+ if p1 > 1 && p2 > 1
218
+ # There are an email address and an error message in the line
219
+ # parser.c:743| while (bracket_count-- > 0) if (*s++ != '>')
220
+ # parser.c:744| {
221
+ # parser.c:745| *errorptr = s[-1] == 0
222
+ # parser.c:746| ? US"'>' missing at end of address"
223
+ # parser.c:747| : string_sprintf("malformed address: %.32s may not follow %.*s",
224
+ # parser.c:748| s-1, (int)(s - US mailbox - 1), mailbox);
225
+ # parser.c:749| goto PARSE_FAILED;
226
+ # parser.c:750| }
227
+ cv = Sisimai::Address.s3s4(e[p1 + 1, p2 - p1 - 1])
228
+ v['diagnosis'] = Sisimai::String.sweep(e[p2 + 1, e.size])
229
+ else
230
+ # There is an email address only in the line
231
+ # kijitora@example.jp
232
+ cv = Sisimai::Address.s3s4(e[2, e.size])
233
+ end
218
234
  end
219
- v['recipient'] = r
235
+ v['recipient'] = cv
220
236
  recipients += 1
221
237
 
222
- elsif cv = e.match(/\A[ ]+[(]generated[ ]from[ ](.+)[)]\z/) ||
223
- e.match(/\A[ ]+generated[ ]by[ ]([^ \t]+[@][^ \t]+)/)
238
+ elsif e.include?(' (generated from ') || e.include?(' generated by ')
224
239
  # (generated from kijitora@example.jp)
225
240
  # pipe to |/bin/echo "Some pipe output"
226
241
  # generated by userx@myhost.test.ex
227
- v['alias'] = cv[1]
242
+ v['alias'] = Sisimai::Address.s3s4(e[e.rindex(' ') + 1, e.size])
243
+
228
244
  else
229
245
  next if e.empty?
230
246
 
231
- if e =~ MarkingsOf[:frozen]
247
+ if StartingOf[:frozen].any? { |a| e.include?(a) }
232
248
  # Message *** has been frozen by the system filter.
233
249
  # Message *** was frozen on arrival by ACL.
234
250
  v['alterrors'] ||= ''
235
251
  v['alterrors'] << e + ' '
236
- else
237
- if !boundary00.empty?
238
- # --NNNNNNNNNN-eximdsn-MMMMMMMMMM
239
- # Content-type: message/delivery-status
240
- # ...
241
- if Sisimai::RFC1894.match(e)
242
- # "e" matched with any field defined in RFC3464
243
- next unless o = Sisimai::RFC1894.field(e)
244
-
245
- if o[-1] == 'addr'
246
- # Final-Recipient: rfc822; kijitora@example.jp
247
- # X-Actual-Recipient: rfc822; kijitora@example.co.jp
248
- next unless o[0] == 'final-recipient'
249
- v['spec'] ||= o[2].include?('@') ? 'SMTP' : 'X-UNIX'
250
-
251
- elsif o[-1] == 'code'
252
- # Diagnostic-Code: SMTP; 550 5.1.1 <userunknown@example.jp>... User Unknown
253
- v['spec'] = o[1]
254
- v['diagnosis'] = o[2]
255
-
256
- else
257
- # Other DSN fields defined in RFC3464
258
- next unless fieldtable[o[0]]
259
- v[fieldtable[o[0]]] = o[2]
260
- end
252
+ elsif !boundary00.empty?
253
+ # --NNNNNNNNNN-eximdsn-MMMMMMMMMM
254
+ # Content-type: message/delivery-status
255
+ # ...
256
+ if Sisimai::RFC1894.match(e)
257
+ # "e" matched with any field defined in RFC3464
258
+ next unless o = Sisimai::RFC1894.field(e)
259
+
260
+ if o[-1] == 'addr'
261
+ # Final-Recipient: rfc822; kijitora@example.jp
262
+ # X-Actual-Recipient: rfc822; kijitora@example.co.jp
263
+ next unless o[0] == 'final-recipient'
264
+ v['spec'] ||= o[2].include?('@') ? 'SMTP' : 'X-UNIX'
265
+
266
+ elsif o[-1] == 'code'
267
+ # Diagnostic-Code: SMTP; 550 5.1.1 <userunknown@example.jp>... User Unknown
268
+ v['spec'] = o[1]
269
+ v['diagnosis'] = o[2]
270
+
261
271
  else
262
- # Error message ?
263
- next if nextcursor == 1
272
+ # Other DSN fields defined in RFC3464
273
+ next unless fieldtable[o[0]]
274
+ v[fieldtable[o[0]]] = o[2]
275
+ end
276
+ else
277
+ # Error message ?
278
+ next if nextcursor == 1
264
279
 
265
- # Content-type: message/delivery-status
266
- nextcursor = 1 if e.start_with?(StartingOf[:deliverystatus][0])
267
- v['alterrors'] ||= ''
268
- if e.start_with?("\s", "\t")
269
- e.sub!(/\A[\s\t]+/, '')
270
- v['alterrors'] << e + ' ' unless v['alterrors'].include?(e)
271
- end
280
+ # Content-type: message/delivery-status
281
+ nextcursor = 1 if e.start_with?(StartingOf[:deliverystatus][0])
282
+ v['alterrors'] ||= ''
283
+ if e.start_with?("\s", "\t")
284
+ e.sub!(/\A[\s\t]+/, '')
285
+ v['alterrors'] << e + ' ' unless v['alterrors'].include?(e)
272
286
  end
287
+ end
288
+ else
289
+ # There is no boundary string in $boundary00
290
+ if dscontents.size == recipients
291
+ # Error message
292
+ next if e.empty?
293
+ v['diagnosis'] ||= ''
294
+ v['diagnosis'] << e + ' '
273
295
  else
274
- if dscontents.size == recipients
275
- # Error message
276
- next if e.empty?
277
- v['diagnosis'] ||= ''
278
- v['diagnosis'] << e + ' '
296
+ # Error message when email address above does not include '@' and domain part.
297
+ if e.include?(' pipe to |/')
298
+ # pipe to |/path/to/prog ...
299
+ # generated by kijitora@example.com
300
+ v['diagnosis'] = e
279
301
  else
280
- # Error message when email address above does not include '@'
281
- # and domain part.
282
- if e =~ %r<\A[ ]+pipe[ ]to[ ][|]/[^ ]+>
283
- # pipe to |/path/to/prog ...
284
- # generated by kijitora@example.com
285
- v['diagnosis'] = e
286
- else
287
- next unless e.start_with?(' ')
288
- v['alterrors'] ||= ''
289
- v['alterrors'] << e + ' '
290
- end
302
+ next unless e.start_with?(' ')
303
+ v['alterrors'] ||= ''
304
+ v['alterrors'] << e + ' '
291
305
  end
292
306
  end
293
307
  end
@@ -327,16 +341,13 @@ module Sisimai::Lhost
327
341
  end
328
342
  return nil unless recipients > 0
329
343
 
330
- unless mhead['received'].empty?
331
- # Get the name of local MTA
332
- # Received: from marutamachi.example.org (c192128.example.net [192.0.2.128])
333
- if cv = mhead['received'][-1].match(/from[ \t]([^ ]+)/) then localhost0 = cv[1] end
334
- end
344
+ # Get the name of the local MTA
345
+ # Received: from marutamachi.example.org (c192128.example.net [192.0.2.128])
346
+ receivedby = mhead['received'] || []
347
+ recvdtoken = Sisimai::RFC5322.received(receivedby[-1])
335
348
 
336
349
  dscontents.each do |e|
337
- # Set default values if each value is empty.
338
- e['lhost'] ||= localhost0
339
-
350
+ # Check the error message, the rhost, the lhost, and the smtp command.
340
351
  unless e['diagnosis']
341
352
  # Empty Diagnostic-Code: or error message
342
353
  unless boundary00.empty?
@@ -371,31 +382,26 @@ module Sisimai::Lhost
371
382
  if e['diagnosis'].start_with?('-') || e['diagnosis'].end_with?('__')
372
383
  # Override the value of diagnostic code message
373
384
  e['diagnosis'] = e['alterrors'] unless e['alterrors'].empty?
374
- else
375
- # Check the both value and try to match
376
- if e['diagnosis'].size < e['alterrors'].size
377
- # Check the value of alterrors
378
- rxdiagnosis = %r/e['diagnosis']/i
379
- # Override the value of diagnostic code message because
380
- # the value of alterrors includes the value of diagnosis.
381
- e['diagnosis'] = e['alterrors'] if e['alterrors'].downcase.include?(e['diagnosis'].downcase)
382
- end
385
+
386
+ elsif e['diagnosis'].size < e['alterrors'].size
387
+ # Override the value of diagnostic code message because the value of alterrors includes
388
+ # the value of diagnosis.
389
+ e['diagnosis'] = e['alterrors'] if e['alterrors'].downcase.include?(e['diagnosis'].downcase)
383
390
  end
384
391
  e.delete('alterrors')
385
392
  end
386
- e['diagnosis'] = Sisimai::String.sweep(e['diagnosis']) || ''
387
- e['diagnosis'].sub!(/\b__.+\z/, '')
393
+ e['diagnosis'] = Sisimai::String.sweep(e['diagnosis']) || ''; p1 = e['diagnosis'].index('__') || -1
394
+ e['diagnosis'] = e['diagnosis'][0, p1] if p1 > 1
388
395
 
389
396
  unless e['rhost']
390
397
  # Get the remote host name
391
398
  # host neko.example.jp [192.0.2.222]: 550 5.1.1 <kijitora@example.jp>... User Unknown
392
- if cv = e['diagnosis'].match(/host[ \t]+([^ \t]+)[ \t]\[.+\]:[ \t]/) then e['rhost'] = cv[1] end
393
-
394
- unless e['rhost']
395
- # Get localhost and remote host name from Received header.
396
- e['rhost'] = Sisimai::RFC5322.received(mhead['received'][-1]).pop unless mhead['received'].empty?
397
- end
399
+ p1 = e['diagnosis'].index('host ') || -1
400
+ p2 = e['diagnosis'].index(' ', p1 + 5) || -1
401
+ e['rhost'] = e['diagnosis'][p1 + 5, p2 - p1 - 5] if p1 > -1
402
+ e['rhost'] ||= recvdtoken[1]
398
403
  end
404
+ e['lhost'] ||= recvdtoken[0]
399
405
 
400
406
  unless e['command']
401
407
  # Get the SMTP command name for the session
@@ -426,55 +432,54 @@ module Sisimai::Lhost
426
432
 
427
433
  unless e['reason']
428
434
  # The reason "expired"
429
- e['reason'] = 'expired' if DelayedFor.any? { |a| e['diagnosis'].include?(a) }
435
+ e['reason'] = 'expired' if DelayedFor.any? { |a| e['diagnosis'].include?(a) }
436
+ e['reason'] ||= 'mailererror' if e['diagnosis'].include?('pipe to |')
430
437
  end
431
438
  end
432
439
  end
433
440
 
434
441
  # Prefer the value of smtp reply code in Diagnostic-Code:
435
- # See eg/maildir-as-a-sample/new/exim-20.eml
436
442
  # Action: failed
437
443
  # Final-Recipient: rfc822;userx@test.ex
438
444
  # Status: 5.0.0
439
445
  # Remote-MTA: dns; 127.0.0.1
440
446
  # Diagnostic-Code: smtp; 450 TEMPERROR: retry timeout exceeded
441
- # The value of "Status:" indicates permanent error but the value
442
- # of SMTP reply code in Diagnostic-Code: field is "TEMPERROR"!!!!
443
- sv = e['status'] || Sisimai::SMTP::Status.find(e['diagnosis'])
444
- rv = e['replycode'] || Sisimai::SMTP::Reply.find(e['diagnosis'])
447
+ #
448
+ # The value of "Status:" indicates permanent error but the value of SMTP reply code in
449
+ # Diagnostic-Code: field is "TEMPERROR"!!!!
450
+ cs = e['status'] || Sisimai::SMTP::Status.find(e['diagnosis'])
451
+ cr = e['replycode'] || Sisimai::SMTP::Reply.find(e['diagnosis'])
445
452
  s1 = 0 # First character of Status as integer
446
453
  r1 = 0 # First character of SMTP reply code as integer
447
454
 
448
455
  while true
449
456
  # "Status:" field did not exist in the bounce message
450
- break if sv
451
- break unless rv
457
+ break if cs
458
+ break unless cr
452
459
 
453
- # Check SMTP reply code
454
- # Generate pseudo DSN code from SMTP reply code
455
- r1 = rv[0, 1].to_i
460
+ # Check SMTP reply code, Generate pseudo DSN code from SMTP reply code
461
+ r1 = cr[0, 1].to_i
456
462
  if r1 == 4
457
463
  # Get the internal DSN(temporary error)
458
- sv = Sisimai::SMTP::Status.code(e['reason'], true)
464
+ cs = Sisimai::SMTP::Status.code(e['reason'], true)
459
465
 
460
466
  elsif r1 == 5
461
467
  # Get the internal DSN(permanent error)
462
- sv = Sisimai::SMTP::Status.code(e['reason'], false)
468
+ cs = Sisimai::SMTP::Status.code(e['reason'], false)
463
469
  end
464
470
  break
465
471
  end
466
472
 
467
- s1 = sv[0, 1].to_i if sv
473
+ s1 = cs[0, 1].to_i if cs
468
474
  v1 = s1 + r1
469
475
  v1 << e['status'][0, 1].to_i if e['status']
470
476
 
471
477
  if v1 > 0
472
- # Status or SMTP reply code exists
473
- # Set pseudo DSN into the value of "status" accessor
474
- e['status'] = sv if r1 > 0
478
+ # Status or SMTP reply code exists, Set pseudo DSN into the value of "status" accessor
479
+ e['status'] = cs if r1 > 0
475
480
  else
476
481
  # Neither Status nor SMTP reply code exist
477
- sv = if %w[expired mailboxfull].include?(e['reason'])
482
+ cs = if %w[expired mailboxfull].include?(e['reason'])
478
483
  # Set pseudo DSN (temporary error)
479
484
  Sisimai::SMTP::Status.code(e['reason'], true)
480
485
  else
@@ -482,10 +487,10 @@ module Sisimai::Lhost
482
487
  Sisimai::SMTP::Status.code(e['reason'], false)
483
488
  end
484
489
  end
485
- e['status'] ||= sv.to_s
490
+ e['status'] ||= cs.to_s
486
491
  end
487
492
 
488
- return { 'ds' => dscontents, 'rfc822' => emailsteak[1] }
493
+ return { 'ds' => dscontents, 'rfc822' => emailparts[1] }
489
494
  end
490
495
  def description; return 'Exim'; end
491
496
  end